From ac3e94932340ecd4ab5f942de905d9a206dd4069 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 8 Aug 2025 13:24:50 +0200 Subject: [PATCH] add client --- include/client/Client.h | 30 +++++++++++++++++++ include/client/IClientSocket.h | 23 +++++++++++++++ include/client/IClientState.h | 33 +++++++++++++++++++++ include/client/socket/FakeSocket.h | 33 +++++++++++++++++++++ include/client/state/GameState.h | 26 +++++++++++++++++ include/server/Server.h | 9 ++---- include/server/socket/FakeSocket.h | 17 +++++++++-- src/client/Client.cpp | 24 +++++++++++++++ src/client/IClientState.cpp | 27 +++++++++++++++++ src/client/socket/FakeSocket.cpp | 19 ++++++++++++ src/client/state/GameState.cpp | 12 ++++++++ src/main.cpp | 47 +++++++++++++++++------------- src/server/Server.cpp | 10 +++++++ src/server/socket/FakeSocket.cpp | 29 ++++++++++++++++-- 14 files changed, 306 insertions(+), 33 deletions(-) create mode 100644 include/client/Client.h create mode 100644 include/client/IClientSocket.h create mode 100644 include/client/IClientState.h create mode 100644 include/client/socket/FakeSocket.h create mode 100644 include/client/state/GameState.h create mode 100644 src/client/Client.cpp create mode 100644 src/client/IClientState.cpp create mode 100644 src/client/socket/FakeSocket.cpp create mode 100644 src/client/state/GameState.cpp diff --git a/include/client/Client.h b/include/client/Client.h new file mode 100644 index 0000000..470d9ea --- /dev/null +++ b/include/client/Client.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace client { + +class Client { + 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()) {} + + void Update(); + + void UpdateState(const std::shared_ptr& a_State); + + private: + void Update(float a_Delta); + + friend class IClientState; +}; + +} // namespace client +} // namespace td diff --git a/include/client/IClientSocket.h b/include/client/IClientSocket.h new file mode 100644 index 0000000..c4d8c65 --- /dev/null +++ b/include/client/IClientSocket.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace client { + +class IClientSocket { + public: + utils::Signal<> OnConnect; + utils::Signal<> OnDisconnect; + utils::Signal OnReceive; + + virtual void Send(const protocol::PacketBase& a_Packet) = 0; + + IClientSocket() {} + virtual ~IClientSocket() {} +}; + +} // namespace client +} // namespace td diff --git a/include/client/IClientState.h b/include/client/IClientState.h new file mode 100644 index 0000000..f805015 --- /dev/null +++ b/include/client/IClientState.h @@ -0,0 +1,33 @@ +#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/socket/FakeSocket.h b/include/client/socket/FakeSocket.h new file mode 100644 index 0000000..0440708 --- /dev/null +++ b/include/client/socket/FakeSocket.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace td { + +namespace server { +class FakeSocket; +} // namespace server + + +namespace client { + +class FakeSocket : public IClientSocket { + private: + std::shared_ptr m_Server; + PeerID m_PeerId; + + struct Private{ explicit Private() = default; }; + + public: + FakeSocket(Private) {} + ~FakeSocket() {} + + static std::shared_ptr Connect(const std::shared_ptr& a_Server); + + void ReceiveFromFakePeer(PeerID a_Peer, const protocol::PacketBase& a_Packet); + + virtual void Send(const protocol::PacketBase& a_Packet) override; +}; + +} // namespace client +} // namespace td diff --git a/include/client/state/GameState.h b/include/client/state/GameState.h new file mode 100644 index 0000000..36baef2 --- /dev/null +++ b/include/client/state/GameState.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace client { + +class GameState : public IClientState { + private: + std::shared_ptr m_World; + + public: + GameState(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; +}; + +} // namespace client +} // namespace td diff --git a/include/server/Server.h b/include/server/Server.h index 83542c7..de8589f 100644 --- a/include/server/Server.h +++ b/include/server/Server.h @@ -19,15 +19,10 @@ class Server { void Update(); - void UpdateState(const std::shared_ptr& a_State) { - m_State = a_State; - m_State->SetServer(this); - } + void UpdateState(const std::shared_ptr& a_State); private: - void Update(float a_Delta) { - m_State->Update(a_Delta); - } + void Update(float a_Delta); friend class IServerState; }; diff --git a/include/server/socket/FakeSocket.h b/include/server/socket/FakeSocket.h index 8572389..8f14e94 100644 --- a/include/server/socket/FakeSocket.h +++ b/include/server/socket/FakeSocket.h @@ -1,23 +1,34 @@ #pragma once +#include +#include #include namespace td { namespace server { class FakeSocket : public IServerSocket { + private: + std::vector>> m_Clients; + public: FakeSocket() {} ~FakeSocket() {} - utils::Signal OnSendToFakePeer; - - void ConnectFakePeer(PeerID a_Peer); + PeerID ConnectFakePeer(const std::shared_ptr& a_Socket); void DisconnectFakePeer(PeerID a_Peer); void ReceiveFromFakePeer(PeerID a_Peer, const protocol::PacketBase& a_Packet); protected: virtual void SendPeer(PeerID a_Peer, const protocol::PacketBase& a_Packet) override; + + private: + /** + * \return -1 if all previous ids are not free + */ + int GetNextFreeId(); + + friend class client::FakeSocket; }; } // namespace server diff --git a/src/client/Client.cpp b/src/client/Client.cpp new file mode 100644 index 0000000..03f0d2c --- /dev/null +++ b/src/client/Client.cpp @@ -0,0 +1,24 @@ +#include + +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/IClientState.cpp b/src/client/IClientState.cpp new file mode 100644 index 0000000..ff2c548 --- /dev/null +++ b/src/client/IClientState.cpp @@ -0,0 +1,27 @@ +#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/socket/FakeSocket.cpp b/src/client/socket/FakeSocket.cpp new file mode 100644 index 0000000..66e6ba9 --- /dev/null +++ b/src/client/socket/FakeSocket.cpp @@ -0,0 +1,19 @@ +#include +#include + +namespace td { +namespace client { + +void FakeSocket::Send(const protocol::PacketBase& a_Packet) { + m_Server->OnReceivePeer(m_PeerId, a_Packet); +} + +std::shared_ptr FakeSocket::Connect(const std::shared_ptr& a_Server) { + auto socket = std::make_shared(Private()); + socket->m_Server = a_Server; + socket->m_PeerId = a_Server->ConnectFakePeer(socket); + return socket; +} + +} // namespace client +} // namespace td diff --git a/src/client/state/GameState.cpp b/src/client/state/GameState.cpp new file mode 100644 index 0000000..bc02c36 --- /dev/null +++ b/src/client/state/GameState.cpp @@ -0,0 +1,12 @@ +#include + +namespace td { +namespace client { + +GameState::GameState(const std::shared_ptr& a_World) : 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 f2ada89..5b18ae5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,10 @@ #include #include +#include +#include +#include + class WorldApply : public td::protocol::PacketHandler { private: td::game::World& m_World; @@ -102,8 +106,12 @@ int main(int argc, char** argv) { td::game::WorldPtr serverWorld = GetWorld(); // server - auto fakeSocket = std::make_shared(); - td::server::Server server(fakeSocket); + auto serverFakeSocket = std::make_shared(); + td::server::Server server(serverFakeSocket); + + // client + auto clientFakeSocket = td::client::FakeSocket::Connect(serverFakeSocket); + td::client::Client client(clientFakeSocket); // init GL context td::Display display(1920, 1080, "Tower-Defense 2"); @@ -124,33 +132,32 @@ 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->ReceiveFromFakePeer(0, td::protocol::packets::LockStepRequestPacket(a_MissingSteps)); - }); - - // temporary tests - display.OnKeyDown.Connect([&fakeSocket](SDL_Keycode key) { - if (key == SDLK_A) { - fakeSocket->ReceiveFromFakePeer(0, td::protocol::packets::SpawnTroopPacket(td::EntityType::Zombie, 1)); - } else if (key == SDLK_Z) { - fakeSocket->ReceiveFromFakePeer(0, td::protocol::packets::PlaceTowerPacket(td::TowerType::Archer, td::TowerCoords(77, 13))); - } - }); - - // make a fake player join - fakeSocket->ConnectFakePeer(0); - + // packets from the server to the client - fakeSocket->OnSendToFakePeer.Connect([&clientHandler](td::PeerID a_Peer, const td::protocol::PacketBase& a_Packet) { + clientFakeSocket->OnReceive.Connect([&clientHandler](const td::protocol::PacketBase& a_Packet) { a_Packet.Dispatch(clientHandler); }); + simulation.OnMissingLockSteps.Connect([&clientFakeSocket](const std::vector& a_MissingSteps) { + clientFakeSocket->Send(td::protocol::packets::LockStepRequestPacket(a_MissingSteps)); + }); + + // temporary tests + display.OnKeyDown.Connect([&clientFakeSocket](SDL_Keycode key) { + if (key == SDLK_A) { + clientFakeSocket->Send(td::protocol::packets::SpawnTroopPacket(td::EntityType::Zombie, 1)); + } else if (key == SDLK_Z) { + clientFakeSocket->Send(td::protocol::packets::PlaceTowerPacket(td::TowerType::Archer, td::TowerCoords(77, 13))); + } + }); + server.UpdateState(std::make_shared(serverWorld)); + client.UpdateState(std::make_shared(clientWorld)); while (!display.IsCloseRequested()) { display.PollEvents(); server.Update(); + client.Update(); float lerp = simulation.Update(); renderer.Render(lerp); display.Update(); diff --git a/src/server/Server.cpp b/src/server/Server.cpp index 453e3af..2c9b506 100644 --- a/src/server/Server.cpp +++ b/src/server/Server.cpp @@ -10,5 +10,15 @@ void Server::Update() { 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/socket/FakeSocket.cpp b/src/server/socket/FakeSocket.cpp index b0b0693..1b58cb7 100644 --- a/src/server/socket/FakeSocket.cpp +++ b/src/server/socket/FakeSocket.cpp @@ -4,20 +4,43 @@ namespace td { namespace server { void FakeSocket::SendPeer(PeerID a_Peer, const protocol::PacketBase& a_Packet) { - OnSendToFakePeer(a_Peer, a_Packet); + auto socket = m_Clients.at(a_Peer); + assert(socket.has_value()); + socket.value()->OnReceive(a_Packet); } void FakeSocket::ReceiveFromFakePeer(PeerID a_Peer, const protocol::PacketBase& a_Packet) { OnReceivePeer(a_Peer, a_Packet); } -void FakeSocket::ConnectFakePeer(PeerID a_Peer) { - OnConnectPeer(a_Peer); +PeerID FakeSocket::ConnectFakePeer(const std::shared_ptr& a_Socket) { + int peerId = GetNextFreeId(); + if (peerId == -1) { + peerId = m_Clients.size(); + m_Clients.push_back(a_Socket); + } else { + m_Clients.emplace(m_Clients.begin() + peerId, a_Socket); + } + a_Socket->OnConnect(); + OnConnectPeer(peerId); + return peerId; } void FakeSocket::DisconnectFakePeer(PeerID a_Peer) { + auto socket = m_Clients.at(a_Peer); + assert(socket.has_value()); + socket.value()->OnDisconnect(); OnDisconnectPeer(a_Peer); } +int FakeSocket::GetNextFreeId() { + auto it = std::find_if(m_Clients.begin(), m_Clients.end(), [](const auto& a_Value) { return !a_Value.has_value(); }); + + if (it == m_Clients.end()) + return -1; + + return std::distance(m_Clients.begin(), it); +} + } // namespace server } // namespace td