- misc fixes to client server handshaking and playsim ticking

This commit is contained in:
Magnus Norddahl 2018-11-11 10:13:57 +01:00
parent beed2817f2
commit 6827e56a4e
10 changed files with 211 additions and 105 deletions

View file

@ -986,11 +986,6 @@ public:
return LastTic;
}
int BaseMakeTic() const
{
return LastTic + 1;
}
private:
int LastTic = 0;
int CurrentTic = 0;
@ -1038,7 +1033,7 @@ public:
for (int i = 0; i < tics; i++)
{
network->SetCurrentTic(gametime.BaseGameTic() + i, gametime.BaseMakeTic() + i);
network->SetCurrentTic(gametime.BaseGameTic() + i);
network->WriteLocalInput(G_BuildTiccmd());
if (advancedemo)
@ -1095,6 +1090,8 @@ void D_DoomLoop()
if (wantToRestart)
{
P_UnPredictPlayer();
wantToRestart = false;
return;
}

View file

@ -228,45 +228,31 @@ int DoomComImpl::FindNode(const sockaddr_in *address)
void DoomComImpl::PacketSend(const NetPacket &packet)
{
int c;
// FIXME: Catch this before we've overflown the buffer. With long chat
// text and lots of backup tics, it could conceivably happen. (Though
// apparently it hasn't yet, which is good.)
if (packet.size > MAX_MSGLEN)
{
I_FatalError("Netbuffer overflow!");
}
assert(!(packet.data[0] & NCMD_COMPRESSED));
uLong size = TRANSMIT_SIZE - 1;
if (packet.size >= 10)
{
mTransmitBuffer[0] = packet.data[0] | NCMD_COMPRESSED;
c = compress2(mTransmitBuffer + 1, &size, packet.data + 1, packet.size - 1, 9);
uLong size = TRANSMIT_SIZE - 1;
int c = compress2(mTransmitBuffer + 1, &size, packet.data + 1, packet.size - 1, 9);
size += 1;
if (c == Z_OK && size < (uLong)packet.size)
{
sendto(mSocket, (char *)mTransmitBuffer, size, 0, (sockaddr *)&mNodeEndpoints[packet.node], sizeof(mNodeEndpoints[packet.node]));
return;
}
}
if (packet.size <= TRANSMIT_SIZE)
{
sendto(mSocket, (char *)packet.data, packet.size, 0, (sockaddr *)&mNodeEndpoints[packet.node], sizeof(mNodeEndpoints[packet.node]));
}
else
{
c = -1; // Just some random error code to avoid sending the compressed buffer.
I_Error("NetPacket is too large to be transmitted");
}
if (c == Z_OK && size < (uLong)packet.size)
{
c = sendto(mSocket, (char *)mTransmitBuffer, size, 0, (sockaddr *)&mNodeEndpoints[packet.node], sizeof(mNodeEndpoints[packet.node]));
}
else
{
if (packet.size > TRANSMIT_SIZE)
{
I_Error("Net compression failed (zlib error %d)", c);
}
else
{
c = sendto(mSocket, (char *)packet.data, packet.size, 0, (sockaddr *)&mNodeEndpoints[packet.node], sizeof(mNodeEndpoints[packet.node]));
}
}
// if (c == -1)
// I_Error ("SendPacket error: %s",strerror(errno));
}
void DoomComImpl::PacketGet(NetPacket &packet)
@ -303,7 +289,8 @@ void DoomComImpl::PacketGet(NetPacket &packet)
Close(node);
packet.node = node;
packet.size = 0;
packet.stream.pbStreamEnd = packet.stream.pbStream;
packet.stream.pbStream = packet.data;
packet.stream.pbStreamEnd = packet.data + packet.size;
return;
}
else if (err != WSAEWOULDBLOCK)
@ -314,7 +301,8 @@ void DoomComImpl::PacketGet(NetPacket &packet)
{
packet.node = -1;
packet.size = 0;
packet.stream.pbStreamEnd = packet.stream.pbStream;
packet.stream.pbStream = packet.data;
packet.stream.pbStreamEnd = packet.data + packet.size;
return;
}
}
@ -325,7 +313,7 @@ void DoomComImpl::PacketGet(NetPacket &packet)
continue;
packet.data[0] = mTransmitBuffer[0] & ~NCMD_COMPRESSED;
if (mTransmitBuffer[0] & NCMD_COMPRESSED)
if ((mTransmitBuffer[0] & NCMD_COMPRESSED) && size > 1)
{
uLongf msgsize = MAX_MSGLEN - 1;
int err = uncompress(packet.data + 1, &msgsize, mTransmitBuffer + 1, size - 1);
@ -343,7 +331,8 @@ void DoomComImpl::PacketGet(NetPacket &packet)
packet.node = node;
packet.size = (short)size;
packet.stream.pbStreamEnd = packet.stream.pbStream + packet.size;
packet.stream.pbStream = packet.data;
packet.stream.pbStreamEnd = packet.data + packet.size;
return;
}
}

View file

@ -31,6 +31,9 @@ struct NetPacket
const uint8_t &operator[](int i) const { return data[i]; }
BYTESTREAM_s stream;
NetPacket(const NetPacket &) = delete;
NetPacket &operator=(const NetPacket &) = delete;
};
// Network packet data.

View file

@ -63,8 +63,8 @@ public:
// Check for incoming packets
virtual void Update() = 0;
// Set current tic for reading and writing
virtual void SetCurrentTic(int receivetic, int sendtic) = 0;
// Set current tic time
virtual void SetCurrentTic(int localtic) = 0;
// Send any pending outgoing data
virtual void EndCurrentTic() = 0;

View file

@ -82,6 +82,7 @@ NetClient::NetClient(FString server)
packet.node = mServerNode;
NetCommand cmd ( NetPacketType::ConnectRequest );
cmd.addString("ZDoom Connect Request");
cmd.writeCommandToPacket ( packet );
mComm->PacketSend(packet);
@ -149,26 +150,62 @@ void NetClient::Update()
}
}
void NetClient::SetCurrentTic(int receivetic, int sendtic)
void NetClient::SetCurrentTic(int tictime)
{
gametic = receivetic;
mSendTic = sendtic;
gametic = tictime;
mSendTic = gametic + 10;
int jitter = 2;
if (mLastReceivedTic == -1)
{
mServerTic = 0;
}
else
{
if (mServerTicDelta == -1 || std::abs(gametic + mServerTicDelta - mLastReceivedTic) > jitter)
{
//Printf("netcable icon! ;)\n");
mServerTicDelta = mLastReceivedTic - gametic - jitter;
}
mServerTic = MAX(gametic + mServerTicDelta, 0);
}
mCurrentInput[consoleplayer] = mSentInput[gametic % BACKUPTICS];
}
void NetClient::EndCurrentTic()
{
mCurrentCommands = mSendCommands;
mSendCommands.Clear();
if (mStatus != NodeStatus::InGame)
return;
int targettic = (mSendTic + mServerTicDelta);
NetPacket packet;
packet.node = mServerNode;
NetCommand cmd ( NetPacketType::Tic );
cmd.addByte ( 0 ); // target gametic
cmd.addBuffer ( &mCurrentInput[consoleplayer].ucmd, sizeof(usercmd_t) );
cmd.addByte (targettic); // target gametic
cmd.addBuffer ( &mSentInput[(mSendTic - 1) % BACKUPTICS].ucmd, sizeof(usercmd_t) );
cmd.writeCommandToPacket ( packet );
mComm->PacketSend(packet);
mCurrentCommands = mSendCommands;
mSendCommands.Clear();
TicUpdate &update = mTicUpdates[mServerTic % BACKUPTICS];
if (update.received)
{
if (playeringame[consoleplayer] && players[consoleplayer].mo)
{
players[consoleplayer].mo->SetXYZ(update.x, update.y, update.z);
players[consoleplayer].mo->Angles.Yaw = update.yaw;
players[consoleplayer].mo->Angles.Pitch = update.pitch;
}
update.received = false;
}
}
int NetClient::GetSendTick() const
@ -196,8 +233,7 @@ void NetClient::RunCommands(int player)
void NetClient::WriteLocalInput(ticcmd_t cmd)
{
mCurrentInput[consoleplayer] = cmd;
mSentInput[gametic % BACKUPTICS] = cmd;
mSentInput[(mSendTic - 1) % BACKUPTICS] = cmd;
}
void NetClient::WriteBotInput(int player, const ticcmd_t &cmd)
@ -284,25 +320,42 @@ void NetClient::OnDisconnect(const NetPacket &packet)
mStatus = NodeStatus::Closed;
}
void NetClient::UpdateLastReceivedTic(int tic)
{
if (mLastReceivedTic != -1)
{
int delta = tic - (mLastReceivedTic & 0xff);
if (delta > 128) delta -= 256;
else if (delta < -128) delta += 256;
mLastReceivedTic += delta;
}
else
{
mLastReceivedTic = tic;
}
mLastReceivedTic = MAX(mLastReceivedTic, 0);
}
void NetClient::OnTic(NetPacket &packet)
{
int tic = packet.stream.ReadByte();
float x = packet.stream.ReadFloat();
float y = packet.stream.ReadFloat();
float z = packet.stream.ReadFloat();
float yaw = packet.stream.ReadFloat();
float pitch = packet.stream.ReadFloat();
UpdateLastReceivedTic(packet.stream.ReadByte());
if (playeringame[consoleplayer] && players[consoleplayer].mo)
{
players[consoleplayer].mo->SetXYZ(x, y, z);
players[consoleplayer].mo->Angles.Yaw = yaw;
players[consoleplayer].mo->Angles.Pitch = pitch;
}
TicUpdate update;
update.received = true;
update.x = packet.stream.ReadFloat();
update.y = packet.stream.ReadFloat();
update.z = packet.stream.ReadFloat();
update.yaw = packet.stream.ReadFloat();
update.pitch = packet.stream.ReadFloat();
mTicUpdates[mLastReceivedTic % BACKUPTICS] = update;
}
void NetClient::OnSpawnPlayer(NetPacket &packet)
{
// To do: this needs a tic and should be inserted in mTicUpdates.
// Otherwise it might not arrive at the intended moment in time.
int player = packet.stream.ReadByte();
const float x = packet.stream.ReadFloat();
const float y = packet.stream.ReadFloat();
@ -320,7 +373,7 @@ void NetClient::OnSpawnPlayer(NetPacket &packet)
// This player is now in the game.
playeringame[player] = true;
player_t p = players[player];
player_t &p = players[player];
if ( cl_showspawnnames )
Printf ( "Spawning player %d at %f,%f,%f (id %d)\n", player, x, y, z, netID );

View file

@ -30,7 +30,7 @@ public:
void Update() override;
void SetCurrentTic(int receivetic, int sendtic) override;
void SetCurrentTic(int tictime) override;
void EndCurrentTic() override;
int GetSendTick() const override;
@ -56,11 +56,28 @@ private:
void OnTic(NetPacket &packet);
void OnSpawnPlayer(NetPacket &packet);
void UpdateLastReceivedTic(int tic);
std::unique_ptr<doomcom_t> mComm;
int mServerNode = -1;
int mPlayer = -1;
NodeStatus mStatus = NodeStatus::Closed;
int mSendTic = 0;
int mServerTic = 0;
int mServerTicDelta = -1;
int mLastReceivedTic = -1;
struct TicUpdate
{
bool received = false;
float x;
float y;
float z;
float yaw;
float pitch;
};
TicUpdate mTicUpdates[BACKUPTICS];
ticcmd_t mCurrentInput[MAXPLAYERS];
ticcmd_t mSentInput[BACKUPTICS];

View file

@ -76,16 +76,17 @@ NetServer::NetServer()
void NetServer::Update()
{
// Read all packets currently available from clients
while (true)
{
NetPacket packet;
mComm->PacketGet(packet);
if (packet.node == -1)
break;
break; // No more packets. We are done.
NetNode &node = mNodes[packet.node];
if (packet.size == 0)
if (packet.size == 0) // Connection to node closed (timed out)
{
OnClose(node, packet);
}
@ -108,15 +109,27 @@ void NetServer::Update()
case NetPacketType::Tic: OnTic(node, packet); break;
}
}
break;
}
}
}
void NetServer::SetCurrentTic(int receivetic, int sendtic)
void NetServer::SetCurrentTic(int tictime)
{
gametic = receivetic;
mSendTic = sendtic;
gametic = tictime;
for (int i = 0; i < MAXNETNODES; i++)
{
NetNode &node = mNodes[i];
if (node.Status == NodeStatus::InGame && node.Player != -1)
{
NetNode::TicUpdate &update = node.TicUpdates[gametic % BACKUPTICS];
if (update.received)
{
mCurrentInput[node.Player].ucmd = update.input;
update.received = false;
}
}
}
}
void NetServer::EndCurrentTic()
@ -160,7 +173,7 @@ void NetServer::EndCurrentTic()
int NetServer::GetSendTick() const
{
return mSendTic;
return gametic;
}
ticcmd_t NetServer::GetPlayerInput(int player) const
@ -223,7 +236,7 @@ void NetServer::OnClose(NetNode &node, const NetPacket &packet)
{
if (node.Status == NodeStatus::InGame)
{
Printf("Player %d left the server", node.Player);
Printf("Player %d left the server\n", node.Player);
playeringame[node.Player] = false;
players[node.Player].settings_controller = false;
@ -234,29 +247,49 @@ void NetServer::OnClose(NetNode &node, const NetPacket &packet)
mComm->Close(packet.node);
}
void NetServer::OnConnectRequest(NetNode &node, const NetPacket &packet)
void NetServer::OnConnectRequest(NetNode &node, NetPacket &packet)
{
// Search for a spot in the player list
if (node.Status != NodeStatus::InGame)
// Make the initial connect packet a bit more complex than a bunch of zeros..
if (strcmp(packet.stream.ReadString(), "ZDoom Connect Request") != 0)
{
for (int i = 0; i < MAXPLAYERS; i++)
if (node.Status == NodeStatus::InGame)
{
if (!playeringame[i])
{
node.Player = i;
playeringame[i] = true;
players[i].settings_controller = false;
break;
}
Printf("Junk data received from a joined player\n");
}
else
{
node.Status = NodeStatus::Closed;
mComm->Close(packet.node);
return;
}
}
if (node.Status == NodeStatus::InGame)
return;
// Search for a spot in the player list
for (int i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
{
node.Player = i;
break;
}
}
if (node.Player != -1) // Join accepted.
{
Printf("Player %d joined the server", node.Player);
Printf("Player %d joined the server\n", node.Player);
for (int i = 0; i < BACKUPTICS; i++)
node.TicUpdates[i].received = false;
node.Status = NodeStatus::InGame;
mNodeForPlayer[node.Player] = packet.node;
playeringame[node.Player] = true;
players[node.Player].settings_controller = false;
NetPacket response;
response.node = packet.node;
@ -267,8 +300,6 @@ void NetServer::OnConnectRequest(NetNode &node, const NetPacket &packet)
mComm->PacketSend(response);
node.Status = NodeStatus::InGame;
FullUpdate ( node );
}
else // Server is full.
@ -294,7 +325,7 @@ void NetServer::OnDisconnect(NetNode &node, const NetPacket &packet)
{
if (node.Status == NodeStatus::InGame)
{
Printf("Player %d left the server", node.Player);
Printf("Player %d left the server\n", node.Player);
playeringame[node.Player] = false;
players[node.Player].settings_controller = false;
@ -307,16 +338,29 @@ void NetServer::OnDisconnect(NetNode &node, const NetPacket &packet)
void NetServer::OnTic(NetNode &node, NetPacket &packet)
{
if (node.Status == NodeStatus::InGame)
if (node.Status != NodeStatus::InGame)
return;
int tic = packet.stream.ReadByte();
int delta = tic - (gametic & 0xff);
if (delta > 128) delta -= 256;
else if (delta < -128) delta += 256;
tic = gametic + delta;
if (tic <= gametic)
{
/* gametic */ packet.stream.ReadByte();
packet.stream.ReadBuffer ( &mCurrentInput[node.Player].ucmd, sizeof(usercmd_t));
}
else
{
node.Status = NodeStatus::Closed;
mComm->Close(packet.node);
// Packet arrived too late.
tic = gametic + 1;
if (tic < 0 || node.TicUpdates[tic % BACKUPTICS].received)
return; // We already received the proper packet.
}
NetNode::TicUpdate update;
update.received = true;
packet.stream.ReadBuffer(&update.input, sizeof(usercmd_t));
node.TicUpdates[tic % BACKUPTICS] = update;
}
void NetServer::CmdSpawnPlayer(NetNode &node, int player)

View file

@ -38,7 +38,13 @@ struct NetNode
int Gametic = 0;
int Player = -1;
ticcmd_t PlayerMovement;
struct TicUpdate
{
bool received = false;
usercmd_t input;
};
TicUpdate TicUpdates[BACKUPTICS];
FDynamicBuffer Commands; // "NetSpecs"
};
@ -49,7 +55,7 @@ public:
void Update() override;
void SetCurrentTic(int receivetic, int sendtic) override;
void SetCurrentTic(int tictime) override;
void EndCurrentTic() override;
int GetSendTick() const override;
@ -70,7 +76,7 @@ public:
private:
void OnClose(NetNode &node, const NetPacket &packet);
void OnConnectRequest(NetNode &node, const NetPacket &packet);
void OnConnectRequest(NetNode &node, NetPacket &packet);
void OnDisconnect(NetNode &node, const NetPacket &packet);
void OnTic(NetNode &node, NetPacket &packet);
@ -80,7 +86,6 @@ private:
std::unique_ptr<doomcom_t> mComm;
NetNode mNodes[MAXNETNODES];
int mNodeForPlayer[MAXPLAYERS];
int mSendTic = 0;
ticcmd_t mCurrentInput[MAXPLAYERS];
FDynamicBuffer mCurrentCommands;

View file

@ -82,10 +82,9 @@ void NetSinglePlayer::Update()
{
}
void NetSinglePlayer::SetCurrentTic(int receivetic, int sendtic)
void NetSinglePlayer::SetCurrentTic(int tictime)
{
gametic = receivetic;
mSendTic = sendtic;
gametic = tictime;
}
void NetSinglePlayer::EndCurrentTic()
@ -96,7 +95,7 @@ void NetSinglePlayer::EndCurrentTic()
int NetSinglePlayer::GetSendTick() const
{
return mSendTic;
return gametic;
}
ticcmd_t NetSinglePlayer::GetPlayerInput(int player) const

View file

@ -30,7 +30,7 @@ public:
void Update() override;
void SetCurrentTic(int receivetic, int sendtic) override;
void SetCurrentTic(int tictime) override;
void EndCurrentTic() override;
int GetSendTick() const override;
@ -53,6 +53,5 @@ private:
ticcmd_t mCurrentInput[MAXPLAYERS];
FDynamicBuffer mCurrentCommands;
int mSendTic = 0;
FDynamicBuffer mSendCommands;
};