From 6d0e56eb4625760cbae04254cfeb74c8ad4e9343 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 18 Jul 2025 13:11:18 +0200 Subject: [PATCH] too many things --- include/td/Maths.h | 2 +- include/td/Types.h | 18 +- include/td/game/Mobs.h | 34 ++-- include/td/game/World.h | 32 +-- include/td/game/WorldTypes.h | 2 + include/td/input/Display.h | 3 + include/td/protocol/command/CommandData.h | 4 +- include/td/protocol/packet/PacketSerialize.h | 18 ++ include/td/simulation/CommandApply.h | 21 ++ include/td/{game => simulation}/GameHistory.h | 0 include/td/simulation/RealTimeSimulation.h | 26 +++ include/td/simulation/WorldSnapshot.h | 17 ++ include/td/simulation/WorldTicker.h | 40 ++++ include/td/simulation/system/EntityMove.h | 14 ++ src/main.cpp | 182 ++++-------------- src/td/Types.cpp | 30 +++ src/td/game/World.cpp | 8 +- src/td/game/WorldTypes.cpp | 44 +++++ src/td/input/Display.cpp | 1 + src/td/protocol/packet/PacketSerialize.cpp | 115 +++++++++++ src/td/render/renderer/EntityRenderer.cpp | 6 +- src/td/simulation/CommandApply.cpp | 15 ++ src/td/{game => simulation}/GameHistory.cpp | 2 +- src/td/simulation/RealTimeSimulation.cpp | 32 +++ src/td/simulation/WorldTicker.cpp | 41 ++++ src/td/simulation/system/EntityMove.cpp | 13 ++ 26 files changed, 529 insertions(+), 191 deletions(-) create mode 100644 include/td/protocol/packet/PacketSerialize.h create mode 100644 include/td/simulation/CommandApply.h rename include/td/{game => simulation}/GameHistory.h (100%) create mode 100644 include/td/simulation/RealTimeSimulation.h create mode 100644 include/td/simulation/WorldSnapshot.h create mode 100644 include/td/simulation/WorldTicker.h create mode 100644 include/td/simulation/system/EntityMove.h create mode 100644 src/td/Types.cpp create mode 100644 src/td/game/WorldTypes.cpp create mode 100644 src/td/protocol/packet/PacketSerialize.cpp create mode 100644 src/td/simulation/CommandApply.cpp rename src/td/{game => simulation}/GameHistory.cpp (96%) create mode 100644 src/td/simulation/RealTimeSimulation.cpp create mode 100644 src/td/simulation/WorldTicker.cpp create mode 100644 src/td/simulation/system/EntityMove.cpp diff --git a/include/td/Maths.h b/include/td/Maths.h index 61b2f59..c5cad53 100644 --- a/include/td/Maths.h +++ b/include/td/Maths.h @@ -18,7 +18,7 @@ struct Vec2 { T g; }; - constexpr Vec2(T X = 0, T Y = 0) : x(X), y(Y) {} + constexpr Vec2(T X = T(0), T Y = T(0)) : x(X), y(Y) {} }; template diff --git a/include/td/Types.h b/include/td/Types.h index 84cc15a..497c4fa 100644 --- a/include/td/Types.h +++ b/include/td/Types.h @@ -2,6 +2,11 @@ #include #include +#include + +namespace sp { +class DataBuffer; +} // namespace sp namespace td { @@ -31,7 +36,7 @@ enum class EntityType : std::uint8_t { Zombie = 0, Spider, Pigman, - Skeleton, + Skelon, Creeper, Silverfish, Blaze, @@ -61,10 +66,7 @@ struct TowerCoords { std::int16_t y; }; -struct EntityCoords { - FpFloat x; - FpFloat y; -}; +using EntityCoords = Vec2; using PeerID = std::uint16_t; @@ -75,4 +77,10 @@ enum class Direction : std::uint8_t { NegativeY = 1 << 3, }; +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coords); +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float); + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, EntityCoords& a_Coords); +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, FpFloat& a_Float); + } // namespace td diff --git a/include/td/game/Mobs.h b/include/td/game/Mobs.h index 3b299a2..9f24e5f 100644 --- a/include/td/game/Mobs.h +++ b/include/td/game/Mobs.h @@ -10,8 +10,11 @@ #include namespace td { +using Vec2fp = Vec2; + namespace game { + struct WalkableTile; enum class EffectType : std::uint8_t { @@ -62,11 +65,12 @@ const MobStats* GetMobStats(MobType type, std::uint8_t level); const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level); const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level); -class Mob : public utils::shape::Rectangle { +class Mob { protected: float m_Health; private: + Vec2fp m_Position; MobID m_ID; PlayerID m_Sender; MobLevel m_Level; @@ -89,12 +93,14 @@ class Mob : public utils::shape::Rectangle { virtual MobType GetType() const = 0; - virtual void Tick(std::uint64_t delta, World* world) {} - virtual bool OnDeath(World* world) { return true; } + Vec2fp& GetPosition() { + return m_Position; + } + MobID GetMobID() const { return m_ID; } @@ -156,15 +162,15 @@ class Mob : public utils::shape::Rectangle { return m_HitCooldown > 0; } - // returns a float between 0 and 1 excluded - float GetTileX() { - return GetCenterX() - static_cast(static_cast(GetCenterX())); - } + // // returns a float between 0 and 1 excluded + // float GetTileX() { + // return GetCenterX() - static_cast(static_cast(GetCenterX())); + // } - // returns a float between 0 and 1 excluded - float GetTileY() { - return GetCenterY() - static_cast(static_cast(GetCenterY())); - } + // // returns a float between 0 and 1 excluded + // float GetTileY() { + // return GetCenterY() - static_cast(static_cast(GetCenterY())); + // } Direction GetDirection() const { return m_Direction; @@ -176,7 +182,7 @@ class Mob : public utils::shape::Rectangle { protected: void InitMob() { m_Health = static_cast(GetStats()->m_MaxLife); - SetSize(GetStats()->m_Size.x, GetStats()->m_Size.y); + // SetSize(GetStats()->m_Size.x, GetStats()->m_Size.y); } private: @@ -201,9 +207,7 @@ class ConcreteMob : public Mob { virtual ~ConcreteMob() {} - virtual void Tick(std::uint64_t delta, World* world) {} - - virtual constexpr MobType GetType() const { + virtual constexpr MobType GetType() const override { return MT; } }; diff --git a/include/td/game/World.h b/include/td/game/World.h index 886cc4b..da2b1f7 100644 --- a/include/td/game/World.h +++ b/include/td/game/World.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -19,11 +20,10 @@ class World { TilePalette m_TilePalette; - MobList m_Mobs; + sim::WorldSnapshot m_CurrentState; - TowerList m_Towers; - - TeamList m_Teams; + private: + sim::WorldTicker m_Ticker; public: World(); @@ -34,8 +34,6 @@ class World { bool LoadMapFromFile(const std::string& fileName); bool SaveMap(const std::string& fileName) const; - void Tick(std::uint64_t delta); - void SpawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir); TowerPtr PlaceTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder); @@ -83,47 +81,49 @@ class World { } const MobList& GetMobList() const { - return m_Mobs; + return m_CurrentState.m_Mobs; } MobList& GetMobList() { - return m_Mobs; + return m_CurrentState.m_Mobs; } const Color* GetTileColor(TilePtr tile) const; Team& GetRedTeam() { - return m_Teams[static_cast(TeamColor::Red)]; + return m_CurrentState.m_Teams[static_cast(TeamColor::Red)]; } const Team& GetRedTeam() const { - return m_Teams[static_cast(TeamColor::Red)]; + return m_CurrentState.m_Teams[static_cast(TeamColor::Red)]; } Team& GetBlueTeam() { - return m_Teams[static_cast(TeamColor::Blue)]; + return m_CurrentState.m_Teams[static_cast(TeamColor::Blue)]; } const Team& GetBlueTeam() const { - return m_Teams[static_cast(TeamColor::Red)]; + return m_CurrentState.m_Teams[static_cast(TeamColor::Red)]; } Team& GetTeam(TeamColor team) { - return m_Teams[static_cast(team)]; + return m_CurrentState.m_Teams[static_cast(team)]; } const Team& GetTeam(TeamColor team) const { - return m_Teams[static_cast(team)]; + return m_CurrentState.m_Teams[static_cast(team)]; } const TeamList& GetTeams() const { - return m_Teams; + return m_CurrentState.m_Teams; } const TowerList& GetTowers() const { - return 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); + private: void TickMobs(std::uint64_t delta); void CleanDeadMobs(); diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h index 8acdbd2..4e95cd6 100644 --- a/include/td/game/WorldTypes.h +++ b/include/td/game/WorldTypes.h @@ -102,6 +102,8 @@ typedef std::array SpawnColorPalette; typedef std::vector TowerList; +sp::DataBuffer& operator>>(sp::DataBuffer& buffer, TilePtr& tile); + } // namespace game diff --git a/include/td/input/Display.h b/include/td/input/Display.h index 95f6b39..cb4c6c1 100644 --- a/include/td/input/Display.h +++ b/include/td/input/Display.h @@ -3,11 +3,14 @@ #include #include +#include namespace td { class Display { public: + utils::Signal OnAspectRatioChange; + Display(int a_Width, int a_Height, const std::string& a_Title); ~Display(); diff --git a/include/td/protocol/command/CommandData.h b/include/td/protocol/command/CommandData.h index bf01602..1b72583 100644 --- a/include/td/protocol/command/CommandData.h +++ b/include/td/protocol/command/CommandData.h @@ -21,8 +21,10 @@ struct UpgradeTower { sp::BitField m_Upgrade; }; +using EntityTypeInt = std::uint8_t; + struct SpawnTroop { - sp::BitField m_Type; + sp::BitField m_Type; sp::BitField m_Level; EntityCoords m_Position; PlayerID m_Sender; diff --git a/include/td/protocol/packet/PacketSerialize.h b/include/td/protocol/packet/PacketSerialize.h new file mode 100644 index 0000000..81ef979 --- /dev/null +++ b/include/td/protocol/packet/PacketSerialize.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace sp { + +class DataBuffer; + +namespace details { + +template <> +void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header); + +template <> +void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData); + +} // namespace details +} // namespace sp \ No newline at end of file diff --git a/include/td/simulation/CommandApply.h b/include/td/simulation/CommandApply.h new file mode 100644 index 0000000..e98c217 --- /dev/null +++ b/include/td/simulation/CommandApply.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace td { +namespace sim { + +class CommandApply : public protocol::CommandHandler { + private: + const game::World& m_World; + WorldSnapshot& m_Snapshot; + + public: + CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot); + + virtual void Handle(const protocol::cdata::SpawnTroop& a_SpawnTroop) override; +}; + +} // namespace sim +} // namespace td diff --git a/include/td/game/GameHistory.h b/include/td/simulation/GameHistory.h similarity index 100% rename from include/td/game/GameHistory.h rename to include/td/simulation/GameHistory.h diff --git a/include/td/simulation/RealTimeSimulation.h b/include/td/simulation/RealTimeSimulation.h new file mode 100644 index 0000000..a97b03e --- /dev/null +++ b/include/td/simulation/RealTimeSimulation.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace td { +namespace sim { + +using GameHistory = std::vector; + +class RealTimeSimulation { + private: + std::uint64_t m_StepTime; + game::World& m_World; + GameHistory m_History; + std::uint64_t m_CurrentTime; + std::uint64_t m_LastTime; + std::size_t m_CurrentStep; + + public: + RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime); + + void Update(); +}; + +} // namespace sim +} // namespace td diff --git a/include/td/simulation/WorldSnapshot.h b/include/td/simulation/WorldSnapshot.h new file mode 100644 index 0000000..2081289 --- /dev/null +++ b/include/td/simulation/WorldSnapshot.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace td { +namespace sim { + +struct WorldSnapshot { + game::MobList m_Mobs; + + game::TowerList m_Towers; + + game::TeamList m_Teams; +}; + +} // namespace sim +} // namespace td diff --git a/include/td/simulation/WorldTicker.h b/include/td/simulation/WorldTicker.h new file mode 100644 index 0000000..42555e3 --- /dev/null +++ b/include/td/simulation/WorldTicker.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace td { +namespace game { +class World; +} + +namespace sim { + +class IWorldSystem { + public: + virtual void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) = 0; +}; + +class WorldTicker { + private: + std::vector> m_Systems; + + public: + WorldTicker(); + + WorldSnapshot NextStep( + const game::World& a_World, const WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta); + + private: + void ApplySteps(const game::World& a_World, WorldSnapshot& a_State, const protocol::LockStep& a_LockStep); + void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta); + WorldSnapshot CreateNext(const WorldSnapshot& a_PreviousState); + + template + void AddSystem() { + m_Systems.push_back(std::move(std::make_unique())); + } +}; + +} // namespace sim +} // namespace td diff --git a/include/td/simulation/system/EntityMove.h b/include/td/simulation/system/EntityMove.h new file mode 100644 index 0000000..0535054 --- /dev/null +++ b/include/td/simulation/system/EntityMove.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace td { +namespace sim { + +class EntityMove : public IWorldSystem { + public: + virtual void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) override; +}; + +} // namespace sim +} // namespace td diff --git a/src/main.cpp b/src/main.cpp index 281f48c..53f6b90 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,159 +5,18 @@ #include #include +#include #include -#include #include +#include -namespace td { -namespace game { - -sp::DataBuffer& operator>>(sp::DataBuffer& buffer, game::TilePtr& tile) { - game::TileType tileType; - buffer >> tileType; - switch (tileType) { - case game::TileType::Tower: { - auto tilePtr = std::make_shared(); - buffer >> tilePtr->color_palette_ref >> tilePtr->team_owner; - tile = tilePtr; - break; - } - case game::TileType::Walk: { - auto tilePtr = std::make_shared(); - buffer >> tilePtr->direction; - tile = tilePtr; - break; - } - case game::TileType::Decoration: { - auto tilePtr = std::make_shared(); - buffer >> tilePtr->color_palette_ref; - tile = tilePtr; - break; - } - default: - break; - } - return buffer; -} -} // namespace game -} // namespace td - -namespace sp { -namespace details { - -template <> -void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header) { - a_Buffer >> a_Header.m_TowerPlacePalette >> a_Header.m_WalkablePalette; - - std::uint16_t decoPaletteSize; - a_Buffer >> decoPaletteSize; - - std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(td::Color); - - a_Header.m_DecorationPalette.resize(decoPaletteSize); - - memcpy(reinterpret_cast(a_Header.m_DecorationPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), - decoPalletteSizeByte); - - a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + decoPalletteSizeByte); - - a_Buffer >> a_Header.m_Background; - - td::utils::shape::Rectangle redCastle, blueCastle; - - a_Buffer >> a_Header.m_RedSpawn >> redCastle; - a_Buffer >> a_Header.m_BlueSpawn >> blueCastle; - - a_Header.m_RedCastle.SetShape(redCastle); - a_Header.m_BlueCastle.SetShape(blueCastle); - - std::uint64_t tilePaletteSize; - a_Buffer >> tilePaletteSize; - - a_Header.m_TilePalette.reserve(tilePaletteSize); - - for (std::uint64_t tileNumber = 0; tileNumber < tilePaletteSize; tileNumber++) { - td::game::TilePtr tile; - a_Buffer >> tile; - a_Header.m_TilePalette.push_back(tile); - } - - a_Buffer >> a_Header.m_SpawnColorPalette; -} - -typedef std::vector ChunkPackedData; - -const int BITS_IN_BYTE = 8; -const int BITS_IN_LONG = BITS_IN_BYTE * sizeof(std::uint64_t); - -static unsigned int countBits(unsigned int number) { - // log function in base 2 - // take only integer part - return static_cast(std::log2(number) + 1); -} - - -template <> -void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData) { - std::uint64_t chunkCount; - a_Buffer >> chunkCount; - - for (std::uint64_t chunkNumber = 0; chunkNumber < chunkCount; chunkNumber++) { - td::game::ChunkPtr chunk = std::make_shared(); - - td::game::ChunkCoord chunkCoords; - a_Buffer >> chunkCoords.x >> chunkCoords.y; - - std::uint64_t chunkPaletteSize; - // std::reverse(reinterpret_cast(&chunkPaletteSize), reinterpret_cast(&chunkPaletteSize) + 4); - a_Buffer >> chunkPaletteSize; - - td::game::ChunkPalette chunkPalette(chunkPaletteSize); - - memcpy(reinterpret_cast(chunkPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), - chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type)); - a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type)); - - chunk->palette = chunkPalette; - - std::uint8_t bitsPerTile = countBits(chunkPaletteSize); - - // A bitmask that contains bitsPerTile set bits - td::game::Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1); - - ChunkPackedData chunkData(td::game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0); - - memcpy(reinterpret_cast(chunkData.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), - chunkData.size() * sizeof(ChunkPackedData::value_type)); - a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkData.size() * sizeof(ChunkPackedData::value_type)); - - for (unsigned int tileNumber = 0; tileNumber < td::game::Chunk::ChunkSize; tileNumber++) { - std::size_t startLong = (tileNumber * bitsPerTile) / BITS_IN_LONG; - std::size_t startOffset = (tileNumber * bitsPerTile) % BITS_IN_LONG; - std::size_t endLong = ((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG; - - td::game::Chunk::ChunkData::value_type value; - if (startLong == endLong) { - value = (chunkData[startLong] >> startOffset); - } else { - int endOffset = BITS_IN_LONG - startOffset; - value = (chunkData[startLong] >> startOffset | chunkData[endLong] << endOffset); - } - value &= individualValueMask; - - chunk->tiles[tileNumber] = value; - } - a_WorldData.m_Chunks.insert({chunkCoords, chunk}); - } -} - -} // namespace details -} // namespace sp +#include class WorldApply : public td::protocol::PacketHandler { private: td::game::World& m_World; + public: WorldApply(td::game::World& a_World) : m_World(a_World) {} @@ -170,7 +29,7 @@ class WorldApply : public td::protocol::PacketHandler { }; td::game::World GetWorld() { -sp::DataBuffer buffer; + sp::DataBuffer buffer; buffer.ReadFile("test/tdmap.tdmap2"); sp::DataBuffer buffer1 = sp::zlib::Decompress(buffer, 84); @@ -196,6 +55,26 @@ sp::DataBuffer buffer; return w; } + + +void FastForward(td::game::World& a_World, const td::sim::GameHistory& a_LockSteps) { + const td::FpFloat delta = td::FpFloat(1) / td::FpFloat(75); + for (const auto& lockstep : a_LockSteps) { + a_World.Tick(lockstep, delta); + } +} + +td::sim::GameHistory GetCustomHistory() { + constexpr std::size_t MAX_COUNT = 20 * 60 * 40; + + td::sim::GameHistory gh(MAX_COUNT); + + auto spawn = std::make_shared(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0); + gh[0].push_back(spawn); + + return gh; +} + int main(int argc, char** argv) { td::game::World w = GetWorld(); @@ -204,9 +83,11 @@ int main(int argc, char** argv) { td::render::Camera cam; - auto mob = std::make_shared(0, 0, 0); - mob->SetCenter({77, 13}); - w.GetMobList().push_back(mob); + display.OnAspectRatioChange.Connect([&cam](float a_AspectRatio){ + cam.UpdatePerspective(a_AspectRatio); + }); + + td::sim::GameHistory gh = GetCustomHistory(); td::render::RenderPipeline renderer; renderer.AddRenderer(cam, w); @@ -215,8 +96,11 @@ int main(int argc, char** argv) { cam.SetCamPos({77, 25, 13}); cam.UpdatePerspective(display.GetAspectRatio()); + td::sim::RealTimeSimulation simulation(w, std::move(gh), 1000); + while (!display.IsCloseRequested()) { display.PollEvents(); + simulation.Update(); renderer.Render(); display.Update(); } diff --git a/src/td/Types.cpp b/src/td/Types.cpp new file mode 100644 index 0000000..1e7508b --- /dev/null +++ b/src/td/Types.cpp @@ -0,0 +1,30 @@ +#include + +#include +#include + +namespace td { + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coords) { + return a_Buffer << a_Coords.x << a_Coords.y; +} + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float) { + auto raw = a_Float.raw_value(); + sp::ToNetwork(raw); + return a_Buffer << raw; +} + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, EntityCoords& a_Coords) { + return a_Buffer >> a_Coords.x >> a_Coords.y; +} + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, FpFloat& a_Float) { + auto raw = a_Float.raw_value(); + a_Buffer >> raw; + sp::FromNetwork(raw); + a_Float = FpFloat::from_raw_value(raw); + return a_Buffer; +} + +} // namespace td diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp index 035bb7b..fefb18b 100644 --- a/src/td/game/World.cpp +++ b/src/td/game/World.cpp @@ -1,9 +1,11 @@ #include +#include + namespace td { namespace game { -World::World() : m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}} { +World::World() : m_CurrentState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}} { } @@ -55,5 +57,9 @@ bool World::LoadMap(const protocol::pdata::WorldData& a_WorldData) { return true; } +void World::Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta) { + m_CurrentState = m_Ticker.NextStep(*this, m_CurrentState, a_LockStep, a_Delta); +} + } // namespace game } // namespace td diff --git a/src/td/game/WorldTypes.cpp b/src/td/game/WorldTypes.cpp new file mode 100644 index 0000000..284443e --- /dev/null +++ b/src/td/game/WorldTypes.cpp @@ -0,0 +1,44 @@ +#include + +#include + +namespace td { +namespace game { + +sp::DataBuffer& operator>>(sp::DataBuffer& buffer, TileType& tile) { + std::uint8_t raw; + buffer >> raw; + tile = TileType(raw); + return buffer; +} + +sp::DataBuffer& operator>>(sp::DataBuffer& buffer, TilePtr& tile) { + game::TileType tileType; + buffer >> tileType; + switch (tileType) { + case game::TileType::Tower: { + auto tilePtr = std::make_shared(); + buffer >> tilePtr->color_palette_ref >> tilePtr->team_owner; + tile = tilePtr; + break; + } + case game::TileType::Walk: { + auto tilePtr = std::make_shared(); + buffer >> tilePtr->direction; + tile = tilePtr; + break; + } + case game::TileType::Decoration: { + auto tilePtr = std::make_shared(); + buffer >> tilePtr->color_palette_ref; + tile = tilePtr; + break; + } + default: + break; + } + return buffer; +} + +} // namespace game +} // namespace td diff --git a/src/td/input/Display.cpp b/src/td/input/Display.cpp index ff37c6e..0536daf 100644 --- a/src/td/input/Display.cpp +++ b/src/td/input/Display.cpp @@ -125,6 +125,7 @@ void Display::PollEvents() { m_LastWidth = event.window.data1; m_LastHeight = event.window.data2; m_AspectRatio = (float)m_LastWidth / m_LastHeight; + OnAspectRatioChange(m_AspectRatio); } default: diff --git a/src/td/protocol/packet/PacketSerialize.cpp b/src/td/protocol/packet/PacketSerialize.cpp new file mode 100644 index 0000000..29ce7ac --- /dev/null +++ b/src/td/protocol/packet/PacketSerialize.cpp @@ -0,0 +1,115 @@ +#include + +#include + +namespace sp { +namespace details { + +template <> +void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header) { + a_Buffer >> a_Header.m_TowerPlacePalette >> a_Header.m_WalkablePalette; + + std::uint16_t decoPaletteSize; + a_Buffer >> decoPaletteSize; + + std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(td::Color); + + a_Header.m_DecorationPalette.resize(decoPaletteSize); + + memcpy(reinterpret_cast(a_Header.m_DecorationPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), + decoPalletteSizeByte); + + a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + decoPalletteSizeByte); + + a_Buffer >> a_Header.m_Background; + + td::utils::shape::Rectangle redCastle, blueCastle; + + a_Buffer >> a_Header.m_RedSpawn >> redCastle; + a_Buffer >> a_Header.m_BlueSpawn >> blueCastle; + + a_Header.m_RedCastle.SetShape(redCastle); + a_Header.m_BlueCastle.SetShape(blueCastle); + + std::uint64_t tilePaletteSize; + a_Buffer >> tilePaletteSize; + + a_Header.m_TilePalette.reserve(tilePaletteSize); + + for (std::uint64_t tileNumber = 0; tileNumber < tilePaletteSize; tileNumber++) { + td::game::TilePtr tile; + a_Buffer >> tile; + a_Header.m_TilePalette.push_back(tile); + } + + a_Buffer >> a_Header.m_SpawnColorPalette; +} + +typedef std::vector ChunkPackedData; + +const int BITS_IN_BYTE = 8; +const int BITS_IN_LONG = BITS_IN_BYTE * sizeof(std::uint64_t); + +static unsigned int countBits(unsigned int number) { + // log function in base 2 + // take only integer part + return static_cast(std::log2(number) + 1); +} + + +template <> +void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData) { + std::uint64_t chunkCount; + a_Buffer >> chunkCount; + + for (std::uint64_t chunkNumber = 0; chunkNumber < chunkCount; chunkNumber++) { + td::game::ChunkPtr chunk = std::make_shared(); + + td::game::ChunkCoord chunkCoords; + a_Buffer >> chunkCoords.x >> chunkCoords.y; + + std::uint64_t chunkPaletteSize; + // std::reverse(reinterpret_cast(&chunkPaletteSize), reinterpret_cast(&chunkPaletteSize) + 4); + a_Buffer >> chunkPaletteSize; + + td::game::ChunkPalette chunkPalette(chunkPaletteSize); + + memcpy(reinterpret_cast(chunkPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), + chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type)); + a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type)); + + chunk->palette = chunkPalette; + + std::uint8_t bitsPerTile = countBits(chunkPaletteSize); + + // A bitmask that contains bitsPerTile set bits + td::game::Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1); + + ChunkPackedData chunkData(td::game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0); + + memcpy(reinterpret_cast(chunkData.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), + chunkData.size() * sizeof(ChunkPackedData::value_type)); + a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkData.size() * sizeof(ChunkPackedData::value_type)); + + for (unsigned int tileNumber = 0; tileNumber < td::game::Chunk::ChunkSize; tileNumber++) { + std::size_t startLong = (tileNumber * bitsPerTile) / BITS_IN_LONG; + std::size_t startOffset = (tileNumber * bitsPerTile) % BITS_IN_LONG; + std::size_t endLong = ((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG; + + td::game::Chunk::ChunkData::value_type value; + if (startLong == endLong) { + value = (chunkData[startLong] >> startOffset); + } else { + int endOffset = BITS_IN_LONG - startOffset; + value = (chunkData[startLong] >> startOffset | chunkData[endLong] << endOffset); + } + value &= individualValueMask; + + chunk->tiles[tileNumber] = value; + } + a_WorldData.m_Chunks.insert({chunkCoords, chunk}); + } +} + +} // namespace details +} // namespace sp \ No newline at end of file diff --git a/src/td/render/renderer/EntityRenderer.cpp b/src/td/render/renderer/EntityRenderer.cpp index 1c8dac4..55eaa67 100644 --- a/src/td/render/renderer/EntityRenderer.cpp +++ b/src/td/render/renderer/EntityRenderer.cpp @@ -16,8 +16,10 @@ EntityRenderer::~EntityRenderer() {} void EntityRenderer::Render() { m_Shader->Start(); for (const auto& mob : m_World.GetMobList()) { - const auto mobCoords = mob->GetCenter(); - m_Shader->SetModelPos({mobCoords.GetX(), 1, mobCoords.GetY()}); + const auto mobCoords = mob->GetPosition(); + float x = static_cast(mobCoords.x); + float z = static_cast(mobCoords.y); + m_Shader->SetModelPos({x, 1, z}); Renderer::Render(*m_EntityVao); } } diff --git a/src/td/simulation/CommandApply.cpp b/src/td/simulation/CommandApply.cpp new file mode 100644 index 0000000..eeaa9e2 --- /dev/null +++ b/src/td/simulation/CommandApply.cpp @@ -0,0 +1,15 @@ +#include + +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::cdata::SpawnTroop& a_SpawnTroop) { + auto zombie = std::make_shared(0, *a_SpawnTroop.m_Level, a_SpawnTroop.m_Sender); + zombie->GetPosition() = a_SpawnTroop.m_Position; + m_Snapshot.m_Mobs.push_back(zombie); +} + +} // namespace sim +} // namespace td diff --git a/src/td/game/GameHistory.cpp b/src/td/simulation/GameHistory.cpp similarity index 96% rename from src/td/game/GameHistory.cpp rename to src/td/simulation/GameHistory.cpp index f295c77..a707db0 100644 --- a/src/td/game/GameHistory.cpp +++ b/src/td/simulation/GameHistory.cpp @@ -1,4 +1,4 @@ -#include +#include namespace td { namespace game { diff --git a/src/td/simulation/RealTimeSimulation.cpp b/src/td/simulation/RealTimeSimulation.cpp new file mode 100644 index 0000000..6abfc42 --- /dev/null +++ b/src/td/simulation/RealTimeSimulation.cpp @@ -0,0 +1,32 @@ +#include + +#include + +namespace td { +namespace sim { + +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) : + m_StepTime(a_StepTime), + m_World(a_World), + m_History(std::move(a_History)), + m_CurrentTime(0), + m_LastTime(GetTime()), + m_CurrentStep(0) {} + +void RealTimeSimulation::Update() { + m_CurrentTime += GetTime() - m_LastTime; + m_LastTime = GetTime(); + if (m_CurrentTime > m_StepTime) { + m_World.Tick(m_History[m_CurrentStep], FpFloat(m_StepTime) / FpFloat(1000)); + m_CurrentStep++; + m_CurrentTime -= m_StepTime; + } +} + +} // namespace sim +} // namespace td diff --git a/src/td/simulation/WorldTicker.cpp b/src/td/simulation/WorldTicker.cpp new file mode 100644 index 0000000..367f90b --- /dev/null +++ b/src/td/simulation/WorldTicker.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include + +#define ADD_SYSTEM(class) std::move(std::make_unique()) + +namespace td { +namespace sim { + +WorldTicker::WorldTicker() { + AddSystem(); +} + +WorldSnapshot WorldTicker::NextStep( + const game::World& a_World, const WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) { + WorldSnapshot next = CreateNext(a_PreviousState); + ApplySteps(a_World, next, a_LockStep); + Tick(a_World, next, a_Delta); + 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(const WorldSnapshot& a_PreviousState) { + return a_PreviousState; +} + +} // namespace sim +} // namespace td diff --git a/src/td/simulation/system/EntityMove.cpp b/src/td/simulation/system/EntityMove.cpp new file mode 100644 index 0000000..4592762 --- /dev/null +++ b/src/td/simulation/system/EntityMove.cpp @@ -0,0 +1,13 @@ +#include + +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->GetPosition().x += a_Delta; + } +} + +} // namespace sim +} // namespace td