127 lines
3.5 KiB
C++
127 lines
3.5 KiB
C++
#include <td/simulation/ClientSimulation.h>
|
|
|
|
#include <chrono>
|
|
|
|
#include <iostream>
|
|
|
|
// TODO: mutex this bad boy
|
|
|
|
namespace td {
|
|
namespace sim {
|
|
|
|
const protocol::LockStep ClientSimulation::EMPTY_LOCKSTEP;
|
|
|
|
std::uint64_t GetTime() {
|
|
return static_cast<std::uint64_t>(
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock().now().time_since_epoch()).count());
|
|
}
|
|
|
|
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_CurrentStep(0),
|
|
m_LastSnapshot(std::make_shared<WorldSnapshot>()),
|
|
m_LastValidStep(0) {
|
|
m_History.reserve(a_History.size());
|
|
for (auto&& lockstep : a_History) {
|
|
m_History.emplace_back(std::move(lockstep));
|
|
}
|
|
Step();
|
|
}
|
|
|
|
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_CurrentStep(0),
|
|
m_LastSnapshot(std::make_shared<WorldSnapshot>()),
|
|
m_LastValidStep(0) {}
|
|
|
|
float ClientSimulation::Update(float a_Delta) {
|
|
// TODO: handle freezes (m_CurrentTime > 2 * 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();
|
|
}
|
|
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));
|
|
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) {
|
|
const auto& steps = a_LockSteps->m_LockSteps;
|
|
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::LockStepResponsePacket& a_LockSteps) {
|
|
for (const auto& [stepTime, lockStep] : a_LockSteps->m_Steps) {
|
|
m_History[stepTime] = lockStep;
|
|
}
|
|
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() {
|
|
if (m_LastValidStep + 1 >= m_CurrentStep)
|
|
return;
|
|
|
|
m_World->ResetSnapshots(m_LastSnapshot, m_LastSnapshot);
|
|
|
|
const std::size_t stepCount = m_CurrentStep - m_LastValidStep;
|
|
m_CurrentStep = m_LastValidStep;
|
|
|
|
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 td
|