diff --git a/include/td/game/Team.h b/include/td/game/Team.h index 70cbd20..031b144 100644 --- a/include/td/game/Team.h +++ b/include/td/game/Team.h @@ -31,23 +31,17 @@ class Team; class TeamCastle : public utils::shape::Rectangle { private: - const Team* m_Team; float m_Life; public: static constexpr int CastleMaxLife = 1000; - TeamCastle(const Team* team) : m_Team(team), m_Life(CastleMaxLife) { + TeamCastle() : m_Life(CastleMaxLife) { SetWidth(5); SetHeight(5); } - TeamCastle() : TeamCastle(nullptr) {} - float GetLife() const { return m_Life; } - const Team* GetTeam() const { return m_Team; } - void SetTeam(const Team* team) { m_Team = team; } - void SetLife(float life) { m_Life = life; } void Damage(float damage) { m_Life = std::max(0.0f, m_Life - damage); } diff --git a/include/td/game/World.h b/include/td/game/World.h index f03f5cf..93ee19f 100644 --- a/include/td/game/World.h +++ b/include/td/game/World.h @@ -14,7 +14,7 @@ class World { std::vector m_DecorationPalette; Color m_Background; - std::unordered_map m_Chunks; + ChunkList m_Chunks; SpawnColorPalette m_SpawnColorPalette; @@ -70,7 +70,7 @@ class World { TowerPtr GetTower(const Vec2f& position) const; // returns null if no tower is here - const std::unordered_map& GetChunks() const { + const ChunkList& GetChunks() const { return m_Chunks; } diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h index 7bf43fb..1d872ec 100644 --- a/include/td/game/WorldTypes.h +++ b/include/td/game/WorldTypes.h @@ -7,14 +7,8 @@ namespace td { namespace game { -struct ChunkCoord { - std::int16_t x, y; - - friend bool operator==(const td::game::ChunkCoord& first, const td::game::ChunkCoord& other) { - return first.x == other.x && first.y == other.y; - } -}; +using ChunkCoord = Vec2; class Game; @@ -80,15 +74,17 @@ struct Chunk { typedef std::array ChunkData; // stores index of tile palette - ChunkData tiles{0}; - ChunkPalette palette; + ChunkData m_Tiles{0}; + ChunkPalette m_Palette; TileIndex GetTileIndex(std::uint16_t tileNumber) const { - TileIndex chunkPaletteIndex = tiles.at(tileNumber); + TileIndex chunkPaletteIndex = m_Tiles.at(tileNumber); if (chunkPaletteIndex == 0) // index 0 means empty tile index 1 = first tile return 0; - return palette.at(chunkPaletteIndex); + return m_Palette.at(chunkPaletteIndex); } + + // TODO: keep data packed }; typedef std::shared_ptr ChunkPtr; @@ -103,6 +99,8 @@ typedef std::array SpawnColorPalette; typedef std::vector TowerList; +using ChunkList = std::unordered_map; + sp::DataBuffer& operator>>(sp::DataBuffer& buffer, TilePtr& tile); diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index 32b954a..139947f 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -92,7 +92,7 @@ struct WorldHeader { }; struct WorldData { - std::unordered_map m_Chunks; + game::ChunkList m_Chunks; }; } // namespace pdata diff --git a/include/td/protocol/packet/PacketSerialize.h b/include/td/protocol/packet/PacketSerialize.h index 85baf49..b732517 100644 --- a/include/td/protocol/packet/PacketSerialize.h +++ b/include/td/protocol/packet/PacketSerialize.h @@ -6,10 +6,60 @@ namespace sp { class DataBuffer; +// temp template <> -void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header); +void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::PredictCommand& a_Header); template <> -void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData); +DataBuffer WriteMessage(const td::protocol::pdata::PredictCommand& a_Header); -} // namespace sp \ No newline at end of file +} // namespace sp + + +namespace td { + +template +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Vec2& a_Vec) { + return a_Buffer << a_Vec.x << a_Vec.y; +} + +template +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Vec2& a_Vec) { + return a_Buffer >> a_Vec.x >> a_Vec.y; +} + +template +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Vec3& a_Vec) { + return a_Buffer << a_Vec.x << a_Vec.y << a_Vec.z; +} + +template +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Vec3& a_Vec) { + return a_Buffer >> a_Vec.x >> a_Vec.y >> a_Vec.z; +} + + +template +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Vec4& a_Vec) { + return a_Buffer << a_Vec.x << a_Vec.y << a_Vec.z << a_Vec.w; +} + +template +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Vec4& a_Vec) { + return a_Buffer >> a_Vec.x >> a_Vec.y >> a_Vec.z >> a_Vec.w; +} + + +namespace game { + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const TeamCastle& a_Castle); +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, TeamCastle& a_Castle); + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const ChunkPtr& a_Chunk); +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, ChunkPtr& a_Chunk); + +sp::DataBuffer& operator<<(sp::DataBuffer& buffer, const TilePtr& tile); +sp::DataBuffer& operator>>(sp::DataBuffer& buffer, TilePtr& tile); + +} // namespace game +} // namespace td diff --git a/src/main.cpp b/src/main.cpp index 53c18b0..66a7d2a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,11 +9,15 @@ #include #include -#include #include +#include #include +#include +#include +#include + class WorldApply : public td::protocol::PacketHandler { private: td::game::World& m_World; @@ -32,18 +36,15 @@ class WorldApply : public td::protocol::PacketHandler { }; td::game::World GetWorld() { - sp::DataBuffer buffer; - buffer.ReadFile("test/tdmap.tdmap2"); + auto comp = std::make_shared(); - sp::DataBuffer buffer1 = sp::zlib::Decompress(buffer, 84); - buffer.SetReadOffset(buffer.GetReadOffset() + 83); - sp::DataBuffer buffer2 = sp::zlib::Decompress(buffer, 511); + std::ifstream fStream("test/tdmap.tdmap2"); + auto out = std::make_shared(fStream); - td::protocol::packets::WorldHeaderPacket header; - header.Read(buffer1); + sp::MessageStream stream(std::move(out), std::move(comp)); - td::protocol::packets::WorldDataPacket data; - data.Read(buffer2); + auto header = stream.ReadMessage(); + auto data = stream.ReadMessage(); td::game::World w; WorldApply wa(w); @@ -52,12 +53,24 @@ td::game::World GetWorld() { d.RegisterHandler(td::protocol::PacketID::WorldData, &wa); d.RegisterHandler(td::protocol::PacketID::WorldHeader, &wa); - d.Dispatch(header); - d.Dispatch(data); + d.Dispatch(*header); + d.Dispatch(*data); return w; } +void Save(td::protocol::packets::WorldHeaderPacket header, td::protocol::packets::WorldDataPacket data) { + auto comp = std::make_shared(); + + std::ofstream fStream("test/tdmap.tdmap2"); + auto out = std::make_shared(fStream); + + sp::MessageStream stream(std::move(out), std::move(comp)); + + stream.WriteMessage(header); + stream.WriteMessage(data); +} + void FastForward(td::game::World& a_World, const td::sim::GameHistory& a_LockSteps) { @@ -81,6 +94,7 @@ td::sim::GameHistory GetCustomHistory() { return gh; } + int main(int argc, char** argv) { td::game::World w = GetWorld(); @@ -89,9 +103,7 @@ int main(int argc, char** argv) { td::render::Camera cam; - display.OnAspectRatioChange.Connect([&cam](float a_AspectRatio){ - cam.UpdatePerspective(a_AspectRatio); - }); + display.OnAspectRatioChange.Connect([&cam](float a_AspectRatio) { cam.UpdatePerspective(a_AspectRatio); }); td::sim::GameHistory gh = GetCustomHistory(); @@ -105,9 +117,10 @@ int main(int argc, char** argv) { td::sim::RealTimeSimulation simulation(w, 500); - display.OnKeyDown.Connect([&simulation](SDL_Keycode key){ + 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); + auto spawn = + std::make_shared(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0); td::Array steps{}; steps[0].push_back(spawn); td::protocol::packets::LockStepsPacket packet{0, steps}; diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp index 840eb59..894cdaa 100644 --- a/src/td/game/World.cpp +++ b/src/td/game/World.cpp @@ -40,10 +40,10 @@ bool World::LoadMap(const protocol::pdata::WorldHeader& a_WorldHeader) { m_SpawnColorPalette = a_WorldHeader.m_SpawnColorPalette; GetRedTeam().GetCastle() = a_WorldHeader.m_RedCastle; - GetRedTeam().GetCastle().SetTeam(&GetRedTeam()); + // GetRedTeam().GetCastle().SetTeam(&GetRedTeam()); GetBlueTeam().GetCastle() = a_WorldHeader.m_BlueCastle; - GetBlueTeam().GetCastle().SetTeam(&GetBlueTeam()); + // GetBlueTeam().GetCastle().SetTeam(&GetBlueTeam()); m_TilePalette = a_WorldHeader.m_TilePalette; diff --git a/src/td/game/WorldTypes.cpp b/src/td/game/WorldTypes.cpp index 284443e..09afe11 100644 --- a/src/td/game/WorldTypes.cpp +++ b/src/td/game/WorldTypes.cpp @@ -5,40 +5,7 @@ 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/protocol/packet/ChunkSerialize.cpp b/src/td/protocol/packet/ChunkSerialize.cpp new file mode 100644 index 0000000..1f9fe2c --- /dev/null +++ b/src/td/protocol/packet/ChunkSerialize.cpp @@ -0,0 +1,79 @@ +#include + +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); +} + +namespace td { +namespace game { + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const ChunkPtr& a_Chunk) { + a_Buffer << a_Chunk->m_Palette; + + int bitsPerTile = countBits(a_Chunk->m_Palette.size()); + + 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); + + for (unsigned int tileNumber = 0; tileNumber < td::game::Chunk::ChunkSize; tileNumber++) { + std::size_t startLong = static_cast((tileNumber * bitsPerTile) / BITS_IN_LONG); + std::size_t startOffset = static_cast((tileNumber * bitsPerTile) % BITS_IN_LONG); + std::size_t endLong = static_cast(((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG); + + std::uint64_t value = static_cast(a_Chunk->m_Tiles[tileNumber]); + + value &= individualValueMask; + + chunkData[startLong] |= (value << startOffset); + + if (startLong != endLong) { + chunkData[endLong] = (value >> (BITS_IN_LONG - startOffset)); + } + } + + return a_Buffer << chunkData; +} + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, ChunkPtr& a_Chunk) { + a_Chunk = std::make_shared(); + + a_Buffer >> a_Chunk->m_Palette; + + std::uint8_t bitsPerTile = countBits(a_Chunk->m_Palette.size()); + + // A bitmask that contains bitsPerTile set bits + td::game::Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1); + + ChunkPackedData chunkData; + a_Buffer >> chunkData; + + 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; + + a_Chunk->m_Tiles[tileNumber] = value; + } + + return a_Buffer; +} + +} // namespace game +} // namespace td \ No newline at end of file diff --git a/src/td/protocol/packet/PacketSerialize.cpp b/src/td/protocol/packet/PacketSerialize.cpp index 1c5b365..7fe85f6 100644 --- a/src/td/protocol/packet/PacketSerialize.cpp +++ b/src/td/protocol/packet/PacketSerialize.cpp @@ -1,113 +1,88 @@ +#include #include -#include - namespace sp { -template <> -void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header) { - a_Buffer >> a_Header.m_TowerPlacePalette >> a_Header.m_WalkablePalette; +// temp +template<> +void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::PredictCommand& a_Header){} - std::uint16_t decoPaletteSize; - a_Buffer >> decoPaletteSize; +template<> +DataBuffer WriteMessage(const td::protocol::pdata::PredictCommand& a_Header){} - std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(td::Color); +} // namespace sp - a_Header.m_DecorationPalette.resize(decoPaletteSize); +namespace td { +namespace game { - 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; +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const TeamCastle& a_Castle) { + return a_Buffer << a_Castle.GetCenterX() << a_Castle.GetCenterY(); } -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); +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, TeamCastle& a_Castle) { + float x, y; + a_Buffer >> x >> y; + a_Castle.SetCenter({x, y}); + return a_Buffer; } +sp::DataBuffer& operator<<(sp::DataBuffer& buffer, const TilePtr& tile) { + buffer << tile->GetType(); -template <> -void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData) { - std::uint64_t chunkCount; - a_Buffer >> chunkCount; + switch (tile->GetType()) { - 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; + case game::TileType::Tower: { + const game::TowerTile* towerTile = dynamic_cast(tile.get()); + buffer << towerTile->color_palette_ref << towerTile->team_owner; + break; } - a_WorldData.m_Chunks.insert({chunkCoords, chunk}); + + case game::TileType::Walk: { + const game::WalkableTile* walkTile = dynamic_cast(tile.get()); + buffer << walkTile->direction; + break; + } + + case game::TileType::Decoration: { + const game::DecorationTile* decoTile = dynamic_cast(tile.get()); + buffer << decoTile->color_palette_ref; + break; + } + + default: + break; } + + return buffer; } -} // namespace sp \ No newline at end of file +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/render/loader/WorldLoader.cpp b/src/td/render/loader/WorldLoader.cpp index 7f96087..fe1a7a5 100644 --- a/src/td/render/loader/WorldLoader.cpp +++ b/src/td/render/loader/WorldLoader.cpp @@ -17,10 +17,7 @@ GL::VertexArray LoadWorldModel(const td::game::World* world) { std::vector positions; std::vector colors; - for (const auto& chunkInfo : world->GetChunks()) { - const td::game::ChunkCoord& coords = chunkInfo.first; - td::game::ChunkPtr chunk = chunkInfo.second; - + for (const auto& [coords, chunk] : world->GetChunks()) { std::int32_t chunkX = coords.x * td::game::Chunk::ChunkWidth; std::int32_t chunkY = coords.y * td::game::Chunk::ChunkHeight; diff --git a/test/tdmap.tdmap b/test/tdmap.tdmap new file mode 100644 index 0000000..798a860 Binary files /dev/null and b/test/tdmap.tdmap differ diff --git a/test/tdmap.tdmap2 b/test/tdmap.tdmap2 index 798a860..df65e86 100644 Binary files a/test/tdmap.tdmap2 and b/test/tdmap.tdmap2 differ diff --git a/xmake.lua b/xmake.lua index e92b89a..f87cf17 100644 --- a/xmake.lua +++ b/xmake.lua @@ -3,7 +3,7 @@ add_rules("mode.debug", "mode.release") add_repositories("persson-repo https://git.ale-pri.com/Persson-dev/xmake-repo.git") add_requires("imgui 1.92.0", {configs = {sdl3 = true, opengl3 = true}}) -add_requires("splib 2.0.0", "zlib") +add_requires("splib 2.0.1", "zlib") add_requires("libsdl3 3.2.16", "glew", "fpm", "enet6") set_languages("c++17")