diff --git a/src/d_main.cpp b/src/d_main.cpp index cd7e710d16..dc38d11bd5 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -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; } diff --git a/src/network/i_net.cpp b/src/network/i_net.cpp index 997aa006fb..5d2a5abfe4 100644 --- a/src/network/i_net.cpp +++ b/src/network/i_net.cpp @@ -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; } } diff --git a/src/network/i_net.h b/src/network/i_net.h index b68b42aec6..72b4670119 100644 --- a/src/network/i_net.h +++ b/src/network/i_net.h @@ -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. diff --git a/src/network/net.h b/src/network/net.h index dff885d506..594a5bdf5d 100644 --- a/src/network/net.h +++ b/src/network/net.h @@ -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; diff --git a/src/network/netclient.cpp b/src/network/netclient.cpp index 875a5e26c1..b043f322fb 100644 --- a/src/network/netclient.cpp +++ b/src/network/netclient.cpp @@ -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 ); diff --git a/src/network/netclient.h b/src/network/netclient.h index 96387102dc..6295280bb5 100644 --- a/src/network/netclient.h +++ b/src/network/netclient.h @@ -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 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]; diff --git a/src/network/netserver.cpp b/src/network/netserver.cpp index 5aba8df2ac..f490cf1f44 100644 --- a/src/network/netserver.cpp +++ b/src/network/netserver.cpp @@ -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) diff --git a/src/network/netserver.h b/src/network/netserver.h index f37070621a..f2584c2f59 100644 --- a/src/network/netserver.h +++ b/src/network/netserver.h @@ -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 mComm; NetNode mNodes[MAXNETNODES]; int mNodeForPlayer[MAXPLAYERS]; - int mSendTic = 0; ticcmd_t mCurrentInput[MAXPLAYERS]; FDynamicBuffer mCurrentCommands; diff --git a/src/network/netsingle.cpp b/src/network/netsingle.cpp index c7ce67947f..9bc337c9fc 100644 --- a/src/network/netsingle.cpp +++ b/src/network/netsingle.cpp @@ -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 diff --git a/src/network/netsingle.h b/src/network/netsingle.h index c08dd6b1f8..884abf9794 100644 --- a/src/network/netsingle.h +++ b/src/network/netsingle.h @@ -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; };