From cba790f804e0ea293ef2fd8f23088e184319bbef Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Sat, 9 Aug 2025 11:23:17 +0200 Subject: [PATCH] add StateMachine --- include/client/Client.h | 20 ++++-------- include/client/ClientState.h | 20 ++++++++++++ include/client/IClientState.h | 33 -------------------- include/client/state/GameState.h | 9 +++--- include/server/IServerState.h | 36 ---------------------- include/server/Server.h | 21 ++++--------- include/server/ServerState.h | 23 ++++++++++++++ include/server/state/EndGameState.h | 4 +-- include/server/state/GameState.h | 11 ++----- include/server/state/LobbyState.h | 8 ++--- include/td/common/StateMachine.h | 39 ++++++++++++++++++++++++ include/td/simulation/ClientSimulation.h | 5 ++- src/client/Client.cpp | 15 --------- src/client/ClientState.cpp | 16 ++++++++++ src/client/IClientState.cpp | 27 ---------------- src/client/state/GameState.cpp | 3 +- src/main.cpp | 24 ++++++++++----- src/server/IServerState.cpp | 33 -------------------- src/server/Server.cpp | 15 --------- src/server/ServerState.cpp | 24 +++++++++++++++ src/server/state/GameState.cpp | 8 +---- src/server/state/LobbyState.cpp | 10 +----- src/td/simulation/ClientSimulation.cpp | 14 ++++----- 23 files changed, 174 insertions(+), 244 deletions(-) create mode 100644 include/client/ClientState.h delete mode 100644 include/client/IClientState.h delete mode 100644 include/server/IServerState.h create mode 100644 include/server/ServerState.h create mode 100644 include/td/common/StateMachine.h create mode 100644 src/client/ClientState.cpp delete mode 100644 src/client/IClientState.cpp delete mode 100644 src/server/IServerState.cpp create mode 100644 src/server/ServerState.cpp diff --git a/include/client/Client.h b/include/client/Client.h index 470d9ea..f1c18e8 100644 --- a/include/client/Client.h +++ b/include/client/Client.h @@ -1,29 +1,21 @@ #pragma once #include -#include -#include +#include namespace td { namespace client { -class Client { +class ClientState; + +class Client : public StateMachine { private: std::shared_ptr m_Socket; - std::shared_ptr m_State; - std::chrono::time_point m_LastTime; public: - Client(const std::shared_ptr& a_Socket) : m_Socket(a_Socket), m_LastTime(std::chrono::system_clock::now()) {} + Client(const std::shared_ptr& a_Socket) : m_Socket(a_Socket) {} - void Update(); - - void UpdateState(const std::shared_ptr& a_State); - - private: - void Update(float a_Delta); - - friend class IClientState; + friend class ClientState; }; } // namespace client diff --git a/include/client/ClientState.h b/include/client/ClientState.h new file mode 100644 index 0000000..f97d613 --- /dev/null +++ b/include/client/ClientState.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace td { +namespace client { + +class ClientState : public Client::State, private utils::SlotGuard { + public: + ClientState(Client& a_Client); + virtual ~ClientState() {} + + protected: + void SendPacket(const protocol::PacketBase& a_Packet); + virtual void HandlePacket(const protocol::PacketBase& a_Packet) {} +}; + +} // namespace server +} // namespace td diff --git a/include/client/IClientState.h b/include/client/IClientState.h deleted file mode 100644 index f805015..0000000 --- a/include/client/IClientState.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace client { - -class Client; - -class IClientState : private utils::SlotGuard { - public: - virtual void HandlePacket(const protocol::PacketBase& a_Packet) = 0; - virtual void Update(float a_Delta) = 0; - - IClientState(); - virtual ~IClientState(); - - protected: - void SendPacket(const protocol::PacketBase& a_Packet); - void SetNewState(const std::shared_ptr& a_NewState); - virtual void Init() {} - - private: - Client* m_Client; - - void SetClient(Client* a_Client); - - friend class Client; -}; - -} // namespace server -} // namespace td diff --git a/include/client/state/GameState.h b/include/client/state/GameState.h index 36baef2..3f484bf 100644 --- a/include/client/state/GameState.h +++ b/include/client/state/GameState.h @@ -1,25 +1,24 @@ #pragma once -#include +#include #include #include namespace td { namespace client { -class GameState : public IClientState { +class GameState : public ClientState { private: std::shared_ptr m_World; public: - GameState(const std::shared_ptr& a_World); + GameState(Client& a_Client, const std::shared_ptr& a_World); ~GameState() {} - virtual void HandlePacket(const protocol::PacketBase& a_Packet) override; virtual void Update(float a_Delta) override; protected: - virtual void Init() override; + virtual void HandlePacket(const protocol::PacketBase& a_Packet) override; }; } // namespace client diff --git a/include/server/IServerState.h b/include/server/IServerState.h deleted file mode 100644 index e79a5bf..0000000 --- a/include/server/IServerState.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace server { - -class Server; - -class IServerState : private utils::SlotGuard { - public: - virtual void HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) = 0; - virtual void Update(float a_Delta) = 0; - virtual void OnPlayerJoin(PlayerID a_Id) = 0; - virtual void OnPlayerLeave(PlayerID a_Id) = 0; - - IServerState(); - virtual ~IServerState(); - - protected: - void SendPacket(PlayerID a_Id, const protocol::PacketBase& a_Packet); - void BroadcastPacket(const protocol::PacketBase& a_Packet); - void SetNewState(const std::shared_ptr& a_NewState); - virtual void Init() {} - - private: - Server* m_Server; - - void SetServer(Server* a_Server); - - friend class Server; -}; - -} // namespace server -} // namespace td diff --git a/include/server/Server.h b/include/server/Server.h index de8589f..a03fcd1 100644 --- a/include/server/Server.h +++ b/include/server/Server.h @@ -1,30 +1,21 @@ #pragma once -#include #include -#include -#include +#include namespace td { namespace server { -class Server { +class ServerState; + +class Server : public StateMachine { private: std::shared_ptr m_Socket; - std::shared_ptr m_State; - std::chrono::time_point m_LastTime; public: - Server(const std::shared_ptr& a_Socket) : m_Socket(a_Socket), m_LastTime(std::chrono::system_clock::now()) {} + Server(const std::shared_ptr& a_Socket) : m_Socket(a_Socket) {} - void Update(); - - void UpdateState(const std::shared_ptr& a_State); - - private: - void Update(float a_Delta); - - friend class IServerState; + friend class ServerState; }; } // namespace server diff --git a/include/server/ServerState.h b/include/server/ServerState.h new file mode 100644 index 0000000..ef08baf --- /dev/null +++ b/include/server/ServerState.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace td { +namespace server { + +class ServerState : public Server::State, private utils::SlotGuard { + public: + virtual void HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) = 0; + virtual void Update(float a_Delta) = 0; + + ServerState(Server& a_Server); + virtual ~ServerState(); + + protected: + void SendPacket(PlayerID a_Id, const protocol::PacketBase& a_Packet); + void BroadcastPacket(const protocol::PacketBase& a_Packet); +}; + +} // namespace server +} // namespace td diff --git a/include/server/state/EndGameState.h b/include/server/state/EndGameState.h index e38e2de..756baa0 100644 --- a/include/server/state/EndGameState.h +++ b/include/server/state/EndGameState.h @@ -1,11 +1,11 @@ #pragma once -#include +#include namespace td { namespace server { -class EndGameState : public IServerState { +class EndGameState : public ServerState { private: /* data */ public: diff --git a/include/server/state/GameState.h b/include/server/state/GameState.h index 83d8530..1b70ad2 100644 --- a/include/server/state/GameState.h +++ b/include/server/state/GameState.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -9,23 +9,18 @@ namespace server { class GameStateHandler; -class GameState : public IServerState { +class GameState : public ServerState { private: std::shared_ptr m_World; sim::ServerSimulation m_Simulation; float m_Time; public: - GameState(const std::shared_ptr& a_World); + GameState(Server& a_Server, const std::shared_ptr& a_World); ~GameState() {} virtual void HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) override; virtual void Update(float a_Delta) override; - virtual void OnPlayerJoin(PlayerID a_Id) override; - virtual void OnPlayerLeave(PlayerID a_Id) override; - - protected: - virtual void Init() override; friend class GameStateHandler; }; diff --git a/include/server/state/LobbyState.h b/include/server/state/LobbyState.h index e427139..e4ba6be 100644 --- a/include/server/state/LobbyState.h +++ b/include/server/state/LobbyState.h @@ -1,22 +1,20 @@ #pragma once -#include +#include namespace td { namespace server { // this class is temporary useless -class LobbyState : public IServerState { +class LobbyState : public ServerState { private: std::shared_ptr m_World; public: - LobbyState(const std::shared_ptr& a_World) : m_World(a_World) {} + LobbyState(Server& a_Server, const std::shared_ptr& a_World); ~LobbyState() {} virtual void HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) override; virtual void Update(float a_Delta) override; - virtual void OnPlayerJoin(PlayerID a_Id) override; - virtual void OnPlayerLeave(PlayerID a_Id) override; }; } // namespace server diff --git a/include/td/common/StateMachine.h b/include/td/common/StateMachine.h new file mode 100644 index 0000000..c17a84a --- /dev/null +++ b/include/td/common/StateMachine.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace td { + +template +class StateMachine { + public: + class State { + public: + State(TDerived& a_StateMachine) : m_StateMachine(a_StateMachine) {} + virtual ~State() {} + + virtual TReturn Update(TArgs... args) = 0; + + protected: + TDerived& m_StateMachine; + }; + + StateMachine() {} + virtual ~StateMachine() {} + + TReturn Update(TArgs... args) { + assert(m_State); + return m_State->Update(args...); + } + + template + void ChangeState(Args&&... args) { + m_State = std::make_unique(static_cast(*this), args...); + } + + private: + std::unique_ptr m_State; +}; + + +} // namespace td diff --git a/include/td/simulation/ClientSimulation.h b/include/td/simulation/ClientSimulation.h index 3f84b53..ca6388f 100644 --- a/include/td/simulation/ClientSimulation.h +++ b/include/td/simulation/ClientSimulation.h @@ -16,8 +16,7 @@ class ClientSimulation : public protocol::PacketHandler { std::uint64_t m_StepTime; game::World& m_World; GameBuffer m_History; - std::uint64_t m_CurrentTime; - std::uint64_t m_LastTime; + float m_CurrentTime; StepTime m_CurrentStep; std::shared_ptr m_LastSnapshot; @@ -45,7 +44,7 @@ class ClientSimulation : public protocol::PacketHandler { /** * \return the progress [0-1] between two steps */ - float Update(); + float Update(float a_Delta); virtual void Handle(const protocol::packets::LockStepsPacket& a_LockSteps) override; virtual void Handle(const protocol::packets::LockStepResponsePacket& a_LockSteps) override; diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 03f0d2c..277b956 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -3,22 +3,7 @@ namespace td { namespace client { -void Client::Update() { - auto timeElapsed = std::chrono::system_clock::now() - m_LastTime; - float timeSeconds = std::chrono::duration(timeElapsed).count(); - Update(timeSeconds); - m_LastTime = std::chrono::system_clock::now(); -} -void Client::UpdateState(const std::shared_ptr& a_State) { - m_State = a_State; - m_State->SetClient(this); -} - -void Client::Update(float a_Delta) { - assert(m_State); - m_State->Update(a_Delta); -} } // namespace client } // namespace td diff --git a/src/client/ClientState.cpp b/src/client/ClientState.cpp new file mode 100644 index 0000000..fdc2f56 --- /dev/null +++ b/src/client/ClientState.cpp @@ -0,0 +1,16 @@ +#include +#include + +namespace td { +namespace client { + +ClientState::ClientState(Client& a_Client) : Client::State(a_Client) { + Connect(m_StateMachine.m_Socket->OnReceive, std::bind(&ClientState::HandlePacket, this, std::placeholders::_1)); +} + +void ClientState::SendPacket(const protocol::PacketBase& a_Packet) { + m_StateMachine.m_Socket->Send(a_Packet); +} + +} // namespace server +} // namespace td diff --git a/src/client/IClientState.cpp b/src/client/IClientState.cpp deleted file mode 100644 index ff2c548..0000000 --- a/src/client/IClientState.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include - -namespace td { -namespace client { - -void IClientState::SetClient(Client* a_Client) { - assert(a_Client); - m_Client = a_Client; - Connect(m_Client->m_Socket->OnReceive, std::bind(&IClientState::HandlePacket, this, std::placeholders::_1)); - Init(); -} - -IClientState::IClientState() : m_Client(nullptr) {} - -IClientState::~IClientState() {} - -void IClientState::SendPacket(const protocol::PacketBase& a_Packet) { - m_Client->m_Socket->Send(a_Packet); -} - -void IClientState::SetNewState(const std::shared_ptr& a_NewState) { - m_Client->UpdateState(a_NewState); -} - -} // namespace server -} // namespace td diff --git a/src/client/state/GameState.cpp b/src/client/state/GameState.cpp index bc02c36..bb72ce5 100644 --- a/src/client/state/GameState.cpp +++ b/src/client/state/GameState.cpp @@ -3,10 +3,9 @@ namespace td { namespace client { -GameState::GameState(const std::shared_ptr& a_World) : m_World(a_World) {} +GameState::GameState(Client& a_Client, const std::shared_ptr& a_World) : ClientState(a_Client), m_World(a_World) {} void GameState::HandlePacket(const protocol::PacketBase& a_Packet) {} void GameState::Update(float a_Delta) {} -void GameState::Init() {} } // namespace client } // namespace td diff --git a/src/main.cpp b/src/main.cpp index 5b18ae5..5062e30 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ +#include +#include #include -#include #include #include #include @@ -102,6 +103,14 @@ void FastForward(td::game::World& a_World, const td::sim::GameHistory& a_LockSte } } +float GetDelta() { + static std::chrono::time_point m_LastTime = std::chrono::system_clock::now(); + auto timeElapsed = std::chrono::system_clock::now() - m_LastTime; + float timeSeconds = std::chrono::duration(timeElapsed).count(); + m_LastTime = std::chrono::system_clock::now(); + return timeSeconds; +} + int main(int argc, char** argv) { td::game::WorldPtr serverWorld = GetWorld(); @@ -132,7 +141,7 @@ int main(int argc, char** argv) { td::sim::ClientSimulation simulation(*clientWorld, td::STEP_TIME); ClientHandler clientHandler(simulation); - + // packets from the server to the client clientFakeSocket->OnReceive.Connect([&clientHandler](const td::protocol::PacketBase& a_Packet) { a_Packet.Dispatch(clientHandler); @@ -151,14 +160,15 @@ int main(int argc, char** argv) { } }); - server.UpdateState(std::make_shared(serverWorld)); - client.UpdateState(std::make_shared(clientWorld)); + server.ChangeState(serverWorld); + client.ChangeState(clientWorld); while (!display.IsCloseRequested()) { display.PollEvents(); - server.Update(); - client.Update(); - float lerp = simulation.Update(); + float delta = GetDelta(); + server.Update(delta); + client.Update(delta); + float lerp = simulation.Update(delta); renderer.Render(lerp); display.Update(); } diff --git a/src/server/IServerState.cpp b/src/server/IServerState.cpp deleted file mode 100644 index 3f99642..0000000 --- a/src/server/IServerState.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include - -namespace td { -namespace server { - -void IServerState::SetServer(Server* a_Server) { - assert(a_Server); - m_Server = a_Server; - Connect(m_Server->m_Socket->OnConnect, std::bind(&IServerState::OnPlayerJoin, this, std::placeholders::_1)); - Connect(m_Server->m_Socket->OnDisconnect, std::bind(&IServerState::OnPlayerLeave, this, std::placeholders::_1)); - Connect(m_Server->m_Socket->OnReceive, std::bind(&IServerState::HandlePacket, this, std::placeholders::_1, std::placeholders::_2)); - Init(); -} - -IServerState::IServerState() : m_Server(nullptr) {} - -IServerState::~IServerState() {} - -void IServerState::SendPacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) { - m_Server->m_Socket->Send(a_Id, a_Packet); -} - -void IServerState::BroadcastPacket(const protocol::PacketBase& a_Packet) { - m_Server->m_Socket->Broadcast(a_Packet); -} - -void IServerState::SetNewState(const std::shared_ptr& a_NewState) { - m_Server->UpdateState(a_NewState); -} - -} // namespace server -} // namespace td diff --git a/src/server/Server.cpp b/src/server/Server.cpp index 2c9b506..d655a2b 100644 --- a/src/server/Server.cpp +++ b/src/server/Server.cpp @@ -3,22 +3,7 @@ namespace td { namespace server { -void Server::Update() { - auto timeElapsed = std::chrono::system_clock::now() - m_LastTime; - float timeSeconds = std::chrono::duration(timeElapsed).count(); - Update(timeSeconds); - m_LastTime = std::chrono::system_clock::now(); -} -void Server::UpdateState(const std::shared_ptr& a_State) { - m_State = a_State; - m_State->SetServer(this); -} - -void Server::Update(float a_Delta) { - assert(m_State); - m_State->Update(a_Delta); -} } // namespace server } // namespace td diff --git a/src/server/ServerState.cpp b/src/server/ServerState.cpp new file mode 100644 index 0000000..86d76c8 --- /dev/null +++ b/src/server/ServerState.cpp @@ -0,0 +1,24 @@ +#include +#include + +#include + +namespace td { +namespace server { + +ServerState::ServerState(Server& a_Server) : Server::State(a_Server) { + Connect(m_StateMachine.m_Socket->OnReceive, std::bind(&ServerState::HandlePacket, this, std::placeholders::_1, std::placeholders::_2)); +} + +ServerState::~ServerState() {} + +void ServerState::SendPacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) { + m_StateMachine.m_Socket->Send(a_Id, a_Packet); +} + +void ServerState::BroadcastPacket(const protocol::PacketBase& a_Packet) { + m_StateMachine.m_Socket->Broadcast(a_Packet); +} + +} // namespace server +} // namespace diff --git a/src/server/state/GameState.cpp b/src/server/state/GameState.cpp index 8ea3ff4..ee6c747 100644 --- a/src/server/state/GameState.cpp +++ b/src/server/state/GameState.cpp @@ -6,9 +6,7 @@ namespace td { namespace server { -GameState::GameState(const std::shared_ptr& a_World) : m_World(a_World), m_Simulation(*m_World, STEP_TIME), m_Time(0) {} - -void GameState::Init() { +GameState::GameState(Server& a_Server, const std::shared_ptr& a_World) : ServerState(a_Server), m_World(a_World), m_Simulation(*m_World, STEP_TIME), m_Time(0) { std::cout << "Switched to Game state !\n"; BroadcastPacket(m_Simulation.MakePacket()); } @@ -28,9 +26,5 @@ void GameState::Update(float a_Delta) { } } -void GameState::OnPlayerJoin(PlayerID a_Id) {} - -void GameState::OnPlayerLeave(PlayerID a_Id) {} - } // namespace server } // namespace td diff --git a/src/server/state/LobbyState.cpp b/src/server/state/LobbyState.cpp index e9a1b1c..feac84d 100644 --- a/src/server/state/LobbyState.cpp +++ b/src/server/state/LobbyState.cpp @@ -11,15 +11,7 @@ void LobbyState::HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packe } void LobbyState::Update(float a_Delta) { - SetNewState(std::make_shared(m_World)); -} - -void LobbyState::OnPlayerJoin(PlayerID a_Id) { - -} - -void LobbyState::OnPlayerLeave(PlayerID a_Id) { - + m_StateMachine.ChangeState(m_World); } } // namespace server diff --git a/src/td/simulation/ClientSimulation.cpp b/src/td/simulation/ClientSimulation.cpp index 8075355..a45d336 100644 --- a/src/td/simulation/ClientSimulation.cpp +++ b/src/td/simulation/ClientSimulation.cpp @@ -20,7 +20,6 @@ ClientSimulation::ClientSimulation(game::World& a_World, GameHistory&& a_History m_StepTime(a_StepTime), m_World(a_World), m_CurrentTime(0), - m_LastTime(GetTime()), m_CurrentStep(0), m_LastSnapshot(std::make_shared()), m_LastValidStep(0) { @@ -36,20 +35,19 @@ ClientSimulation::ClientSimulation(game::World& a_World, std::uint64_t a_StepTim m_World(a_World), m_History(std::numeric_limits::max()), m_CurrentTime(0), - m_LastTime(GetTime()), m_CurrentStep(0), m_LastSnapshot(std::make_shared()), m_LastValidStep(0) {} -float ClientSimulation::Update() { +float ClientSimulation::Update(float a_Delta) { // TODO: handle freezes (m_CurrentTime > 2 * m_StepTime) - m_CurrentTime += GetTime() - m_LastTime; - m_LastTime = GetTime(); - if (m_CurrentTime > m_StepTime) { + static const float stepTimeSecond = static_cast(STEP_TIME) / 1000.0f; + m_CurrentTime += a_Delta; + if (m_CurrentTime > stepTimeSecond) { + m_CurrentTime = std::fmod(m_CurrentTime, stepTimeSecond); Step(); - m_CurrentTime %= m_StepTime; } - return (float)m_CurrentTime / (float)m_StepTime; + return (float)m_CurrentTime / stepTimeSecond; } bool ClientSimulation::Step() {