#include #include #include // TODO: mutex this bad boy namespace td { namespace sim { const protocol::LockStep ClientSimulation::EMPTY_LOCKSTEP; std::uint64_t GetTime() { return static_cast( std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count()); } ClientSimulation::ClientSimulation(std::shared_ptr 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()), 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 a_World, std::uint64_t a_StepTime) : m_StepTime(a_StepTime), m_World(a_World), m_History(std::numeric_limits::max()), m_CurrentTime(0), m_CurrentStep(0), m_LastSnapshot(std::make_shared()), m_LastValidStep(0) {} float ClientSimulation::Update(float a_Delta) { // TODO: handle freezes (m_CurrentTime > 2 * m_StepTime) static const float stepTimeSecond = static_cast(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 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