move ClientSimulation in Client

This commit is contained in:
2025-08-10 12:19:50 +02:00
parent 8bdcffcfa6
commit e0080fa50c
8 changed files with 59 additions and 54 deletions

View File

@@ -2,7 +2,7 @@
#include <client/ClientState.h>
#include <td/game/World.h>
#include <td/simulation/ServerSimulation.h>
#include <td/simulation/ClientSimulation.h>
namespace td {
namespace client {
@@ -10,14 +10,19 @@ namespace client {
class GameState : public ClientState {
private:
std::shared_ptr<game::World> m_World;
// sim::ClientSimulation m_Simulation;
sim::ClientSimulation m_Simulation;
float m_CurrentLerp;
public:
GameState(Client& a_Client, const std::shared_ptr<game::World>& a_World);
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;
};

View File

@@ -29,8 +29,9 @@ class StateMachine {
}
template <typename T, typename... Args>
void ChangeState(Args&&... 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:

View File

@@ -5,19 +5,17 @@
#include <td/display/DisplayState.h>
#include <td/render/Renderer.h>
#include <td/simulation/ClientSimulation.h>
#include <client/state/GameState.h>
namespace td {
class ClientHandler;
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;
std::unique_ptr<sim::ClientSimulation> m_Simulation;
render::Camera m_Camera;
std::unique_ptr<ClientHandler> m_ClientHandler;
client::GameState* m_ClientState;
public:
DebugWorldState(Display& a_Display);

View File

@@ -14,7 +14,7 @@ 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;
float m_CurrentTime;
StepTime m_CurrentStep;
@@ -33,13 +33,13 @@ 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

View File

@@ -3,9 +3,39 @@
namespace td {
namespace client {
GameState::GameState(Client& a_Client, const std::shared_ptr<game::World>& a_World) : ClientState(a_Client), m_World(a_World) {}
void GameState::HandlePacket(const protocol::PacketBase& a_Packet) {}
void GameState::Update(float a_Delta) {}
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

View File

@@ -17,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) {

View File

@@ -10,7 +10,6 @@
#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>
@@ -30,24 +29,6 @@
namespace td {
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);
}
};
class WorldApply : public protocol::PacketHandler {
private:
game::World& m_World;
@@ -122,26 +103,15 @@ DebugWorldState::DebugWorldState(Display& a_Display) : DisplayState(a_Display) {
m_Camera.SetCamPos({77, 7, 13});
m_Camera.UpdatePerspective(m_StateMachine.GetAspectRatio());
m_Simulation = std::make_unique<sim::ClientSimulation>(*clientWorld, STEP_TIME);
m_ClientHandler = std::make_unique<ClientHandler>(*m_Simulation);
// packets from the server to the client
clientFakeSocket->OnReceive.Connect([this](const protocol::PacketBase& a_Packet) { a_Packet.Dispatch(*m_ClientHandler); });
m_Simulation->OnMissingLockSteps.Connect([clientFakeSocket](const std::vector<td::StepTime>& a_MissingSteps) {
clientFakeSocket->Send(protocol::packets::LockStepRequestPacket(a_MissingSteps));
});
m_ClientState = m_Client->ChangeState<client::GameState>(clientWorld, STEP_TIME);
m_Server->ChangeState<server::GameState>(serverWorld);
m_Client->ChangeState<client::GameState>(clientWorld);
}
void DebugWorldState::Update(float a_Delta) {
m_Server->Update(a_Delta);
m_Client->Update(a_Delta);
float lerp = m_Simulation->Update(a_Delta);
m_Renderer.Render(lerp);
// TODO: m_ClientState might be invalid !
m_Renderer.Render(m_ClientState->GetCurrentLerp());
}
void DebugWorldState::OnAspectRatioChange(float a_Ratio) {

View File

@@ -16,7 +16,7 @@ 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),
@@ -30,7 +30,7 @@ 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()),
@@ -41,7 +41,7 @@ ClientSimulation::ClientSimulation(game::World& a_World, std::uint64_t a_StepTim
float ClientSimulation::Update(float a_Delta) {
// TODO: handle freezes (m_CurrentTime > 2 * m_StepTime)
static const float stepTimeSecond = static_cast<float>(STEP_TIME) / 1000.0f;
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);
@@ -53,13 +53,13 @@ float ClientSimulation::Update(float a_Delta) {
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++;
@@ -103,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;