Merge branch 'experimental' into dev
This commit is contained in:
95
src/td/simulation/ClientSimulation.cpp
Normal file
95
src/td/simulation/ClientSimulation.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <td/simulation/ClientSimulation.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
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(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime) :
|
||||
m_StepTime(a_StepTime),
|
||||
m_World(a_World),
|
||||
m_CurrentTime(0),
|
||||
m_LastTime(GetTime()),
|
||||
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(game::World& a_World, std::uint64_t a_StepTime) :
|
||||
m_StepTime(a_StepTime),
|
||||
m_World(a_World),
|
||||
m_History(std::numeric_limits<std::uint16_t>::max()),
|
||||
m_CurrentTime(0),
|
||||
m_LastTime(GetTime()),
|
||||
m_CurrentStep(0),
|
||||
m_LastSnapshot(std::make_shared<WorldSnapshot>()),
|
||||
m_LastValidStep(0) {
|
||||
Step();
|
||||
}
|
||||
|
||||
float ClientSimulation::Update() {
|
||||
// TODO: handle freezes (m_CurrentTime > 2 * m_StepTime)
|
||||
m_CurrentTime += GetTime() - m_LastTime;
|
||||
m_LastTime = GetTime();
|
||||
if (m_CurrentTime > m_StepTime) {
|
||||
Step();
|
||||
m_CurrentTime -= m_StepTime;
|
||||
}
|
||||
return (float)m_CurrentTime / (float)m_StepTime;
|
||||
}
|
||||
|
||||
void 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;
|
||||
} else {
|
||||
m_World.Tick(EMPTY_LOCKSTEP, FpFloat(m_StepTime) / FpFloat(1000));
|
||||
}
|
||||
m_CurrentStep++;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
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::FastReplay() {
|
||||
if (m_LastValidStep >= m_CurrentStep)
|
||||
return;
|
||||
|
||||
m_World.ResetSnapshots(m_LastSnapshot, m_LastSnapshot);
|
||||
|
||||
const std::size_t stepCount = m_CurrentStep - m_LastValidStep;
|
||||
m_CurrentStep = m_LastValidStep;
|
||||
|
||||
FastForward(stepCount);
|
||||
}
|
||||
|
||||
} // namespace sim
|
||||
} // namespace td
|
||||
35
src/td/simulation/CommandApply.cpp
Normal file
35
src/td/simulation/CommandApply.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <td/simulation/CommandApply.h>
|
||||
|
||||
namespace td {
|
||||
namespace sim {
|
||||
|
||||
CommandApply::CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot) : m_World(a_World), m_Snapshot(a_Snapshot) {}
|
||||
|
||||
void CommandApply::Handle(const protocol::commands::EndCommand& a_End) {
|
||||
(void) m_World;
|
||||
}
|
||||
|
||||
void CommandApply::Handle(const protocol::commands::PlaceTowerCommand& a_PlaceTower) {
|
||||
static game::TowerFactory factory;
|
||||
|
||||
auto tower = std::shared_ptr<game::Tower>(factory.CreateMessage(*a_PlaceTower->m_Type).release());
|
||||
tower->m_Builder = *a_PlaceTower->m_Placer;
|
||||
tower->SetCenter(utils::shape::Point(a_PlaceTower->m_Position.x, a_PlaceTower->m_Position.y));
|
||||
|
||||
m_Snapshot.m_Towers.push_back(tower);
|
||||
}
|
||||
|
||||
void CommandApply::Handle(const protocol::commands::PlayerJoinCommand& a_PlayerJoin) {}
|
||||
|
||||
void CommandApply::Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) {
|
||||
auto zombie = std::make_shared<game::Zombie>();
|
||||
zombie->m_Position = a_SpawnTroop->m_Position;
|
||||
m_Snapshot.m_Mobs.push_back(zombie);
|
||||
}
|
||||
|
||||
void CommandApply::Handle(const protocol::commands::TeamChangeCommand& a_TeamChange) {}
|
||||
void CommandApply::Handle(const protocol::commands::UpgradeTowerCommand& a_UpgradeTower) {}
|
||||
void CommandApply::Handle(const protocol::commands::UseItemCommand& a_UseItem) {}
|
||||
|
||||
} // namespace sim
|
||||
} // namespace td
|
||||
36
src/td/simulation/GameHistory.cpp
Normal file
36
src/td/simulation/GameHistory.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <td/simulation/GameHistory.h>
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
GameHistory::GameHistory() : m_History(std::numeric_limits<HistorySizeType>::max()) {}
|
||||
|
||||
void GameHistory::SetLockStep(HistorySizeType a_Index, protocol::LockStep&& a_LockStep) {
|
||||
m_History[a_Index] = std::move(a_LockStep);
|
||||
}
|
||||
|
||||
const protocol::LockStep& GameHistory::GetLockStep(HistorySizeType a_Index) const {
|
||||
return *m_History[a_Index];
|
||||
}
|
||||
|
||||
bool GameHistory::HasLockStep(HistorySizeType a_Index) const {
|
||||
return m_History[a_Index].has_value();
|
||||
}
|
||||
|
||||
void GameHistory::FromPacket(protocol::pdata::LockSteps&& a_Steps) {
|
||||
for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) {
|
||||
protocol::LockStep& step = a_Steps.m_LockSteps[i];
|
||||
SetLockStep(i + a_Steps.m_FirstFrameNumber, std::move(step));
|
||||
}
|
||||
}
|
||||
|
||||
protocol::packets::LockStepsPacket GameHistory::ToPacket(HistorySizeType a_StartIndex) {
|
||||
std::array<protocol::LockStep, LOCKSTEP_BUFFER_SIZE> steps;
|
||||
for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) {
|
||||
steps[i] = GetLockStep(a_StartIndex + i);
|
||||
}
|
||||
return {a_StartIndex, std::move(steps)};
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
53
src/td/simulation/WorldTicker.cpp
Normal file
53
src/td/simulation/WorldTicker.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <td/simulation/WorldTicker.h>
|
||||
|
||||
#include <td/simulation/system/EntityMove.h>
|
||||
#include <td/simulation/CommandApply.h>
|
||||
|
||||
namespace td {
|
||||
namespace sim {
|
||||
|
||||
WorldTicker::WorldTicker() {
|
||||
AddSystem<EntityMove>();
|
||||
}
|
||||
|
||||
WorldSnapshot WorldTicker::NextStep(
|
||||
const game::World& a_World, WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) {
|
||||
WorldSnapshot next = CreateNext(a_PreviousState);
|
||||
Tick(a_World, next, a_Delta);
|
||||
ApplySteps(a_World, next, a_LockStep);
|
||||
return next;
|
||||
}
|
||||
|
||||
void WorldTicker::ApplySteps(const game::World& a_World, WorldSnapshot& a_State, const protocol::LockStep& a_LockStep) {
|
||||
CommandApply cmdHandler(a_World, a_State);
|
||||
for (const auto& cmd : a_LockStep) {
|
||||
cmd->Dispatch(cmdHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldTicker::Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) {
|
||||
for (const auto& system : m_Systems) {
|
||||
system->Tick(a_World, a_State, a_Delta);
|
||||
}
|
||||
}
|
||||
|
||||
WorldSnapshot WorldTicker::CreateNext(WorldSnapshot& a_PreviousState) {
|
||||
static game::MobFactory mobFactory;
|
||||
|
||||
WorldSnapshot next {
|
||||
.m_Towers = a_PreviousState.m_Towers,
|
||||
.m_Teams = a_PreviousState.m_Teams
|
||||
};
|
||||
next.m_Mobs.reserve(a_PreviousState.m_Mobs.size());
|
||||
// creating a "linked list" of mobs between steps
|
||||
for (auto& mob : a_PreviousState.m_Mobs) {
|
||||
game::MobPtr newMob = std::shared_ptr<game::Mob>(mobFactory.CreateMessage(mob->GetId()).release());
|
||||
*newMob = *mob;
|
||||
mob->m_Next = newMob;
|
||||
next.m_Mobs.push_back(newMob);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
} // namespace sim
|
||||
} // namespace td
|
||||
13
src/td/simulation/system/EntityMove.cpp
Normal file
13
src/td/simulation/system/EntityMove.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <td/simulation/system/EntityMove.h>
|
||||
|
||||
namespace td {
|
||||
namespace sim {
|
||||
|
||||
void EntityMove::Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) {
|
||||
for (auto& mob : a_State.m_Mobs) {
|
||||
mob->m_Position.x += a_Delta;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sim
|
||||
} // namespace td
|
||||
Reference in New Issue
Block a user