5 Commits

Author SHA1 Message Date
5d196c4b61 add SlotGuard doc 2025-08-06 14:15:13 +02:00
0d9e5b647f use SlotGuard 2025-08-06 14:13:38 +02:00
079d653405 add SlotGuard 2025-08-06 14:07:23 +02:00
990c6f078d server state test 2025-08-06 13:55:22 +02:00
599dfa0cee begin server 2025-08-06 12:53:33 +02:00
18 changed files with 389 additions and 8 deletions

View File

@@ -0,0 +1,31 @@
#pragma once
#include <server/PlayerIds.h>
#include <td/misc/Signal.h>
#include <td/protocol/packet/Packets.h>
namespace td {
namespace server {
class IServerSocket {
public:
utils::Signal<PlayerID> OnConnect;
utils::Signal<PlayerID> OnDisconnect;
utils::Signal<PlayerID, const protocol::PacketBase&> OnReceive;
virtual void Send(PlayerID, const protocol::PacketBase&) = 0;
IServerSocket() {}
virtual ~IServerSocket() {}
private:
PlayerIds m_Ids;
protected:
void OnConnectPeer(PeerID a_PeerId);
void OnDisconnectPeer(PeerID a_PeerId);
void OnReceivePeer(PeerID a_PeerId, const protocol::PacketBase& a_Packet);
};
} // namespace server
} // namespace td

View File

@@ -0,0 +1,34 @@
#pragma once
#include <server/IServerSocket.h>
#include <td/misc/SlotGuard.h>
namespace td {
namespace server {
class Server;
class IServerState : public utils::SlotGuard {
protected:
void SendPacket(PlayerID a_Id, const protocol::PacketBase& a_Packet);
void SetNewState(const std::shared_ptr<IServerState>& a_NewState);
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();
private:
Server* m_Server;
void SetServer(Server* a_Server);
friend class Server;
};
} // namespace server
} // namespace td

View File

@@ -0,0 +1,41 @@
#pragma once
#include <map>
#include <td/Types.h>
namespace td {
namespace server {
class PlayerIds {
private:
std::map<PeerID, PlayerID> m_PeerToPlayer;
std::map<PlayerID, PeerID> m_PlayerToPeer;
PlayerID m_NextId;
public:
PlayerIds() : m_NextId(0) {}
~PlayerIds() {}
void AddConnection(PeerID a_PeerId) {
m_PeerToPlayer.emplace(a_PeerId, m_NextId);
m_PlayerToPeer.emplace(m_NextId, a_PeerId);
m_NextId++;
}
PlayerID GetPlayerId(PeerID a_PeerId) const {
return m_PeerToPlayer.at(a_PeerId);
}
PeerID GetPeerId(PlayerID a_PlayerId) const {
return m_PlayerToPeer.at(a_PlayerId);
}
void RemovePeer(PeerID a_PeerId) {
PlayerID playerId = GetPlayerId(a_PeerId);
m_PeerToPlayer.erase(a_PeerId);
m_PlayerToPeer.erase(playerId);
}
};
} // namespace server
} // namespace td

31
include/server/Server.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include <memory>
#include <server/IServerSocket.h>
#include <server/IServerState.h>
namespace td {
namespace server {
class Server {
private:
std::shared_ptr<IServerSocket> m_Socket;
std::shared_ptr<IServerState> m_State;
public:
Server(const std::shared_ptr<IServerSocket>& a_Socket) : m_Socket(a_Socket) {}
void Update(float a_Delta) {
m_State->Update(a_Delta);
}
void UpdateState(const std::shared_ptr<IServerState>& a_State) {
m_State = a_State;
m_State->SetServer(this);
}
friend class IServerState;
};
} // namespace server
} // namespace td

View File

@@ -0,0 +1,19 @@
#pragma once
#include <server/IServerSocket.h>
namespace td {
namespace server {
class TcpSocket : public IServerSocket {
private:
/* data */
public:
TcpSocket(/* args */) {}
~TcpSocket() {}
virtual void Send(PlayerID, const protocol::PacketBase&) {}
};
} // namespace server
} // namespace td

View File

@@ -0,0 +1,17 @@
#pragma once
#include <server/IServerState.h>
namespace td {
namespace server {
class EndGameState : public IServerState {
private:
/* data */
public:
EndGameState(/* args */) {}
~EndGameState() {}
};
} // namespace server
} // namespace td

View File

@@ -0,0 +1,22 @@
#pragma once
#include <server/IServerState.h>
namespace td {
namespace server {
class GameState : public IServerState{
private:
/* data */
public:
GameState(/* args */) {}
~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;
};
} // namespace server
} // namespace td

View File

@@ -0,0 +1,22 @@
#pragma once
#include <server/IServerState.h>
namespace td {
namespace server {
class LobbyState : public IServerState {
private:
/* data */
public:
LobbyState(/* args */) {}
~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
} // namespace td

View File

@@ -10,14 +10,22 @@ namespace utils {
template <typename... Args> template <typename... Args>
class Signal : private NonCopyable { class Signal : private NonCopyable {
private: public:
using CallBack = std::function<void(Args...)>; using FnType = void(Args...);
using CallBack = std::function<FnType>;
private:
std::vector<CallBack> m_Callbacks; std::vector<CallBack> m_Callbacks;
public: public:
void Connect(CallBack&& a_Callback) { void Connect(const CallBack& a_Callback) {
m_Callbacks.push_back(std::move(a_Callback)); m_Callbacks.push_back(a_Callback);
}
void Disconnect(const CallBack& a_Callback) {
auto it = std::find_if(m_Callbacks.begin(), m_Callbacks.end(),
[&a_Callback](CallBack& callback) { return a_Callback.template target<FnType>() == callback.template target<FnType>(); });
m_Callbacks.erase(it);
} }
void operator()(Args... args) const { void operator()(Args... args) const {

View File

@@ -0,0 +1,34 @@
#pragma once
#include <td/misc/SlotGuard.h>
namespace td {
namespace utils {
/**
* \brief Wrapper class to automatically disconnect from a Signal on object destruction
* \sa Signal
*/
class SlotGuard {
private:
std::vector<std::function<void()>> m_Deleters;
public:
/**
* \brief Connect a signal to a function (with the same signature)
*/
template <typename... Args>
void Connect(Signal<Args...>& a_Signal, const typename Signal<Args...>::CallBack& a_Callback) {
a_Signal.Connect(a_Callback);
m_Deleters.push_back([&a_Signal, &a_Callback]() { a_Signal.Disconnect(a_Callback); });
}
~SlotGuard() {
for (auto& deleter : m_Deleters) {
deleter();
}
}
};
} // namespace utils
} // namespace td

View File

@@ -4,6 +4,7 @@
#include <td/render/Camera.h> #include <td/render/Camera.h>
#include <td/render/loader/GLLoader.h> #include <td/render/loader/GLLoader.h>
#include <td/render/shader/CameraShaderProgram.h> #include <td/render/shader/CameraShaderProgram.h>
#include <td/misc/SlotGuard.h>
namespace td { namespace td {
namespace render { namespace render {
@@ -17,7 +18,7 @@ class BasicRenderer {
}; };
template <typename TShader> template <typename TShader>
class Renderer : public BasicRenderer { class Renderer : public BasicRenderer, public utils::SlotGuard {
protected: protected:
std::unique_ptr<TShader> m_Shader; std::unique_ptr<TShader> m_Shader;
Camera& m_Camera; Camera& m_Camera;
@@ -62,12 +63,12 @@ class RenderPipeline {
template <typename TShader> template <typename TShader>
Renderer<TShader>::Renderer(Camera& a_Camera) : m_Shader(std::make_unique<TShader>()), m_Camera(a_Camera) { Renderer<TShader>::Renderer(Camera& a_Camera) : m_Shader(std::make_unique<TShader>()), m_Camera(a_Camera) {
a_Camera.OnPerspectiveChange.Connect([this]() { Connect(a_Camera.OnPerspectiveChange, [this](){
m_Shader->Start(); m_Shader->Start();
m_Shader->SetProjectionMatrix(m_Camera.GetProjectionMatrix()); m_Shader->SetProjectionMatrix(m_Camera.GetProjectionMatrix());
}); });
a_Camera.OnViewChange.Connect([this]() { Connect(a_Camera.OnViewChange, [this]() {
m_Shader->Start(); m_Shader->Start();
m_Shader->SetViewMatrix(m_Camera.GetViewMatrix()); m_Shader->SetViewMatrix(m_Camera.GetViewMatrix());
}); });

View File

@@ -15,6 +15,10 @@
#include <sp/io/MessageStream.h> #include <sp/io/MessageStream.h>
#include <sp/io/StdIo.h> #include <sp/io/StdIo.h>
#include <server/Server.h>
#include <server/socket/TcpSocket.h>
#include <server/state/LobbyState.h>
class WorldApply : public td::protocol::PacketHandler { class WorldApply : public td::protocol::PacketHandler {
private: private:
td::game::World& m_World; td::game::World& m_World;
@@ -92,7 +96,6 @@ td::sim::GameHistory GetCustomHistory() {
return gh; return gh;
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
td::game::World w = GetWorld(); td::game::World w = GetWorld();
@@ -128,6 +131,15 @@ int main(int argc, char** argv) {
} }
}); });
// server
auto socket = std::make_shared<td::server::TcpSocket>();
td::server::Server server(socket);
server.UpdateState(std::make_shared<td::server::LobbyState>());
server.Update(1.0f);
server.Update(1.0f);
socket->OnDisconnect(0);
while (!display.IsCloseRequested()) { while (!display.IsCloseRequested()) {
display.PollEvents(); display.PollEvents();
float lerp = simulation.Update(); float lerp = simulation.Update();

View File

@@ -0,0 +1,21 @@
#include <server/IServerSocket.h>
namespace td {
namespace server {
void IServerSocket::OnConnectPeer(PeerID a_PeerId) {
m_Ids.AddConnection(a_PeerId);
OnConnect(m_Ids.GetPlayerId(a_PeerId));
}
void IServerSocket::OnDisconnectPeer(PeerID a_PeerId) {
OnDisconnect(m_Ids.GetPlayerId(a_PeerId));
m_Ids.RemovePeer(a_PeerId);
}
void IServerSocket::OnReceivePeer(PeerID a_PeerId, const protocol::PacketBase& a_Packet) {
OnReceive(m_Ids.GetPlayerId(a_PeerId), a_Packet);
}
} // namespace server
} // namespace td

View File

@@ -0,0 +1,28 @@
#include <server/IServerState.h>
#include <server/Server.h>
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));
}
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::SetNewState(const std::shared_ptr<IServerState>& a_NewState) {
m_Server->UpdateState(a_NewState);
}
} // namespace server
} // namespace td

1
src/server/Server.cpp Normal file
View File

@@ -0,0 +1 @@
#include <server/Server.h>

View File

@@ -0,0 +1,8 @@
#include <server/socket/TcpSocket.h>
namespace td {
namespace server {
} // namespace server
} // namespace td

View File

@@ -0,0 +1,25 @@
#include <server/state/GameState.h>
#include <iostream>
namespace td {
namespace server {
void GameState::HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) {
}
void GameState::Update(float a_Delta) {
}
void GameState::OnPlayerJoin(PlayerID a_Id) {
}
void GameState::OnPlayerLeave(PlayerID a_Id) {
std::cout << "Game leave !" << std::endl;
}
} // namespace server
} // namespace td

View File

@@ -0,0 +1,26 @@
#include <server/state/LobbyState.h>
#include <server/state/GameState.h>
#include <iostream>
namespace td {
namespace server {
void LobbyState::HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) {
}
void LobbyState::Update(float a_Delta) {
SetNewState(std::make_shared<GameState>());
}
void LobbyState::OnPlayerJoin(PlayerID a_Id) {
}
void LobbyState::OnPlayerLeave(PlayerID a_Id) {
std::cout << "Lobby leave !" << std::endl;
}
} // namespace server
} // namespace td