fill client holes (lockstep)
This commit is contained in:
@@ -19,6 +19,7 @@ class GameStateHandler : public protocol::PacketHandler {
|
|||||||
|
|
||||||
virtual void Handle(const protocol::packets::SpawnTroopPacket& a_Packet) override;
|
virtual void Handle(const protocol::packets::SpawnTroopPacket& a_Packet) override;
|
||||||
virtual void Handle(const protocol::packets::PlaceTowerPacket& a_Packet) override;
|
virtual void Handle(const protocol::packets::PlaceTowerPacket& a_Packet) override;
|
||||||
|
virtual void Handle(const protocol::packets::LockStepRequestPacket& a_Packet) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|||||||
@@ -110,6 +110,14 @@ struct PlaceTower {
|
|||||||
TowerCoords m_Position;
|
TowerCoords m_Position;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LockStepRequest {
|
||||||
|
std::vector<StepTime> m_Missing;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LockStepResponse {
|
||||||
|
std::map<StepTime, LockStep> m_Steps;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace pdata
|
} // namespace pdata
|
||||||
} // namespace protocol
|
} // namespace protocol
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ enum class PacketID : std::uint8_t {
|
|||||||
BeginGame,
|
BeginGame,
|
||||||
Disconnect,
|
Disconnect,
|
||||||
KeepAlive,
|
KeepAlive,
|
||||||
|
LockStepRequest,
|
||||||
|
LockStepResponse,
|
||||||
LockSteps,
|
LockSteps,
|
||||||
LoggingSuccess,
|
LoggingSuccess,
|
||||||
PlaceTower,
|
PlaceTower,
|
||||||
@@ -49,6 +51,8 @@ using BeginGamePacket = PacketMessage<pdata::BeginGame, PacketID::BeginGame>;
|
|||||||
using ChatMessagePacket = PacketMessage<pdata::ChatMessage, PacketID::ChatMessage>;
|
using ChatMessagePacket = PacketMessage<pdata::ChatMessage, PacketID::ChatMessage>;
|
||||||
using DisconnectPacket = PacketMessage<pdata::Disconnect, PacketID::Disconnect>;
|
using DisconnectPacket = PacketMessage<pdata::Disconnect, PacketID::Disconnect>;
|
||||||
using KeepAlivePacket = PacketMessage<pdata::KeepAlive, PacketID::KeepAlive>;
|
using KeepAlivePacket = PacketMessage<pdata::KeepAlive, PacketID::KeepAlive>;
|
||||||
|
using LockStepRequestPacket = PacketMessage<pdata::LockStepRequest, PacketID::LockStepRequest>;
|
||||||
|
using LockStepResponsePacket = PacketMessage<pdata::LockStepResponse, PacketID::LockStepResponse>;
|
||||||
using LockStepsPacket = PacketMessage<pdata::LockSteps, PacketID::LockSteps>;
|
using LockStepsPacket = PacketMessage<pdata::LockSteps, PacketID::LockSteps>;
|
||||||
using LoggingSuccessPacket = PacketMessage<pdata::LoggingSuccess, PacketID::LoggingSuccess>;
|
using LoggingSuccessPacket = PacketMessage<pdata::LoggingSuccess, PacketID::LoggingSuccess>;
|
||||||
using PlaceTowerPacket = PacketMessage<pdata::PlaceTower, PacketID::PlaceTower>;
|
using PlaceTowerPacket = PacketMessage<pdata::PlaceTower, PacketID::PlaceTower>;
|
||||||
@@ -63,7 +67,7 @@ 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::LockStepsPacket, packets::LoggingSuccessPacket, packets::PlaceTowerPacket,
|
packets::KeepAlivePacket, packets::LockStepRequestPacket, packets::LockStepResponsePacket, packets::LockStepsPacket, packets::LoggingSuccessPacket, packets::PlaceTowerPacket,
|
||||||
packets::PlayerJoinPacket, packets::PlayerLeavePacket, packets::PlayerLoginPacket, packets::PredictCommandPacket,
|
packets::PlayerJoinPacket, packets::PlayerLeavePacket, packets::PlayerLoginPacket, packets::PredictCommandPacket,
|
||||||
packets::SpawnTroopPacket, packets::WorldHeaderPacket, packets::WorldDataPacket>;
|
packets::SpawnTroopPacket, packets::WorldHeaderPacket, packets::WorldDataPacket>;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <td/game/World.h>
|
#include <td/game/World.h>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <td/misc/Signal.h>
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
namespace sim {
|
namespace sim {
|
||||||
@@ -17,16 +18,18 @@ class ClientSimulation : public protocol::PacketHandler {
|
|||||||
GameBuffer m_History;
|
GameBuffer m_History;
|
||||||
std::uint64_t m_CurrentTime;
|
std::uint64_t m_CurrentTime;
|
||||||
std::uint64_t m_LastTime;
|
std::uint64_t m_LastTime;
|
||||||
std::size_t m_CurrentStep;
|
StepTime m_CurrentStep;
|
||||||
|
|
||||||
std::shared_ptr<WorldSnapshot> m_LastSnapshot;
|
std::shared_ptr<WorldSnapshot> m_LastSnapshot;
|
||||||
std::uint64_t m_LastValidStep;
|
StepTime m_LastValidStep;
|
||||||
|
|
||||||
static const protocol::LockStep EMPTY_LOCKSTEP;
|
static const protocol::LockStep EMPTY_LOCKSTEP;
|
||||||
|
|
||||||
using protocol::PacketHandler::Handle;
|
using protocol::PacketHandler::Handle;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
utils::Signal<const std::vector<StepTime>&> OnMissingLockSteps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Replay constructor
|
* \brief Replay constructor
|
||||||
* \param a_StepTime in ms
|
* \param a_StepTime in ms
|
||||||
@@ -45,15 +48,18 @@ class ClientSimulation : public protocol::PacketHandler {
|
|||||||
float Update();
|
float Update();
|
||||||
|
|
||||||
virtual void Handle(const protocol::packets::LockStepsPacket& a_LockSteps) override;
|
virtual void Handle(const protocol::packets::LockStepsPacket& a_LockSteps) override;
|
||||||
virtual void Handle(const protocol::packets::PredictCommandPacket& a_Predict) override;
|
virtual void Handle(const protocol::packets::LockStepResponsePacket& a_LockSteps) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Step();
|
/**
|
||||||
|
* \returns false if the empty lockstep was used
|
||||||
|
*/
|
||||||
|
bool Step();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Ticks a_Count times
|
* \brief Ticks a_Count times
|
||||||
*/
|
*/
|
||||||
void FastForward(std::size_t a_Count);
|
std::size_t FastForward(std::size_t a_Count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Tries to recompute simulation if needed (for example in late command receival)
|
* \brief Tries to recompute simulation if needed (for example in late command receival)
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ class ServerSimulation : public protocol::CommandHandler {
|
|||||||
|
|
||||||
virtual void Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) override;
|
virtual void Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) override;
|
||||||
virtual void Handle(const protocol::commands::PlaceTowerCommand& a_PlaceTower) override;
|
virtual void Handle(const protocol::commands::PlaceTowerCommand& a_PlaceTower) override;
|
||||||
|
|
||||||
|
protocol::packets::LockStepResponsePacket GetResponse(const protocol::packets::LockStepRequestPacket& a_Missing) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sim
|
} // namespace sim
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ class ClientHandler : public td::protocol::PacketHandler {
|
|||||||
m_Simulation.Handle(a_LockStep);
|
m_Simulation.Handle(a_LockStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Handle(const td::protocol::packets::PredictCommandPacket& a_Predict) {
|
void Handle(const td::protocol::packets::LockStepResponsePacket& a_LockStep) {
|
||||||
m_Simulation.Handle(a_Predict);
|
m_Simulation.Handle(a_LockStep);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,6 +125,10 @@ int main(int argc, char** argv) {
|
|||||||
td::sim::ClientSimulation simulation(*clientWorld, td::STEP_TIME);
|
td::sim::ClientSimulation simulation(*clientWorld, td::STEP_TIME);
|
||||||
ClientHandler clientHandler(simulation);
|
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
|
// temporary tests
|
||||||
display.OnKeyDown.Connect([&fakeSocket](SDL_Keycode key) {
|
display.OnKeyDown.Connect([&fakeSocket](SDL_Keycode key) {
|
||||||
if (key == SDLK_A) {
|
if (key == SDLK_A) {
|
||||||
@@ -134,6 +138,7 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// make a fake player join
|
||||||
fakeSocket->ConnectFakePeer(0);
|
fakeSocket->ConnectFakePeer(0);
|
||||||
|
|
||||||
// packets from the server to the client
|
// packets from the server to the client
|
||||||
|
|||||||
@@ -20,5 +20,9 @@ void GameStateHandler::Handle(const protocol::packets::PlaceTowerPacket& a_Packe
|
|||||||
m_GameState.m_Simulation.Handle(place);
|
m_GameState.m_Simulation.Handle(place);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameStateHandler::Handle(const protocol::packets::LockStepRequestPacket& a_Packet) {
|
||||||
|
m_GameState.SendPacket(m_PlayerId, m_GameState.m_Simulation.GetResponse(a_Packet));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// TODO: mutex this bad boy
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
namespace sim {
|
namespace sim {
|
||||||
|
|
||||||
@@ -37,9 +39,7 @@ ClientSimulation::ClientSimulation(game::World& a_World, std::uint64_t a_StepTim
|
|||||||
m_LastTime(GetTime()),
|
m_LastTime(GetTime()),
|
||||||
m_CurrentStep(0),
|
m_CurrentStep(0),
|
||||||
m_LastSnapshot(std::make_shared<WorldSnapshot>()),
|
m_LastSnapshot(std::make_shared<WorldSnapshot>()),
|
||||||
m_LastValidStep(0) {
|
m_LastValidStep(0) {}
|
||||||
// Step();
|
|
||||||
}
|
|
||||||
|
|
||||||
float ClientSimulation::Update() {
|
float ClientSimulation::Update() {
|
||||||
// TODO: handle freezes (m_CurrentTime > 2 * m_StepTime)
|
// TODO: handle freezes (m_CurrentTime > 2 * m_StepTime)
|
||||||
@@ -52,15 +52,20 @@ float ClientSimulation::Update() {
|
|||||||
return (float)m_CurrentTime / (float)m_StepTime;
|
return (float)m_CurrentTime / (float)m_StepTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSimulation::Step() {
|
bool ClientSimulation::Step() {
|
||||||
const auto& step = m_History[m_CurrentStep];
|
const auto& step = m_History[m_CurrentStep];
|
||||||
if (step.has_value()) {
|
if (step.has_value()) {
|
||||||
m_LastSnapshot = 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_LastValidStep = m_CurrentStep;
|
||||||
|
m_LastSnapshot = snapshot;
|
||||||
|
}
|
||||||
} else {
|
} 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++;
|
m_CurrentStep++;
|
||||||
|
return step.has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSimulation::Handle(const protocol::packets::LockStepsPacket& a_LockSteps) {
|
void ClientSimulation::Handle(const protocol::packets::LockStepsPacket& a_LockSteps) {
|
||||||
@@ -68,16 +73,32 @@ void ClientSimulation::Handle(const protocol::packets::LockStepsPacket& a_LockSt
|
|||||||
for (std::size_t i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) {
|
for (std::size_t i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) {
|
||||||
m_History[a_LockSteps->m_FirstFrameNumber + i] = steps[i];
|
m_History[a_LockSteps->m_FirstFrameNumber + i] = steps[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to be in the state [0-1]
|
||||||
|
if (a_LockSteps->m_FirstFrameNumber == 0)
|
||||||
|
Step();
|
||||||
|
|
||||||
FastReplay();
|
FastReplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSimulation::Handle(const protocol::packets::PredictCommandPacket& a_Predict) {}
|
void ClientSimulation::Handle(const protocol::packets::LockStepResponsePacket& a_LockSteps) {
|
||||||
|
for (const auto& [stepTime, lockStep] : a_LockSteps->m_Steps) {
|
||||||
void ClientSimulation::FastForward(std::size_t a_Count) {
|
m_History[stepTime] = lockStep;
|
||||||
for (std::size_t i = 0; i < a_Count; i++) {
|
|
||||||
Step();
|
|
||||||
}
|
}
|
||||||
std::cout << "Was behind " << a_Count << " ticks !\n";
|
FastReplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ClientSimulation::FastForward(std::size_t a_Count) {
|
||||||
|
std::size_t stepCount;
|
||||||
|
bool hasMissingStep = false;
|
||||||
|
for (std::size_t i = 0; i < a_Count; i++) {
|
||||||
|
if (!Step() && !hasMissingStep) {
|
||||||
|
hasMissingStep = true;
|
||||||
|
stepCount = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "Was behind " << (hasMissingStep ? stepCount : a_Count) << " ticks !\n";
|
||||||
|
return hasMissingStep ? stepCount : a_Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientSimulation::FastReplay() {
|
void ClientSimulation::FastReplay() {
|
||||||
@@ -86,11 +107,21 @@ void ClientSimulation::FastReplay() {
|
|||||||
|
|
||||||
m_World.ResetSnapshots(m_LastSnapshot, m_LastSnapshot);
|
m_World.ResetSnapshots(m_LastSnapshot, m_LastSnapshot);
|
||||||
|
|
||||||
// TODO: cover holes
|
|
||||||
const std::size_t stepCount = m_CurrentStep - m_LastValidStep;
|
const std::size_t stepCount = m_CurrentStep - m_LastValidStep;
|
||||||
m_CurrentStep = m_LastValidStep;
|
m_CurrentStep = m_LastValidStep;
|
||||||
|
|
||||||
FastForward(stepCount);
|
if (FastForward(stepCount) == stepCount)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::vector<StepTime> missingSteps;
|
||||||
|
missingSteps.reserve(stepCount);
|
||||||
|
|
||||||
|
for (StepTime step = m_LastValidStep + 1; step < m_CurrentStep; step++) {
|
||||||
|
if (!m_History[step].has_value())
|
||||||
|
missingSteps.push_back(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
OnMissingLockSteps(missingSteps);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sim
|
} // namespace sim
|
||||||
|
|||||||
@@ -36,5 +36,17 @@ void ServerSimulation::Handle(const protocol::commands::PlaceTowerCommand& a_Pla
|
|||||||
AddToCommandHistory(m_History[m_CurrentTime + LOCKSTEP_BUFFER_SIZE], a_PlaceTower);
|
AddToCommandHistory(m_History[m_CurrentTime + LOCKSTEP_BUFFER_SIZE], a_PlaceTower);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protocol::packets::LockStepResponsePacket ServerSimulation::GetResponse(
|
||||||
|
const protocol::packets::LockStepRequestPacket& a_Missing) const {
|
||||||
|
std::map<StepTime, protocol::LockStep> result;
|
||||||
|
|
||||||
|
for (StepTime step : a_Missing->m_Missing) {
|
||||||
|
result.emplace(step, m_History[step]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace sim
|
} // namespace sim
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
|||||||
Reference in New Issue
Block a user