Compare commits
12 Commits
ba84864b6a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
f879c5fe8f
|
|||
|
b5ff44d793
|
|||
|
24252896c7
|
|||
|
4c0078a5f2
|
|||
|
4072e49b32
|
|||
|
e0080fa50c
|
|||
|
8bdcffcfa6
|
|||
|
6b987cf78d
|
|||
|
cba790f804
|
|||
|
ac3e949323
|
|||
|
b09c7f9efd
|
|||
|
110e6a62d2
|
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# (CubeCraft) Tower Defense
|
||||
|
||||
## Currently in contruction 🏗️👷⚙️
|
||||
24
include/client/Client.h
Normal file
24
include/client/Client.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <client/IClientSocket.h>
|
||||
#include <td/common/StateMachine.h>
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
class ClientState;
|
||||
|
||||
class Client : public StateMachine<Client, void, float> {
|
||||
private:
|
||||
std::shared_ptr<IClientSocket> m_Socket;
|
||||
|
||||
public:
|
||||
Client(const std::shared_ptr<IClientSocket>& a_Socket) : m_Socket(a_Socket) {}
|
||||
|
||||
void SendPacket(const protocol::PacketBase& a_Packet);
|
||||
|
||||
friend class ClientState;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
20
include/client/ClientState.h
Normal file
20
include/client/ClientState.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <client/Client.h>
|
||||
#include <td/misc/SlotGuard.h>
|
||||
|
||||
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
|
||||
23
include/client/IClientSocket.h
Normal file
23
include/client/IClientSocket.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <td/Types.h>
|
||||
#include <td/misc/Signal.h>
|
||||
#include <td/protocol/packet/Packets.h>
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
class IClientSocket {
|
||||
public:
|
||||
utils::Signal<> OnConnect;
|
||||
utils::Signal<> OnDisconnect;
|
||||
utils::Signal<const protocol::PacketBase&> OnReceive;
|
||||
|
||||
virtual void Send(const protocol::PacketBase& a_Packet) = 0;
|
||||
|
||||
IClientSocket() {}
|
||||
virtual ~IClientSocket() {}
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
33
include/client/socket/FakeSocket.h
Normal file
33
include/client/socket/FakeSocket.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <client/IClientSocket.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
namespace server {
|
||||
class FakeSocket;
|
||||
} // namespace server
|
||||
|
||||
|
||||
namespace client {
|
||||
|
||||
class FakeSocket : public IClientSocket {
|
||||
private:
|
||||
std::shared_ptr<server::FakeSocket> m_Server;
|
||||
PeerID m_PeerId;
|
||||
|
||||
struct Private{ explicit Private() = default; };
|
||||
|
||||
public:
|
||||
FakeSocket(Private) {}
|
||||
~FakeSocket() {}
|
||||
|
||||
static std::shared_ptr<FakeSocket> Connect(const std::shared_ptr<server::FakeSocket>& 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
|
||||
31
include/client/state/GameState.h
Normal file
31
include/client/state/GameState.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <client/ClientState.h>
|
||||
#include <td/game/World.h>
|
||||
#include <td/simulation/ClientSimulation.h>
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
class GameState : public ClientState {
|
||||
private:
|
||||
std::shared_ptr<game::World> m_World;
|
||||
sim::ClientSimulation m_Simulation;
|
||||
float m_CurrentLerp;
|
||||
|
||||
public:
|
||||
GameState(Client& a_Client, const std::shared_ptr<game::World>& a_World, std::uint64_t a_StepTime);
|
||||
~GameState() {}
|
||||
|
||||
virtual void Update(float a_Delta) override;
|
||||
|
||||
float GetCurrentLerp() const {
|
||||
return m_CurrentLerp;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void HandlePacket(const protocol::PacketBase& a_Packet) override;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <server/IServerSocket.h>
|
||||
#include <td/misc/SlotGuard.h>
|
||||
|
||||
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<IServerState>& a_NewState);
|
||||
virtual void Init() {}
|
||||
|
||||
private:
|
||||
Server* m_Server;
|
||||
|
||||
void SetServer(Server* a_Server);
|
||||
|
||||
friend class Server;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
@@ -1,35 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <server/IServerSocket.h>
|
||||
#include <server/IServerState.h>
|
||||
#include <chrono>
|
||||
#include <td/common/StateMachine.h>
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
class Server {
|
||||
class ServerState;
|
||||
|
||||
class Server : public StateMachine<Server, void, float> {
|
||||
private:
|
||||
std::shared_ptr<IServerSocket> m_Socket;
|
||||
std::shared_ptr<IServerState> m_State;
|
||||
std::chrono::time_point<std::chrono::system_clock> m_LastTime;
|
||||
|
||||
public:
|
||||
Server(const std::shared_ptr<IServerSocket>& a_Socket) : m_Socket(a_Socket), m_LastTime(std::chrono::system_clock::now()) {}
|
||||
Server(const std::shared_ptr<IServerSocket>& a_Socket) : m_Socket(a_Socket) {}
|
||||
|
||||
void Update();
|
||||
|
||||
void UpdateState(const std::shared_ptr<IServerState>& a_State) {
|
||||
m_State = a_State;
|
||||
m_State->SetServer(this);
|
||||
}
|
||||
|
||||
private:
|
||||
void Update(float a_Delta) {
|
||||
m_State->Update(a_Delta);
|
||||
}
|
||||
|
||||
friend class IServerState;
|
||||
friend class ServerState;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
|
||||
23
include/server/ServerState.h
Normal file
23
include/server/ServerState.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <server/Server.h>
|
||||
#include <td/misc/SlotGuard.h>
|
||||
|
||||
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
|
||||
@@ -1,22 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <client/socket/FakeSocket.h>
|
||||
#include <optional>
|
||||
#include <server/IServerSocket.h>
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
class FakeSocket : public IServerSocket {
|
||||
private:
|
||||
std::vector<std::optional<std::weak_ptr<client::FakeSocket>>> m_Clients;
|
||||
|
||||
public:
|
||||
FakeSocket() {}
|
||||
~FakeSocket() {}
|
||||
|
||||
utils::Signal<PeerID, const protocol::PacketBase&> OnSend;
|
||||
|
||||
void ConnectFakePeer(PeerID a_Peer);
|
||||
PeerID ConnectFakePeer(const std::shared_ptr<client::FakeSocket>& 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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <server/IServerState.h>
|
||||
#include <server/ServerState.h>
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
class EndGameState : public IServerState {
|
||||
class EndGameState : public ServerState {
|
||||
private:
|
||||
/* data */
|
||||
public:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <server/IServerState.h>
|
||||
#include <server/ServerState.h>
|
||||
#include <td/game/World.h>
|
||||
#include <td/simulation/ServerSimulation.h>
|
||||
|
||||
@@ -9,23 +9,18 @@ namespace server {
|
||||
|
||||
class GameStateHandler;
|
||||
|
||||
class GameState : public IServerState {
|
||||
class GameState : public ServerState {
|
||||
private:
|
||||
std::shared_ptr<game::World> m_World;
|
||||
sim::ServerSimulation m_Simulation;
|
||||
float m_Time;
|
||||
|
||||
public:
|
||||
GameState(const std::shared_ptr<game::World>& a_World);
|
||||
GameState(Server& a_Server, const std::shared_ptr<game::World>& 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;
|
||||
};
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <server/IServerState.h>
|
||||
#include <server/ServerState.h>
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
// this class is temporary useless
|
||||
class LobbyState : public IServerState {
|
||||
class LobbyState : public ServerState {
|
||||
private:
|
||||
std::shared_ptr<game::World> m_World;
|
||||
public:
|
||||
LobbyState(const std::shared_ptr<game::World>& a_World) : m_World(a_World) {}
|
||||
LobbyState(Server& a_Server, const std::shared_ptr<game::World>& 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
|
||||
|
||||
47
include/td/common/StateMachine.h
Normal file
47
include/td/common/StateMachine.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace td {
|
||||
|
||||
template <typename TDerived, typename TReturn, typename... TArgs>
|
||||
class StateMachine {
|
||||
public:
|
||||
class State {
|
||||
public:
|
||||
State(TDerived& a_StateMachine) : m_StateMachine(a_StateMachine) {}
|
||||
virtual ~State() {}
|
||||
|
||||
virtual TReturn Update(TArgs... args) = 0;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T* ChangeState(Args&&... args) {
|
||||
return m_StateMachine.template ChangeState<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
protected:
|
||||
TDerived& m_StateMachine;
|
||||
};
|
||||
|
||||
StateMachine() {}
|
||||
StateMachine(StateMachine&&) = default;
|
||||
virtual ~StateMachine() {}
|
||||
|
||||
virtual TReturn Update(TArgs... args) {
|
||||
assert(m_State && "You must change state at least once before updating !");
|
||||
return m_State->Update(args...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T* ChangeState(Args&&... args) {
|
||||
m_State = std::make_unique<T>(static_cast<TDerived&>(*this), std::forward<Args>(args)...);
|
||||
return static_cast<T*>(m_State.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<State> m_State;
|
||||
};
|
||||
|
||||
|
||||
} // namespace td
|
||||
69
include/td/common/StateStack.h
Normal file
69
include/td/common/StateStack.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace td {
|
||||
|
||||
template <typename TDerived, typename TReturn, typename... TArgs>
|
||||
class StateStack {
|
||||
public:
|
||||
class State {
|
||||
public:
|
||||
State(TDerived& a_StateStack) : m_StateStack(a_StateStack) {}
|
||||
virtual ~State() {}
|
||||
|
||||
virtual TReturn Update(TArgs... args) = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Called when enabled (can be used to connect signals)
|
||||
*/
|
||||
virtual void OnEnable() {}
|
||||
|
||||
/**
|
||||
* \brief Called when disabled (can be used to disconnect signals)
|
||||
*/
|
||||
virtual void OnDisable() {}
|
||||
|
||||
protected:
|
||||
TDerived& m_StateStack;
|
||||
|
||||
friend class StateStack;
|
||||
};
|
||||
|
||||
StateStack() {}
|
||||
StateStack(StateStack&&) = default;
|
||||
virtual ~StateStack() {}
|
||||
|
||||
virtual TReturn Update(TArgs... args) {
|
||||
assert(!m_States.empty() && "You must push at least one state before updating !");
|
||||
return m_States.back()->Update(args...);
|
||||
}
|
||||
|
||||
void PopState() {
|
||||
assert(!m_States.empty() && "You must push at least one state before poping !");
|
||||
m_States.back()->OnDisable();
|
||||
m_States.pop_back();
|
||||
if (m_States.empty())
|
||||
return;
|
||||
m_States.back()->OnEnable();
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T* PushState(Args&&... args) {
|
||||
if (!m_States.empty())
|
||||
m_States.back()->OnDisable();
|
||||
auto newState = std::make_unique<T>(static_cast<TDerived&>(*this), std::forward<Args>(args)...);
|
||||
newState->OnEnable();
|
||||
m_States.push_back(std::move(newState));
|
||||
return static_cast<T*>(newState.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<State>> m_States;
|
||||
};
|
||||
|
||||
|
||||
} // namespace td
|
||||
@@ -2,13 +2,23 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <SDL3/SDL_keycode.h>
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <td/common/StateMachine.h>
|
||||
#include <td/misc/Signal.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
class Display {
|
||||
class Display : public StateMachine<Display, void, float> {
|
||||
private:
|
||||
SDL_Window* m_Window;
|
||||
SDL_GLContext m_GLContext;
|
||||
|
||||
int m_LastWidth, m_LastHeight;
|
||||
float m_AspectRatio;
|
||||
|
||||
bool m_ShouldClose;
|
||||
|
||||
public:
|
||||
utils::Signal<float> OnAspectRatioChange;
|
||||
utils::Signal<SDL_Keycode> OnKeyDown;
|
||||
@@ -17,7 +27,8 @@ class Display {
|
||||
~Display();
|
||||
|
||||
void PollEvents();
|
||||
void Update();
|
||||
void Update(float a_Delta) override;
|
||||
void Close();
|
||||
|
||||
bool IsCloseRequested() {
|
||||
return m_ShouldClose;
|
||||
@@ -34,15 +45,6 @@ class Display {
|
||||
int GetWindowHeight() {
|
||||
return m_LastHeight;
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_Window* m_Window;
|
||||
SDL_GLContext m_GLContext;
|
||||
|
||||
int m_LastWidth, m_LastHeight;
|
||||
float m_AspectRatio;
|
||||
|
||||
bool m_ShouldClose;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
16
include/td/display/DisplayState.h
Normal file
16
include/td/display/DisplayState.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <td/display/Display.h>
|
||||
#include <td/misc/SlotGuard.h>
|
||||
|
||||
namespace td {
|
||||
class DisplayState : public Display::State, private utils::SlotGuard {
|
||||
public:
|
||||
DisplayState(Display& a_Display);
|
||||
virtual ~DisplayState() {}
|
||||
|
||||
protected:
|
||||
virtual void OnAspectRatioChange(float a_Ratio) {}
|
||||
virtual void OnKeyDown(SDL_Keycode a_Key) {}
|
||||
};
|
||||
} // namespace td
|
||||
15
include/td/display/menu/CreatePartyMenu.h
Normal file
15
include/td/display/menu/CreatePartyMenu.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <td/display/state/MainMenuState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
class CreatePartyMenu : public MainMenuState::Menu {
|
||||
public:
|
||||
CreatePartyMenu(MainMenuState& a_MainMenu);
|
||||
~CreatePartyMenu();
|
||||
|
||||
virtual void Update() override;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
15
include/td/display/menu/JoinPartyMenu.h
Normal file
15
include/td/display/menu/JoinPartyMenu.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <td/display/state/MainMenuState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
class JoinPartyMenu : public MainMenuState::Menu {
|
||||
public:
|
||||
JoinPartyMenu(MainMenuState& a_MainMenu);
|
||||
~JoinPartyMenu();
|
||||
|
||||
virtual void Update() override;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
15
include/td/display/menu/MainMenu.h
Normal file
15
include/td/display/menu/MainMenu.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <td/display/state/MainMenuState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
class MainMenu : public MainMenuState::Menu {
|
||||
public:
|
||||
MainMenu(MainMenuState& a_MainMenu);
|
||||
~MainMenu();
|
||||
|
||||
virtual void Update() override;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
15
include/td/display/menu/SettingsMenu.h
Normal file
15
include/td/display/menu/SettingsMenu.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <td/display/state/MainMenuState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
class SettingsMenu : public MainMenuState::Menu {
|
||||
public:
|
||||
SettingsMenu(MainMenuState& a_MainMenu);
|
||||
~SettingsMenu();
|
||||
|
||||
virtual void Update() override;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
31
include/td/display/state/DebugWorldState.h
Normal file
31
include/td/display/state/DebugWorldState.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <client/Client.h>
|
||||
#include <server/Server.h>
|
||||
#include <td/display/DisplayState.h>
|
||||
#include <td/render/Renderer.h>
|
||||
#include <td/simulation/ClientSimulation.h>
|
||||
#include <client/state/GameState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
class DebugWorldState : public DisplayState {
|
||||
private:
|
||||
render::RenderPipeline m_Renderer;
|
||||
render::Camera m_Camera;
|
||||
std::unique_ptr<client::Client> m_Client;
|
||||
std::unique_ptr<server::Server> m_Server;
|
||||
client::GameState* m_ClientState;
|
||||
|
||||
public:
|
||||
DebugWorldState(Display& a_Display);
|
||||
~DebugWorldState();
|
||||
|
||||
virtual void Update(float a_Delta) override;
|
||||
|
||||
protected:
|
||||
virtual void OnAspectRatioChange(float a_Ratio) override;
|
||||
virtual void OnKeyDown(SDL_Keycode a_Key) override;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
27
include/td/display/state/MainMenuState.h
Normal file
27
include/td/display/state/MainMenuState.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <td/common/StateStack.h>
|
||||
#include <td/display/DisplayState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
class MainMenuState;
|
||||
|
||||
using MainMenuStateStack = StateStack<MainMenuState, void>;
|
||||
|
||||
class MainMenuState : public DisplayState, public MainMenuStateStack {
|
||||
public:
|
||||
using Menu = MainMenuStateStack::State;
|
||||
|
||||
MainMenuState(Display& a_Display);
|
||||
~MainMenuState();
|
||||
|
||||
virtual void Update(float a_Delta) override;
|
||||
|
||||
void RenderBackButton();
|
||||
|
||||
protected:
|
||||
virtual void OnKeyDown(SDL_Keycode a_Key) override;
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
@@ -8,8 +8,11 @@
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* \brief Signal class
|
||||
*/
|
||||
template <typename... Args>
|
||||
class Signal : private NonCopyable {
|
||||
class SignalRaw : private NonCopyable {
|
||||
public:
|
||||
using FnType = void(Args...);
|
||||
using CallBack = std::function<FnType>;
|
||||
@@ -35,5 +38,63 @@ class Signal : private NonCopyable {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Memory managed Signal class
|
||||
*/
|
||||
template <typename... Args>
|
||||
class Signal {
|
||||
public:
|
||||
using SignalBase = SignalRaw<Args...>;
|
||||
using CallBack = typename SignalBase::CallBack;
|
||||
using SignalPtr = std::shared_ptr<SignalBase>;
|
||||
class ConnectionGuard;
|
||||
|
||||
private:
|
||||
SignalPtr m_Signal;
|
||||
|
||||
public:
|
||||
Signal() : m_Signal(std::make_shared<SignalBase>()) {}
|
||||
Signal(const Signal&) = default;
|
||||
|
||||
void Connect(const CallBack& a_Callback) {
|
||||
m_Signal->Connect(a_Callback);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ConnectionGuard> ConnectSafe(const CallBack& a_Callback) {
|
||||
Connect(a_Callback);
|
||||
return std::make_unique<ConnectionGuard>(*this, a_Callback);
|
||||
}
|
||||
|
||||
void Disconnect(const CallBack& a_Callback) {
|
||||
m_Signal->Disconnect(a_Callback);
|
||||
}
|
||||
|
||||
void operator()(Args... args) const {
|
||||
m_Signal->operator()(args...);
|
||||
}
|
||||
};
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
virtual ~Connection() {}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
class Signal<Args...>::ConnectionGuard : public Connection {
|
||||
private:
|
||||
using CallBack = typename Signal<Args...>::CallBack;
|
||||
|
||||
std::weak_ptr<SignalRaw<Args...>> m_Signal;
|
||||
CallBack m_Callback;
|
||||
|
||||
public:
|
||||
ConnectionGuard(const Signal<Args...>& a_Signal, const CallBack& a_Callback) : m_Signal(a_Signal.m_Signal), m_Callback(a_Callback) {}
|
||||
|
||||
~ConnectionGuard() {
|
||||
if (!m_Signal.expired())
|
||||
m_Signal.lock()->Disconnect(m_Callback);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <td/misc/SlotGuard.h>
|
||||
#include <td/misc/Signal.h>
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
@@ -12,22 +12,19 @@ namespace utils {
|
||||
*/
|
||||
class SlotGuard {
|
||||
private:
|
||||
std::vector<std::function<void()>> m_Deleters;
|
||||
std::vector<std::unique_ptr<Connection>> m_Connections;
|
||||
|
||||
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); });
|
||||
void Connect(Signal<Args...> a_Signal, const typename Signal<Args...>::CallBack& a_Callback) {
|
||||
m_Connections.push_back(a_Signal.ConnectSafe(a_Callback));
|
||||
}
|
||||
|
||||
~SlotGuard() {
|
||||
for (auto& deleter : m_Deleters) {
|
||||
deleter();
|
||||
}
|
||||
void Disconnect() {
|
||||
m_Connections.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,10 +14,9 @@ using GameBuffer = std::vector<std::optional<td::protocol::LockStep>>;
|
||||
class ClientSimulation : public protocol::PacketHandler {
|
||||
private:
|
||||
std::uint64_t m_StepTime;
|
||||
game::World& m_World;
|
||||
std::shared_ptr<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<WorldSnapshot> m_LastSnapshot;
|
||||
@@ -34,18 +33,18 @@ class ClientSimulation : public protocol::PacketHandler {
|
||||
* \brief Replay constructor
|
||||
* \param a_StepTime in ms
|
||||
*/
|
||||
ClientSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime);
|
||||
ClientSimulation(std::shared_ptr<game::World> a_World, GameHistory&& a_History, std::uint64_t a_StepTime);
|
||||
|
||||
/**
|
||||
* \brief Live update constructor (continuous game updates)
|
||||
* \param a_StepTime in ms
|
||||
*/
|
||||
ClientSimulation(game::World& a_World, std::uint64_t a_StepTime);
|
||||
ClientSimulation(std::shared_ptr<game::World> a_World, std::uint64_t a_StepTime);
|
||||
|
||||
/**
|
||||
* \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;
|
||||
|
||||
11
src/client/Client.cpp
Normal file
11
src/client/Client.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <client/Client.h>
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
void Client::SendPacket(const protocol::PacketBase& a_Packet) {
|
||||
m_Socket->Send(a_Packet);
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
16
src/client/ClientState.cpp
Normal file
16
src/client/ClientState.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <client/ClientState.h>
|
||||
#include <client/Client.h>
|
||||
|
||||
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
|
||||
19
src/client/socket/FakeSocket.cpp
Normal file
19
src/client/socket/FakeSocket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <client/socket/FakeSocket.h>
|
||||
#include <server/socket/FakeSocket.h>
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
void FakeSocket::Send(const protocol::PacketBase& a_Packet) {
|
||||
m_Server->OnReceivePeer(m_PeerId, a_Packet);
|
||||
}
|
||||
|
||||
std::shared_ptr<FakeSocket> FakeSocket::Connect(const std::shared_ptr<server::FakeSocket>& a_Server) {
|
||||
auto socket = std::make_shared<FakeSocket>(Private());
|
||||
socket->m_Server = a_Server;
|
||||
socket->m_PeerId = a_Server->ConnectFakePeer(socket);
|
||||
return socket;
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
41
src/client/state/GameState.cpp
Normal file
41
src/client/state/GameState.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <client/state/GameState.h>
|
||||
|
||||
namespace td {
|
||||
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) :
|
||||
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) {
|
||||
SendPacket(protocol::packets::LockStepRequestPacket(a_MissingSteps));
|
||||
});
|
||||
}
|
||||
|
||||
void GameState::HandlePacket(const protocol::PacketBase& a_Packet) {
|
||||
ClientHandler handler(m_Simulation);
|
||||
a_Packet.Dispatch(handler);
|
||||
}
|
||||
|
||||
void GameState::Update(float a_Delta) {
|
||||
m_CurrentLerp = m_Simulation.Update(a_Delta);
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
157
src/main.cpp
157
src/main.cpp
@@ -1,159 +1,24 @@
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <td/display/state/MainMenuState.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <td/game/World.h>
|
||||
#include <td/input/Display.h>
|
||||
#include <td/protocol/packet/PacketSerialize.h>
|
||||
#include <td/protocol/packet/Packets.h>
|
||||
#include <td/render/renderer/EntityRenderer.h>
|
||||
#include <td/render/renderer/TowerRenderer.h>
|
||||
#include <td/render/renderer/WorldRenderer.h>
|
||||
#include <td/simulation/ClientSimulation.h>
|
||||
|
||||
#include <sp/common/DataBuffer.h>
|
||||
#include <sp/extensions/Compress.h>
|
||||
#include <sp/io/MessageStream.h>
|
||||
#include <sp/io/StdIo.h>
|
||||
|
||||
#include <server/Server.h>
|
||||
#include <server/socket/FakeSocket.h>
|
||||
#include <server/state/GameState.h>
|
||||
|
||||
class WorldApply : public td::protocol::PacketHandler {
|
||||
private:
|
||||
td::game::World& m_World;
|
||||
using td::protocol::PacketHandler::Handle;
|
||||
|
||||
public:
|
||||
WorldApply(td::game::World& a_World) : m_World(a_World) {}
|
||||
|
||||
void Handle(const td::protocol::packets::WorldHeaderPacket& a_Header) override {
|
||||
m_World.LoadMap(*a_Header);
|
||||
}
|
||||
|
||||
void Handle(const td::protocol::packets::WorldDataPacket& a_Data) override {
|
||||
m_World.LoadMap(*a_Data);
|
||||
}
|
||||
};
|
||||
|
||||
class ClientHandler : public td::protocol::PacketHandler {
|
||||
private:
|
||||
td::sim::ClientSimulation& m_Simulation;
|
||||
|
||||
using td::protocol::PacketHandler::Handle;
|
||||
|
||||
public:
|
||||
ClientHandler(td::sim::ClientSimulation& a_Simulation) : m_Simulation(a_Simulation) {}
|
||||
|
||||
void Handle(const td::protocol::packets::LockStepsPacket& a_LockStep) {
|
||||
m_Simulation.Handle(a_LockStep);
|
||||
}
|
||||
|
||||
void Handle(const td::protocol::packets::LockStepResponsePacket& a_LockStep) {
|
||||
m_Simulation.Handle(a_LockStep);
|
||||
}
|
||||
};
|
||||
|
||||
void Save(const td::protocol::PacketBase& header, const td::protocol::PacketBase& data) {
|
||||
auto comp = std::make_shared<sp::ZlibCompress>();
|
||||
|
||||
std::ofstream fStream("test/tdmap.tdmap3");
|
||||
auto out = std::make_shared<sp::StdOuput>(fStream);
|
||||
|
||||
sp::MessageStream<td::protocol::PacketFactory> stream(std::move(out), std::move(comp));
|
||||
|
||||
stream.WriteMessage(header, false);
|
||||
stream.WriteMessage(data, false);
|
||||
}
|
||||
|
||||
td::game::WorldPtr GetWorld() {
|
||||
auto comp = std::make_shared<sp::ZlibCompress>();
|
||||
|
||||
std::ifstream fStream("test/tdmap.tdmap2");
|
||||
auto out = std::make_shared<sp::StdInput>(fStream);
|
||||
|
||||
sp::MessageStream<td::protocol::PacketFactory> stream(std::move(out), std::move(comp));
|
||||
|
||||
auto header = stream.ReadMessage(td::protocol::PacketID::WorldHeader);
|
||||
auto data = stream.ReadMessage(td::protocol::PacketID::WorldData);
|
||||
|
||||
auto w = std::make_shared<td::game::World>();
|
||||
auto wa = std::make_shared<WorldApply>(*w);
|
||||
|
||||
td::protocol::PacketDispatcher d;
|
||||
d.RegisterHandler(wa);
|
||||
|
||||
d.Dispatch(*header);
|
||||
d.Dispatch(*data);
|
||||
|
||||
Save(*header, *data);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
void FastForward(td::game::World& a_World, const td::sim::GameHistory& a_LockSteps) {
|
||||
const td::FpFloat delta = td::FpFloat(1) / td::FpFloat(75);
|
||||
for (const auto& lockstep : a_LockSteps) {
|
||||
a_World.Tick(lockstep, delta);
|
||||
}
|
||||
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) {
|
||||
td::game::WorldPtr serverWorld = GetWorld();
|
||||
|
||||
// server
|
||||
auto fakeSocket = std::make_shared<td::server::FakeSocket>();
|
||||
td::server::Server server(fakeSocket);
|
||||
|
||||
// init GL context
|
||||
td::Display display(1920, 1080, "Tower-Defense 2");
|
||||
|
||||
td::render::Camera cam;
|
||||
|
||||
display.OnAspectRatioChange.Connect([&cam](float a_AspectRatio) { cam.UpdatePerspective(a_AspectRatio); });
|
||||
|
||||
td::game::WorldPtr clientWorld = GetWorld();
|
||||
|
||||
td::render::RenderPipeline renderer;
|
||||
renderer.AddRenderer<td::render::WorldRenderer>(cam, *clientWorld);
|
||||
renderer.AddRenderer<td::render::EntityRenderer>(cam, *clientWorld);
|
||||
renderer.AddRenderer<td::render::TowerRenderer>(cam, *clientWorld);
|
||||
|
||||
cam.SetCamPos({77, 7, 13});
|
||||
cam.UpdatePerspective(display.GetAspectRatio());
|
||||
|
||||
td::sim::ClientSimulation simulation(*clientWorld, td::STEP_TIME);
|
||||
ClientHandler clientHandler(simulation);
|
||||
|
||||
simulation.OnMissingLockSteps.Connect([&fakeSocket](const std::vector<td::StepTime>& a_MissingSteps){
|
||||
fakeSocket->OnReceive(0, td::protocol::packets::LockStepRequestPacket(a_MissingSteps));
|
||||
});
|
||||
|
||||
// temporary tests
|
||||
display.OnKeyDown.Connect([&fakeSocket](SDL_Keycode key) {
|
||||
if (key == SDLK_A) {
|
||||
fakeSocket->OnReceive(0, td::protocol::packets::SpawnTroopPacket(td::EntityType::Zombie, 1));
|
||||
} else if (key == SDLK_Z) {
|
||||
fakeSocket->OnReceive(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->OnSend.Connect([&clientHandler](td::PeerID a_Peer, const td::protocol::PacketBase& a_Packet) {
|
||||
a_Packet.Dispatch(clientHandler);
|
||||
});
|
||||
|
||||
server.UpdateState(std::make_shared<td::server::GameState>(serverWorld));
|
||||
display.ChangeState<td::MainMenuState>();
|
||||
|
||||
while (!display.IsCloseRequested()) {
|
||||
display.PollEvents();
|
||||
server.Update();
|
||||
float lerp = simulation.Update();
|
||||
renderer.Render(lerp);
|
||||
display.Update();
|
||||
float delta = GetDelta();
|
||||
display.Update(delta);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#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));
|
||||
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<IServerState>& a_NewState) {
|
||||
m_Server->UpdateState(a_NewState);
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
@@ -3,12 +3,7 @@
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
void Server::Update() {
|
||||
auto timeElapsed = std::chrono::system_clock::now() - m_LastTime;
|
||||
float timeSeconds = std::chrono::duration<float, std::chrono::seconds::period>(timeElapsed).count();
|
||||
Update(timeSeconds);
|
||||
m_LastTime = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
|
||||
24
src/server/ServerState.cpp
Normal file
24
src/server/ServerState.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <server/ServerState.h>
|
||||
#include <server/Server.h>
|
||||
|
||||
#include <td/common/StateMachine.h>
|
||||
|
||||
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
|
||||
@@ -4,16 +4,45 @@ namespace td {
|
||||
namespace server {
|
||||
|
||||
void FakeSocket::SendPeer(PeerID a_Peer, const protocol::PacketBase& a_Packet) {
|
||||
OnSend(a_Peer, a_Packet);
|
||||
auto socket = m_Clients.at(a_Peer);
|
||||
assert(socket.has_value());
|
||||
assert(!socket.value().expired());
|
||||
socket.value().lock()->OnReceive(a_Packet);
|
||||
}
|
||||
|
||||
void FakeSocket::ConnectFakePeer(PeerID a_Peer) {
|
||||
OnConnectPeer(a_Peer);
|
||||
void FakeSocket::ReceiveFromFakePeer(PeerID a_Peer, const protocol::PacketBase& a_Packet) {
|
||||
OnReceivePeer(a_Peer, a_Packet);
|
||||
}
|
||||
|
||||
PeerID FakeSocket::ConnectFakePeer(const std::shared_ptr<client::FakeSocket>& 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());
|
||||
assert(!socket.value().expired());
|
||||
socket.value().lock()->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
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
GameState::GameState(const std::shared_ptr<game::World>& 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<game::World>& 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());
|
||||
}
|
||||
@@ -19,6 +17,7 @@ void GameState::HandlePacket(PlayerID a_Id, const protocol::PacketBase& a_Packet
|
||||
}
|
||||
|
||||
void GameState::Update(float a_Delta) {
|
||||
// TODO: don't make STEP_TIME constant
|
||||
static const float stepTimeSecond = static_cast<float>(STEP_TIME) / 1000.0f;
|
||||
m_Time += a_Delta;
|
||||
if (m_Time > stepTimeSecond) {
|
||||
@@ -28,9 +27,5 @@ void GameState::Update(float a_Delta) {
|
||||
}
|
||||
}
|
||||
|
||||
void GameState::OnPlayerJoin(PlayerID a_Id) {}
|
||||
|
||||
void GameState::OnPlayerLeave(PlayerID a_Id) {}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
|
||||
@@ -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<GameState>(m_World));
|
||||
}
|
||||
|
||||
void LobbyState::OnPlayerJoin(PlayerID a_Id) {
|
||||
|
||||
}
|
||||
|
||||
void LobbyState::OnPlayerLeave(PlayerID a_Id) {
|
||||
|
||||
m_StateMachine.ChangeState<GameState>(m_World);
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <td/input/Display.h>
|
||||
#include <td/display/Display.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <SDL3/SDL.h>
|
||||
@@ -80,8 +80,7 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) :
|
||||
utils::LOG(utils::Format(
|
||||
"GL Context : %i.%i %s, Color : R:%i G:%i B:%i A:%i, Depth bits : %i", major, minor, mask_desc, r, g, b, a, depth));
|
||||
|
||||
utils::LOG(utils::Format(
|
||||
"MultiSamples : Buffers : %i, Samples : %i", mBuffers, mSamples));
|
||||
utils::LOG(utils::Format("MultiSamples : Buffers : %i, Samples : %i", mBuffers, mSamples));
|
||||
|
||||
SDL_GL_MakeCurrent(m_Window, m_GLContext);
|
||||
|
||||
@@ -125,6 +124,9 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) :
|
||||
ImGui_ImplOpenGL3_Init("#version 330");
|
||||
}
|
||||
|
||||
void Display::Close() {
|
||||
m_ShouldClose = true;
|
||||
}
|
||||
|
||||
void Display::PollEvents() {
|
||||
SDL_Event event;
|
||||
@@ -145,7 +147,7 @@ void Display::PollEvents() {
|
||||
}
|
||||
|
||||
case SDL_EVENT_KEY_DOWN: {
|
||||
if(!event.key.repeat)
|
||||
if (!event.key.repeat)
|
||||
OnKeyDown(event.key.key);
|
||||
break;
|
||||
}
|
||||
@@ -161,7 +163,11 @@ void Display::PollEvents() {
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void Display::Update() {
|
||||
void Display::Update(float a_Delta) {
|
||||
StateMachine::Update(a_Delta);
|
||||
#ifndef NDEBUG
|
||||
ImGui::ShowDemoWindow();
|
||||
#endif
|
||||
ImGui::Render();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||
10
src/td/display/DisplayState.cpp
Normal file
10
src/td/display/DisplayState.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <td/display/DisplayState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
DisplayState::DisplayState(Display& a_Display) : Display::State(a_Display) {
|
||||
Connect(m_StateMachine.OnKeyDown, std::bind(&DisplayState::OnKeyDown, this, std::placeholders::_1));
|
||||
Connect(m_StateMachine.OnAspectRatioChange, std::bind(&DisplayState::OnAspectRatioChange, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
16
src/td/display/menu/CreatePartyMenu.cpp
Normal file
16
src/td/display/menu/CreatePartyMenu.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <td/display/menu/CreatePartyMenu.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
CreatePartyMenu::CreatePartyMenu(MainMenuState& a_MainMenu) : MainMenuState::Menu(a_MainMenu) {}
|
||||
|
||||
CreatePartyMenu::~CreatePartyMenu() {}
|
||||
|
||||
void CreatePartyMenu::Update() {
|
||||
ImGui::Text("CreatePartyMenu");
|
||||
m_StateStack.RenderBackButton();
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
16
src/td/display/menu/JoinPartyMenu.cpp
Normal file
16
src/td/display/menu/JoinPartyMenu.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <td/display/menu/JoinPartyMenu.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
JoinPartyMenu::JoinPartyMenu(MainMenuState& a_MainMenu) : MainMenuState::Menu(a_MainMenu) {}
|
||||
|
||||
JoinPartyMenu::~JoinPartyMenu() {}
|
||||
|
||||
void JoinPartyMenu::Update() {
|
||||
ImGui::Text("JoinPartyMenu");
|
||||
m_StateStack.RenderBackButton();
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
28
src/td/display/menu/MainMenu.cpp
Normal file
28
src/td/display/menu/MainMenu.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <td/display/menu/MainMenu.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <td/display/menu/CreatePartyMenu.h>
|
||||
#include <td/display/menu/JoinPartyMenu.h>
|
||||
#include <td/display/menu/SettingsMenu.h>
|
||||
#include <td/display/state/DebugWorldState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
MainMenu::MainMenu(MainMenuState& a_MainMenu) : MainMenuState::Menu(a_MainMenu) {}
|
||||
|
||||
MainMenu::~MainMenu() {}
|
||||
|
||||
void MainMenu::Update() {
|
||||
if (ImGui::Button("Create Party"))
|
||||
m_StateStack.PushState<CreatePartyMenu>();
|
||||
if (ImGui::Button("Join Party"))
|
||||
m_StateStack.PushState<JoinPartyMenu>();
|
||||
if (ImGui::Button("Settings"))
|
||||
m_StateStack.PushState<SettingsMenu>();
|
||||
#ifndef NDEBUG
|
||||
if (ImGui::Button("Debug world"))
|
||||
m_StateStack.ChangeState<DebugWorldState>();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
16
src/td/display/menu/SettingsMenu.cpp
Normal file
16
src/td/display/menu/SettingsMenu.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <td/display/menu/SettingsMenu.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
SettingsMenu::SettingsMenu(MainMenuState& a_MainMenu) : MainMenuState::Menu(a_MainMenu) {}
|
||||
|
||||
SettingsMenu::~SettingsMenu() {}
|
||||
|
||||
void SettingsMenu::Update() {
|
||||
ImGui::Text("SettingsMenu");
|
||||
m_StateStack.RenderBackButton();
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
134
src/td/display/state/DebugWorldState.cpp
Normal file
134
src/td/display/state/DebugWorldState.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <td/display/state/DebugWorldState.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <td/game/World.h>
|
||||
#include <td/protocol/packet/PacketSerialize.h>
|
||||
#include <td/protocol/packet/Packets.h>
|
||||
#include <td/render/renderer/EntityRenderer.h>
|
||||
#include <td/render/renderer/TowerRenderer.h>
|
||||
#include <td/render/renderer/WorldRenderer.h>
|
||||
|
||||
#include <sp/common/DataBuffer.h>
|
||||
#include <sp/extensions/Compress.h>
|
||||
#include <sp/io/MessageStream.h>
|
||||
#include <sp/io/StdIo.h>
|
||||
|
||||
#include <server/Server.h>
|
||||
#include <server/socket/FakeSocket.h>
|
||||
#include <server/state/GameState.h>
|
||||
|
||||
#include <client/Client.h>
|
||||
#include <client/socket/FakeSocket.h>
|
||||
#include <client/state/GameState.h>
|
||||
|
||||
#include <td/display/Display.h>
|
||||
#include <td/display/state/DebugWorldState.h>
|
||||
|
||||
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) {
|
||||
auto comp = std::make_shared<sp::ZlibCompress>();
|
||||
|
||||
std::ofstream fStream("test/tdmap.tdmap3");
|
||||
auto out = std::make_shared<sp::StdOuput>(fStream);
|
||||
|
||||
sp::MessageStream<protocol::PacketFactory> stream(std::move(out), std::move(comp));
|
||||
|
||||
stream.WriteMessage(header, false);
|
||||
stream.WriteMessage(data, false);
|
||||
}
|
||||
|
||||
game::WorldPtr GetWorld() {
|
||||
auto comp = std::make_shared<sp::ZlibCompress>();
|
||||
|
||||
std::ifstream fStream("test/tdmap.tdmap2");
|
||||
auto out = std::make_shared<sp::StdInput>(fStream);
|
||||
|
||||
sp::MessageStream<protocol::PacketFactory> stream(std::move(out), std::move(comp));
|
||||
|
||||
auto header = stream.ReadMessage(protocol::PacketID::WorldHeader);
|
||||
auto data = stream.ReadMessage(protocol::PacketID::WorldData);
|
||||
|
||||
auto w = std::make_shared<game::World>();
|
||||
auto wa = std::make_shared<WorldApply>(*w);
|
||||
|
||||
protocol::PacketDispatcher d;
|
||||
d.RegisterHandler(wa);
|
||||
|
||||
d.Dispatch(*header);
|
||||
d.Dispatch(*data);
|
||||
|
||||
Save(*header, *data);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
DebugWorldState::DebugWorldState(Display& a_Display) : DisplayState(a_Display) {
|
||||
// server
|
||||
game::WorldPtr serverWorld = GetWorld();
|
||||
auto serverFakeSocket = std::make_shared<server::FakeSocket>();
|
||||
m_Server = std::make_unique<server::Server>(serverFakeSocket);
|
||||
|
||||
// client
|
||||
game::WorldPtr clientWorld = GetWorld();
|
||||
auto clientFakeSocket = client::FakeSocket::Connect(serverFakeSocket);
|
||||
m_Client = std::make_unique<client::Client>(clientFakeSocket);
|
||||
|
||||
// render
|
||||
m_Renderer.AddRenderer<render::WorldRenderer>(m_Camera, *clientWorld);
|
||||
m_Renderer.AddRenderer<render::EntityRenderer>(m_Camera, *clientWorld);
|
||||
m_Renderer.AddRenderer<render::TowerRenderer>(m_Camera, *clientWorld);
|
||||
|
||||
// camera
|
||||
m_Camera.SetCamPos({77, 7, 13});
|
||||
m_Camera.UpdatePerspective(m_StateMachine.GetAspectRatio());
|
||||
|
||||
// states
|
||||
m_ClientState = m_Client->ChangeState<client::GameState>(clientWorld, STEP_TIME);
|
||||
m_Server->ChangeState<server::GameState>(serverWorld);
|
||||
}
|
||||
|
||||
void DebugWorldState::Update(float a_Delta) {
|
||||
m_Server->Update(a_Delta);
|
||||
m_Client->Update(a_Delta);
|
||||
// TODO: m_ClientState might be invalid !
|
||||
m_Renderer.Render(m_ClientState->GetCurrentLerp());
|
||||
}
|
||||
|
||||
void DebugWorldState::OnAspectRatioChange(float a_Ratio) {
|
||||
m_Camera.UpdatePerspective(a_Ratio);
|
||||
}
|
||||
|
||||
void DebugWorldState::OnKeyDown(SDL_Keycode a_Key) {
|
||||
// temporary tests
|
||||
if (a_Key == SDLK_A) {
|
||||
m_Client->SendPacket(td::protocol::packets::SpawnTroopPacket(td::EntityType::Zombie, 1));
|
||||
} else if (a_Key == SDLK_Z) {
|
||||
m_Client->SendPacket(td::protocol::packets::PlaceTowerPacket(td::TowerType::Archer, td::TowerCoords(77, 13)));
|
||||
}
|
||||
}
|
||||
|
||||
DebugWorldState::~DebugWorldState() {}
|
||||
|
||||
} // namespace td
|
||||
31
src/td/display/state/MainMenuState.cpp
Normal file
31
src/td/display/state/MainMenuState.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <td/display/state/MainMenuState.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <td/display/menu/MainMenu.h>
|
||||
#include <td/display/state/DebugWorldState.h>
|
||||
|
||||
namespace td {
|
||||
|
||||
MainMenuState::MainMenuState(Display& a_Display) : DisplayState(a_Display) {
|
||||
PushState<MainMenu>();
|
||||
}
|
||||
|
||||
MainMenuState::~MainMenuState() {}
|
||||
|
||||
void MainMenuState::Update(float a_Delta) {
|
||||
ImGui::Begin("MainWindow");
|
||||
MainMenuStateStack::Update();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void MainMenuState::RenderBackButton() {
|
||||
if (ImGui::Button("Back"))
|
||||
PopState();
|
||||
}
|
||||
|
||||
void MainMenuState::OnKeyDown(SDL_Keycode a_Key) {
|
||||
if (a_Key == SDLK_ESCAPE)
|
||||
PopState();
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
@@ -16,7 +16,6 @@ WorldRenderer::~WorldRenderer() {}
|
||||
void WorldRenderer::Render(float a_Lerp) {
|
||||
m_Shader->Start();
|
||||
Renderer::Render(*m_WorldVao);
|
||||
ImGui::ShowDemoWindow();
|
||||
}
|
||||
|
||||
} // namespace render
|
||||
|
||||
@@ -16,11 +16,10 @@ std::uint64_t GetTime() {
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock().now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
ClientSimulation::ClientSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime) :
|
||||
ClientSimulation::ClientSimulation(std::shared_ptr<game::World> a_World, GameHistory&& a_History, std::uint64_t a_StepTime) :
|
||||
m_StepTime(a_StepTime),
|
||||
m_World(a_World),
|
||||
m_CurrentTime(0),
|
||||
m_LastTime(GetTime()),
|
||||
m_CurrentStep(0),
|
||||
m_LastSnapshot(std::make_shared<WorldSnapshot>()),
|
||||
m_LastValidStep(0) {
|
||||
@@ -31,37 +30,36 @@ ClientSimulation::ClientSimulation(game::World& a_World, GameHistory&& a_History
|
||||
Step();
|
||||
}
|
||||
|
||||
ClientSimulation::ClientSimulation(game::World& a_World, std::uint64_t a_StepTime) :
|
||||
ClientSimulation::ClientSimulation(std::shared_ptr<game::World> a_World, std::uint64_t a_StepTime) :
|
||||
m_StepTime(a_StepTime),
|
||||
m_World(a_World),
|
||||
m_History(std::numeric_limits<StepTime>::max()),
|
||||
m_CurrentTime(0),
|
||||
m_LastTime(GetTime()),
|
||||
m_CurrentStep(0),
|
||||
m_LastSnapshot(std::make_shared<WorldSnapshot>()),
|
||||
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<float>(m_StepTime) / 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() {
|
||||
const auto& step = m_History[m_CurrentStep];
|
||||
if (step.has_value()) {
|
||||
auto snapshot = m_World.Tick(step.value(), FpFloat(m_StepTime) / FpFloat(1000));
|
||||
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));
|
||||
m_World->Tick(EMPTY_LOCKSTEP, FpFloat(m_StepTime) / FpFloat(1000));
|
||||
std::cout << "Empty tick (" << m_CurrentStep << ") !\n";
|
||||
}
|
||||
m_CurrentStep++;
|
||||
@@ -105,7 +103,7 @@ void ClientSimulation::FastReplay() {
|
||||
if (m_LastValidStep + 1 >= m_CurrentStep)
|
||||
return;
|
||||
|
||||
m_World.ResetSnapshots(m_LastSnapshot, m_LastSnapshot);
|
||||
m_World->ResetSnapshots(m_LastSnapshot, m_LastSnapshot);
|
||||
|
||||
const std::size_t stepCount = m_CurrentStep - m_LastValidStep;
|
||||
m_CurrentStep = m_LastValidStep;
|
||||
|
||||
@@ -3,8 +3,7 @@ add_rules("mode.debug", "mode.release")
|
||||
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("splib 2.3.0", "zlib")
|
||||
add_requires("libsdl3 3.2.16", "glew", "fpm", "enet6")
|
||||
add_requires("libsdl3 3.2.16", "splib 2.3.0", "zlib", "glew", "fpm", "enet6")
|
||||
|
||||
set_languages("c++17")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user