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::PlaceTowerPacket& a_Packet) override;
|
||||
virtual void Handle(const protocol::packets::LockStepRequestPacket& a_Packet) override;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
|
||||
@@ -110,6 +110,14 @@ struct PlaceTower {
|
||||
TowerCoords m_Position;
|
||||
};
|
||||
|
||||
struct LockStepRequest {
|
||||
std::vector<StepTime> m_Missing;
|
||||
};
|
||||
|
||||
struct LockStepResponse {
|
||||
std::map<StepTime, LockStep> m_Steps;
|
||||
};
|
||||
|
||||
} // namespace pdata
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
|
||||
@@ -22,6 +22,8 @@ enum class PacketID : std::uint8_t {
|
||||
BeginGame,
|
||||
Disconnect,
|
||||
KeepAlive,
|
||||
LockStepRequest,
|
||||
LockStepResponse,
|
||||
LockSteps,
|
||||
LoggingSuccess,
|
||||
PlaceTower,
|
||||
@@ -49,6 +51,8 @@ using BeginGamePacket = PacketMessage<pdata::BeginGame, PacketID::BeginGame>;
|
||||
using ChatMessagePacket = PacketMessage<pdata::ChatMessage, PacketID::ChatMessage>;
|
||||
using DisconnectPacket = PacketMessage<pdata::Disconnect, PacketID::Disconnect>;
|
||||
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 LoggingSuccessPacket = PacketMessage<pdata::LoggingSuccess, PacketID::LoggingSuccess>;
|
||||
using PlaceTowerPacket = PacketMessage<pdata::PlaceTower, PacketID::PlaceTower>;
|
||||
@@ -63,7 +67,7 @@ using WorldDataPacket = PacketMessage<pdata::WorldData, PacketID::WorldData>;
|
||||
} // namespace packets
|
||||
|
||||
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::SpawnTroopPacket, packets::WorldHeaderPacket, packets::WorldDataPacket>;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <td/game/World.h>
|
||||
#include <optional>
|
||||
#include <td/misc/Signal.h>
|
||||
|
||||
namespace td {
|
||||
namespace sim {
|
||||
@@ -17,16 +18,18 @@ class ClientSimulation : public protocol::PacketHandler {
|
||||
GameBuffer m_History;
|
||||
std::uint64_t m_CurrentTime;
|
||||
std::uint64_t m_LastTime;
|
||||
std::size_t m_CurrentStep;
|
||||
StepTime m_CurrentStep;
|
||||
|
||||
std::shared_ptr<WorldSnapshot> m_LastSnapshot;
|
||||
std::uint64_t m_LastValidStep;
|
||||
StepTime m_LastValidStep;
|
||||
|
||||
static const protocol::LockStep EMPTY_LOCKSTEP;
|
||||
|
||||
using protocol::PacketHandler::Handle;
|
||||
|
||||
public:
|
||||
utils::Signal<const std::vector<StepTime>&> OnMissingLockSteps;
|
||||
|
||||
/**
|
||||
* \brief Replay constructor
|
||||
* \param a_StepTime in ms
|
||||
@@ -45,15 +48,18 @@ class ClientSimulation : public protocol::PacketHandler {
|
||||
float Update();
|
||||
|
||||
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:
|
||||
void Step();
|
||||
/**
|
||||
* \returns false if the empty lockstep was used
|
||||
*/
|
||||
bool Step();
|
||||
|
||||
/**
|
||||
* \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)
|
||||
|
||||
@@ -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::PlaceTowerCommand& a_PlaceTower) override;
|
||||
|
||||
protocol::packets::LockStepResponsePacket GetResponse(const protocol::packets::LockStepRequestPacket& a_Missing) const;
|
||||
};
|
||||
|
||||
} // namespace sim
|
||||
|
||||
@@ -49,8 +49,8 @@ class ClientHandler : public td::protocol::PacketHandler {
|
||||
m_Simulation.Handle(a_LockStep);
|
||||
}
|
||||
|
||||
void Handle(const td::protocol::packets::PredictCommandPacket& a_Predict) {
|
||||
m_Simulation.Handle(a_Predict);
|
||||
void Handle(const td::protocol::packets::LockStepResponsePacket& a_LockStep) {
|
||||
m_Simulation.Handle(a_LockStep);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -125,6 +125,10 @@ int main(int argc, char** argv) {
|
||||
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) {
|
||||
@@ -134,6 +138,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
});
|
||||
|
||||
// make a fake player join
|
||||
fakeSocket->ConnectFakePeer(0);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void GameStateHandler::Handle(const protocol::packets::LockStepRequestPacket& a_Packet) {
|
||||
m_GameState.SendPacket(m_PlayerId, m_GameState.m_Simulation.GetResponse(a_Packet));
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// TODO: mutex this bad boy
|
||||
|
||||
namespace td {
|
||||
namespace sim {
|
||||
|
||||
@@ -37,9 +39,7 @@ ClientSimulation::ClientSimulation(game::World& a_World, std::uint64_t a_StepTim
|
||||
m_LastTime(GetTime()),
|
||||
m_CurrentStep(0),
|
||||
m_LastSnapshot(std::make_shared<WorldSnapshot>()),
|
||||
m_LastValidStep(0) {
|
||||
// Step();
|
||||
}
|
||||
m_LastValidStep(0) {}
|
||||
|
||||
float ClientSimulation::Update() {
|
||||
// TODO: handle freezes (m_CurrentTime > 2 * m_StepTime)
|
||||
@@ -52,15 +52,20 @@ float ClientSimulation::Update() {
|
||||
return (float)m_CurrentTime / (float)m_StepTime;
|
||||
}
|
||||
|
||||
void ClientSimulation::Step() {
|
||||
bool ClientSimulation::Step() {
|
||||
const auto& step = m_History[m_CurrentStep];
|
||||
if (step.has_value()) {
|
||||
m_LastSnapshot = m_World.Tick(step.value(), FpFloat(m_StepTime) / FpFloat(1000));
|
||||
m_LastValidStep = m_CurrentStep;
|
||||
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));
|
||||
std::cout << "Empty tick (" << m_CurrentStep << ") !\n";
|
||||
}
|
||||
m_CurrentStep++;
|
||||
return step.has_value();
|
||||
}
|
||||
|
||||
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++) {
|
||||
m_History[a_LockSteps->m_FirstFrameNumber + i] = steps[i];
|
||||
}
|
||||
|
||||
// to be in the state [0-1]
|
||||
if (a_LockSteps->m_FirstFrameNumber == 0)
|
||||
Step();
|
||||
|
||||
FastReplay();
|
||||
}
|
||||
|
||||
void ClientSimulation::Handle(const protocol::packets::PredictCommandPacket& a_Predict) {}
|
||||
|
||||
void ClientSimulation::FastForward(std::size_t a_Count) {
|
||||
for (std::size_t i = 0; i < a_Count; i++) {
|
||||
Step();
|
||||
void ClientSimulation::Handle(const protocol::packets::LockStepResponsePacket& a_LockSteps) {
|
||||
for (const auto& [stepTime, lockStep] : a_LockSteps->m_Steps) {
|
||||
m_History[stepTime] = lockStep;
|
||||
}
|
||||
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() {
|
||||
@@ -86,11 +107,21 @@ void ClientSimulation::FastReplay() {
|
||||
|
||||
m_World.ResetSnapshots(m_LastSnapshot, m_LastSnapshot);
|
||||
|
||||
// TODO: cover holes
|
||||
const std::size_t stepCount = 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
|
||||
|
||||
@@ -36,5 +36,17 @@ void ServerSimulation::Handle(const protocol::commands::PlaceTowerCommand& a_Pla
|
||||
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 td
|
||||
|
||||
Reference in New Issue
Block a user