14 Commits

Author SHA1 Message Date
165ebf7b2e add valgrind 2025-08-20 12:35:04 +02:00
a02cb2b309 PlayerManager 2025-08-20 12:18:44 +02:00
bd56fb0646 add server mspt 2025-08-19 18:55:25 +02:00
39580c15c5 add Timer class 2025-08-19 18:46:54 +02:00
ee39c1e429 add playerlist packet 2025-08-19 18:42:02 +02:00
631e14e66e IServerSocket dispatch 2025-08-19 18:30:42 +02:00
53d2e3cf6b load custom imgui theme 2025-08-19 17:26:29 +02:00
cd33ea28dc renderer: use shared_ptr 2025-08-19 17:19:57 +02:00
a50898a88b begin player auth 2025-08-15 11:25:35 +02:00
833173b5e8 main menu fullscreen 2025-08-15 09:47:45 +02:00
1e4af7f298 add sdl_init error message 2025-08-14 14:16:04 +02:00
e720439109 Add vscode file 2025-08-13 21:09:40 +00:00
5f1e9a8d81 Add missing sdl init 2025-08-13 21:06:02 +00:00
953b5dcc86 remove WorldApply 2025-08-12 11:35:06 +02:00
34 changed files with 419 additions and 106 deletions

13
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"configurations": [
{
"name": "TD2",
"cppStandard": "c++17",
"includePath": [
"include"
],
"compileCommands": ".vscode/compile_commands.json"
}
],
"version": 4
}

View File

@@ -13,7 +13,7 @@ class Client : public StateMachine<Client, void, float> {
std::shared_ptr<IClientSocket> m_Socket; std::shared_ptr<IClientSocket> m_Socket;
public: public:
Client(const std::shared_ptr<IClientSocket>& a_Socket) : m_Socket(a_Socket) {} Client(const std::shared_ptr<IClientSocket>& a_Socket, const std::string& a_PlayerName);
void SendPacket(const protocol::PacketBase& a_Packet); void SendPacket(const protocol::PacketBase& a_Packet);

View File

@@ -6,14 +6,13 @@
namespace td { namespace td {
namespace client { namespace client {
class ClientState : public Client::State, private utils::SlotGuard { class ClientState : public Client::State, public protocol::PacketHandler, private utils::SlotGuard {
public: public:
ClientState(Client& a_Client); ClientState(Client& a_Client);
virtual ~ClientState() {} virtual ~ClientState() {}
protected: protected:
void SendPacket(const protocol::PacketBase& a_Packet); void SendPacket(const protocol::PacketBase& a_Packet);
virtual void HandlePacket(const protocol::PacketBase& a_Packet) {}
}; };
} // namespace server } // namespace server

View File

@@ -23,8 +23,8 @@ class GameState : public ClientState {
return m_CurrentLerp; return m_CurrentLerp;
} }
protected: virtual void Handle(const protocol::packets::LockStepsPacket& a_LockStep) override;
virtual void HandlePacket(const protocol::PacketBase& a_Packet) override; virtual void Handle(const protocol::packets::LockStepResponsePacket& a_LockStep) override;
}; };
} // namespace client } // namespace client

View File

@@ -0,0 +1,21 @@
#pragma once
#include <client/ClientState.h>
#include <td/game/World.h>
#include <td/simulation/ClientSimulation.h>
namespace td {
namespace client {
class LoggingState : public ClientState {
public:
LoggingState(Client& a_Client, const std::string& a_PlayerName);
~LoggingState();
virtual void Update(float a_Delta) override {}
virtual void Handle(const protocol::packets::PlayerJoinPacket& a_Packet) override;
};
} // namespace client
} // namespace td

View File

@@ -9,18 +9,27 @@ namespace server {
class IServerSocket { class IServerSocket {
public: public:
utils::Signal<PlayerID> OnConnect; using PlayerPacketHandlerType = std::unique_ptr<protocol::PacketHandler>(PlayerID);
utils::Signal<PlayerID> OnDisconnect; using PlayerPacketHandler = std::function<PlayerPacketHandlerType>;
utils::Signal<PlayerID, const PlayerInfo&> OnPlayerJoin;
utils::Signal<PlayerID> OnPlayerLeave;
utils::Signal<PlayerID, const protocol::PacketBase&> OnReceive; utils::Signal<PlayerID, const protocol::PacketBase&> OnReceive;
void Send(PlayerID a_PlayerId, const protocol::PacketBase& a_Packet); void Send(PlayerID a_PlayerId, const protocol::PacketBase& a_Packet);
void Broadcast(const protocol::PacketBase& a_Packet); void Broadcast(const protocol::PacketBase& a_Packet);
IServerSocket() {} void Disconnect(PlayerID a_PlayerId);
void RegisterHandler(const PlayerPacketHandler& a_Handler);
void UnregisterHandler(const PlayerPacketHandler& a_Handler);
IServerSocket();
virtual ~IServerSocket() {} virtual ~IServerSocket() {}
private: private:
PlayerIds m_Ids; PlayerIds m_Ids;
std::vector<PlayerPacketHandler> m_Handlers;
protected: protected:
void OnConnectPeer(PeerID a_PeerId); void OnConnectPeer(PeerID a_PeerId);

View File

@@ -0,0 +1,39 @@
#pragma once
#include <td/protocol/packet/Packets.h>
namespace td {
namespace server {
class IServerSocket;
class PlayerManager {
private:
std::map<PlayerID, PlayerInfo> m_Players;
std::shared_ptr<IServerSocket> m_Socket;
public:
PlayerManager(const std::shared_ptr<IServerSocket>& a_Socket);
~PlayerManager();
void RemovePlayer(PlayerID a_Player);
PlayerInfo GetPlayer(PlayerID a_Player);
private:
class ConnectionHandler : public protocol::PacketHandler {
private:
std::map<PlayerID, PlayerInfo>& m_Players;
IServerSocket& m_Socket;
PlayerID m_Player;
public:
ConnectionHandler(std::map<PlayerID, PlayerInfo>& a_Players, IServerSocket& a_Socket, PlayerID a_Player);
~ConnectionHandler() = default;
virtual void Handle(const protocol::packets::PlayerLoginPacket& a_Packet) override;
virtual void Handle(const protocol::packets::DisconnectPacket& a_Packet) override;
};
};
} // namespace server
} // namespace td

View File

@@ -2,6 +2,7 @@
#include <server/IServerSocket.h> #include <server/IServerSocket.h>
#include <td/common/StateMachine.h> #include <td/common/StateMachine.h>
#include <server/PlayerManager.h>
namespace td { namespace td {
namespace server { namespace server {
@@ -11,9 +12,13 @@ class ServerState;
class Server : public StateMachine<Server, void, float> { class Server : public StateMachine<Server, void, float> {
private: private:
std::shared_ptr<IServerSocket> m_Socket; std::shared_ptr<IServerSocket> m_Socket;
PlayerManager m_Players;
float m_LastMspt;
public: public:
Server(const std::shared_ptr<IServerSocket>& a_Socket) : m_Socket(a_Socket) {} Server(const std::shared_ptr<IServerSocket>& a_Socket);
virtual void Update(float a_Delta);
friend class ServerState; friend class ServerState;
}; };

View File

@@ -10,6 +10,8 @@ class ServerState : public Server::State, private utils::SlotGuard {
public: public:
virtual void HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) = 0; virtual void HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet) = 0;
virtual void Update(float a_Delta) = 0; virtual void Update(float a_Delta) = 0;
virtual void OnPlayerJoin(PlayerID a_Id, const td::PlayerInfo& a_Info) {}
virtual void OnPlayerLeave(PlayerID a_Id) {}
ServerState(Server& a_Server); ServerState(Server& a_Server);
virtual ~ServerState(); virtual ~ServerState();

View File

@@ -78,6 +78,11 @@ enum class Direction : std::uint8_t {
NegativeY = 1 << 3, NegativeY = 1 << 3,
}; };
struct PlayerInfo {
PlayerID m_PlayerId;
std::string m_PlayerName;
};
sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coords); sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coords);
sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float); sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float);

View File

@@ -0,0 +1,7 @@
#pragma once
namespace td {
void LoadTheme();
} // namespace td

22
include/td/misc/Time.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <chrono>
namespace td {
class Timer {
private:
std::chrono::time_point<std::chrono::system_clock> m_LastTime;
public:
Timer() : m_LastTime(std::chrono::system_clock::now()) {}
float GetDelta() {
auto timeElapsed = std::chrono::system_clock::now() - m_LastTime;
float timeSeconds = std::chrono::duration<float, std::chrono::seconds::period>(timeElapsed).count();
m_LastTime = std::chrono::system_clock::now();
return timeSeconds;
}
};
} // namespace td

View File

@@ -16,27 +16,22 @@
namespace td { namespace td {
namespace protocol { namespace protocol {
struct PlayerInfo {
PlayerID m_PlayerId;
std::string m_PlayerName;
};
struct MapData { struct MapData {
}; };
namespace pdata { namespace pdata {
/** Client attempts to login (very basic) */
struct PlayerLogin {
std::string m_PlayerName;
};
/** Server indicates success */ /** Server indicates success */
struct LoggingSuccess { struct LoggingSuccess {
PlayerID m_PlayerId; PlayerID m_PlayerId;
}; };
/** Client attempts to login (very basic) */
struct PlayerLogin {
std::string m_PlayerName;
};
/** Player joins the lobby */ /** Player joins the lobby */
struct PlayerJoin { struct PlayerJoin {
PlayerInfo m_Player; PlayerInfo m_Player;
@@ -47,6 +42,11 @@ struct PlayerLeave {
PlayerID m_PlayerId; PlayerID m_PlayerId;
}; };
/** List of players who joined before */
struct PlayerList {
std::vector<PlayerInfo> m_Players;
};
struct PredictCommand { struct PredictCommand {
CommandPtr m_Command; CommandPtr m_Command;
StepTime m_FrameNumber; StepTime m_FrameNumber;

View File

@@ -29,6 +29,7 @@ enum class PacketID : std::uint8_t {
PlaceTower, PlaceTower,
PlayerJoin, PlayerJoin,
PlayerLeave, PlayerLeave,
PlayerList,
PlayerLogin, PlayerLogin,
PredictCommand, PredictCommand,
SpawnTroop, SpawnTroop,
@@ -58,6 +59,7 @@ using LoggingSuccessPacket = PacketMessage<pdata::LoggingSuccess, PacketID::Logg
using PlaceTowerPacket = PacketMessage<pdata::PlaceTower, PacketID::PlaceTower>; using PlaceTowerPacket = PacketMessage<pdata::PlaceTower, PacketID::PlaceTower>;
using PlayerJoinPacket = PacketMessage<pdata::PlayerJoin, PacketID::PlayerJoin>; using PlayerJoinPacket = PacketMessage<pdata::PlayerJoin, PacketID::PlayerJoin>;
using PlayerLeavePacket = PacketMessage<pdata::PlayerLeave, PacketID::PlayerLeave>; using PlayerLeavePacket = PacketMessage<pdata::PlayerLeave, PacketID::PlayerLeave>;
using PlayerListPacket = PacketMessage<pdata::PlayerList, PacketID::PlayerList>;
using PlayerLoginPacket = PacketMessage<pdata::PlayerLogin, PacketID::PlayerLogin>; using PlayerLoginPacket = PacketMessage<pdata::PlayerLogin, PacketID::PlayerLogin>;
using PredictCommandPacket = PacketMessage<pdata::PredictCommand, PacketID::PredictCommand>; using PredictCommandPacket = PacketMessage<pdata::PredictCommand, PacketID::PredictCommand>;
using SpawnTroopPacket = PacketMessage<pdata::SpawnTroop, PacketID::SpawnTroop>; using SpawnTroopPacket = PacketMessage<pdata::SpawnTroop, PacketID::SpawnTroop>;
@@ -67,11 +69,17 @@ using WorldDataPacket = PacketMessage<pdata::WorldData, PacketID::WorldData>;
} // namespace packets } // namespace packets
using AllPackets = std::tuple<packets::BeginGamePacket, packets::ChatMessagePacket, packets::DisconnectPacket, using AllPackets = std::tuple<packets::BeginGamePacket, packets::ChatMessagePacket, packets::DisconnectPacket,
packets::KeepAlivePacket, packets::LockStepRequestPacket, packets::LockStepResponsePacket, packets::LockStepsPacket, packets::LoggingSuccessPacket, packets::PlaceTowerPacket, packets::KeepAlivePacket, packets::LockStepRequestPacket, packets::LockStepResponsePacket, packets::LockStepsPacket,
packets::PlayerJoinPacket, packets::PlayerLeavePacket, packets::PlayerLoginPacket, packets::PredictCommandPacket, packets::LoggingSuccessPacket, packets::PlaceTowerPacket, packets::PlayerJoinPacket, packets::PlayerLeavePacket,
packets::SpawnTroopPacket, packets::WorldHeaderPacket, packets::WorldDataPacket>; packets::PlayerListPacket, packets::PlayerLoginPacket, packets::PredictCommandPacket, packets::SpawnTroopPacket,
packets::WorldHeaderPacket, packets::WorldDataPacket>;
class PacketHandler : public sp::GenericHandler<AllPackets> {}; class PacketHandler : public sp::GenericHandler<AllPackets> {
public:
void HandleBase(const PacketBase& a_Packet) {
a_Packet.Dispatch(*this);
}
};
using PacketDispatcher = sp::MessageDispatcher<PacketBase>; using PacketDispatcher = sp::MessageDispatcher<PacketBase>;

View File

@@ -9,11 +9,11 @@ namespace render {
class EntityRenderer : public Renderer<shader::EntityShader> { class EntityRenderer : public Renderer<shader::EntityShader> {
private: private:
const game::World& m_World; game::WorldPtr m_World;
std::unique_ptr<GL::VertexArray> m_EntityVao; std::unique_ptr<GL::VertexArray> m_EntityVao;
public: public:
EntityRenderer(Camera& a_Camera, const game::World& a_World); EntityRenderer(Camera& a_Camera, const game::WorldPtr& a_World);
virtual ~EntityRenderer(); virtual ~EntityRenderer();
virtual void Render(float a_Lerp) override; virtual void Render(float a_Lerp) override;

View File

@@ -9,11 +9,11 @@ namespace render {
class TowerRenderer : public Renderer<shader::EntityShader> { class TowerRenderer : public Renderer<shader::EntityShader> {
private: private:
const game::World& m_World; game::WorldPtr m_World;
std::unique_ptr<GL::VertexArray> m_EntityVao; std::unique_ptr<GL::VertexArray> m_EntityVao;
public: public:
TowerRenderer(Camera& a_Camera, const game::World& a_World); TowerRenderer(Camera& a_Camera, const game::WorldPtr& a_World);
virtual ~TowerRenderer(); virtual ~TowerRenderer();
virtual void Render(float a_Lerp) override; virtual void Render(float a_Lerp) override;

View File

@@ -13,7 +13,7 @@ class WorldRenderer : public Renderer<shader::WorldShader> {
std::unique_ptr<GL::VertexArray> m_WorldVao; std::unique_ptr<GL::VertexArray> m_WorldVao;
public: public:
WorldRenderer(Camera& a_Camera, const game::World& a_World); WorldRenderer(Camera& a_Camera, const game::WorldPtr& a_World);
virtual ~WorldRenderer(); virtual ~WorldRenderer();
virtual void Render(float a_Lerp) override; virtual void Render(float a_Lerp) override;

View File

@@ -1,8 +1,14 @@
#include <client/Client.h> #include <client/Client.h>
#include <client/state/LoggingState.h>
namespace td { namespace td {
namespace client { namespace client {
Client::Client(const std::shared_ptr<IClientSocket>& a_Socket, const std::string& a_PlayerName) : m_Socket(a_Socket) {
ChangeState<LoggingState>(a_PlayerName);
}
void Client::SendPacket(const protocol::PacketBase& a_Packet) { void Client::SendPacket(const protocol::PacketBase& a_Packet) {
m_Socket->Send(a_Packet); m_Socket->Send(a_Packet);
} }

View File

@@ -5,7 +5,7 @@ namespace td {
namespace client { namespace client {
ClientState::ClientState(Client& a_Client) : Client::State(a_Client) { ClientState::ClientState(Client& a_Client) : Client::State(a_Client) {
Connect(m_StateMachine.m_Socket->OnReceive, std::bind(&ClientState::HandlePacket, this, std::placeholders::_1)); Connect(m_StateMachine.m_Socket->OnReceive, std::bind(&ClientState::HandleBase, this, std::placeholders::_1));
} }
void ClientState::SendPacket(const protocol::PacketBase& a_Packet) { void ClientState::SendPacket(const protocol::PacketBase& a_Packet) {

View File

@@ -3,24 +3,6 @@
namespace td { namespace td {
namespace client { namespace client {
class ClientHandler : public protocol::PacketHandler {
private:
sim::ClientSimulation& m_Simulation;
using protocol::PacketHandler::Handle;
public:
ClientHandler(sim::ClientSimulation& a_Simulation) : m_Simulation(a_Simulation) {}
void Handle(const protocol::packets::LockStepsPacket& a_LockStep) {
m_Simulation.Handle(a_LockStep);
}
void Handle(const protocol::packets::LockStepResponsePacket& a_LockStep) {
m_Simulation.Handle(a_LockStep);
}
};
GameState::GameState(Client& a_Client, const std::shared_ptr<game::World>& a_World, std::uint64_t a_StepTime) : GameState::GameState(Client& a_Client, const std::shared_ptr<game::World>& a_World, std::uint64_t a_StepTime) :
ClientState(a_Client), m_World(a_World), m_Simulation(a_World, a_StepTime) { ClientState(a_Client), m_World(a_World), m_Simulation(a_World, a_StepTime) {
m_Simulation.OnMissingLockSteps.Connect([this](const std::vector<td::StepTime>& a_MissingSteps) { m_Simulation.OnMissingLockSteps.Connect([this](const std::vector<td::StepTime>& a_MissingSteps) {
@@ -28,9 +10,12 @@ GameState::GameState(Client& a_Client, const std::shared_ptr<game::World>& a_Wor
}); });
} }
void GameState::HandlePacket(const protocol::PacketBase& a_Packet) { void GameState::Handle(const protocol::packets::LockStepsPacket& a_LockStep) {
ClientHandler handler(m_Simulation); m_Simulation.Handle(a_LockStep);
a_Packet.Dispatch(handler); }
void GameState::Handle(const protocol::packets::LockStepResponsePacket& a_LockStep) {
m_Simulation.Handle(a_LockStep);
} }
void GameState::Update(float a_Delta) { void GameState::Update(float a_Delta) {

View File

@@ -0,0 +1,19 @@
#include <client/state/LoggingState.h>
#include <iostream>
namespace td {
namespace client {
LoggingState::LoggingState(Client& a_Client, const std::string& a_PlayerName) : ClientState(a_Client) {
SendPacket(td::protocol::packets::PlayerLoginPacket(a_PlayerName));
}
LoggingState::~LoggingState() {}
void LoggingState::Handle(const protocol::packets::PlayerJoinPacket& a_Packet) {
std::cout << "[Client] " << a_Packet->m_Player.m_PlayerName << "(" << +a_Packet->m_Player.m_PlayerId << ") joined !\n";
}
} // namespace client
} // namespace td

View File

@@ -1,13 +1,6 @@
#include <chrono> #include <chrono>
#include <td/display/state/MainMenuState.h> #include <td/display/state/MainMenuState.h>
#include <td/misc/Time.h>
float GetDelta() {
static std::chrono::time_point<std::chrono::system_clock> m_LastTime = std::chrono::system_clock::now();
auto timeElapsed = std::chrono::system_clock::now() - m_LastTime;
float timeSeconds = std::chrono::duration<float, std::chrono::seconds::period>(timeElapsed).count();
m_LastTime = std::chrono::system_clock::now();
return timeSeconds;
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
// init GL context // init GL context
@@ -15,10 +8,10 @@ int main(int argc, char** argv) {
display.ChangeState<td::MainMenuState>(); display.ChangeState<td::MainMenuState>();
td::Timer timer;
while (!display.IsCloseRequested()) { while (!display.IsCloseRequested()) {
display.PollEvents(); display.PollEvents();
float delta = GetDelta(); display.Update(timer.GetDelta());
display.Update(delta);
} }
return 0; return 0;

View File

@@ -3,18 +3,38 @@
namespace td { namespace td {
namespace server { namespace server {
IServerSocket::IServerSocket() {
}
void IServerSocket::RegisterHandler(const PlayerPacketHandler& a_Handler) {
m_Handlers.push_back(a_Handler);
}
void IServerSocket::UnregisterHandler(const PlayerPacketHandler& a_Handler) {
auto it = std::find_if(m_Handlers.begin(), m_Handlers.end(),
[&a_Handler](PlayerPacketHandler& handler) { return a_Handler.template target<PlayerPacketHandlerType>() == handler.template target<PlayerPacketHandlerType>(); });
m_Handlers.erase(it);
}
void IServerSocket::OnConnectPeer(PeerID a_PeerId) { void IServerSocket::OnConnectPeer(PeerID a_PeerId) {
// here, the client is not a player yet (we need to wait for auth)
m_Ids.AddConnection(a_PeerId); m_Ids.AddConnection(a_PeerId);
OnConnect(m_Ids.GetPlayerId(a_PeerId));
} }
void IServerSocket::OnDisconnectPeer(PeerID a_PeerId) { void IServerSocket::OnDisconnectPeer(PeerID a_PeerId) {
OnDisconnect(m_Ids.GetPlayerId(a_PeerId)); OnPlayerLeave(m_Ids.GetPlayerId(a_PeerId));
m_Ids.RemovePeer(a_PeerId); m_Ids.RemovePeer(a_PeerId);
} }
void IServerSocket::OnReceivePeer(PeerID a_PeerId, const protocol::PacketBase& a_Packet) { void IServerSocket::OnReceivePeer(PeerID a_PeerId, const protocol::PacketBase& a_Packet) {
OnReceive(m_Ids.GetPlayerId(a_PeerId), a_Packet); auto playerId = m_Ids.GetPlayerId(a_PeerId);
for (const auto& factory : m_Handlers) {
auto handler = factory(playerId);
a_Packet.Dispatch(*handler);
}
OnReceive(playerId, a_Packet);
} }
void IServerSocket::Send(PlayerID a_PlayerId, const protocol::PacketBase& a_Packet) { void IServerSocket::Send(PlayerID a_PlayerId, const protocol::PacketBase& a_Packet) {
@@ -27,5 +47,9 @@ void IServerSocket::Broadcast(const protocol::PacketBase& a_Packet) {
} }
} }
void IServerSocket::Disconnect(PlayerID a_PlayerId) {
OnDisconnectPeer(m_Ids.GetPeerId(a_PlayerId));
}
} // namespace server } // namespace server
} // namespace td } // namespace td

View File

@@ -0,0 +1,54 @@
#include <server/PlayerManager.h>
#include <iostream>
#include <server/IServerSocket.h>
namespace td {
namespace server {
PlayerManager::ConnectionHandler::ConnectionHandler(
std::map<PlayerID, PlayerInfo>& a_Players, IServerSocket& a_Socket, PlayerID a_Player) :
m_Players(a_Players), m_Socket(a_Socket), m_Player(a_Player) {}
void PlayerManager::ConnectionHandler::Handle(const protocol::packets::PlayerLoginPacket& a_Packet) {
PlayerInfo pInfo{m_Player, a_Packet->m_PlayerName};
std::vector<PlayerInfo> players;
players.reserve(m_Players.size());
for (auto& [id, player] : m_Players) {
players.push_back(player);
}
m_Socket.Send(m_Player, protocol::packets::LoggingSuccessPacket(m_Player));
m_Socket.Send(m_Player, protocol::packets::PlayerListPacket(players));
m_Socket.Broadcast(protocol::packets::PlayerJoinPacket(pInfo));
m_Players.emplace(m_Player, pInfo);
m_Socket.OnPlayerJoin(m_Player, pInfo);
std::cout << "[Server] " << a_Packet->m_PlayerName << " joined !\n";
}
void PlayerManager::ConnectionHandler::Handle(const protocol::packets::DisconnectPacket& a_Packet) {
std::cout << "[Server] " << +m_Player << " wants to disconnect !\n";
m_Socket.Disconnect(m_Player);
m_Players.erase(m_Player);
}
PlayerManager::PlayerManager(const std::shared_ptr<IServerSocket>& a_Socket) : m_Socket(a_Socket) {
a_Socket->RegisterHandler(
[this](PlayerID a_PlayerId) { return std::make_unique<ConnectionHandler>(m_Players, *m_Socket, a_PlayerId); });
}
PlayerManager::~PlayerManager() {}
void PlayerManager::RemovePlayer(PlayerID a_Player) {
m_Socket->Disconnect(a_Player);
m_Players.erase(a_Player);
}
PlayerInfo PlayerManager::GetPlayer(PlayerID a_Player) {
return m_Players.at(a_Player);
}
} // namespace server
} // namespace td

View File

@@ -1,9 +1,18 @@
#include <server/Server.h> #include <server/Server.h>
#include <chrono>
namespace td { namespace td {
namespace server { namespace server {
Server::Server(const std::shared_ptr<IServerSocket>& a_Socket) : m_Socket(a_Socket), m_Players(a_Socket), m_LastMspt(0) {}
void Server::Update(float a_Delta) {
auto before = std::chrono::system_clock::now();
StateMachine<Server, void, float>::Update(a_Delta);
m_LastMspt = std::chrono::duration<float, std::chrono::milliseconds::period>(std::chrono::system_clock::now() - before).count();
// std::cout << "Tick : " << m_LastMspt << "ms\n";
}
} // namespace server } // namespace server
} // namespace td } // namespace td

View File

@@ -1,5 +1,5 @@
#include <server/ServerState.h>
#include <server/Server.h> #include <server/Server.h>
#include <server/ServerState.h>
#include <td/common/StateMachine.h> #include <td/common/StateMachine.h>
@@ -8,6 +8,8 @@ namespace server {
ServerState::ServerState(Server& a_Server) : Server::State(a_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)); Connect(m_StateMachine.m_Socket->OnReceive, std::bind(&ServerState::HandlePacket, this, std::placeholders::_1, std::placeholders::_2));
Connect(m_StateMachine.m_Socket->OnPlayerJoin, std::bind(&ServerState::OnPlayerJoin, this, std::placeholders::_1, std::placeholders::_2));
Connect(m_StateMachine.m_Socket->OnPlayerLeave, std::bind(&ServerState::OnPlayerLeave, this, std::placeholders::_1));
} }
ServerState::~ServerState() {} ServerState::~ServerState() {}
@@ -21,4 +23,4 @@ void ServerState::BroadcastPacket(const protocol::PacketBase& a_Packet) {
} }
} // namespace server } // namespace server
} // namespace } // namespace td

View File

@@ -8,11 +8,15 @@
#include <td/misc/Format.h> #include <td/misc/Format.h>
#include <td/misc/Log.h> #include <td/misc/Log.h>
#include <td/display/ImGuiTheme.h>
namespace td { namespace td {
Display::Display(int a_Width, int a_Height, const std::string& a_Title) : Display::Display(int a_Width, int a_Height, const std::string& a_Title) :
m_LastWidth(0), m_LastHeight(0), m_AspectRatio(1), m_ShouldClose(false) { m_LastWidth(0), m_LastHeight(0), m_AspectRatio(1), m_ShouldClose(false) {
if (!SDL_Init(SDL_INIT_VIDEO)) {
utils::LOGE(utils::Format("Could not initialize SDL! SDL error: %s", SDL_GetError()));
}
m_Window = SDL_CreateWindow(a_Title.c_str(), a_Width, a_Height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); m_Window = SDL_CreateWindow(a_Title.c_str(), a_Width, a_Height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
@@ -109,9 +113,11 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) :
// Setup scaling // Setup scaling
float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
// ImGuiStyle& style = ImGui::GetStyle(); // ImGuiStyle& style = ImGui::GetStyle();
// style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this // style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing
// this
// // requires resetting Style + calling this again) // // requires resetting Style + calling this again)
// style.FontSizeBase = 13 * main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave // style.FontSizeBase = 13 * main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We
// leave
// // both here for documentation purpose) // // both here for documentation purpose)
@@ -122,6 +128,7 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) :
// Setup Platform/Renderer backends // Setup Platform/Renderer backends
ImGui_ImplSDL3_InitForOpenGL(m_Window, m_GLContext); ImGui_ImplSDL3_InitForOpenGL(m_Window, m_GLContext);
ImGui_ImplOpenGL3_Init("#version 330"); ImGui_ImplOpenGL3_Init("#version 330");
LoadTheme();
} }
void Display::Close() { void Display::Close() {

View File

@@ -0,0 +1,88 @@
#include <td/display/ImGuiTheme.h>
#include <imgui.h>
namespace td {
void LoadTheme() {
static const bool bStyleDark_ = true;
static const float alpha_ = 0.8f;
ImGuiStyle& style = ImGui::GetStyle();
// light style from Pacôme Danhiez (user itamago) https://github.com/ocornut/imgui/pull/511#issuecomment-175719267
style.Alpha = 1.0f;
style.FrameRounding = 3.0f;
style.Colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 0.94f);
// style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
style.Colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f);
style.Colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.39f);
style.Colors[ImGuiCol_BorderShadow] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);
style.Colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.94f);
style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f);
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f);
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f);
style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f);
style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f);
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 1.00f);
style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f);
style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f);
// style.Colors[ImGuiCol_ComboBg] = ImVec4(0.86f, 0.86f, 0.86f, 0.99f);
style.Colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
style.Colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);
style.Colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
// style.Colors[ImGuiCol_Column] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
// style.Colors[ImGuiCol_ColumnHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);
// style.Colors[ImGuiCol_ColumnActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.50f);
style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
// style.Colors[ImGuiCol_CloseButton] = ImVec4(0.59f, 0.59f, 0.59f, 0.50f);
// style.Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);
// style.Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);
style.Colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
// style.Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
if (bStyleDark_) {
for (int i = 0; i <= ImGuiCol_COUNT; i++) {
ImVec4& col = style.Colors[i];
float H, S, V;
ImGui::ColorConvertRGBtoHSV(col.x, col.y, col.z, H, S, V);
if (S < 0.1f) {
V = 1.0f - V;
}
ImGui::ColorConvertHSVtoRGB(H, S, V, col.x, col.y, col.z);
if (col.w < 1.00f) {
col.w *= alpha_;
}
}
} else {
for (int i = 0; i <= ImGuiCol_COUNT; i++) {
ImVec4& col = style.Colors[i];
if (col.w < 1.00f) {
col.x *= alpha_;
col.y *= alpha_;
col.z *= alpha_;
col.w *= alpha_;
}
}
}
}
} // namespace td

View File

@@ -29,24 +29,6 @@
namespace td { namespace td {
// TODO: get rid of this class
class WorldApply : public protocol::PacketHandler {
private:
game::World& m_World;
using protocol::PacketHandler::Handle;
public:
WorldApply(game::World& a_World) : m_World(a_World) {}
void Handle(const protocol::packets::WorldHeaderPacket& a_Header) override {
m_World.LoadMap(*a_Header);
}
void Handle(const protocol::packets::WorldDataPacket& a_Data) override {
m_World.LoadMap(*a_Data);
}
};
void Save(const protocol::PacketBase& header, const protocol::PacketBase& data) { void Save(const protocol::PacketBase& header, const protocol::PacketBase& data) {
auto comp = std::make_shared<sp::ZlibCompress>(); auto comp = std::make_shared<sp::ZlibCompress>();
@@ -67,19 +49,15 @@ game::WorldPtr GetWorld() {
sp::MessageStream<protocol::PacketFactory> stream(std::move(out), std::move(comp)); sp::MessageStream<protocol::PacketFactory> stream(std::move(out), std::move(comp));
auto header = stream.ReadMessage(protocol::PacketID::WorldHeader); auto header = stream.ReadConcreteMessage<protocol::packets::WorldHeaderPacket>();
auto data = stream.ReadMessage(protocol::PacketID::WorldData); auto data = stream.ReadConcreteMessage<protocol::packets::WorldDataPacket>();
auto w = std::make_shared<game::World>(); auto w = std::make_shared<game::World>();
auto wa = std::make_shared<WorldApply>(*w);
protocol::PacketDispatcher d; w->LoadMap(**header);
d.RegisterHandler(wa); w->LoadMap(**data);
d.Dispatch(*header); // Save(*header, *data);
d.Dispatch(*data);
Save(*header, *data);
return w; return w;
} }
@@ -93,12 +71,12 @@ DebugWorldState::DebugWorldState(Display& a_Display) : DisplayState(a_Display) {
// client // client
game::WorldPtr clientWorld = GetWorld(); game::WorldPtr clientWorld = GetWorld();
auto clientFakeSocket = client::FakeSocket::Connect(serverFakeSocket); auto clientFakeSocket = client::FakeSocket::Connect(serverFakeSocket);
m_Client = std::make_unique<client::Client>(clientFakeSocket); m_Client = std::make_unique<client::Client>(clientFakeSocket, "Player0");
// render // render
m_Renderer.AddRenderer<render::WorldRenderer>(m_Camera, *clientWorld); m_Renderer.AddRenderer<render::WorldRenderer>(m_Camera, clientWorld);
m_Renderer.AddRenderer<render::EntityRenderer>(m_Camera, *clientWorld); m_Renderer.AddRenderer<render::EntityRenderer>(m_Camera, clientWorld);
m_Renderer.AddRenderer<render::TowerRenderer>(m_Camera, *clientWorld); m_Renderer.AddRenderer<render::TowerRenderer>(m_Camera, clientWorld);
// camera // camera
m_Camera.SetCamPos({77, 7, 13}); m_Camera.SetCamPos({77, 7, 13});

View File

@@ -12,8 +12,19 @@ MainMenuState::MainMenuState(Display& a_Display) : DisplayState(a_Display) {
MainMenuState::~MainMenuState() {} MainMenuState::~MainMenuState() {}
static int GetWindowFullScreenFlags() {
return ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoBackground;
}
static void SetNextWindowFullScreen() {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
}
void MainMenuState::Update(float a_Delta) { void MainMenuState::Update(float a_Delta) {
ImGui::Begin("MainWindow"); SetNextWindowFullScreen();
ImGui::Begin("MainWindow", nullptr, GetWindowFullScreenFlags());
MainMenuStateStack::Update(); MainMenuStateStack::Update();
ImGui::End(); ImGui::End();
} }

View File

@@ -5,7 +5,7 @@
namespace td { namespace td {
namespace render { namespace render {
EntityRenderer::EntityRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { EntityRenderer::EntityRenderer(Camera& a_Camera, const game::WorldPtr& a_World) : Renderer(a_Camera), m_World(a_World) {
m_EntityVao = std::make_unique<GL::VertexArray>(WorldLoader::LoadMobModel()); m_EntityVao = std::make_unique<GL::VertexArray>(WorldLoader::LoadMobModel());
m_Shader->Start(); m_Shader->Start();
m_Shader->SetColorEffect({1, 0, 1}); m_Shader->SetColorEffect({1, 0, 1});
@@ -17,7 +17,7 @@ EntityRenderer::~EntityRenderer() {}
void EntityRenderer::Render(float a_Lerp) { void EntityRenderer::Render(float a_Lerp) {
m_Shader->Start(); m_Shader->Start();
for (const auto& mob : m_World.GetMobList()) { for (const auto& mob : m_World->GetMobList()) {
float x = Lerp<game::Mob>(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast<float>(a_Mob.m_Position.x); }); float x = Lerp<game::Mob>(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast<float>(a_Mob.m_Position.x); });
float z = Lerp<game::Mob>(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast<float>(a_Mob.m_Position.y); }); float z = Lerp<game::Mob>(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast<float>(a_Mob.m_Position.y); });

View File

@@ -5,7 +5,7 @@
namespace td { namespace td {
namespace render { namespace render {
TowerRenderer::TowerRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { TowerRenderer::TowerRenderer(Camera& a_Camera, const game::WorldPtr& a_World) : Renderer(a_Camera), m_World(a_World) {
m_EntityVao = std::make_unique<GL::VertexArray>(WorldLoader::LoadMobModel()); m_EntityVao = std::make_unique<GL::VertexArray>(WorldLoader::LoadMobModel());
m_Shader->Start(); m_Shader->Start();
m_Shader->SetColorEffect({0, 0, 1}); m_Shader->SetColorEffect({0, 0, 1});
@@ -17,7 +17,7 @@ TowerRenderer::~TowerRenderer() {}
void TowerRenderer::Render(float a_Lerp) { void TowerRenderer::Render(float a_Lerp) {
m_Shader->Start(); m_Shader->Start();
for (const auto& tower : m_World.GetTowers()) { for (const auto& tower : m_World->GetTowers()) {
m_Shader->SetModelPos({tower->GetCenterX(), 1, tower->GetCenterY()}); m_Shader->SetModelPos({tower->GetCenterX(), 1, tower->GetCenterY()});
Renderer::Render(*m_EntityVao); Renderer::Render(*m_EntityVao);
} }

View File

@@ -7,8 +7,8 @@
namespace td { namespace td {
namespace render { namespace render {
WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera) { WorldRenderer::WorldRenderer(Camera& a_Camera, const game::WorldPtr& a_World) : Renderer(a_Camera) {
m_WorldVao = std::make_unique<GL::VertexArray>(WorldLoader::LoadWorldModel(&a_World)); m_WorldVao = std::make_unique<GL::VertexArray>(WorldLoader::LoadWorldModel(a_World.get()));
} }
WorldRenderer::~WorldRenderer() {} WorldRenderer::~WorldRenderer() {}

View File

@@ -3,7 +3,7 @@ add_rules("mode.debug", "mode.release")
add_repositories("persson-repo https://git.ale-pri.com/Persson-dev/xmake-repo.git") add_repositories("persson-repo https://git.ale-pri.com/Persson-dev/xmake-repo.git")
add_requires("imgui 1.92.0", {configs = {sdl3 = true, opengl3 = true}}) add_requires("imgui 1.92.0", {configs = {sdl3 = true, opengl3 = true}})
add_requires("libsdl3 3.2.16", "splib 2.3.0", "zlib", "glew", "fpm", "enet6") add_requires("libsdl3 3.2.16", "splib 2.3.1", "zlib", "glew", "fpm", "enet6")
set_languages("c++17") set_languages("c++17")
@@ -13,6 +13,8 @@ if is_mode("release") then
set_warnings("all", "error") set_warnings("all", "error")
end end
option("valgrind", {description = "Run binary with valgrind", default = false})
target("Tower-Defense2") target("Tower-Defense2")
add_includedirs("include", {public = true}) add_includedirs("include", {public = true})
set_kind("binary") set_kind("binary")
@@ -21,6 +23,11 @@ target("Tower-Defense2")
set_rundir(".") set_rundir(".")
add_defines("TD_GL_LOADER_GLEW") add_defines("TD_GL_LOADER_GLEW")
if has_config("valgrind") then
on_run(function (target)
os.execv("valgrind", {"-s", "--leak-check=full", target:targetfile()})
end)
end
-- Tests -- Tests