diff --git a/include/server/state/GameStateHandler.h b/include/server/state/GameStateHandler.h index afc202f..011caa4 100644 --- a/include/server/state/GameStateHandler.h +++ b/include/server/state/GameStateHandler.h @@ -19,6 +19,7 @@ class GameStateHandler : public protocol::PacketHandler { virtual void Handle(const protocol::packets::SpawnTroopPacket& a_Packet) override; virtual void Handle(const protocol::packets::PlaceTowerPacket& a_Packet) override; + virtual void Handle(const protocol::packets::LockStepRequestPacket& a_Packet) override; }; } // namespace server diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index 4e3ac86..b461f83 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -110,6 +110,14 @@ struct PlaceTower { TowerCoords m_Position; }; +struct LockStepRequest { + std::vector m_Missing; +}; + +struct LockStepResponse { + std::map m_Steps; +}; + } // namespace pdata } // namespace protocol } // namespace td diff --git a/include/td/protocol/packet/Packets.h b/include/td/protocol/packet/Packets.h index 0fce78f..e4ecb34 100644 --- a/include/td/protocol/packet/Packets.h +++ b/include/td/protocol/packet/Packets.h @@ -22,6 +22,8 @@ enum class PacketID : std::uint8_t { BeginGame, Disconnect, KeepAlive, + LockStepRequest, + LockStepResponse, LockSteps, LoggingSuccess, PlaceTower, @@ -49,6 +51,8 @@ using BeginGamePacket = PacketMessage; using ChatMessagePacket = PacketMessage; using DisconnectPacket = PacketMessage; using KeepAlivePacket = PacketMessage; +using LockStepRequestPacket = PacketMessage; +using LockStepResponsePacket = PacketMessage; using LockStepsPacket = PacketMessage; using LoggingSuccessPacket = PacketMessage; using PlaceTowerPacket = PacketMessage; @@ -63,7 +67,7 @@ using WorldDataPacket = PacketMessage; } // namespace packets using AllPackets = std::tuple; diff --git a/include/td/simulation/ClientSimulation.h b/include/td/simulation/ClientSimulation.h index d445ab2..3f84b53 100644 --- a/include/td/simulation/ClientSimulation.h +++ b/include/td/simulation/ClientSimulation.h @@ -2,6 +2,7 @@ #include #include +#include namespace td { namespace sim { @@ -17,16 +18,18 @@ class ClientSimulation : public protocol::PacketHandler { GameBuffer m_History; std::uint64_t m_CurrentTime; std::uint64_t m_LastTime; - std::size_t m_CurrentStep; + StepTime m_CurrentStep; std::shared_ptr m_LastSnapshot; - std::uint64_t m_LastValidStep; + StepTime m_LastValidStep; static const protocol::LockStep EMPTY_LOCKSTEP; using protocol::PacketHandler::Handle; public: + utils::Signal&> OnMissingLockSteps; + /** * \brief Replay constructor * \param a_StepTime in ms @@ -45,15 +48,18 @@ class ClientSimulation : public protocol::PacketHandler { float Update(); virtual void Handle(const protocol::packets::LockStepsPacket& a_LockSteps) override; - virtual void Handle(const protocol::packets::PredictCommandPacket& a_Predict) override; + virtual void Handle(const protocol::packets::LockStepResponsePacket& a_LockSteps) override; private: - void Step(); + /** + * \returns false if the empty lockstep was used + */ + bool Step(); /** * \brief Ticks a_Count times */ - void FastForward(std::size_t a_Count); + std::size_t FastForward(std::size_t a_Count); /** * \brief Tries to recompute simulation if needed (for example in late command receival) diff --git a/include/td/simulation/ServerSimulation.h b/include/td/simulation/ServerSimulation.h index 599041f..61af852 100644 --- a/include/td/simulation/ServerSimulation.h +++ b/include/td/simulation/ServerSimulation.h @@ -31,6 +31,8 @@ class ServerSimulation : public protocol::CommandHandler { virtual void Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) override; virtual void Handle(const protocol::commands::PlaceTowerCommand& a_PlaceTower) override; + + protocol::packets::LockStepResponsePacket GetResponse(const protocol::packets::LockStepRequestPacket& a_Missing) const; }; } // namespace sim diff --git a/src/main.cpp b/src/main.cpp index 72ad7fb..6bc672f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,8 +49,8 @@ class ClientHandler : public td::protocol::PacketHandler { m_Simulation.Handle(a_LockStep); } - void Handle(const td::protocol::packets::PredictCommandPacket& a_Predict) { - m_Simulation.Handle(a_Predict); + void Handle(const td::protocol::packets::LockStepResponsePacket& a_LockStep) { + m_Simulation.Handle(a_LockStep); } }; @@ -125,6 +125,10 @@ int main(int argc, char** argv) { td::sim::ClientSimulation simulation(*clientWorld, td::STEP_TIME); ClientHandler clientHandler(simulation); + simulation.OnMissingLockSteps.Connect([&fakeSocket](const std::vector& a_MissingSteps){ + fakeSocket->OnReceive(0, td::protocol::packets::LockStepRequestPacket(a_MissingSteps)); + }); + // temporary tests display.OnKeyDown.Connect([&fakeSocket](SDL_Keycode key) { if (key == SDLK_A) { @@ -134,6 +138,7 @@ int main(int argc, char** argv) { } }); + // make a fake player join fakeSocket->ConnectFakePeer(0); // packets from the server to the client diff --git a/src/server/state/GameStateHandler.cpp b/src/server/state/GameStateHandler.cpp index 3b0e9fc..525a728 100644 --- a/src/server/state/GameStateHandler.cpp +++ b/src/server/state/GameStateHandler.cpp @@ -20,5 +20,9 @@ void GameStateHandler::Handle(const protocol::packets::PlaceTowerPacket& a_Packe m_GameState.m_Simulation.Handle(place); } +void GameStateHandler::Handle(const protocol::packets::LockStepRequestPacket& a_Packet) { + m_GameState.SendPacket(m_PlayerId, m_GameState.m_Simulation.GetResponse(a_Packet)); +} + } // namespace server } // namespace td diff --git a/src/td/simulation/ClientSimulation.cpp b/src/td/simulation/ClientSimulation.cpp index e8045e6..8075355 100644 --- a/src/td/simulation/ClientSimulation.cpp +++ b/src/td/simulation/ClientSimulation.cpp @@ -4,6 +4,8 @@ #include +// TODO: mutex this bad boy + namespace td { namespace sim { @@ -37,9 +39,7 @@ ClientSimulation::ClientSimulation(game::World& a_World, std::uint64_t a_StepTim m_LastTime(GetTime()), m_CurrentStep(0), m_LastSnapshot(std::make_shared()), - m_LastValidStep(0) { - // Step(); -} + m_LastValidStep(0) {} float ClientSimulation::Update() { // TODO: handle freezes (m_CurrentTime > 2 * m_StepTime) @@ -52,15 +52,20 @@ float ClientSimulation::Update() { return (float)m_CurrentTime / (float)m_StepTime; } -void ClientSimulation::Step() { +bool ClientSimulation::Step() { const auto& step = m_History[m_CurrentStep]; if (step.has_value()) { - m_LastSnapshot = m_World.Tick(step.value(), FpFloat(m_StepTime) / FpFloat(1000)); - m_LastValidStep = m_CurrentStep; + auto snapshot = m_World.Tick(step.value(), FpFloat(m_StepTime) / FpFloat(1000)); + if (m_LastValidStep + 1 == m_CurrentStep) { + m_LastValidStep = m_CurrentStep; + m_LastSnapshot = snapshot; + } } else { m_World.Tick(EMPTY_LOCKSTEP, FpFloat(m_StepTime) / FpFloat(1000)); + std::cout << "Empty tick (" << m_CurrentStep << ") !\n"; } m_CurrentStep++; + return step.has_value(); } void ClientSimulation::Handle(const protocol::packets::LockStepsPacket& a_LockSteps) { @@ -68,16 +73,32 @@ void ClientSimulation::Handle(const protocol::packets::LockStepsPacket& a_LockSt for (std::size_t i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { m_History[a_LockSteps->m_FirstFrameNumber + i] = steps[i]; } + + // to be in the state [0-1] + if (a_LockSteps->m_FirstFrameNumber == 0) + Step(); + FastReplay(); } -void ClientSimulation::Handle(const protocol::packets::PredictCommandPacket& a_Predict) {} - -void ClientSimulation::FastForward(std::size_t a_Count) { - for (std::size_t i = 0; i < a_Count; i++) { - Step(); +void ClientSimulation::Handle(const protocol::packets::LockStepResponsePacket& a_LockSteps) { + for (const auto& [stepTime, lockStep] : a_LockSteps->m_Steps) { + m_History[stepTime] = lockStep; } - std::cout << "Was behind " << a_Count << " ticks !\n"; + FastReplay(); +} + +std::size_t ClientSimulation::FastForward(std::size_t a_Count) { + std::size_t stepCount; + bool hasMissingStep = false; + for (std::size_t i = 0; i < a_Count; i++) { + if (!Step() && !hasMissingStep) { + hasMissingStep = true; + stepCount = i + 1; + } + } + std::cout << "Was behind " << (hasMissingStep ? stepCount : a_Count) << " ticks !\n"; + return hasMissingStep ? stepCount : a_Count; } void ClientSimulation::FastReplay() { @@ -86,11 +107,21 @@ void ClientSimulation::FastReplay() { m_World.ResetSnapshots(m_LastSnapshot, m_LastSnapshot); - // TODO: cover holes const std::size_t stepCount = m_CurrentStep - m_LastValidStep; m_CurrentStep = m_LastValidStep; - FastForward(stepCount); + if (FastForward(stepCount) == stepCount) + return; + + std::vector missingSteps; + missingSteps.reserve(stepCount); + + for (StepTime step = m_LastValidStep + 1; step < m_CurrentStep; step++) { + if (!m_History[step].has_value()) + missingSteps.push_back(step); + } + + OnMissingLockSteps(missingSteps); } } // namespace sim diff --git a/src/td/simulation/ServerSimulation.cpp b/src/td/simulation/ServerSimulation.cpp index bde94dd..311dff8 100644 --- a/src/td/simulation/ServerSimulation.cpp +++ b/src/td/simulation/ServerSimulation.cpp @@ -36,5 +36,17 @@ void ServerSimulation::Handle(const protocol::commands::PlaceTowerCommand& a_Pla AddToCommandHistory(m_History[m_CurrentTime + LOCKSTEP_BUFFER_SIZE], a_PlaceTower); } +protocol::packets::LockStepResponsePacket ServerSimulation::GetResponse( + const protocol::packets::LockStepRequestPacket& a_Missing) const { + std::map result; + + for (StepTime step : a_Missing->m_Missing) { + result.emplace(step, m_History[step]); + } + + return {result}; +} + + } // namespace sim } // namespace td