diff --git a/include/td/game/Team.h b/include/td/game/Team.h index a73fb76..70cbd20 100644 --- a/include/td/game/Team.h +++ b/include/td/game/Team.h @@ -80,7 +80,29 @@ public: std::uint8_t GetPlayerCount() const; }; -typedef std::array TeamList; +struct TeamList { + std::array m_Teams; + + TeamList() : m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}{ + + } + + Team& operator[](std::size_t a_Index) { + return m_Teams[a_Index]; + } + + Team& operator[](TeamColor a_Index) { + return m_Teams[static_cast(a_Index)]; + } + + const Team& operator[](std::size_t a_Index) const { + return m_Teams[a_Index]; + } + + const Team& operator[](TeamColor a_Index) const { + return m_Teams[static_cast(a_Index)]; + } +}; } // namespace game } // namespace td diff --git a/include/td/game/Towers.h b/include/td/game/Towers.h index 4715855..574c1d2 100644 --- a/include/td/game/Towers.h +++ b/include/td/game/Towers.h @@ -107,15 +107,15 @@ class ConcreteTower : public sp::ConcreteMessage public: using HandlerType = typename sp::ConcreteMessage::HandlerType; - virtual TowerSize GetSize() const { + virtual TowerSize GetSize() const override { return Size; } - virtual TowerType GetType() const { + virtual TowerType GetType() const override { return Type; } - virtual void Tick(std::uint64_t delta, World* world) {} + virtual void Tick(std::uint64_t delta, World* world) override {} virtual void Dispatch(HandlerType& handler) const override { handler.Handle(*this); diff --git a/include/td/game/World.h b/include/td/game/World.h index 5c591bd..f03f5cf 100644 --- a/include/td/game/World.h +++ b/include/td/game/World.h @@ -20,8 +20,8 @@ class World { TilePalette m_TilePalette; - sim::WorldSnapshot m_CurrentState; - sim::WorldSnapshot m_NextState; + std::shared_ptr m_CurrentState; + std::shared_ptr m_NextState; private: sim::WorldTicker m_Ticker; @@ -82,52 +82,55 @@ class World { } const MobList& GetMobList() const { - return m_CurrentState.m_Mobs; + return m_CurrentState->m_Mobs; } MobList& GetMobList() { - return m_CurrentState.m_Mobs; + return m_CurrentState->m_Mobs; } const Color* GetTileColor(const TilePtr& tile) const; Team& GetRedTeam() { - return m_CurrentState.m_Teams[static_cast(TeamColor::Red)]; + return m_CurrentState->m_Teams[TeamColor::Red]; } const Team& GetRedTeam() const { - return m_CurrentState.m_Teams[static_cast(TeamColor::Red)]; + return m_CurrentState->m_Teams[TeamColor::Red]; } Team& GetBlueTeam() { - return m_CurrentState.m_Teams[static_cast(TeamColor::Blue)]; + return m_CurrentState->m_Teams[TeamColor::Blue]; } const Team& GetBlueTeam() const { - return m_CurrentState.m_Teams[static_cast(TeamColor::Red)]; + return m_CurrentState->m_Teams[TeamColor::Red]; } Team& GetTeam(TeamColor team) { - return m_CurrentState.m_Teams[static_cast(team)]; + return m_CurrentState->m_Teams[team]; } const Team& GetTeam(TeamColor team) const { - return m_CurrentState.m_Teams[static_cast(team)]; + return m_CurrentState->m_Teams[team]; } const TeamList& GetTeams() const { - return m_CurrentState.m_Teams; + return m_CurrentState->m_Teams; } const TowerList& GetTowers() const { - return m_CurrentState.m_Towers; + return m_CurrentState->m_Towers; } TowerPtr GetTowerById(TowerID tower); const Player* GetPlayerById(PlayerID id) const; - void Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta); + const std::shared_ptr& Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta); + + void ResetSnapshots(std::shared_ptr& a_Current, std::shared_ptr& a_Next); private: void TickMobs(std::uint64_t delta); void CleanDeadMobs(); + }; diff --git a/include/td/input/Display.h b/include/td/input/Display.h index cb4c6c1..7d1bf87 100644 --- a/include/td/input/Display.h +++ b/include/td/input/Display.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace td { @@ -10,6 +11,7 @@ namespace td { class Display { public: utils::Signal OnAspectRatioChange; + utils::Signal OnKeyDown; Display(int a_Width, int a_Height, const std::string& a_Title); ~Display(); diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index 32b954a..d395ec3 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -74,7 +74,7 @@ struct BeginGame { struct LockSteps { std::uint16_t m_FirstFrameNumber; - Array m_LockSteps; + std::array m_LockSteps; }; struct WorldHeader { diff --git a/include/td/simulation/RealTimeSimulation.h b/include/td/simulation/RealTimeSimulation.h index 694f500..2fe4db2 100644 --- a/include/td/simulation/RealTimeSimulation.h +++ b/include/td/simulation/RealTimeSimulation.h @@ -1,34 +1,61 @@ #pragma once #include +#include namespace td { namespace sim { using GameHistory = std::vector; +using GameBuffer = std::vector>; class RealTimeSimulation { private: std::uint64_t m_StepTime; game::World& m_World; - GameHistory m_History; + GameBuffer m_History; std::uint64_t m_CurrentTime; std::uint64_t m_LastTime; std::size_t m_CurrentStep; + std::shared_ptr m_LastSnapshot; + std::uint64_t m_LastValidStep; + + static const protocol::LockStep EMPTY_LOCKSTEP; + public: /** + * \brief Replay constructor * \param a_StepTime in ms */ - RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime); + RealTimeSimulation(game::World& a_World, const GameHistory& a_History, std::uint64_t a_StepTime); + + /** + * \brief Live update constructor (continuous game updates) + * \param a_StepTime in ms + */ + RealTimeSimulation(game::World& a_World, std::uint64_t a_StepTime); /** * \return the progress [0-1] between two steps */ float Update(); + void HandlePacket(const protocol::packets::LockStepsPacket& a_LockSteps); + void HandlePacket(const protocol::packets::PredictCommandPacket& a_Predict); + private: void Step(); + + /** + * \brief Ticks a_Count times + */ + void FastForward(std::size_t a_Count); + + /** + * \brief Tries to recompute simulation if needed (for example in late command receival) + */ + void FastReplay(); }; } // namespace sim diff --git a/src/main.cpp b/src/main.cpp index 6e3140b..08a570a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -103,7 +103,17 @@ int main(int argc, char** argv) { cam.SetCamPos({77, 5, 13}); cam.UpdatePerspective(display.GetAspectRatio()); - td::sim::RealTimeSimulation simulation(w, std::move(gh), 500); + td::sim::RealTimeSimulation simulation(w, 500); + + display.OnKeyDown.Connect([&simulation](SDL_Keycode key){ + if (key == SDLK_A) { + auto spawn = std::make_shared(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0); + std::array steps{}; + steps[0].push_back(spawn); + td::protocol::packets::LockStepsPacket packet{0, steps}; + simulation.HandlePacket(packet); + } + }); while (!display.IsCloseRequested()) { display.PollEvents(); diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp index b4c0afc..840eb59 100644 --- a/src/td/game/World.cpp +++ b/src/td/game/World.cpp @@ -5,7 +5,7 @@ namespace td { namespace game { -World::World() : m_CurrentState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}}, m_NextState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}} {} +World::World() : m_CurrentState(std::make_shared()), m_NextState(m_CurrentState) {} const Color* World::GetTileColor(const TilePtr& tile) const { switch (tile->GetType()) { @@ -55,9 +55,15 @@ bool World::LoadMap(const protocol::pdata::WorldData& a_WorldData) { return true; } -void World::Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta) { +const std::shared_ptr& World::Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta) { m_CurrentState = m_NextState; - m_NextState = m_Ticker.NextStep(*this, m_NextState, a_LockStep, a_Delta); + m_NextState = std::make_shared(m_Ticker.NextStep(*this, *m_NextState, a_LockStep, a_Delta)); + return m_CurrentState; +} + +void World::ResetSnapshots(std::shared_ptr& a_Current, std::shared_ptr& a_Next) { + m_CurrentState = a_Current; + m_NextState = a_Next; } } // namespace game diff --git a/src/td/input/Display.cpp b/src/td/input/Display.cpp index 0536daf..d744d10 100644 --- a/src/td/input/Display.cpp +++ b/src/td/input/Display.cpp @@ -119,6 +119,7 @@ void Display::PollEvents() { case SDL_EVENT_QUIT: case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { m_ShouldClose = true; + break; } case SDL_EVENT_WINDOW_RESIZED: { @@ -126,6 +127,13 @@ void Display::PollEvents() { m_LastHeight = event.window.data2; m_AspectRatio = (float)m_LastWidth / m_LastHeight; OnAspectRatioChange(m_AspectRatio); + break; + } + + case SDL_EVENT_KEY_DOWN: { + if(!event.key.repeat) + OnKeyDown(event.key.key); + break; } default: diff --git a/src/td/simulation/CommandApply.cpp b/src/td/simulation/CommandApply.cpp index dd227af..8e17aef 100644 --- a/src/td/simulation/CommandApply.cpp +++ b/src/td/simulation/CommandApply.cpp @@ -5,7 +5,9 @@ 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 CommandApply::Handle(const protocol::commands::EndCommand& a_End) { + (void) m_World; +} void CommandApply::Handle(const protocol::commands::PlaceTowerCommand& a_PlaceTower) { static game::TowerFactory factory; diff --git a/src/td/simulation/GameHistory.cpp b/src/td/simulation/GameHistory.cpp index a707db0..0639723 100644 --- a/src/td/simulation/GameHistory.cpp +++ b/src/td/simulation/GameHistory.cpp @@ -33,7 +33,7 @@ void GameHistory::FromPacket(td::protocol::pdata::LockSteps&& a_Steps) { } td::protocol::packets::LockStepsPacket GameHistory::ToPacket(HistorySizeType a_StartIndex) { - Array steps; + std::array steps; for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { steps[i] = GetLockStep(a_StartIndex + i); } diff --git a/src/td/simulation/RealTimeSimulation.cpp b/src/td/simulation/RealTimeSimulation.cpp index 438c424..26ec283 100644 --- a/src/td/simulation/RealTimeSimulation.cpp +++ b/src/td/simulation/RealTimeSimulation.cpp @@ -5,18 +5,37 @@ namespace td { namespace sim { +const protocol::LockStep RealTimeSimulation::EMPTY_LOCKSTEP; + std::uint64_t GetTime() { return static_cast( std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count()); } -RealTimeSimulation::RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime) : +RealTimeSimulation::RealTimeSimulation(game::World& a_World, const GameHistory& a_History, std::uint64_t a_StepTime) : m_StepTime(a_StepTime), m_World(a_World), - m_History(std::move(a_History)), m_CurrentTime(0), m_LastTime(GetTime()), - m_CurrentStep(0) { + m_CurrentStep(0), + m_LastSnapshot(std::make_shared()), + m_LastValidStep(0) { + m_History.reserve(a_History.size()); + for (const auto& lockstep : a_History) { + m_History.emplace_back(lockstep); + } + Step(); +} + +RealTimeSimulation::RealTimeSimulation(game::World& 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_LastTime(GetTime()), + m_CurrentStep(0), + m_LastSnapshot(std::make_shared()), + m_LastValidStep(0) { Step(); } @@ -28,13 +47,47 @@ float RealTimeSimulation::Update() { Step(); m_CurrentTime -= m_StepTime; } - return (float) m_CurrentTime / (float) m_StepTime; + return (float)m_CurrentTime / (float)m_StepTime; } void RealTimeSimulation::Step() { - m_World.Tick(m_History[m_CurrentStep], FpFloat(m_StepTime) / FpFloat(1000)); + 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 RealTimeSimulation::HandlePacket(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 RealTimeSimulation::HandlePacket(const protocol::packets::PredictCommandPacket& a_Predict) {} + +void RealTimeSimulation::FastForward(std::size_t a_Count) { + for (std::size_t i = 0; i < a_Count; i++) { + Step(); + } +} + +void RealTimeSimulation::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