From cc57e03bc45ed13eb13505b5b46c1b0716348a35 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 10 Jul 2025 16:34:28 +0200 Subject: [PATCH 01/39] splib transition --- include/td/common/Array.h | 31 +++ include/td/game/Game.h | 14 + include/td/game/GameHistory.h | 3 +- include/td/protocol/command/CommandData.h | 22 +- include/td/protocol/command/CommandDeclare.h | 22 -- .../td/protocol/command/CommandDispatcher.h | 17 -- include/td/protocol/command/CommandFactory.h | 19 -- .../td/protocol/command/CommandSerializer.h | 21 -- include/td/protocol/command/CommandVisitor.h | 39 --- include/td/protocol/command/Commands.h | 116 +++----- include/td/protocol/packet/PacketData.h | 3 +- include/td/protocol/packet/PacketDeclare.h | 48 ---- include/td/protocol/packet/PacketDispatcher.h | 17 -- include/td/protocol/packet/PacketFactory.h | 19 -- include/td/protocol/packet/PacketSerializer.h | 21 -- include/td/protocol/packet/PacketVisitor.h | 39 --- include/td/protocol/packet/Packets.h | 124 +++------ src/main.cpp | 11 +- src/td/game/Game.cpp | 1 + src/td/game/GameHistory.cpp | 6 +- src/td/protocol/command/CommandFactory.cpp | 24 -- src/td/protocol/command/CommandSerializer.cpp | 229 --------------- src/td/protocol/command/CommandVisitor.cpp | 11 - src/td/protocol/command/Commands.cpp | 18 -- src/td/protocol/packet/PacketFactory.cpp | 26 -- src/td/protocol/packet/PacketSerializer.cpp | 262 ------------------ src/td/protocol/packet/PacketVisitor.cpp | 11 - src/td/protocol/packet/Packets.cpp | 23 -- xmake.lua | 76 +---- 29 files changed, 146 insertions(+), 1127 deletions(-) create mode 100644 include/td/common/Array.h create mode 100644 include/td/game/Game.h delete mode 100644 include/td/protocol/command/CommandDeclare.h delete mode 100644 include/td/protocol/command/CommandDispatcher.h delete mode 100644 include/td/protocol/command/CommandFactory.h delete mode 100644 include/td/protocol/command/CommandSerializer.h delete mode 100644 include/td/protocol/command/CommandVisitor.h delete mode 100644 include/td/protocol/packet/PacketDeclare.h delete mode 100644 include/td/protocol/packet/PacketDispatcher.h delete mode 100644 include/td/protocol/packet/PacketFactory.h delete mode 100644 include/td/protocol/packet/PacketSerializer.h delete mode 100644 include/td/protocol/packet/PacketVisitor.h create mode 100644 src/td/game/Game.cpp delete mode 100644 src/td/protocol/command/CommandFactory.cpp delete mode 100644 src/td/protocol/command/CommandSerializer.cpp delete mode 100644 src/td/protocol/command/CommandVisitor.cpp delete mode 100644 src/td/protocol/command/Commands.cpp delete mode 100644 src/td/protocol/packet/PacketFactory.cpp delete mode 100644 src/td/protocol/packet/PacketSerializer.cpp delete mode 100644 src/td/protocol/packet/PacketVisitor.cpp delete mode 100644 src/td/protocol/packet/Packets.cpp diff --git a/include/td/common/Array.h b/include/td/common/Array.h new file mode 100644 index 0000000..10acb9b --- /dev/null +++ b/include/td/common/Array.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace td { +template +class Array { + private: + T* m_Data; + + public: + Array() : m_Data(new T[S]) {} + + Array(const std::initializer_list& args) { + std::size_t i = 0; + for(const T& element : args) { + m_Data[i] = element; + i++; + } + } + + T& operator[](std::size_t a_Index) { + return m_Data[a_Index]; + } + + ~Array() { + delete [] m_Data; + } +}; + +} // namespace td diff --git a/include/td/game/Game.h b/include/td/game/Game.h new file mode 100644 index 0000000..0928249 --- /dev/null +++ b/include/td/game/Game.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace td { +namespace game { + +class Game { + private: + GameHistory m_History; +}; + +} // namespace game +} // namespace td diff --git a/include/td/game/GameHistory.h b/include/td/game/GameHistory.h index c5c4f61..73cda9c 100644 --- a/include/td/game/GameHistory.h +++ b/include/td/game/GameHistory.h @@ -1,5 +1,6 @@ #pragma once +#define BOOST_PFR_USE_CPP17 0 #include #include #include @@ -30,7 +31,7 @@ class GameHistory { void FromPacket(td::protocol::pdata::LockSteps&& a_Steps); - td::protocol::packets::LockSteps ToPacket(HistorySizeType a_StartIndex); + td::protocol::packets::LockStepsPacket ToPacket(HistorySizeType a_StartIndex); }; } // namespace game diff --git a/include/td/protocol/command/CommandData.h b/include/td/protocol/command/CommandData.h index f4e0aac..ed3e850 100644 --- a/include/td/protocol/command/CommandData.h +++ b/include/td/protocol/command/CommandData.h @@ -3,40 +3,40 @@ #include #include #include +#include namespace td { namespace protocol { namespace cdata { - struct PlaceTower { - TowerType m_Type : 4; - PlayerID m_Placer : 4; + sp::BitField m_Type; + sp::BitField m_Placer; TowerCoords m_Position; }; struct UpgradeTower { - TowerID m_Tower : 12; - std::uint8_t m_Upgrade : 4; + sp::BitField m_Tower; + sp::BitField m_Upgrade; }; struct SpawnTroop { - EntityType m_Type : 5; - std::uint8_t m_Level : 3; + sp::BitField m_Type; + sp::BitField m_Level; EntityCoords m_Position; PlayerID m_Sender; }; struct UseItem { - ShopItem m_Item : 4; - PlayerID m_User : 4; + sp::BitField m_Item; + sp::BitField m_User; EntityCoords m_Position; }; struct TeamChange { - PlayerID m_Player : 7; - Team m_NewTeam : 1; + sp::BitField m_Player; + sp::BitField m_NewTeam; }; struct PlayerJoin { diff --git a/include/td/protocol/command/CommandDeclare.h b/include/td/protocol/command/CommandDeclare.h deleted file mode 100644 index 76013d7..0000000 --- a/include/td/protocol/command/CommandDeclare.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - - -namespace td { -namespace protocol { - -/** - * \def DeclareAllPacket - * \brief Avoids repetitive operations on commands - */ -#define DeclareAllCommand() \ - DeclareCommand(End) \ - DeclareCommand(PlaceTower) \ - DeclareCommand(PlayerJoin) \ - DeclareCommand(SpawnTroop) \ - DeclareCommand(TeamChange) \ - DeclareCommand(UpgradeTower) \ - DeclareCommand(UseItem) - - -} // namespace protocol -} // namespace td \ No newline at end of file diff --git a/include/td/protocol/command/CommandDispatcher.h b/include/td/protocol/command/CommandDispatcher.h deleted file mode 100644 index 0dc3ef9..0000000 --- a/include/td/protocol/command/CommandDispatcher.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -/** - * \file CommandDispatcher.h - * \brief File containing the td::protocol::CommandDispatcher class - */ - -#include -#include - -namespace td { -namespace protocol { - -using CommandDispatcher = Dispatcher; - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/CommandFactory.h b/include/td/protocol/command/CommandFactory.h deleted file mode 100644 index 3bae063..0000000 --- a/include/td/protocol/command/CommandFactory.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace protocol { -namespace CommandFactory { - -template ::value>::type> -std::shared_ptr CreateCommand() { - return std::make_shared(); -} - -const std::shared_ptr& CreateReadOnlyCommand(CommandType a_Type); - -} // namespace CommandFactory -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/CommandSerializer.h b/include/td/protocol/command/CommandSerializer.h deleted file mode 100644 index 181fcb3..0000000 --- a/include/td/protocol/command/CommandSerializer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace protocol { - -class Command; - -using CommandPtr = std::shared_ptr; - -namespace CommandSerializer { - -DataBuffer Serialize(const Command& a_Command); - -std::shared_ptr Deserialize(DataBuffer& a_Data); - -} // namespace CommandSerializer -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/CommandVisitor.h b/include/td/protocol/command/CommandVisitor.h deleted file mode 100644 index 987ffd3..0000000 --- a/include/td/protocol/command/CommandVisitor.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/** - * \file CommandVisitor.h - * \brief File containing the td::protocol::CommandVisitor class - */ - -#include - -namespace td { -namespace protocol { - -#define DeclareCommand(CommandName, ...) \ - /** This function is called when the packet processed by CommandVisitor::Check is a CommandName */ \ - virtual void Visit(const commands::CommandName&) {} - -/** - * \class CommandVisitor - * \brief This class uses double-dispatch in order to find the real type of a packet - */ -class CommandVisitor : private NonCopyable { - protected: - CommandVisitor() {} - virtual ~CommandVisitor() {} - - public: - /** - * \brief Calls the right CommandVisitor::Visit method corresponding to the real type of the packet - * \param packet the Command to visit - */ - void Check(const Command& packet); - - DeclareAllCommand() -}; - -#undef DeclareCommand - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/Commands.h b/include/td/protocol/command/Commands.h index 4444d21..87f5c4e 100644 --- a/include/td/protocol/command/Commands.h +++ b/include/td/protocol/command/Commands.h @@ -5,107 +5,59 @@ * \brief File containing the definitions of the lockstep commands */ +#include +#include +#include +#include +#include #include #include #include -#include -#include namespace td { namespace protocol { -class CommandVisitor; - /** A Command id is 8 bits wide */ -using CommandID = std::uint8_t; - -#define DeclareCommand(CommandName, ...) /** CommandName */ CommandName, - -/** - * \enum CommandType - * \brief Map a Command to an id - */ -enum class CommandType : CommandID { - - DeclareAllCommand() - - /** The number of Commands */ - COMMAND_COUNT -}; - - -#undef DeclareCommand - - -class Command : private NonCopyable { - public: - /** - * \return The real type of the Command - */ - virtual CommandType GetType() const = 0; - - private: - /** Use a CommandVisitor to make double-dispatch possible */ - virtual void Accept(CommandVisitor& a_Visitor) const = 0; - - friend class CommandVisitor; +enum class CommandID : std::uint8_t { + End = 0, + PlaceTower, + PlayerJoin, + SpawnTroop, + TeamChange, + UpgradeTower, + UseItem, }; +class CommandHandler; +using CommandBase = sp::MessageBase; +template +using CommandMessage = sp::ConcreteMessage; namespace commands { -/** - * \class ConcreteCommand - * \brief A Command associated with an id and holding data - * \tparam PT The Command type - * \tparam Data The structure holding the data of the Command (in td::protocol::data namespace) - */ -template -class ConcreteCommand : public Command { - public: - /** The type of the struct holding the data */ - using CommandDataType = Data; - - /** The structure holding the actual data */ - CommandDataType m_Data; - - /** Construct the Command with data of type CommandDataType */ - ConcreteCommand(const CommandDataType& a_Data = {}); - - constexpr CommandType GetType() const override { - return CT; - }; - - private: - void Accept(CommandVisitor& a_Visitor) const override; -}; - - - - - -// define TD_INSTANCIATE_COMMANDS -// before including this file -// if you want to instantiate templates -#ifdef TD_INSTANCIATE_COMMANDS -#define DeclareCommand(CommandName, ...) \ - using CommandName = ConcreteCommand; \ - template class ConcreteCommand; -#else -#define DeclareCommand(CommandName, ...) /** Defines the CommandName Command */ \ - using CommandName = ConcreteCommand; -#endif - -DeclareAllCommand() - -#undef DeclareCommand +using EndCommand = CommandMessage; +using PlaceTowerCommand = CommandMessage; +using PlayerJoinCommand = CommandMessage; +using SpawnTroopCommand = CommandMessage; +using TeamChangeCommand = CommandMessage; +using UpgradeTowerCommand = CommandMessage; +using UseItemCommand = CommandMessage; } // namespace commands -using LockStep = std::vector>; +using AllCommands = std::tuple; + +class CommandHandler : public sp::MessageHandler {}; + +using CommandDispatcher = sp::MessageDispatcher; + +using CommandFactory = sp::MessageFactory; + +using LockStep = std::vector>; } // namespace protocol } // namespace td diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index fdcfb62..3bd37aa 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -3,6 +3,7 @@ #include #include #include +#include // Make it dynamic ? #define LOCKSTEP_BUFFER_SIZE 10 @@ -68,7 +69,7 @@ struct BeginGame { struct LockSteps { std::uint16_t m_FirstFrameNumber; - std::array m_LockSteps; + Array m_LockSteps; }; } // namespace pdata diff --git a/include/td/protocol/packet/PacketDeclare.h b/include/td/protocol/packet/PacketDeclare.h deleted file mode 100644 index 1354d6d..0000000 --- a/include/td/protocol/packet/PacketDeclare.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -/** - * \file PacketDeclare.h - * \brief Holds the definitions of the packets (but not their content) - */ - -namespace td { -namespace protocol { - -/** - * \enum PacketSender - * \brief Indicate who should send a packet - */ -enum class PacketSenderType { - /** Sent by clients and server */ - Both = 1, - /** Sent by clients to the server */ - Client, - /** Sent by server to the clients */ - Server, -}; - -enum class PacketSendType { - Reliable = 1, - Unreliable, - UnreliableOrdered, -}; - - -/** - * \def DeclareAllPacket - * \brief Avoids repetitive operations on packets - */ -#define DeclareAllPacket() \ - DeclarePacket(ChatMessage, Reliable, Both) \ - DeclarePacket(BeginGame, Reliable, Server) \ - DeclarePacket(Disconnect, Reliable, Both) \ - DeclarePacket(KeepAlive, Reliable, Both) \ - DeclarePacket(LockSteps, Unreliable, Both) \ - DeclarePacket(LoggingSuccess, Reliable, Server) \ - DeclarePacket(PlayerJoin, Reliable, Server) \ - DeclarePacket(PlayerLeave, Reliable, Server) \ - DeclarePacket(PlayerLogin, Reliable, Client) \ - - -} // namespace protocol -} // namespace td \ No newline at end of file diff --git a/include/td/protocol/packet/PacketDispatcher.h b/include/td/protocol/packet/PacketDispatcher.h deleted file mode 100644 index d0a5d21..0000000 --- a/include/td/protocol/packet/PacketDispatcher.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -/** - * \file PacketDispatcher.h - * \brief File containing the td::protocol::PacketDispatcher class - */ - -#include -#include - -namespace td { -namespace protocol { - -using PacketDispatcher = Dispatcher; - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/packet/PacketFactory.h b/include/td/protocol/packet/PacketFactory.h deleted file mode 100644 index 0f346ad..0000000 --- a/include/td/protocol/packet/PacketFactory.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace protocol { -namespace PacketFactory { - -template ::value>::type> -std::unique_ptr CreatePacket() { - return std::make_unique(); -} - -const std::unique_ptr& CreateReadOnlyPacket(PacketType a_Type); - -} // namespace PacketFactory -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/packet/PacketSerializer.h b/include/td/protocol/packet/PacketSerializer.h deleted file mode 100644 index fd71ca2..0000000 --- a/include/td/protocol/packet/PacketSerializer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace protocol { - -class Packet; - -using PacketPtr = std::unique_ptr; - -namespace PacketSerializer { - -DataBuffer Serialize(const Packet& a_Packet); - -std::unique_ptr Deserialize(DataBuffer& a_Data); - -} // namespace PacketSerializer -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/packet/PacketVisitor.h b/include/td/protocol/packet/PacketVisitor.h deleted file mode 100644 index d2a6799..0000000 --- a/include/td/protocol/packet/PacketVisitor.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/** - * \file PacketVisitor.h - * \brief File containing the td::protocol::PacketVisitor class - */ - -#include - -namespace td { -namespace protocol { - -#define DeclarePacket(PacketName, ...) \ - /** This function is called when the packet processed by PacketVisitor::Check is a PacketName */ \ - virtual void Visit(const packets::PacketName&) {} - -/** - * \class PacketVisitor - * \brief This class uses double-dispatch in order to find the real type of a packet - */ -class PacketVisitor : private NonCopyable { - protected: - PacketVisitor() {} - virtual ~PacketVisitor() {} - - public: - /** - * \brief Calls the right PacketVisitor::Visit method corresponding to the real type of the packet - * \param packet the Packet to visit - */ - void Check(const Packet& packet); - - DeclareAllPacket() -}; - -#undef DeclarePacket - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/packet/Packets.h b/include/td/protocol/packet/Packets.h index 24599fe..92822e1 100644 --- a/include/td/protocol/packet/Packets.h +++ b/include/td/protocol/packet/Packets.h @@ -7,107 +7,61 @@ #include #include -#include + +#include +#include +#include +#include +#include namespace td { namespace protocol { -class PacketVisitor; - -/** A Packet id is 8 bits wide */ -using PacketID = std::uint8_t; -using PeerID = std::uint16_t; - -#define DeclarePacket(PacketName, ...) /** PacketName */ PacketName, - -/** - * \enum PacketType - * \brief Map a Packet to an id - */ -enum class PacketType : PacketID { - - DeclareAllPacket() - - /** The number of packets */ - PACKET_COUNT +enum class PacketID : std::uint8_t { + ChatMessage = 0, + BeginGame, + Disconnect, + KeepAlive, + LockSteps, + LoggingSuccess, + PlayerJoin, + PlayerLeave, + PlayerLogin }; +class PacketHandler; -#undef DeclarePacket - - -class Packet : private NonCopyable { - public: - /** - * \return The real type of the packet - */ - virtual PacketType GetType() const = 0; - - /** - * \brief The network peer who sent the packet - */ - PeerID m_Sender; - - private: - /** Use a PacketVisitor to make double-dispatch possible */ - virtual void Accept(PacketVisitor& a_Visitor) const = 0; - - friend class PacketVisitor; -}; - +using PacketBase = sp::MessageBase; +template +using PacketMessage = sp::ConcreteMessage; namespace packets { -/** - * \class ConcretePacket - * \brief A Packet associated with an id and holding data - * \tparam PT The packet type - * \tparam Data The structure holding the data of the packet (in td::protocol::data namespace) - */ -template -class ConcretePacket : public Packet { - public: - /** The type of the struct holding the data */ - using PacketDataType = Data; +using BeginGamePacket = PacketMessage; +using ChatMessagePacket = PacketMessage; +using DisconnectPacket = PacketMessage; +using KeepAlivePacket = PacketMessage; +using LockStepsPacket = PacketMessage; +using LoggingSuccessPacket = PacketMessage; +using PlayerJoinPacket = PacketMessage; +using PlayerLeavePacket = PacketMessage; +using PlayerLoginPacket = PacketMessage; - /** The structure holding the actual data */ - PacketDataType m_Data; - - /** Construct the packet with data of type PacketDataType */ - ConcretePacket(const PacketDataType& a_Data = {}); - - constexpr PacketType GetType() const override { - return PT; - }; - - private: - void Accept(PacketVisitor& a_Visitor) const override; -}; - - - - - -// define TD_INSTANCIATE_PACKETS -// before including this file -// if you want to instantiate templates -#ifdef TD_INSTANCIATE_PACKETS -#define DeclarePacket(PacketName, ...) \ - using PacketName = ConcretePacket; \ - template class ConcretePacket; -#else -#define DeclarePacket(PacketName, ...) /** Defines the PacketName packet */ \ - using PacketName = ConcretePacket; -#endif - -DeclareAllPacket() - -#undef DeclarePacket } // namespace packets +using AllPackets = std::tuple; + +class PacketHandler : public sp::MessageHandler {}; + +using PacketDispatcher = sp::MessageDispatcher; + +using PacketFactory = sp::MessageFactory; + } // namespace protocol } // namespace td diff --git a/src/main.cpp b/src/main.cpp index 9a466a1..80fa251 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,12 +1,9 @@ #include // #include #include -#include -#include -#include -#include +#include -class Test : public td::protocol::CommandVisitor {}; +class Test : public td::protocol::CommandHandler {}; int main(int argc, char** argv) { // Test visitor; @@ -14,8 +11,8 @@ int main(int argc, char** argv) { // visitor.Check(chat); - td::protocol::commands::UpgradeTower com{{1, 2}}; - std::cout << (unsigned)com.GetType() << std::endl; + td::protocol::commands::UpgradeTowerCommand com{1, 2}; + std::cout << (unsigned)com.GetId() << std::endl; td::protocol::CommandDispatcher disptacher; return 0; } diff --git a/src/td/game/Game.cpp b/src/td/game/Game.cpp new file mode 100644 index 0000000..c3677ec --- /dev/null +++ b/src/td/game/Game.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/td/game/GameHistory.cpp b/src/td/game/GameHistory.cpp index 2ed127e..f295c77 100644 --- a/src/td/game/GameHistory.cpp +++ b/src/td/game/GameHistory.cpp @@ -32,12 +32,12 @@ void GameHistory::FromPacket(td::protocol::pdata::LockSteps&& a_Steps) { } } -td::protocol::packets::LockSteps GameHistory::ToPacket(HistorySizeType a_StartIndex) { - std::array steps; +td::protocol::packets::LockStepsPacket GameHistory::ToPacket(HistorySizeType a_StartIndex) { + Array steps; for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { steps[i] = GetLockStep(a_StartIndex + i); } - return {{a_StartIndex, std::move(steps)}}; + return {a_StartIndex, std::move(steps)}; } } // namespace game diff --git a/src/td/protocol/command/CommandFactory.cpp b/src/td/protocol/command/CommandFactory.cpp deleted file mode 100644 index 0dbdc9f..0000000 --- a/src/td/protocol/command/CommandFactory.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include - -#include -#include -#include - -namespace td { -namespace protocol { -namespace CommandFactory { - -using CommandCreator = std::function()>; - -#define DeclareCommand(CommandName, ...) std::make_shared(), - -static std::array, static_cast(CommandType::COMMAND_COUNT)> Commands = {DeclareAllCommand()}; - -const std::shared_ptr& CreateReadOnlyCommand(CommandType a_Type) { - assert(a_Type < CommandType::COMMAND_COUNT); - return Commands[static_cast(a_Type)]; -} - -} // namespace CommandFactory -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/command/CommandSerializer.cpp b/src/td/protocol/command/CommandSerializer.cpp deleted file mode 100644 index 80b1d3d..0000000 --- a/src/td/protocol/command/CommandSerializer.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include - -#include -#include - -#include -#include - -namespace td { -namespace protocol { -namespace CommandSerializer { - -#define DeclareCommand(CommandName, ...) \ - void Visit(const commands::CommandName& a_Command) override { \ - const auto& commandData = a_Command.m_Data; \ - SerializeCommandData(commandData); \ - } \ - \ - void SerializeCommandData(const commands::CommandName::CommandDataType& a_Command); - - - - -class Serializer : public CommandVisitor { - private: - DataBuffer& m_Buffer; - - public: - Serializer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer) {} - - void Serialize(const Command& a_Command) { - m_Buffer << static_cast(a_Command.GetType()); - Check(a_Command); - } - - DeclareAllCommand() -}; - -#undef DeclareCommand - - - - - -#define DeclareCommand(CommandName, ...) \ - void Visit(const commands::CommandName& a_Command) override { \ - auto commandPtr = CommandFactory::CreateCommand(); \ - auto& commandData = commandPtr->m_Data; \ - \ - DeserializeCommandData(commandData); \ - \ - m_Command = std::move(commandPtr); \ - } \ - \ - void DeserializeCommandData(commands::CommandName::CommandDataType& a_Command); - - - -class Deserializer : public CommandVisitor { - private: - DataBuffer& m_Buffer; - CommandPtr m_Command; - - public: - Deserializer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer) {} - - bool Deserialize(const CommandPtr& a_Command) { - try { - Check(*a_Command.get()); - } catch (std::exception& e) { - utils::LOGE(utils::Format("[CommandSerializer::Deserializer] %s", e.what())); - return false; - } - return true; - } - - CommandPtr& GetCommand() { - return m_Command; - } - - DeclareAllCommand() -}; - - - - - -DataBuffer Serialize(const Command& a_Command) { - DataBuffer buffer; - - Serializer serializer(buffer); - serializer.Serialize(a_Command); - - return buffer; -} - -std::shared_ptr Deserialize(DataBuffer& a_Data) { - CommandID commandId; - a_Data >> commandId; - - if (commandId >= static_cast(CommandType::COMMAND_COUNT)) - return nullptr; - - CommandType commandType = CommandType(commandId); - - // for double-dispatch - const CommandPtr& emptyCommand = CommandFactory::CreateReadOnlyCommand(commandType); - - Deserializer deserializer(a_Data); - - if (deserializer.Deserialize(emptyCommand)) { - CommandPtr command = std::move(deserializer.GetCommand()); - return command; - } - - return nullptr; -} - - - - - -//--------------------------------------------- -// Command serializer implementation -//---------------------------------------------- - - - - - -void Serializer::SerializeCommandData(const cdata::End& a_Command) {} - -void Deserializer::DeserializeCommandData(cdata::End& a_Command) {} - - - - - -void Serializer::SerializeCommandData(const cdata::PlaceTower& a_Command) { - m_Buffer << static_cast((static_cast(a_Command.m_Type) << 4 | a_Command.m_Placer & 0xF)) - << a_Command.m_Position; -} - -void Deserializer::DeserializeCommandData(cdata::PlaceTower& a_Command) { - std::uint8_t union1; - m_Buffer >> union1 >> a_Command.m_Position; - a_Command.m_Type = td::TowerType(union1 >> 4); - a_Command.m_Placer = union1 & 0xF; -} - - - - - -void Serializer::SerializeCommandData(const cdata::PlayerJoin& a_Command) { - m_Buffer << a_Command.m_ID << a_Command.m_Name; -} - -void Deserializer::DeserializeCommandData(cdata::PlayerJoin& a_Command) { - m_Buffer >> a_Command.m_ID >> a_Command.m_Name; -} - - - - -void Serializer::SerializeCommandData(const cdata::SpawnTroop& a_Command) { - m_Buffer << static_cast(static_cast(a_Command.m_Type) << 3 | a_Command.m_Level & 0x7) - << a_Command.m_Position << a_Command.m_Sender; -} - -void Deserializer::DeserializeCommandData(cdata::SpawnTroop& a_Command) { - std::uint8_t union1; - m_Buffer >> union1 >> a_Command.m_Position >> a_Command.m_Sender; - a_Command.m_Type = td::EntityType(union1 >> 3); - a_Command.m_Level = union1 & 0x7; -} - - - - - -void Serializer::SerializeCommandData(const cdata::TeamChange& a_Command) { - m_Buffer << static_cast(a_Command.m_Player << 1 | static_cast(a_Command.m_NewTeam)); -} - -void Deserializer::DeserializeCommandData(cdata::TeamChange& a_Command) { - std::uint8_t union1; - m_Buffer >> union1; - a_Command.m_Player = union1 >> 1; - a_Command.m_NewTeam = td::Team(union1 & 1); -} - - - - - -void Serializer::SerializeCommandData(const cdata::UpgradeTower& a_Command) { - m_Buffer << static_cast(a_Command.m_Tower << 4 | a_Command.m_Upgrade & 0xF); -} - -void Deserializer::DeserializeCommandData(cdata::UpgradeTower& a_Command) { - std::uint16_t union1; - m_Buffer >> union1; - a_Command.m_Tower = union1 >> 4; - a_Command.m_Upgrade = union1 & 0xF; -} - - - - - -void Serializer::SerializeCommandData(const cdata::UseItem& a_Command) { - m_Buffer << static_cast(static_cast(a_Command.m_Item) << 4 | a_Command.m_User & 0xF) - << a_Command.m_Position; -} - -void Deserializer::DeserializeCommandData(cdata::UseItem& a_Command) { - std::uint8_t union1; - m_Buffer >> union1 >> a_Command.m_Position; - a_Command.m_Item = td::ShopItem(union1 >> 4); - a_Command.m_User = union1 & 0xF; -} - - - - -} // namespace CommandSerializer -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/command/CommandVisitor.cpp b/src/td/protocol/command/CommandVisitor.cpp deleted file mode 100644 index a018309..0000000 --- a/src/td/protocol/command/CommandVisitor.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace td { -namespace protocol { - -void CommandVisitor::Check(const Command& a_Command) { - a_Command.Accept(*this); -} - -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/command/Commands.cpp b/src/td/protocol/command/Commands.cpp deleted file mode 100644 index 09f575e..0000000 --- a/src/td/protocol/command/Commands.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#define TD_INSTANCIATE_COMMANDS -#include - -#include - -namespace td { -namespace protocol { - -template -commands::ConcreteCommand::ConcreteCommand(const CommandDataType& a_Data) : m_Data(a_Data) {} - -template -void commands::ConcreteCommand::Accept(CommandVisitor& a_Visitor) const { - a_Visitor.Visit(*this); -} - -} // namespace protocol -} // namespace td \ No newline at end of file diff --git a/src/td/protocol/packet/PacketFactory.cpp b/src/td/protocol/packet/PacketFactory.cpp deleted file mode 100644 index d1aa219..0000000 --- a/src/td/protocol/packet/PacketFactory.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#include -#include -#include - -namespace td { -namespace protocol { -namespace PacketFactory { - -using PacketCreator = std::function()>; - -#define DeclarePacket(PacketName, ...) std::make_unique(), - -static std::array, static_cast(PacketType::PACKET_COUNT)> Packets = { - DeclareAllPacket() -}; - -const std::unique_ptr& CreateReadOnlyPacket(PacketType a_Type) { - assert(a_Type < PacketType::PACKET_COUNT); - return Packets[static_cast(a_Type)]; -} - -} // namespace PacketFactory -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/packet/PacketSerializer.cpp b/src/td/protocol/packet/PacketSerializer.cpp deleted file mode 100644 index 0fc785a..0000000 --- a/src/td/protocol/packet/PacketSerializer.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include - -#include -#include -#include - -#include -#include - -namespace td { -namespace protocol { -namespace PacketSerializer { - -#define DeclarePacket(PacketName, ...) \ - void Visit(const packets::PacketName& a_Packet) override { \ - const auto& packetData = a_Packet.m_Data; \ - SerializePacketData(packetData); \ - } \ - \ - void SerializePacketData(const packets::PacketName::PacketDataType& a_Packet); - - - - -class Serializer : public PacketVisitor { - private: - DataBuffer& m_Buffer; - - public: - Serializer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer) {} - - void Serialize(const Packet& a_Packet) { - m_Buffer << static_cast(a_Packet.GetType()); - Check(a_Packet); - } - - DeclareAllPacket() -}; - -#undef DeclarePacket - - - - - -#define DeclarePacket(PacketName, ...) \ - void Visit(const packets::PacketName& a_Packet) override { \ - auto packetPtr = PacketFactory::CreatePacket(); \ - auto& packetData = packetPtr->m_Data; \ - \ - DeserializePacketData(packetData); \ - \ - m_Packet = std::move(packetPtr); \ - } \ - \ - void DeserializePacketData(packets::PacketName::PacketDataType& a_Packet); - - - -class Deserializer : public PacketVisitor { - private: - DataBuffer& m_Buffer; - PacketPtr m_Packet; - - public: - Deserializer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer) {} - - bool Deserialize(const PacketPtr& a_Packet) { - try { - Check(*a_Packet.get()); - } catch (std::exception& e) { - utils::LOGE(utils::Format("[PacketSerializer::Deserializer] %s", e.what())); - return false; - } - return true; - } - - PacketPtr& GetPacket() { - return m_Packet; - } - - DeclareAllPacket() -}; - - - - - -DataBuffer Serialize(const Packet& a_Packet) { - DataBuffer buffer; - - Serializer serializer(buffer); - serializer.Serialize(a_Packet); - - return buffer; -} - -std::unique_ptr Deserialize(DataBuffer& a_Data) { - PacketID packetId; - a_Data >> packetId; - - if (packetId >= static_cast(PacketType::PACKET_COUNT)) - return nullptr; - - PacketType packetType = PacketType(packetId); - - // for double-dispatch - const PacketPtr& emptyPacket = PacketFactory::CreateReadOnlyPacket(packetType); - - Deserializer deserializer(a_Data); - - if (deserializer.Deserialize(emptyPacket)) { - PacketPtr packet = std::move(deserializer.GetPacket()); - return packet; - } - - return nullptr; -} - - - - - -//--------------------------------------------- -// Packet serializer implementation -//---------------------------------------------- - - - - - -void Serializer::SerializePacketData(const pdata::PlayerLogin& a_Packet) { - m_Buffer << a_Packet.m_PlayerName; -} - -void Deserializer::DeserializePacketData(pdata::PlayerLogin& a_Packet) { - m_Buffer >> a_Packet.m_PlayerName; -} - - - - - -void Serializer::SerializePacketData(const pdata::LoggingSuccess& a_Packet) { - m_Buffer << a_Packet.m_PlayerId; -} - -void Deserializer::DeserializePacketData(pdata::LoggingSuccess& a_Packet) { - m_Buffer >> a_Packet.m_PlayerId; -} - - - - - -void Serializer::SerializePacketData(const pdata::PlayerJoin& a_Packet) { - m_Buffer << a_Packet.m_Player; -} - -void Deserializer::DeserializePacketData(pdata::PlayerJoin& a_Packet) { - m_Buffer >> a_Packet.m_Player; -} - - - - -void Serializer::SerializePacketData(const pdata::PlayerLeave& a_Packet) { - m_Buffer << a_Packet.m_PlayerId; -} - -void Deserializer::DeserializePacketData(pdata::PlayerLeave& a_Packet) { - m_Buffer >> a_Packet.m_PlayerId; -} - - - - - -void Serializer::SerializePacketData(const pdata::KeepAlive& a_Packet) { - m_Buffer << a_Packet.m_KeepAliveId; -} - -void Deserializer::DeserializePacketData(pdata::KeepAlive& a_Packet) { - m_Buffer >> a_Packet.m_KeepAliveId; -} - - - - - -void Serializer::SerializePacketData(const pdata::Disconnect& a_Packet) { - m_Buffer << a_Packet.m_Reason; -} - -void Deserializer::DeserializePacketData(pdata::Disconnect& a_Packet) { - m_Buffer >> a_Packet.m_Reason; -} - - - - - -void Serializer::SerializePacketData(const pdata::ChatMessage& a_Packet) { - m_Buffer << a_Packet.m_Text; -} - -void Deserializer::DeserializePacketData(pdata::ChatMessage& a_Packet) { - m_Buffer >> a_Packet.m_Text; -} - - - - - -void Serializer::SerializePacketData(const pdata::LockSteps& a_Packet) { - m_Buffer << a_Packet.m_FirstFrameNumber; - for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { - auto& lockStep = a_Packet.m_LockSteps[i]; - m_Buffer << VarInt{lockStep.size()}; - - for (int j = 0; j < lockStep.size(); i++) { - auto command = lockStep[j]; - m_Buffer << CommandSerializer::Serialize(*command); - } - } -} - -void Deserializer::DeserializePacketData(pdata::LockSteps& a_Packet) { - m_Buffer >> a_Packet.m_FirstFrameNumber; - for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { - VarInt lockStepSize; - m_Buffer >> lockStepSize; - - for (std::size_t j = 0; j < lockStepSize.GetValue(); i++) { - auto command = CommandSerializer::Deserialize(m_Buffer); - if (command) { - a_Packet.m_LockSteps[i].push_back(command); - } else { - throw std::runtime_error("Failed to deserialise command"); - } - } - } -} - - - - - -void Serializer::SerializePacketData(const pdata::BeginGame& a_Packet) { - m_Buffer << a_Packet.m_Map << a_Packet.m_BlueTeam << a_Packet.m_RedTeam << a_Packet.m_Map; -} - -void Deserializer::DeserializePacketData(pdata::BeginGame& a_Packet) { - m_Buffer >> a_Packet.m_Map >> a_Packet.m_BlueTeam >> a_Packet.m_RedTeam >> a_Packet.m_Map; -} - - - - -} // namespace PacketSerializer -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/packet/PacketVisitor.cpp b/src/td/protocol/packet/PacketVisitor.cpp deleted file mode 100644 index 940046c..0000000 --- a/src/td/protocol/packet/PacketVisitor.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace td { -namespace protocol { - -void PacketVisitor::Check(const Packet& a_Packet) { - a_Packet.Accept(*this); -} - -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/packet/Packets.cpp b/src/td/protocol/packet/Packets.cpp deleted file mode 100644 index 1fbc535..0000000 --- a/src/td/protocol/packet/Packets.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#define TD_INSTANCIATE_PACKETS -#include - -#include - -namespace td { -namespace protocol { - -template -packets::ConcretePacket::ConcretePacket(const PacketDataType& a_Data) : m_Data(a_Data) {} - -template -void packets::ConcretePacket::Accept(PacketVisitor& a_Visitor) const { - a_Visitor.Visit(*this); -} - -#define DeclarePacket(PacketName, packetSendType, packetSenderType) \ - static_assert(static_cast(PacketSendType::packetSendType) && static_cast(PacketSenderType::packetSenderType)); - -DeclareAllPacket() - -} // namespace protocol -} // namespace td diff --git a/xmake.lua b/xmake.lua index 7ad71f9..4adb1c4 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,13 +1,16 @@ add_rules("mode.debug", "mode.release") -add_requires("fpm") +add_repositories("nazara-repo https://github.com/NazaraEngine/xmake-repo.git") +add_repositories("persson-repo https://git.ale-pri.com/Persson-dev/xmake-repo.git") + +add_requires("fpm", "enet6", "nazarautils", "splib 2.0.0") set_languages("c++17") target("Tower-Defense2") add_includedirs("include", {public = true}) set_kind("static") add_files("src/**.cpp") - add_packages("fpm", {public = true}) + add_packages("splib", "fpm", "enet6", "nazarautils", {public = true}) @@ -25,72 +28,3 @@ for _, file in ipairs(os.files("test/**.cpp")) do add_tests("compile_and_run") end --- --- If you want to known more usage about xmake, please see https://xmake.io --- --- ## FAQ --- --- You can enter the project directory firstly before building project. --- --- $ cd projectdir --- --- 1. How to build project? --- --- $ xmake --- --- 2. How to configure project? --- --- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release] --- --- 3. Where is the build output directory? --- --- The default output directory is `./build` and you can configure the output directory. --- --- $ xmake f -o outputdir --- $ xmake --- --- 4. How to run and debug target after building project? --- --- $ xmake run [targetname] --- $ xmake run -d [targetname] --- --- 5. How to install target to the system directory or other output directory? --- --- $ xmake install --- $ xmake install -o installdir --- --- 6. Add some frequently-used compilation flags in xmake.lua --- --- @code --- -- add debug and release modes --- add_rules("mode.debug", "mode.release") --- --- -- add macro definition --- add_defines("NDEBUG", "_GNU_SOURCE=1") --- --- -- set warning all as error --- set_warnings("all", "error") --- --- -- set language: c99, c++11 --- set_languages("c99", "c++11") --- --- -- set optimization: none, faster, fastest, smallest --- set_optimize("fastest") --- --- -- add include search directories --- add_includedirs("/usr/include", "/usr/local/include") --- --- -- add link libraries and search directories --- add_links("tbox") --- add_linkdirs("/usr/local/lib", "/usr/lib") --- --- -- add system link libraries --- add_syslinks("z", "pthread") --- --- -- add compilation and link flags --- add_cxflags("-stdnolib", "-fno-strict-aliasing") --- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true}) --- --- @endcode --- - From be8b5dd8a7021815bebf4895baaa7fcc57bb7220 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 10 Jul 2025 16:35:38 +0200 Subject: [PATCH 02/39] remove obsolete tests --- .../protocol/command/CommandFactory_test.cpp | 17 ---- .../command/CommandSerializer_test.cpp | 93 ------------------- .../protocol/packet/PacketFactory_test.cpp | 17 ---- .../protocol/packet/PacketSerializer_test.cpp | 36 ------- 4 files changed, 163 deletions(-) delete mode 100644 test/blitz/protocol/command/CommandFactory_test.cpp delete mode 100644 test/blitz/protocol/command/CommandSerializer_test.cpp delete mode 100644 test/blitz/protocol/packet/PacketFactory_test.cpp delete mode 100644 test/blitz/protocol/packet/PacketSerializer_test.cpp diff --git a/test/blitz/protocol/command/CommandFactory_test.cpp b/test/blitz/protocol/command/CommandFactory_test.cpp deleted file mode 100644 index 9ce58cb..0000000 --- a/test/blitz/protocol/command/CommandFactory_test.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include - -static int Test() { - for (std::size_t i = 0; i < static_cast(td::protocol::CommandType::COMMAND_COUNT); i++) { - td::protocol::CommandType commandType = td::protocol::CommandType(i); - - if (td::protocol::CommandFactory::CreateReadOnlyCommand(commandType)->GetType() != commandType) - return TD_TEST_FAILED; - } - return TD_TEST_SUCCESSFUL; -} - -int main() { - return Test(); -} \ No newline at end of file diff --git a/test/blitz/protocol/command/CommandSerializer_test.cpp b/test/blitz/protocol/command/CommandSerializer_test.cpp deleted file mode 100644 index de8f731..0000000 --- a/test/blitz/protocol/command/CommandSerializer_test.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include - -#include -#include - -namespace tp = td::protocol; - -class Comparator { - public: - bool operator()(const tp::cdata::End& left, const tp::cdata::End& right) { - return true; - } - bool operator()(const tp::cdata::PlaceTower& left, const tp::cdata::PlaceTower& right) { - return left.m_Placer == right.m_Placer && left.m_Position.x == right.m_Position.x && left.m_Position.y == right.m_Position.y && - left.m_Type == right.m_Type; - } - bool operator()(const tp::cdata::PlayerJoin& left, const tp::cdata::PlayerJoin& right) { - return left.m_ID == right.m_ID && left.m_Name == right.m_Name; - } - bool operator()(const tp::cdata::SpawnTroop& left, const tp::cdata::SpawnTroop& right) { - return left.m_Level == right.m_Level && left.m_Position.x == right.m_Position.x && left.m_Position.y == right.m_Position.y && - left.m_Sender == right.m_Sender && left.m_Type == right.m_Type; - } - bool operator()(const tp::cdata::TeamChange& left, const tp::cdata::TeamChange& right) { - return left.m_NewTeam == right.m_NewTeam && left.m_Player == right.m_Player; - } - bool operator()(const tp::cdata::UpgradeTower& left, const tp::cdata::UpgradeTower& right) { - return left.m_Tower == right.m_Tower && left.m_Upgrade == right.m_Upgrade; - } - bool operator()(const tp::cdata::UseItem& left, const tp::cdata::UseItem& right) { - return left.m_Item == right.m_Item && left.m_Position.x == right.m_Position.x && left.m_Position.y == right.m_Position.y && - left.m_User == right.m_User; - } -}; - -template -static int TestCommand(const Command_T& command) { - - td::DataBuffer buffer = tp::CommandSerializer::Serialize(command); - - auto abstractCommand = tp::CommandSerializer::Deserialize(buffer); - - td_test_assert(abstractCommand); - - Command_T* command2 = dynamic_cast(abstractCommand.get()); - - td_test_assert(command2); - td_test_assert(command.GetType() == command2->GetType()); - - return Comparator{}(command.m_Data, command2->m_Data); -} - -#define DeclareCommand(Command, ...) td_test_assert(TestCommand({})); - -static int TestAllCommands() { - td_test_assert(TestCommand({})); - td_test_assert(TestCommand({tp::cdata::PlaceTower{ - td::TowerType::Necromancer, - 8, - td::TowerCoords{-50, 69}, - }})); - td_test_assert(TestCommand({tp::cdata::PlayerJoin{ - 4, - "Persson", - }})); - td_test_assert(TestCommand({tp::cdata::SpawnTroop{ - td::EntityType::Blaze, - 4, - td::EntityCoords{td::FpFloat{54}, td::FpFloat{58}}, - 2, - }})); - td_test_assert(TestCommand({tp::cdata::TeamChange{ - 7, - td::Team::Red, - }})); - td_test_assert(TestCommand({tp::cdata::UpgradeTower{ - 69, - 3, - }})); - td_test_assert(TestCommand({tp::cdata::UseItem{ - td::ShopItem::Freeze, - 5, - td::EntityCoords{td::FpFloat{24}, td::FpFloat{-69}}, - }})); - - DeclareAllCommand() - - return TD_TEST_SUCCESSFUL; -} - -int main() { - return TestAllCommands(); -} \ No newline at end of file diff --git a/test/blitz/protocol/packet/PacketFactory_test.cpp b/test/blitz/protocol/packet/PacketFactory_test.cpp deleted file mode 100644 index 4518e33..0000000 --- a/test/blitz/protocol/packet/PacketFactory_test.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include - -static int Test() { - for (std::size_t i = 0; i < static_cast(td::protocol::PacketType::PACKET_COUNT); i++) { - td::protocol::PacketType packetType = td::protocol::PacketType(i); - - if (td::protocol::PacketFactory::CreateReadOnlyPacket(packetType)->GetType() != packetType) - return TD_TEST_FAILED; - } - return TD_TEST_SUCCESSFUL; -} - -int main() { - return Test(); -} \ No newline at end of file diff --git a/test/blitz/protocol/packet/PacketSerializer_test.cpp b/test/blitz/protocol/packet/PacketSerializer_test.cpp deleted file mode 100644 index 38f4c1c..0000000 --- a/test/blitz/protocol/packet/PacketSerializer_test.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include - -#include -#include - -namespace tp = td::protocol; - -template -static int TestPacket() { - Packet_T packet; - - td::DataBuffer buffer = tp::PacketSerializer::Serialize(packet); - - auto abstractPacket = tp::PacketSerializer::Deserialize(buffer); - - td_test_assert(abstractPacket); - - Packet_T* packet2 = dynamic_cast(abstractPacket.get()); - - td_test_assert(packet2); - td_test_assert(packet.GetType() == packet2->GetType()); - - return std::memcmp(&packet.m_Data, &packet2->m_Data, sizeof(Packet_Data_T)); -} - -#define DeclarePacket(Packet, ...) TestPacket(); - -static int TestAllPackets() { - DeclareAllPacket() - - return TD_TEST_SUCCESSFUL; -} - -int main() { - return TestAllPackets(); -} \ No newline at end of file From d1690192dbf2d9a3574f2c279850f9e262997bb7 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 10 Jul 2025 16:48:21 +0200 Subject: [PATCH 03/39] fix Array --- include/td/common/Array.h | 23 +++++++++++++++++++++++ include/td/game/GameHistory.h | 1 - 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/td/common/Array.h b/include/td/common/Array.h index 10acb9b..3f20862 100644 --- a/include/td/common/Array.h +++ b/include/td/common/Array.h @@ -3,6 +3,10 @@ #include namespace td { + +/** + * Reflectable std::array + */ template class Array { private: @@ -11,6 +15,15 @@ class Array { public: Array() : m_Data(new T[S]) {} + Array(const Array& a_Other) : Array() { + std::memcpy(m_Data, a_Other.m_Data, S); + } + + Array(Array&& a_Other) { + m_Data = a_Other.m_Data; + a_Other.m_Data = nullptr; + } + Array(const std::initializer_list& args) { std::size_t i = 0; for(const T& element : args) { @@ -19,6 +32,16 @@ class Array { } } + Array& operator=(const Array& a_Other) { + std::memcpy(m_Data, a_Other.m_Data, S); + return *this; + } + + Array& operator=(Array&& a_Other) { + std::swap(m_Data, a_Other.m_Data); + return *this; + } + T& operator[](std::size_t a_Index) { return m_Data[a_Index]; } diff --git a/include/td/game/GameHistory.h b/include/td/game/GameHistory.h index 73cda9c..4aa84e0 100644 --- a/include/td/game/GameHistory.h +++ b/include/td/game/GameHistory.h @@ -1,6 +1,5 @@ #pragma once -#define BOOST_PFR_USE_CPP17 0 #include #include #include From aaf76a3ff0d3e72e24dea03e4029b031af1ff9f8 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 16 Jul 2025 00:32:40 +0200 Subject: [PATCH 04/39] nice --- imgui.ini | 16 ++ include/td/Maths.h | 183 +++++++++++++ include/td/Types.h | 12 +- include/td/common/DataBuffer.h | 300 --------------------- include/td/common/VarInt.h | 58 ---- include/td/game/Game.h | 44 ++- include/td/game/Mobs.h | 260 ++++++++++++++++++ include/td/game/Team.h | 86 ++++++ include/td/game/Towers.h | 266 ++++++++++++++++++ include/td/game/World.h | 137 ++++++++++ include/td/game/WorldTypes.h | 119 ++++++++ include/td/input/Display.h | 43 +++ include/td/misc/Shapes.h | 96 +++++++ include/td/protocol/command/CommandData.h | 2 +- include/td/protocol/packet/PacketData.h | 20 +- include/td/protocol/packet/Packets.h | 8 +- include/td/render/Camera.h | 37 +++ include/td/render/OpenGL.h | 8 + include/td/render/Renderer.h | 47 ++++ include/td/render/loader/GLLoader.h | 86 ++++++ include/td/render/loader/WorldLoader.h | 25 ++ include/td/render/renderer/WorldRenderer.h | 25 ++ include/td/render/shader/EntityShader.h | 30 +++ include/td/render/shader/ShaderProgram.h | 43 +++ include/td/render/shader/WorldShader.h | 21 ++ src/main.cpp | 212 ++++++++++++++- src/td/Maths.cpp | 169 ++++++++++++ src/td/common/DataBuffer.cpp | 163 ----------- src/td/common/VarInt.cpp | 52 ---- src/td/game/World.cpp | 59 ++++ src/td/input/Display.cpp | 156 +++++++++++ src/td/render/Camera.cpp | 26 ++ src/td/render/Renderer.cpp | 26 ++ src/td/render/loader/GLLoader.cpp | 99 +++++++ src/td/render/loader/WorldLoader.cpp | 218 +++++++++++++++ src/td/render/renderer/WorldRenderer.cpp | 26 ++ src/td/render/shader/EntityShader.cpp | 123 +++++++++ src/td/render/shader/ShaderProgram.cpp | 145 ++++++++++ src/td/render/shader/WorldShader.cpp | 102 +++++++ test/tdmap.tdmap2 | Bin 0 -> 595 bytes xmake.lua | 14 +- 41 files changed, 2963 insertions(+), 599 deletions(-) create mode 100644 imgui.ini create mode 100644 include/td/Maths.h delete mode 100644 include/td/common/DataBuffer.h delete mode 100644 include/td/common/VarInt.h create mode 100644 include/td/game/Mobs.h create mode 100644 include/td/game/Team.h create mode 100644 include/td/game/Towers.h create mode 100644 include/td/game/World.h create mode 100644 include/td/game/WorldTypes.h create mode 100644 include/td/input/Display.h create mode 100644 include/td/misc/Shapes.h create mode 100644 include/td/render/Camera.h create mode 100644 include/td/render/OpenGL.h create mode 100644 include/td/render/Renderer.h create mode 100644 include/td/render/loader/GLLoader.h create mode 100644 include/td/render/loader/WorldLoader.h create mode 100644 include/td/render/renderer/WorldRenderer.h create mode 100644 include/td/render/shader/EntityShader.h create mode 100755 include/td/render/shader/ShaderProgram.h create mode 100644 include/td/render/shader/WorldShader.h create mode 100644 src/td/Maths.cpp delete mode 100644 src/td/common/DataBuffer.cpp delete mode 100644 src/td/common/VarInt.cpp create mode 100644 src/td/game/World.cpp create mode 100644 src/td/input/Display.cpp create mode 100644 src/td/render/Camera.cpp create mode 100644 src/td/render/Renderer.cpp create mode 100644 src/td/render/loader/GLLoader.cpp create mode 100644 src/td/render/loader/WorldLoader.cpp create mode 100644 src/td/render/renderer/WorldRenderer.cpp create mode 100644 src/td/render/shader/EntityShader.cpp create mode 100755 src/td/render/shader/ShaderProgram.cpp create mode 100644 src/td/render/shader/WorldShader.cpp create mode 100644 test/tdmap.tdmap2 diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..c9168d1 --- /dev/null +++ b/imgui.ini @@ -0,0 +1,16 @@ +[Window][Debug##Default] +Pos=20,60 +Size=400,400 + +[Window][Dear ImGui Demo] +Pos=1116,220 +Size=927,695 + +[Window][Dear ImGui Debug Log] +Pos=60,60 +Size=670,354 + +[Window][Example: Console] +Pos=958,210 +Size=520,600 + diff --git a/include/td/Maths.h b/include/td/Maths.h new file mode 100644 index 0000000..61b2f59 --- /dev/null +++ b/include/td/Maths.h @@ -0,0 +1,183 @@ +#pragma once + +#include + +namespace td { + +static constexpr float PI = 3.141592653f; + +template +struct Vec2 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + constexpr Vec2(T X = 0, T Y = 0) : x(X), y(Y) {} +}; + +template +inline bool operator==(const Vec2& vec2, const Vec2& other) { + return vec2.x == other.x && vec2.y == other.y; +} + +template +struct Vec3 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + union { + T z; + T b; + }; + + constexpr Vec3(T X = 0, T Y = 0, T Z = 0) : x(X), y(Y), z(Z) {} +}; + +template +inline bool operator==(const Vec3& vec3, const Vec3& other) { + return vec3.x == other.x && vec3.y == other.y && vec3.z == other.z; +} + +template +struct Vec4 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + union { + T z; + T b; + }; + + union { + T w; + T a; + }; + + constexpr Vec4(Vec3 vec, T W = 1) : x(vec.x), y(vec.y), z(vec.z), w(W) {} + constexpr Vec4(T X = 0, T Y = 0, T Z = 0, T W = 0) : x(X), y(Y), z(Z), w(W) {} +}; + +template +inline bool operator==(const Vec4& vec4, const Vec4& other) { + return vec4.x == other.x && vec4.y == other.y && vec4.z == other.z && vec4.w = other.w; +} + +using Vec2i = Vec2; +using Vec2u = Vec2; +using Vec2f = Vec2; +using Vec2d = Vec2; + +using Vec3i = Vec3; +using Vec3u = Vec3; +using Vec3f = Vec3; +using Vec3d = Vec3; + +using Vec4i = Vec4; +using Vec4u = Vec4; +using Vec4f = Vec4; +using Vec4d = Vec4; + +using Color = Vec3; + +template +struct Mat4 { + static const std::size_t MATRIX_SIZE = 4; + + T x0, x1, x2, x3; + T y0, y1, y2, y3; + T z0, z1, z2, z3; + T w0, w1, w2, w3; + + T operator[](std::size_t offset) const { + return reinterpret_cast(this)[offset]; + } + + T& operator[](std::size_t offset) { + return reinterpret_cast(this)[offset]; + } + + T* data() { + return reinterpret_cast(this); + } + + const T* data() const { + return reinterpret_cast(this); + } + + T at(std::size_t row, std::size_t column) const { + return operator[](row * MATRIX_SIZE + column); + } + + T& at(std::size_t row, std::size_t column) { + return operator[](row * MATRIX_SIZE + column); + } +}; + +typedef Mat4 Mat4f; +typedef Mat4 Mat4i; +typedef Mat4 Mat4d; + +template +inline bool operator==(const Mat4& mat, const Mat4& other) { + return mat.x0 == other.x0 && mat.y0 == other.y0 && mat.z0 == other.z0 && mat.w0 == other.w0 && mat.x1 == other.x1 && + mat.y1 == other.y1 && mat.z1 == other.z1 && mat.w1 == other.w1 && mat.x2 == other.x2 && mat.y2 == other.y2 && + mat.z2 == other.z2 && mat.w2 == other.w2 && mat.x3 == other.x3 && mat.y3 == other.y3 && mat.z3 == other.z3 && + mat.w3 == other.w3; +} + +namespace maths { + +template +Mat4 Transpose(const Mat4& mat) { + Mat4 result; + + result.x1 = mat.y0; + result.x2 = mat.z0; + result.x3 = mat.w0; + result.y0 = mat.x1; + result.y2 = mat.z1; + result.y3 = mat.w1; + result.z0 = mat.x2; + result.z1 = mat.y2; + result.z3 = mat.w2; + result.w0 = mat.x3; + result.w1 = mat.y3; + result.w2 = mat.z3; + result.x0 = mat.x0; + result.y1 = mat.y1; + result.z2 = mat.z2; + result.w3 = mat.w3; + + return result; +} + +Mat4f Perspective(float fovY, float aspectRatio, float zNear, float zFar); +Mat4f Look(const Vec3f& eye, const Vec3f& center, const Vec3f& up); + +Mat4f Inverse(const Mat4f& mat); + +} // namespace maths + + + +} // namespace td diff --git a/include/td/Types.h b/include/td/Types.h index c293bf8..84cc15a 100644 --- a/include/td/Types.h +++ b/include/td/Types.h @@ -7,8 +7,9 @@ namespace td { using FpFloat = fpm::fixed_16_16; -enum class Team : std::uint8_t { - Blue = 0, +enum class TeamColor : std::int8_t { + None = -1, + Blue, Red, }; @@ -67,4 +68,11 @@ struct EntityCoords { using PeerID = std::uint16_t; +enum class Direction : std::uint8_t { + PositiveX = 1 << 0, + NegativeX = 1 << 1, + PositiveY = 1 << 2, + NegativeY = 1 << 3, +}; + } // namespace td diff --git a/include/td/common/DataBuffer.h b/include/td/common/DataBuffer.h deleted file mode 100644 index 2cc9eff..0000000 --- a/include/td/common/DataBuffer.h +++ /dev/null @@ -1,300 +0,0 @@ -#pragma once - -/** - * \file DataBuffer.h - * \brief File containing the td::DataBuffer class - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace td { - -/** - * \class DataBuffer - * \brief Class used to manipulate memory - */ -class DataBuffer { - private: - typedef std::vector Data; - Data m_Buffer; - std::size_t m_ReadOffset; - - public: - typedef Data::iterator iterator; - typedef Data::const_iterator const_iterator; - typedef Data::reference reference; - typedef Data::const_reference const_reference; - typedef Data::difference_type difference_type; - - DataBuffer(); - DataBuffer(const DataBuffer& other); - DataBuffer(const DataBuffer& other, difference_type offset); - DataBuffer(DataBuffer&& other); - DataBuffer(const std::string& str); - - DataBuffer& operator=(const DataBuffer& other); - DataBuffer& operator=(DataBuffer&& other); - - /** - * \brief Append data to the buffer - */ - template - void Append(const T& data) { - std::size_t size = sizeof(data); - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + size); - std::memcpy(&m_Buffer[end_pos], &data, size); - } - - /** - * \brief Append data to the buffer - */ - template - DataBuffer& operator<<(const T& data) { - Append(data); - return *this; - } - - /** - * \brief Append a string to the buffer - * \warning Don't use it for binary data ! - * \param str The string to append - */ - DataBuffer& operator<<(const std::string& str); - - /** - * \brief Append data to the buffer from another const buffer - * \param data The buffer to append - */ - DataBuffer& operator<<(const DataBuffer& data); - - /** - * \brief Append a vector to the buffer by first writing the size - * \param data The vector to append - */ - template - DataBuffer& operator<<(const std::vector& data) { - *this << VarInt{data.size()}; - for (const auto& element : data) { - *this << element; - } - return *this; - } - - /** - * \brief Append an array to the buffer by first writing the size - * \param data The buffer to append - */ - template - DataBuffer& operator<<(const std::array& data) { - for (const auto& element : data) { - *this << element; - } - return *this; - } - - /** - * \brief Read some data from the buffer and assign to desired variable - */ - template - DataBuffer& operator>>(T& data) { - assert(m_ReadOffset + sizeof(T) <= GetSize()); - data = *(reinterpret_cast(&m_Buffer[m_ReadOffset])); - m_ReadOffset += sizeof(T); - return *this; - } - - /** - * \brief Read some data from the buffer and assign to the new buffer - * \param data The buffer to assign - */ - DataBuffer& operator>>(DataBuffer& data); - - /** - * \brief Read a string from the buffer - * \param str The string to assign in the new buffer - * \warning Don't use it for binary data ! - */ - DataBuffer& operator>>(std::string& str); - - /** - * \brief Read a vector (size + data) from the buffer - * \pre The vector is assumed to be empty - */ - template - DataBuffer& operator>>(std::vector& data) { - VarInt arraySize; - *this >> arraySize; - for (std::size_t i = 0; i < arraySize.GetValue(); i++) { - T newElement; - *this >> newElement; - data.push_back(newElement); - } - return *this; - } - - /** - * \brief Read an array from the buffer - */ - template - DataBuffer& operator>>(std::array& data) { - for (std::size_t i = 0; i < Size; i++) { - T newElement; - *this >> newElement; - data[i] = newElement; - } - return *this; - } - - /** - * \brief Write some data to the buffer - * \param buffer The buffer to write - * \param amount The amount of data to write - */ - void WriteSome(const char* buffer, std::size_t amount); - - /** - * \brief Write some data to the buffer - * \param buffer The buffer to write - * \param amount The amount of data to write - */ - void WriteSome(const std::uint8_t* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(char* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(std::uint8_t* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(DataBuffer& buffer, std::size_t amount); - - /** - * \brief Resize the buffer - * \param size The new size of the buffer - */ - void Resize(std::size_t size) { - m_Buffer.resize(size); - } - - /** - * \brief Reserve some space in the buffer - * \param amount The amount of space to reserve - */ - void Reserve(std::size_t amount) { - m_Buffer.reserve(amount); - } - - - /** - * \brief Clear the buffer - */ - void Clear() { - m_Buffer.clear(); - m_ReadOffset = 0; - } - - /** - * \brief When the buffer has been read entirely - */ - bool IsFinished() const { - return m_ReadOffset >= m_Buffer.size(); - } - - /** - * \brief Get the buffer data - */ - std::uint8_t* data() { - return m_Buffer.data(); - } - - /** - * \brief Get the buffer data - */ - const std::uint8_t* data() const { - return m_Buffer.data(); - } - - /** - * \brief Get the read offset - */ - std::size_t GetReadOffset() const { - return m_ReadOffset; - } - - /** - * \brief Set the read offset - * \param pos The new read offset - */ - void SetReadOffset(std::size_t pos); - - /** - * \brief Get the size of the buffer - */ - std::size_t GetSize() const; - - /** - * \brief Get the remaining size of the buffer - */ - std::size_t GetRemaining() const; - - /** - * \brief Read a file into the buffer - * \param fileName The name of the file to read - */ - bool ReadFile(const std::string& fileName); - - /** - * \brief Write a file into the buffer - * \param fileName The name of the file to write to - */ - bool WriteFile(const std::string& fileName) const; - - /** - * \brief Allocate the buffer on the heap - * \warning Don't forget to free the data ! - */ - std::uint8_t* HeapAllocatedData() const { - std::uint8_t* newBuffer = new std::uint8_t[GetSize()]; - std::memcpy(newBuffer, data(), GetSize()); - return newBuffer; - } - - /** - * \brief Operator == to compare two DataBuffer - */ - bool operator==(const DataBuffer& other) const { - return m_Buffer == other.m_Buffer; - } - - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; -}; - -/** - * \brief Operator << to write a DataBuffer to an ostream - */ -std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer); - -} // namespace td diff --git a/include/td/common/VarInt.h b/include/td/common/VarInt.h deleted file mode 100644 index 12ac986..0000000 --- a/include/td/common/VarInt.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -/** - * \file VarInt.h - * \brief File containing the td::VarInt class - */ - -#include -#include - -namespace td { - -class DataBuffer; - -/** - * \class VarInt - * \brief Variable-length format such that smaller numbers use fewer bytes. - */ -class VarInt { - private: - std::uint64_t m_Value; - - public: - VarInt() : m_Value(0) {} - /** - * \brief Construct a variable integer from a value - * \param value The value of the variable integer - */ - VarInt(std::uint64_t value) : m_Value(value) {} - - /** - * \brief Get the value of the variable integer - */ - std::uint64_t GetValue() const { - return m_Value; - } - - /** - * \brief Get the length of the serialized variable integer - */ - std::size_t GetSerializedLength() const; - - /** - * \brief Serialize the variable integer - * \param out The buffer to write the serialized variable integer to - * \param var The variable integer to serialize - */ - friend DataBuffer& operator<<(DataBuffer& out, const VarInt& var); - - /** - * \brief Deserialize the variable integer - * \param in The buffer to read the serialized variable integer from - * \param var The variable integer to deserialize - */ - friend DataBuffer& operator>>(DataBuffer& in, VarInt& var); -}; - -} // namespace td diff --git a/include/td/game/Game.h b/include/td/game/Game.h index 0928249..dd5bac9 100644 --- a/include/td/game/Game.h +++ b/include/td/game/Game.h @@ -1,14 +1,46 @@ #pragma once -#include +#include namespace td { namespace game { -class Game { - private: - GameHistory m_History; +enum class GameState : std::uint8_t { + Lobby, + Game, + EndGame, + Disconnected, + Closed }; -} // namespace game -} // namespace td +typedef std::map PlayerList; + + +class Game { +protected: + World* m_World; + + GameState m_GameState = GameState::Lobby; + PlayerList m_Players; +public: + Game(World* world); + virtual ~Game(); + + virtual void Tick(std::uint64_t delta); + + GameState GetGameState() const { return m_GameState; } + void SetGameState(GameState gameState) { m_GameState = gameState; }; + + const World* GetWorld() const { return m_World; } + World* GetWorld() { return m_World; } + + const PlayerList& GetPlayers() const { return m_Players; } + PlayerList& GetPlayers() { return m_Players; } + + const Player* GetPlayerById(PlayerID id) const; + Player* GetPlayerById(PlayerID id); + +}; + +} // namespace game +} // namespace td diff --git a/include/td/game/Mobs.h b/include/td/game/Mobs.h new file mode 100644 index 0000000..3a9fe37 --- /dev/null +++ b/include/td/game/Mobs.h @@ -0,0 +1,260 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include + +namespace td { +namespace game { + +struct WalkableTile; + +enum class EffectType : std::uint8_t { + Slowness = 0, + Stun, + Fire, + Poison, + Heal, +}; + +enum class MobType : std::uint8_t { + Zombie = 0, + Spider, + Skeleton, + Pigman, + Creeper, + Silverfish, + Blaze, + Witch, + Slime, + Giant, + + MOB_COUNT +}; + +typedef std::uint32_t MobID; +typedef std::uint8_t MobLevel; +typedef std::vector TowerImmunities; +typedef std::vector EffectImmunities; + +class MobStats { +private: + float m_Damage; + float m_Speed; + Vec2f m_Size; + std::uint16_t m_MoneyCost; + std::uint16_t m_ExpCost; + std::uint16_t m_MaxLife; + std::uint16_t m_ExpReward; +public: + MobStats(float damage, float speed, Vec2f size, std::uint16_t moneyCost, + std::uint16_t expCost, std::uint16_t expReward, + std::uint16_t maxLife) : m_Damage(damage), m_Speed(speed), + m_Size(size), m_MoneyCost(moneyCost), m_ExpCost(expCost), + m_MaxLife(maxLife), m_ExpReward(expReward) { + } + + float GetDamage() const { return m_Damage; } + float GetMovementSpeed() const { return m_Speed; } + const Vec2f& GetSize() const { return m_Size; } + std::uint16_t GetMoneyCost() const { return m_MoneyCost; } + std::uint16_t GetExpCost() const { return m_ExpCost; } + std::uint16_t GetExpReward() const { return m_ExpReward; } + std::uint16_t GetMaxLife() const { return m_MaxLife; } +}; + +struct EffectDuration { + EffectType type; + float duration; // in seconds + Tower* tower; // the tower that gived the effect +}; + +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 { +protected: + float m_Health; +private: + MobID m_ID; + PlayerID m_Sender; + MobLevel m_Level; + Direction m_Direction; + std::vector m_Effects; + const Tower* m_LastDamage; // the last tower that damaged the mob + float m_HitCooldown; + + // utils::Timer m_EffectFireTimer; + // utils::Timer m_EffectPoisonTimer; + // utils::Timer m_EffectHealTimer; + + TeamCastle* m_CastleTarget; + // utils::CooldownTimer m_AttackTimer; + +public: + Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), + m_HitCooldown(0), m_CastleTarget(nullptr) { + + } + + virtual MobType GetType() const = 0; + + virtual void Tick(std::uint64_t delta, World* world); + + virtual bool OnDeath(World* world) { return true; } + + MobID GetMobID() const { return m_ID; } + const TowerImmunities& GetTowerImmunities() const { return GetMobTowerImmunities(GetType(), m_Level); } + const EffectImmunities& GetEffectImmunities() const { return GetMobEffectImmunities(GetType(), m_Level); } + PlayerID GetSender() const { return m_Sender; } + MobLevel GetLevel() const { return m_Level; } + const MobStats* GetStats() const { return GetMobStats(GetType(), m_Level); } + void SetHealth(float newHealth) { m_Health = newHealth; } + float GetHealth() const { return m_Health; } + bool IsDead() const { return m_Health <= 0; } + bool IsAlive() const { return m_Health > 0; } + const Tower* GetLastDamageTower() { return m_LastDamage; } + bool HasReachedEnemyCastle() { return m_CastleTarget != nullptr; } + + void Damage(float dmg, const Tower* damager) { + m_Health = std::max(0.0f, m_Health - dmg); + m_LastDamage = damager; + m_HitCooldown = 0.1; + } + + void Heal(float heal) { + m_Health = std::min(static_cast(GetStats()->GetMaxLife()), m_Health + heal); + } + + void SetMobReachedCastle(TeamCastle* castle) { m_CastleTarget = castle; } // used when mob is in front of the castle + + bool IsImmuneTo(TowerType type); + + bool IsImmuneTo(EffectType type); + void AddEffect(EffectType type, float durationSec, Tower* tower); + bool HasEffect(EffectType type); + + bool HasTakenDamage() { return m_HitCooldown > 0; } + + 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 + + Direction GetDirection() const { return m_Direction; } + void SetDirection(Direction dir) { m_Direction = dir; } +protected: + void InitMob() { + m_Health = static_cast(GetStats()->GetMaxLife()); + SetSize(GetStats()->GetSize().x, GetStats()->GetSize().y); + } +private: + void UpdateEffects(std::uint64_t delta, World* world); + void AttackCastle(std::uint64_t delta, World* world); + void Move(std::uint64_t delta, World* world); + void Walk(std::uint64_t delta, World* world); + void MoveBack(const TeamCastle& castle, World* world); + void ChangeDirection(const WalkableTile& tile, World* world); + bool IsTouchingCastle(const TeamCastle& castle) const; + EffectDuration& GetEffect(EffectType type); +}; + +typedef std::shared_ptr MobPtr; + +class Zombie : public Mob { +public: + Zombie(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Zombie; } +}; + +class Spider : public Mob { +public: + Spider(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Spider; } +}; + +class Skeleton : public Mob { +public: + Skeleton(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Skeleton; } +}; + +class PigMan : public Mob { +public: + PigMan(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Pigman; } +}; + +class Creeper : public Mob { +public: + Creeper(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Creeper; } +}; + +class Silverfish : public Mob { +public: + Silverfish(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Silverfish; } +}; + +class Blaze : public Mob { +public: + Blaze(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Blaze; } +}; + +class Witch : public Mob { +public: + Witch(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Witch; } +}; + +class Slime : public Mob { +public: + Slime(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Slime; } +}; + +class Giant : public Mob { +public: + Giant(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Giant; } +}; + +namespace MobFactory { + +MobPtr CreateMob(MobID id, MobType type, std::uint8_t level, PlayerID sender); +std::string GetMobName(MobType type); + +} + +class MobListener { +public: + virtual void OnMobSpawn(Mob* mob) {} + virtual void OnMobDie(Mob* mob) {} + + virtual void OnMobDamage(Mob* target, float damage, Tower* damager) {} + + virtual void OnMobTouchCastle(Mob* damager, TeamCastle* enemyCastle) {} + virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) {} +}; + +// typedef utils::ObjectNotifier MobNotifier; + +} // namespace game +} // namespace td + diff --git a/include/td/game/Team.h b/include/td/game/Team.h new file mode 100644 index 0000000..79e5547 --- /dev/null +++ b/include/td/game/Team.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +namespace td { +namespace game { + +class Player; + +class Spawn : public utils::shape::Rectangle { +private: + Direction m_Direction; +public: + Spawn() { + SetWidth(5); + SetHeight(5); + } + + Direction GetDirection() const { return m_Direction; } + + void SetDirection(Direction direction) { m_Direction = direction; } +}; + +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) { + 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); } + + void SetShape(utils::shape::Rectangle rect) { + SetCenter(rect.GetCenter()); + SetSize(rect.GetSize()); + } +}; + +class Team { +private: + std::vector m_Players; + TeamColor m_Color; + Spawn m_Spawn; + TeamCastle m_TeamCastle; +public: + Team(TeamColor color) : m_Color(color) {} + + void AddPlayer(Player* newPlayer); + void RemovePlayer(const Player* player); + + TeamColor GetColor() const; + + const Spawn& GetSpawn() const { return m_Spawn; } + Spawn& GetSpawn() { return m_Spawn; } + + const TeamCastle& GetCastle() const { return m_TeamCastle; } + TeamCastle& GetCastle() { return m_TeamCastle; } + + std::uint8_t GetPlayerCount() const; +}; + +typedef std::array TeamList; + +} // namespace game +} // namespace td diff --git a/include/td/game/Towers.h b/include/td/game/Towers.h new file mode 100644 index 0000000..af84e02 --- /dev/null +++ b/include/td/game/Towers.h @@ -0,0 +1,266 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace td { +namespace game { + +class World; +class Mob; + +typedef std::shared_ptr MobPtr; + +enum class TowerType : std::uint8_t { + Archer = 0, + Ice, + Sorcerer, + Zeus, + Mage, + Artillery, + Quake, + Poison, + + Leach, + Turret, + Necromancer, + + TowerCount +}; + +enum class TowerSize : std::uint8_t { + Little = 3, // 3x3 + Big = 5, // 5x5 +}; + +enum class TowerPath : std::uint8_t { + Top = 0, + Base, // Base Path + Bottom +}; + +class TowerStats { +private: + float m_Rate; + float m_Damage; + std::uint8_t m_Range; +public: + TowerStats(float rate, float damage, std::uint8_t range) : m_Rate(rate), m_Damage(damage), + m_Range(range) { + } + + float GetDamageRate() const { return m_Rate; } + float GetDamage() const { return m_Damage; } + std::uint8_t GetRange() const { return m_Range; } +}; + +class TowerLevel { +private: + // 1, 2, 3, 4 + std::uint8_t m_Level : 3; + // 0 : base path 1 : top path (if there is bottom path) 2 : bottom path (if there is top path) + TowerPath m_Path : 2; +public: + TowerLevel() : m_Level(1), m_Path(TowerPath::Base) {} + TowerLevel(std::uint8_t level, TowerPath path) : m_Level(level), m_Path(path) {} + + std::uint8_t GetLevel() const { return m_Level; } + TowerPath GetPath() const { return m_Path; } + + void SetLevel(std::uint8_t level) { m_Level = level; } + void SetPath(TowerPath path) { m_Path = path; } + + // operator to sort maps + friend bool operator<(const TowerLevel& level, const TowerLevel& other) { + return level.GetLevel() + static_cast(level.GetPath()) * 4 < + other.GetLevel() + static_cast(other.GetPath()) * 4; + } +}; + +const TowerStats* GetTowerStats(TowerType type, TowerLevel level); + +typedef std::uint16_t TowerID; + +class Tower : public utils::shape::Circle { +private: + TowerID m_ID; + TowerType m_Type; + TowerLevel m_Level{}; + PlayerID m_Builder; +protected: + // utils::CooldownTimer m_Timer; +public: + Tower(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) : utils::shape::Circle(x + 0.5f, y + 0.5f, 0), m_ID(id), m_Type(type), m_Builder(builder) + // m_Timer(GetStats()->GetDamageRate() * 1000) + { // converting seconds to millis + SetRadius(GetStats()->GetRange()); + } + + virtual TowerType GetType() const = 0; + virtual TowerSize GetSize() const = 0; + virtual void Tick(std::uint64_t delta, World* world) = 0; + + void Upgrade(std::uint8_t level, TowerPath path) { + m_Level.SetLevel(level); + m_Level.SetPath(path); + // m_Timer.SetCooldown(GetStats()->GetDamageRate() * 1000); // converting seconds to millis + // m_Timer.Reset(); + SetRadius(GetStats()->GetRange()); + } + + std::uint16_t GetID() const { return m_ID; } + const TowerLevel& GetLevel() const { return m_Level; } + const TowerStats* GetStats() const { return GetTowerStats(m_Type, m_Level); } + PlayerID GetBuilder() const { return m_Builder; } + + bool IsMobInRange(MobPtr mob); +}; + +typedef std::shared_ptr TowerPtr; + +namespace TowerFactory { + +TowerPtr CreateTower(TowerType type, TowerID id, std::int32_t x, std::int32_t y, PlayerID builder); +std::string GetTowerName(TowerType type); + +} // namespace TowerFactory + + +class TowerInfo { +private: + std::string m_Name, m_Description; + bool m_IsBigTower; +public: + TowerInfo(std::string&& name, std::string&& description, bool big) : m_Name(std::move(name)), + m_Description(std::move(description)), m_IsBigTower(big) { + } + + const std::string& GetName() const { return m_Name; } + const std::string& GetDescription() const { return m_Description; } + + bool IsBigTower() const { return m_IsBigTower; } +}; + +const TowerInfo& GetTowerInfo(TowerType type); + +// ---------- Little Towers ---------- + +class LittleTower : public Tower { +public: + LittleTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {} + + virtual TowerSize GetSize() const { return TowerSize::Little; } + + virtual TowerType GetType() const = 0; + virtual void Tick(std::uint64_t delta, World* world) = 0; +}; + +class ArcherTower : public LittleTower { +public: + ArcherTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + constexpr static float ExplosionRadius = 1.5f; + constexpr static float FireDurationSec = 10.0f; + + virtual TowerType GetType() const { return TowerType::Archer; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class IceTower : public LittleTower { +public: + IceTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Ice; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class MageTower : public LittleTower { +public: + MageTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Mage; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class PoisonTower : public LittleTower { +public: + PoisonTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Poison; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class QuakeTower : public LittleTower { +public: + QuakeTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Quake; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class ArtilleryTower : public LittleTower { +public: + ArtilleryTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Artillery; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class SorcererTower : public LittleTower { +public: + SorcererTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Sorcerer; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class ZeusTower : public LittleTower { +public: + ZeusTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Zeus; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +// ---------- Big Towers ---------- + +class BigTower : public Tower { +public: + BigTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {} + + virtual TowerSize GetSize() const { return TowerSize::Big; } + + virtual TowerType GetType() const = 0; + virtual void Tick(std::uint64_t delta, World* world) = 0; +}; + +class TurretTower : public BigTower { +public: + TurretTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Turret; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class NecromancerTower : public BigTower { +public: + NecromancerTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Necromancer; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class LeachTower : public BigTower { +public: + LeachTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Leach; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +} // namespace game +} // namespace td diff --git a/include/td/game/World.h b/include/td/game/World.h new file mode 100644 index 0000000..886cc4b --- /dev/null +++ b/include/td/game/World.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include + +namespace td { +namespace game { + +class World { + protected: + TowerTileColorPalette m_TowerPlacePalette; + Color m_WalkablePalette; + std::vector m_DecorationPalette; + Color m_Background; + + std::unordered_map m_Chunks; + + SpawnColorPalette m_SpawnColorPalette; + + TilePalette m_TilePalette; + + MobList m_Mobs; + + TowerList m_Towers; + + TeamList m_Teams; + + public: + World(); + + bool LoadMap(const protocol::pdata::WorldHeader& worldHeader); + bool LoadMap(const protocol::pdata::WorldData& worldData); + + 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); + TowerPtr RemoveTower(TowerID id); + + TilePtr GetTile(std::int32_t x, std::int32_t y) const; + + const TowerTileColorPalette& GetTowerTileColorPalette() const { + return m_TowerPlacePalette; + } + const Color& GetWalkableTileColor() const { + return m_WalkablePalette; + } + const std::vector& GetDecorationPalette() const { + return m_DecorationPalette; + } + const Color& GetBackgroundColor() const { + return m_Background; + } + + const TilePalette& GetTilePalette() const { + return m_TilePalette; + } + + TilePtr GetTilePtr(TileIndex index) const { + if (index == 0) + return nullptr; + return m_TilePalette.at(index - 1); + } + + bool CanPlaceLittleTower(const Vec2f& worldPos, PlayerID player) const; + bool CanPlaceBigTower(const Vec2f& worldPos, PlayerID player) const; + + TowerPtr GetTower(const Vec2f& position) const; // returns null if no tower is here + + const std::unordered_map& GetChunks() const { + return m_Chunks; + } + + const Color& GetSpawnColor(TeamColor color) const { + return m_SpawnColorPalette[static_cast(color)]; + } + const SpawnColorPalette& GetSpawnColors() const { + return m_SpawnColorPalette; + } + + const MobList& GetMobList() const { + return m_Mobs; + } + MobList& GetMobList() { + return m_Mobs; + } + + const Color* GetTileColor(TilePtr tile) const; + + Team& GetRedTeam() { + return m_Teams[static_cast(TeamColor::Red)]; + } + const Team& GetRedTeam() const { + return m_Teams[static_cast(TeamColor::Red)]; + } + + Team& GetBlueTeam() { + return m_Teams[static_cast(TeamColor::Blue)]; + } + const Team& GetBlueTeam() const { + return m_Teams[static_cast(TeamColor::Red)]; + } + + Team& GetTeam(TeamColor team) { + return m_Teams[static_cast(team)]; + } + const Team& GetTeam(TeamColor team) const { + return m_Teams[static_cast(team)]; + } + + const TeamList& GetTeams() const { + return m_Teams; + } + + const TowerList& GetTowers() const { + return m_Towers; + } + + TowerPtr GetTowerById(TowerID tower); + + const Player* GetPlayerById(PlayerID id) const; + + private: + void TickMobs(std::uint64_t delta); + void CleanDeadMobs(); +}; + + + + + +} // namespace game +} // namespace td diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h new file mode 100644 index 0000000..8acdbd2 --- /dev/null +++ b/include/td/game/WorldTypes.h @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include +#include + +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; + } +}; + + +class Game; + +enum class TileType : std::uint8_t { + None = 0, + Tower, + Walk, + Decoration, + /*Heal, + Lava, + Bedrock, + Freeze, + Ice,*/ +}; + +static constexpr Color BLACK{0, 0, 0}; +static constexpr Color WHITE{255, 255, 255}; + +static constexpr Color RED{255, 0, 0}; +static constexpr Color GREEN{0, 255, 0}; +static constexpr Color BLUE{0, 0, 255}; + +struct Tile { + virtual TileType GetType() const = 0; +}; + +struct TowerTile : Tile { + std::uint8_t color_palette_ref; + TeamColor team_owner; + + virtual TileType GetType() const { + return TileType::Tower; + } +}; + +struct WalkableTile : Tile { + Direction direction; + + virtual TileType GetType() const { + return TileType::Walk; + } +}; + +struct DecorationTile : Tile { + std::uint16_t color_palette_ref; + + virtual TileType GetType() const { + return TileType::Decoration; + } +}; + +typedef std::shared_ptr TilePtr; +typedef std::vector ChunkPalette; + +typedef std::shared_ptr WalkableTilePtr; + +typedef std::uint32_t TileIndex; + +// 32 x 32 area +struct Chunk { + enum { ChunkWidth = 32, ChunkHeight = 32, ChunkSize = ChunkWidth * ChunkHeight }; + typedef std::array ChunkData; + + // stores index of tile palette + ChunkData tiles{0}; + ChunkPalette palette; + + TileIndex GetTileIndex(std::uint16_t tileNumber) const { + TileIndex chunkPaletteIndex = tiles.at(tileNumber); + if (chunkPaletteIndex == 0) // index 0 means empty tile index 1 = first tile + return 0; + return palette.at(chunkPaletteIndex); + } +}; + +typedef std::shared_ptr ChunkPtr; + +typedef std::array TowerTileColorPalette; + +typedef std::vector TilePalette; + +typedef std::vector MobList; + +typedef std::array SpawnColorPalette; + +typedef std::vector TowerList; + + +} // namespace game + +} // namespace td + + + +namespace std { +template <> +struct hash { + std::size_t operator()(const td::game::ChunkCoord& key) const noexcept { + return std::hash()(key.x << 16 | key.y); + } +}; +} // namespace std \ No newline at end of file diff --git a/include/td/input/Display.h b/include/td/input/Display.h new file mode 100644 index 0000000..95f6b39 --- /dev/null +++ b/include/td/input/Display.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +namespace td { + +class Display { + public: + Display(int a_Width, int a_Height, const std::string& a_Title); + ~Display(); + + void PollEvents(); + void Update(); + + bool IsCloseRequested() { + return m_ShouldClose; + } + + float GetAspectRatio() { + return m_AspectRatio; + } + + int GetWindowWidth() { + return m_LastWidth; + } + + int GetWindowHeight() { + return m_LastHeight; + } + + private: + SDL_Window* m_Window; + SDL_GLContext m_GLContext; + + int m_LastWidth, m_LastHeight; + float m_AspectRatio; + + bool m_ShouldClose; +}; + +} // namespace td \ No newline at end of file diff --git a/include/td/misc/Shapes.h b/include/td/misc/Shapes.h new file mode 100644 index 0000000..6a789f8 --- /dev/null +++ b/include/td/misc/Shapes.h @@ -0,0 +1,96 @@ +#pragma once + +#include + +namespace td { +namespace utils { +namespace shape { + +class Point { +private: + float m_X, m_Y; +public: + Point() : m_X(0), m_Y(0) {} + Point(float x, float y) : m_X(x), m_Y(y) {} + + float GetX() const { return m_X; } + float GetY() const { return m_Y; } + + void SetX(float x) { m_X = x; } + void SetY(float y) { m_Y = y; } + + float Distance(const Point& point) const; + float DistanceSquared(const Point& point) const; +}; + +class Circle; + +class Rectangle { +private: + Point m_Center; + float m_Width, m_Height; +public: + Rectangle() {} + + const Point& GetCenter() const { return m_Center; } + float GetCenterX() const { return m_Center.GetX(); } + float GetCenterY() const { return m_Center.GetY(); } + + float GetWidth() const { return m_Width; } + float GetHeight() const { return m_Height; } + + Point GetTopLeft() const { return { m_Center.GetX() - (m_Width / 2.0f), m_Center.GetY() - (m_Height / 2.0f) }; } + Point GetBottomRight() const { return { m_Center.GetX() + (m_Width / 2.0f), m_Center.GetY() + (m_Height / 2.0f) }; } + + void SetCenter(const Point& center) { m_Center = center; } + void SetCenterX(float x) { m_Center.SetX(x); } + void SetCenterY(float y) { m_Center.SetY(y); } + + void SetSize(float width, float height) { SetWidth(width); SetHeight(height); } + void SetSize(Point size) { SetSize(size.GetX(), size.GetY()); } + Point GetSize() { return { m_Width, m_Height }; } + + void SetWidth(float width) { m_Width = width; } + void SetHeight(float height) { m_Height = height; } + + bool CollidesWith(const Point& point) const; + bool CollidesWith(const Rectangle& rect) const; + bool CollidesWith(const Circle& circle) const; + + // distance from the closest side of the rectangle + float Distance(const Circle& circle) const; + float DistanceSquared(const Circle& circle) const; +}; + +class Circle { +private: + Point m_Center; + float m_Radius; +public: + Circle(float x, float y, float radius) : m_Center(x, y), m_Radius(radius) {} + Circle() : m_Radius(0) {} + + const Point& GetCenter() const { return m_Center; } + float GetCenterX() const { return m_Center.GetX(); } + float GetCenterY() const { return m_Center.GetY(); } + + float GetRadius() const { return m_Radius; } + + void SetCenter(const Point& center) { m_Center = center; } + void SetCenterX(float x) { m_Center.SetX(x); } + void SetCenterY(float y) { m_Center.SetY(y); } + + void SetRadius(float radius) { m_Radius = radius; } + + bool CollidesWith(const Point& point) const; + bool CollidesWith(const Rectangle& rect) const; + bool CollidesWith(const Circle& circle) const; + + // distance from the closest side of the rectangle + float Distance(const Rectangle& rect) const; + float DistanceSquared(const Rectangle& rect) const; +}; + +} // namespace shape +} // namespace utils +} // namespace td diff --git a/include/td/protocol/command/CommandData.h b/include/td/protocol/command/CommandData.h index ed3e850..bf01602 100644 --- a/include/td/protocol/command/CommandData.h +++ b/include/td/protocol/command/CommandData.h @@ -36,7 +36,7 @@ struct UseItem { struct TeamChange { sp::BitField m_Player; - sp::BitField m_NewTeam; + sp::BitField m_NewTeam; }; struct PlayerJoin { diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index 3bd37aa..de85032 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -4,6 +4,7 @@ #include #include #include +#include // Make it dynamic ? #define LOCKSTEP_BUFFER_SIZE 10 @@ -60,7 +61,6 @@ struct ChatMessage { // TODO: handle players joining in the first second struct BeginGame { - MapData m_Map; std::vector m_BlueTeam; std::vector m_RedTeam; // optional, used for players joining mid game @@ -72,6 +72,24 @@ struct LockSteps { Array m_LockSteps; }; +struct WorldHeader { + game::TowerTileColorPalette m_TowerPlacePalette; + Color m_WalkablePalette; + std::vector m_DecorationPalette; + Color m_Background; + + game::SpawnColorPalette m_SpawnColorPalette; + + game::TilePalette m_TilePalette; + + game::Spawn m_RedSpawn, m_BlueSpawn; + game::TeamCastle m_RedCastle, m_BlueCastle; +}; + +struct WorldData { + std::unordered_map m_Chunks; +}; + } // namespace pdata } // namespace protocol } // namespace td diff --git a/include/td/protocol/packet/Packets.h b/include/td/protocol/packet/Packets.h index 92822e1..4be3b2f 100644 --- a/include/td/protocol/packet/Packets.h +++ b/include/td/protocol/packet/Packets.h @@ -26,7 +26,9 @@ enum class PacketID : std::uint8_t { LoggingSuccess, PlayerJoin, PlayerLeave, - PlayerLogin + PlayerLogin, + WorldHeader, + WorldData, }; class PacketHandler; @@ -49,13 +51,15 @@ using LoggingSuccessPacket = PacketMessage; using PlayerLeavePacket = PacketMessage; using PlayerLoginPacket = PacketMessage; +using WorldHeaderPacket = PacketMessage; +using WorldDataPacket = PacketMessage; } // namespace packets using AllPackets = std::tuple; + packets::PlayerLeavePacket, packets::PlayerLoginPacket, packets::WorldHeaderPacket, packets::WorldDataPacket>; class PacketHandler : public sp::MessageHandler {}; diff --git a/include/td/render/Camera.h b/include/td/render/Camera.h new file mode 100644 index 0000000..1f621a1 --- /dev/null +++ b/include/td/render/Camera.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace td { +namespace render { + +class Camera { + private: + Mat4f m_ViewMatrix; + Mat4f m_ProjectionMatrix; + Mat4f m_InvViewMatrix; + Mat4f m_InvProjectionMatrix; + + float m_CamDistance = 25.0f; + Vec3f m_CamPos{0, m_CamDistance, 0}; + Vec2f m_CamLook{}; + + float m_Yaw = -PI / 2.0f; + float m_Pitch = -PI / 2.0f + 0.0000001f; + + public: + const Mat4f& GetViewMatrix() const { + return m_ViewMatrix; + } + + const Mat4f& GetProjectionMatrix() const { + return m_ProjectionMatrix; + } + + void UpdatePerspective(float a_AspectRatio); + + void SetCamPos(const Vec3f& a_NewPos); +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/OpenGL.h b/include/td/render/OpenGL.h new file mode 100644 index 0000000..75fd640 --- /dev/null +++ b/include/td/render/OpenGL.h @@ -0,0 +1,8 @@ +#pragma once + +#ifdef TD_GL_LOADER_GLEW +#include +#else +#include +using namespace gl; +#endif diff --git a/include/td/render/Renderer.h b/include/td/render/Renderer.h new file mode 100644 index 0000000..407134c --- /dev/null +++ b/include/td/render/Renderer.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace render { + +class Renderer { + protected: + Camera& m_Camera; + + public: + Renderer(Camera& a_Camera) : m_Camera(a_Camera) {} + virtual ~Renderer() {} + + virtual void Render() = 0; + + void Render(const GL::VertexArray& a_Vao); +}; + +class RenderPipeline { + private: + std::vector> m_Renderers; + + public: + RenderPipeline(); + ~RenderPipeline() = default; + + void AddRenderer(std::unique_ptr&& a_Renderer) { + m_Renderers.push_back(std::move(a_Renderer)); + } + + void Clear() { + m_Renderers.clear(); + } + + void Render() { + for (auto& renderer : m_Renderers) { + renderer->Render(); + } + } +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/loader/GLLoader.h b/include/td/render/loader/GLLoader.h new file mode 100644 index 0000000..2447489 --- /dev/null +++ b/include/td/render/loader/GLLoader.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include + +namespace td { +namespace GL { + +struct VertexAttribPointer { + unsigned int m_Index; + unsigned int m_Size; + unsigned int m_Offset; +}; + +class ElementBuffer : private sp::NonCopyable { + private: + unsigned int m_ID = 0; + std::size_t m_TriangleCount; + + public: + ElementBuffer(ElementBuffer&& other) { + std::swap(m_ID, other.m_ID); + m_TriangleCount = other.m_TriangleCount; + } + + explicit ElementBuffer(const std::vector& indicies); + ~ElementBuffer(); + + void Bind() const; + void Unbind() const; + + std::size_t GetTriangleCount() const { + return m_TriangleCount; + } +}; + +class VertexBuffer : private sp::NonCopyable { + private: + unsigned int m_ID = 0, m_DataStride; + std::vector m_VertexAttribs; + + public: + VertexBuffer(VertexBuffer&& other) { + std::swap(m_ID, other.m_ID); + m_VertexAttribs = std::move(other.m_VertexAttribs); + m_DataStride = other.m_DataStride; + } + + VertexBuffer(const std::vector& data, unsigned int stride); + ~VertexBuffer(); + + void Bind() const; + void Unbind() const; + void AddVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset); + void BindVertexAttribs() const; +}; + +class VertexArray : private sp::NonCopyable { + private: + unsigned int m_ID = 0; + ElementBuffer m_ElementBuffer; + std::vector m_VertexBuffers; // use to destroy vbos when become unused + public: + VertexArray(VertexArray&& other) : m_ElementBuffer(std::move(other.m_ElementBuffer)) { + std::swap(m_ID, other.m_ID); + m_VertexBuffers = std::move(other.m_VertexBuffers); + } + + VertexArray(ElementBuffer&& indicies); + ~VertexArray(); + + std::size_t GetVertexCount() const { + return m_ElementBuffer.GetTriangleCount(); + } + + void BindVertexBuffer(VertexBuffer&& vbo); + void Bind() const; + void Unbind() const; + + private: + void BindElementArrayBuffer(); +}; + +} // namespace GL +} // namespace td diff --git a/include/td/render/loader/WorldLoader.h b/include/td/render/loader/WorldLoader.h new file mode 100644 index 0000000..b9d2482 --- /dev/null +++ b/include/td/render/loader/WorldLoader.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace td { +namespace render { + +namespace WorldLoader { + +struct RenderData { + std::vector positions; + std::vector colors; +}; + +GL::VertexArray LoadMobModel(); +GL::VertexArray LoadWorldModel(const td::game::World* world); +GL::VertexArray LoadTileSelectModel(); +RenderData LoadTowerModel(game::TowerPtr tower); + +} // namespace WorldLoader + + +} // namespace render +} // namespace td diff --git a/include/td/render/renderer/WorldRenderer.h b/include/td/render/renderer/WorldRenderer.h new file mode 100644 index 0000000..61fd3c2 --- /dev/null +++ b/include/td/render/renderer/WorldRenderer.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace render { + +class WorldRenderer : public Renderer { + private: + const game::World& m_World; + shader::WorldShader m_Shader; + std::unique_ptr m_WorldVao; + + public: + WorldRenderer(Camera& a_Camera, const game::World& a_World); + virtual ~WorldRenderer(); + + virtual void Render() override; +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/shader/EntityShader.h b/include/td/render/shader/EntityShader.h new file mode 100644 index 0000000..765d908 --- /dev/null +++ b/include/td/render/shader/EntityShader.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace td { +namespace shader { + +class EntityShader : public ShaderProgram { + private: + unsigned int m_LocationProjectionMatrix = 0; + unsigned int m_LocationViewMatrix = 0; + unsigned int m_LocationPosition = 0; + unsigned int m_LocationColorEffect = 0; + + protected: + virtual void GetAllUniformLocation(); + + public: + EntityShader(); + + void LoadShader(); + + void SetColorEffect(const Vec3f& color); + void SetProjectionMatrix(const Mat4f& proj) const; + void SetViewMatrix(const Mat4f& view) const; + void SetModelPos(const Vec3f& pos) const; +}; + +} // namespace shader +} // namespace td diff --git a/include/td/render/shader/ShaderProgram.h b/include/td/render/shader/ShaderProgram.h new file mode 100755 index 0000000..23aef9d --- /dev/null +++ b/include/td/render/shader/ShaderProgram.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace shader { + +class ShaderProgram { + public: + ShaderProgram(); + virtual ~ShaderProgram(); + + void Start() const; + void Stop() const; + + void LoadProgramFile(const std::string& vertexFile, const std::string& fragmentFile); + void LoadProgram(const std::string& vertexSource, const std::string& fragmentSource); + + protected: + virtual void GetAllUniformLocation() = 0; + int GetUniformLocation(const std::string& uniformName) const; + + void LoadFloat(unsigned int location, float value) const; + void LoadInt(unsigned int location, int value) const; + void LoadVector(unsigned int location, const Vec2f& vector) const; + void LoadVector(unsigned int location, const Vec3f& vector) const; + void LoadBoolean(unsigned int location, bool value) const; + void LoadMat4(unsigned int location, const Mat4f& mat) const; + void CleanUp() const; + + private: + unsigned int m_ProgramID; + unsigned int m_VertexShaderID; + unsigned int m_FragmentShaderID; + + unsigned int LoadShaderFromFile(const std::string& file, GLenum type); + unsigned int LoadShader(const std::string& source, GLenum type); +}; + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/include/td/render/shader/WorldShader.h b/include/td/render/shader/WorldShader.h new file mode 100644 index 0000000..f7d743e --- /dev/null +++ b/include/td/render/shader/WorldShader.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace td { +namespace shader { + +class WorldShader : public ShaderProgram { +private: + unsigned int m_LocationProjection = 0, m_LocationView = 0; +protected: + void GetAllUniformLocation(); +public: + WorldShader(); + + void SetProjectionMatrix(const Mat4f& proj) const; + void SetViewMatrix(const Mat4f& view) const; +}; + +} // namespace shader +} // namespace td diff --git a/src/main.cpp b/src/main.cpp index 80fa251..5dd40b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,210 @@ #include -// #include -#include -#include -class Test : public td::protocol::CommandHandler {}; +#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 + +class WorldApply : public td::protocol::PacketHandler { + private: + td::game::World& m_World; + public: + WorldApply(td::game::World& a_World) : m_World(a_World) {} + + void Handle(const td::protocol::pdata::WorldHeader& a_Header) override { + m_World.LoadMap(a_Header); + } + void Handle(const td::protocol::pdata::WorldData& a_Data) override { + m_World.LoadMap(a_Data); + } +}; int main(int argc, char** argv) { - // Test visitor; - // td::protocol::packets::ChatMessage chat{{"coucou"}}; - // visitor.Check(chat); + sp::DataBuffer buffer; + buffer.ReadFile("test/tdmap.tdmap2"); + sp::DataBuffer buffer1 = sp::zlib::Decompress(buffer, 84); + buffer.SetReadOffset(buffer.GetReadOffset() + 83); + sp::DataBuffer buffer2 = sp::zlib::Decompress(buffer, 511); + + td::protocol::packets::WorldHeaderPacket header; + header.Read(buffer1); + + td::protocol::packets::WorldDataPacket data; + data.Read(buffer2); + + td::game::World w; + WorldApply wa(w); + + td::protocol::PacketDispatcher d; + d.RegisterHandler(td::protocol::PacketID::WorldData, &wa); + d.RegisterHandler(td::protocol::PacketID::WorldHeader, &wa); + + d.Dispatch(header); + d.Dispatch(data); + + td::Display display(1920, 1080, "Tower-Defense 2"); + + td::render::Camera cam; + cam.SetCamPos({77, 25, 13}); + cam.UpdatePerspective(display.GetAspectRatio()); + + td::render::RenderPipeline renderer; + renderer.AddRenderer(std::make_unique(cam, w)); + + while (!display.IsCloseRequested()) { + display.PollEvents(); + renderer.Render(); + display.Update(); + } - td::protocol::commands::UpgradeTowerCommand com{1, 2}; - std::cout << (unsigned)com.GetId() << std::endl; - td::protocol::CommandDispatcher disptacher; return 0; } diff --git a/src/td/Maths.cpp b/src/td/Maths.cpp new file mode 100644 index 0000000..97358af --- /dev/null +++ b/src/td/Maths.cpp @@ -0,0 +1,169 @@ +#include + +#include +#include + +namespace td { +namespace maths { + +template +T Length(const Vec3& vect) { + return std::sqrt(vect.x * vect.x + vect.y * vect.y + vect.z * vect.z); +} + +template +Vec3 Normalize(const Vec3& vect) { + T length = Length(vect); + + return {vect.x / length, vect.y / length, vect.z / length}; +} + +template +Vec4 Normalize(const Vec4& vect) { + T length = std::sqrt(vect.x * vect.x + vect.y * vect.y + vect.z * vect.z + vect.w * vect.w); + + return {vect.x / length, vect.y / length, vect.z / length, vect.w / length}; +} + +template +T Dot(const Vec3& vect, const Vec3& other) { + return vect.x * other.x + vect.y * other.y + vect.z * other.z; +} + +template +Vec3 Cross(const Vec3& vect, const Vec3& other) { + return { + vect.y * other.z - vect.z * other.y, + vect.z * other.x - vect.x * other.z, + vect.x * other.y - vect.y * other.x, + }; +} + +template +T Dot(const Vec4& vect, const Vec4& other) { + return vect.x * other.x + vect.y * other.y + vect.z * other.z + vect.w * other.w; +} + +template +T Distance(const Vec3& vect, const Vec3& other) { + return Length(vect - other); +} + +template +Vec4 Dot(const Mat4& mat, const Vec4& vect) { + return {mat.x0 * vect.x + mat.x1 * vect.y + mat.x2 * vect.z + mat.x3 * vect.w, + mat.y0 * vect.x + mat.y1 * vect.y + mat.y2 * vect.z + mat.y3 * vect.w, + mat.z0 * vect.x + mat.z1 * vect.y + mat.z2 * vect.z + mat.z3 * vect.w, + mat.w0 * vect.x + mat.w1 * vect.y + mat.w2 * vect.z + mat.w3 * vect.w}; +} + +template +Mat4 Dot(const Mat4& mat, const Mat4& other) { + Mat4 result{}; + + for (std::size_t i = 0; i < Mat4::MATRIX_SIZE; i++) { + for (std::size_t j = 0; j < Mat4::MATRIX_SIZE; j++) { + for (std::size_t k = 0; k < Mat4::MATRIX_SIZE; k++) { + result.at(i, j) += mat.at(i, k) * other.at(k, j); + } + } + } + + return result; +} + +template +Mat4 Identity() { + Mat4 result{}; + + result.x0 = static_cast(1); + result.y1 = static_cast(1); + result.z2 = static_cast(1); + result.w3 = static_cast(1); + + return result; +} + +Mat4f Perspective(float fovY, float aspectRatio, float zNear, float zFar) { + const float tanHalfFovy = std::tan(fovY / 2.0f); + + Mat4f result{}; + result.x0 = 1.0f / (aspectRatio * tanHalfFovy); + result.y1 = 1.0f / (tanHalfFovy); + result.z2 = -(zFar + zNear) / (zFar - zNear); + result.z3 = -1.0f; + result.w2 = -(2.0f * zFar * zNear) / (zFar - zNear); + + return result; +} + +Mat4f Look(const Vec3f& eye, const Vec3f& center, const Vec3f& up) { + const Vec3f f = Normalize(center); + const Vec3f s = Normalize(Cross(f, up)); + const Vec3f u = Cross(s, f); + + Mat4f result = Identity(); + result.x0 = s.x; + result.y0 = s.y; + result.z0 = s.z; + result.x1 = u.x; + result.y1 = u.y; + result.z1 = u.z; + result.x2 = -f.x; + result.y2 = -f.y; + result.z2 = -f.z; + result.w0 = -Dot(s, eye); + result.w1 = -Dot(u, eye); + result.w2 = Dot(f, eye); + + return result; +} + +Mat4f Inverse(const Mat4f& mat) { + float s0 = mat.at(0, 0) * mat.at(1, 1) - mat.at(1, 0) * mat.at(0, 1); + float s1 = mat.at(0, 0) * mat.at(1, 2) - mat.at(1, 0) * mat.at(0, 2); + float s2 = mat.at(0, 0) * mat.at(1, 3) - mat.at(1, 0) * mat.at(0, 3); + float s3 = mat.at(0, 1) * mat.at(1, 2) - mat.at(1, 1) * mat.at(0, 2); + float s4 = mat.at(0, 1) * mat.at(1, 3) - mat.at(1, 1) * mat.at(0, 3); + float s5 = mat.at(0, 2) * mat.at(1, 3) - mat.at(1, 2) * mat.at(0, 3); + + float c5 = mat.at(2, 2) * mat.at(3, 3) - mat.at(3, 2) * mat.at(2, 3); + float c4 = mat.at(2, 1) * mat.at(3, 3) - mat.at(3, 1) * mat.at(2, 3); + float c3 = mat.at(2, 1) * mat.at(3, 2) - mat.at(3, 1) * mat.at(2, 2); + float c2 = mat.at(2, 0) * mat.at(3, 3) - mat.at(3, 0) * mat.at(2, 3); + float c1 = mat.at(2, 0) * mat.at(3, 2) - mat.at(3, 0) * mat.at(2, 2); + float c0 = mat.at(2, 0) * mat.at(3, 1) - mat.at(3, 0) * mat.at(2, 1); + + float det = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0; + + assert(det != 0 && "Determinant equals 0 !"); + + float invdet = 1.0 / det; + + Mat4f result; + + result.at(0, 0) = (mat.at(1, 1) * c5 - mat.at(1, 2) * c4 + mat.at(1, 3) * c3) * invdet; + result.at(0, 1) = (-mat.at(0, 1) * c5 + mat.at(0, 2) * c4 - mat.at(0, 3) * c3) * invdet; + result.at(0, 2) = (mat.at(3, 1) * s5 - mat.at(3, 2) * s4 + mat.at(3, 3) * s3) * invdet; + result.at(0, 3) = (-mat.at(2, 1) * s5 + mat.at(2, 2) * s4 - mat.at(2, 3) * s3) * invdet; + + result.at(1, 0) = (-mat.at(1, 0) * c5 + mat.at(1, 2) * c2 - mat.at(1, 3) * c1) * invdet; + result.at(1, 1) = (mat.at(0, 0) * c5 - mat.at(0, 2) * c2 + mat.at(0, 3) * c1) * invdet; + result.at(1, 2) = (-mat.at(3, 0) * s5 + mat.at(3, 2) * s2 - mat.at(3, 3) * s1) * invdet; + result.at(1, 3) = (mat.at(2, 0) * s5 - mat.at(2, 2) * s2 + mat.at(2, 3) * s1) * invdet; + + result.at(2, 0) = (mat.at(1, 0) * c4 - mat.at(1, 1) * c2 + mat.at(1, 3) * c0) * invdet; + result.at(2, 1) = (-mat.at(0, 0) * c4 + mat.at(0, 1) * c2 - mat.at(0, 3) * c0) * invdet; + result.at(2, 2) = (mat.at(3, 0) * s4 - mat.at(3, 1) * s2 + mat.at(3, 3) * s0) * invdet; + result.at(2, 3) = (-mat.at(2, 0) * s4 + mat.at(2, 1) * s2 - mat.at(2, 3) * s0) * invdet; + + result.at(3, 0) = (-mat.at(1, 0) * c3 + mat.at(1, 1) * c1 - mat.at(1, 2) * c0) * invdet; + result.at(3, 1) = (mat.at(0, 0) * c3 - mat.at(0, 1) * c1 + mat.at(0, 2) * c0) * invdet; + result.at(3, 2) = (-mat.at(3, 0) * s3 + mat.at(3, 1) * s1 - mat.at(3, 2) * s0) * invdet; + result.at(3, 3) = (mat.at(2, 0) * s3 - mat.at(2, 1) * s1 + mat.at(2, 2) * s0) * invdet; + + return result; +} + +} // namespace maths +} // namespace td \ No newline at end of file diff --git a/src/td/common/DataBuffer.cpp b/src/td/common/DataBuffer.cpp deleted file mode 100644 index 9ed5062..0000000 --- a/src/td/common/DataBuffer.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include - -#include -#include -#include - -#include -#include - -namespace td { - -DataBuffer::DataBuffer() : m_ReadOffset(0) {} - -DataBuffer::DataBuffer(const DataBuffer& other) : m_Buffer(other.m_Buffer), m_ReadOffset(other.m_ReadOffset) {} - -DataBuffer::DataBuffer(DataBuffer&& other) : m_Buffer(std::move(other.m_Buffer)), m_ReadOffset(std::move(other.m_ReadOffset)) {} - -DataBuffer::DataBuffer(const std::string& str) : m_Buffer(str.begin(), str.end()), m_ReadOffset(0) {} - -DataBuffer::DataBuffer(const DataBuffer& other, Data::difference_type offset) : m_ReadOffset(0) { - m_Buffer.reserve(other.GetSize() - static_cast(offset)); - std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer)); -} - -DataBuffer& DataBuffer::operator<<(const std::string& str) { - std::size_t strlen = str.length() + 1; // including null character - Resize(GetSize() + strlen); - std::memcpy(m_Buffer.data() + GetSize() - strlen, str.data(), strlen); - return *this; -} - -DataBuffer& DataBuffer::operator<<(const DataBuffer& data) { - m_Buffer.insert(m_Buffer.end(), data.begin(), data.end()); - return *this; -} - -DataBuffer& DataBuffer::operator>>(std::string& str) { - std::size_t stringSize = strlen(reinterpret_cast(m_Buffer.data()) + m_ReadOffset) + 1; // including null character - str.resize(stringSize); - std::copy(m_Buffer.begin() + static_cast(m_ReadOffset), - m_Buffer.begin() + static_cast(m_ReadOffset + stringSize), str.begin()); - m_ReadOffset += stringSize; - str.resize(stringSize - 1); - return *this; -} - -DataBuffer& DataBuffer::operator>>(DataBuffer& data) { - data.Resize(GetSize() - m_ReadOffset); - std::copy(m_Buffer.begin() + static_cast(m_ReadOffset), m_Buffer.end(), data.begin()); - m_ReadOffset = m_Buffer.size(); - return *this; -} - -void DataBuffer::WriteSome(const char* buffer, std::size_t amount) { - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + amount); - std::memcpy(m_Buffer.data() + end_pos, buffer, amount); -} - -void DataBuffer::WriteSome(const std::uint8_t* buffer, std::size_t amount) { - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + amount); - std::memcpy(m_Buffer.data() + end_pos, buffer, amount); -} - -void DataBuffer::ReadSome(char* buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer); - m_ReadOffset += amount; -} - - -void DataBuffer::ReadSome(std::uint8_t* buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer); - m_ReadOffset += amount; -} - - -void DataBuffer::ReadSome(DataBuffer& buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - buffer.Resize(amount); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer.begin()); - m_ReadOffset += amount; -} - - -DataBuffer& DataBuffer::operator=(const DataBuffer& other) { - m_Buffer = other.m_Buffer; - m_ReadOffset = other.m_ReadOffset; - return *this; -} - -DataBuffer& DataBuffer::operator=(DataBuffer&& other) { - m_Buffer = std::move(other.m_Buffer); - m_ReadOffset = std::move(other.m_ReadOffset); - return *this; -} - -void DataBuffer::SetReadOffset(std::size_t pos) { - assert(pos <= GetSize()); - m_ReadOffset = pos; -} - -std::size_t DataBuffer::GetSize() const { - return m_Buffer.size(); -} - -std::size_t DataBuffer::GetRemaining() const { - return m_Buffer.size() - m_ReadOffset; -} - -DataBuffer::iterator DataBuffer::begin() { - return m_Buffer.begin(); -} - -DataBuffer::iterator DataBuffer::end() { - return m_Buffer.end(); -} - -DataBuffer::const_iterator DataBuffer::begin() const { - return m_Buffer.begin(); -} - -DataBuffer::const_iterator DataBuffer::end() const { - return m_Buffer.end(); -} - -std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer) { - for (unsigned char u : buffer) - os << std::hex << std::setfill('0') << std::setw(2) << static_cast(u) << " "; - os << std::dec; - return os; -} - -bool DataBuffer::ReadFile(const std::string& fileName) { - try { - std::ifstream file(fileName, std::istream::binary); - std::ostringstream ss; - ss << file.rdbuf(); - const std::string& s = ss.str(); - m_Buffer = DataBuffer::Data(s.begin(), s.end()); - m_ReadOffset = 0; - } catch (std::exception& e) { - utils::LOGE(utils::Format("[IO] Failed to read file %s ! reason : %s", fileName.c_str(), e.what())); - return false; - } - return m_Buffer.size() > 0; -} - -bool DataBuffer::WriteFile(const std::string& fileName) const { - try { - std::ofstream file(fileName, std::ostream::binary); - file.write(reinterpret_cast(m_Buffer.data()), static_cast(m_Buffer.size())); - file.flush(); - } catch (std::exception& e) { - utils::LOGE(utils::Format("[IO] Failed to read file %s ! reason : %s", fileName.c_str(), e.what())); - return false; - } - return true; -} - -} // namespace td diff --git a/src/td/common/VarInt.cpp b/src/td/common/VarInt.cpp deleted file mode 100644 index 0136f2f..0000000 --- a/src/td/common/VarInt.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include - -#include -#include - -namespace td { - -static constexpr int SEGMENT_BITS = 0x7F; -static constexpr int CONTINUE_BIT = 0x80; - -std::size_t VarInt::GetSerializedLength() const { - DataBuffer buffer; - buffer << *this; - return buffer.GetSize(); -} - -DataBuffer& operator<<(DataBuffer& out, const VarInt& var) { - std::uint64_t value = var.m_Value; - while (true) { - if ((value & ~static_cast(SEGMENT_BITS)) == 0) { - out << static_cast(value); - return out; - } - - out << static_cast((value & SEGMENT_BITS) | CONTINUE_BIT); - - value >>= 7; - } -} - -DataBuffer& operator>>(DataBuffer& in, VarInt& var) { - var.m_Value = 0; - unsigned int position = 0; - std::uint8_t currentByte; - - while (true) { - in.ReadSome(¤tByte, 1); - var.m_Value |= static_cast(currentByte & SEGMENT_BITS) << position; - - if ((currentByte & CONTINUE_BIT) == 0) - break; - - position += 7; - - if (position >= 8 * sizeof(var.m_Value)) - throw std::runtime_error("VarInt is too big"); - } - - return in; -} - -} // namespace td diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp new file mode 100644 index 0000000..035bb7b --- /dev/null +++ b/src/td/game/World.cpp @@ -0,0 +1,59 @@ +#include + +namespace td { +namespace game { + +World::World() : m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}} { + +} + +const Color* World::GetTileColor(TilePtr tile) const { + switch (tile->GetType()) { + case TileType::Tower: { + TowerTile* towerTile = dynamic_cast(tile.get()); + return &m_TowerPlacePalette[towerTile->color_palette_ref]; + } + case TileType::Walk: { + return &m_WalkablePalette; + } + case TileType::Decoration: { + DecorationTile* towerTile = dynamic_cast(tile.get()); + return &m_DecorationPalette[towerTile->color_palette_ref]; + break; + } + default: { + return nullptr; + } + } + return nullptr; +} + +bool World::LoadMap(const protocol::pdata::WorldHeader& a_WorldHeader) { + m_TowerPlacePalette = a_WorldHeader.m_TowerPlacePalette; + m_WalkablePalette = a_WorldHeader.m_WalkablePalette; + m_DecorationPalette = a_WorldHeader.m_DecorationPalette; + m_Background = a_WorldHeader.m_Background; + + GetRedTeam().GetSpawn() = a_WorldHeader.m_RedSpawn; + GetBlueTeam().GetSpawn() = a_WorldHeader.m_BlueSpawn; + + m_SpawnColorPalette = a_WorldHeader.m_SpawnColorPalette; + + GetRedTeam().GetCastle() = a_WorldHeader.m_RedCastle; + GetRedTeam().GetCastle().SetTeam(&GetRedTeam()); + + GetBlueTeam().GetCastle() = a_WorldHeader.m_BlueCastle; + GetBlueTeam().GetCastle().SetTeam(&GetBlueTeam()); + + m_TilePalette = a_WorldHeader.m_TilePalette; + + return true; +} + +bool World::LoadMap(const protocol::pdata::WorldData& a_WorldData) { + m_Chunks = a_WorldData.m_Chunks; + return true; +} + +} // namespace game +} // namespace td diff --git a/src/td/input/Display.cpp b/src/td/input/Display.cpp new file mode 100644 index 0000000..ec3a2cc --- /dev/null +++ b/src/td/input/Display.cpp @@ -0,0 +1,156 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace td { + +Display::Display(int a_Width, int a_Height, const std::string& a_Title) : + m_LastWidth(0), m_LastHeight(0), m_AspectRatio(1), m_ShouldClose(false) { + + m_Window = SDL_CreateWindow(a_Title.c_str(), a_Width, a_Height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + + m_LastWidth = a_Width; + m_LastHeight = a_Height; + m_AspectRatio = (float)m_LastWidth / m_LastHeight; + + // Prepare and create context +#ifdef __ANDROID__ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#endif + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + + m_GLContext = SDL_GL_CreateContext(m_Window); + + if (!m_GLContext) { + utils::LOGE(utils::Format("Could not create context! SDL error: %s", SDL_GetError())); + } + + int major, minor, mask; + int r, g, b, a, depth; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &mask); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); + + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r); + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g); + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b); + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &a); + + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth); + + const char* mask_desc; + + if (mask & SDL_GL_CONTEXT_PROFILE_CORE) { + mask_desc = "core"; + } else if (mask & SDL_GL_CONTEXT_PROFILE_COMPATIBILITY) { + mask_desc = "compatibility"; + } else if (mask & SDL_GL_CONTEXT_PROFILE_ES) { + mask_desc = "es"; + } else { + mask_desc = "?"; + } + + utils::LOG(utils::Format( + "GL Context : %i.%i %s, Color : R:%i G:%i B:%i A:%i, Depth bits : %i", major, minor, mask_desc, r, g, b, a, depth)); + + SDL_GL_MakeCurrent(m_Window, m_GLContext); + + GLenum error = glewInit(); + if (error) { + utils::LOGE(utils::Format("Error initializing glew : %s", glewGetErrorString(error))); + } + + // WindowResizeEvent(WindowWidth, WindowHeight); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + // ImGui::StyleColorsLight(); + + // Setup scaling + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this + // requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave + // both here for documentation purpose) + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForOpenGL(m_Window, m_GLContext); + ImGui_ImplOpenGL3_Init("#version 330"); +} + + +void Display::PollEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_QUIT: + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { + m_ShouldClose = true; + } + + case SDL_EVENT_WINDOW_RESIZED: { + m_LastWidth = event.window.data1; + m_LastHeight = event.window.data2; + m_AspectRatio = (float)m_LastWidth / m_LastHeight; + } + + default: + break; + } + ImGui_ImplSDL3_ProcessEvent(&event); + } + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); +} + +void Display::Update() { + ImGui::Render(); + ImGuiIO& io = ImGui::GetIO(); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + // glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + SDL_GL_SwapWindow(m_Window); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + + +Display::~Display() { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + SDL_GL_DestroyContext(m_GLContext); + SDL_DestroyWindow(m_Window); + SDL_Quit(); +} + +} // namespace td diff --git a/src/td/render/Camera.cpp b/src/td/render/Camera.cpp new file mode 100644 index 0000000..e1bfe19 --- /dev/null +++ b/src/td/render/Camera.cpp @@ -0,0 +1,26 @@ +#include + +#include + +namespace td { +namespace render { + +void Camera::UpdatePerspective(float a_AspectRatio) { + m_ProjectionMatrix = maths::Perspective(80.0f / 180.0f * PI, a_AspectRatio, 0.1f, 160.0f); + m_InvProjectionMatrix = maths::Inverse(m_ProjectionMatrix); +} + +void Camera::SetCamPos(const Vec3f& a_NewPos) { + Vec3f front = { + std::cos(m_Yaw) * std::cos(m_Pitch), + std::sin(m_Pitch), + std::sin(m_Yaw) * std::cos(m_Pitch) + }; + + m_CamPos = a_NewPos; + m_ViewMatrix = maths::Look(m_CamPos, front, { 0, 1, 0 }); + m_InvViewMatrix = maths::Transpose(maths::Inverse(m_ViewMatrix)); // why transpose ? I don't know +} + +} // namespace render +} // namespace td diff --git a/src/td/render/Renderer.cpp b/src/td/render/Renderer.cpp new file mode 100644 index 0000000..cf811eb --- /dev/null +++ b/src/td/render/Renderer.cpp @@ -0,0 +1,26 @@ +#include + +#include + +namespace td { +namespace render { + +void Renderer::Render(const GL::VertexArray& a_Vao) { + a_Vao.Bind(); + glDrawArrays(GL_TRIANGLES, 0, a_Vao.GetVertexCount()); + // glDrawElements(GL_TRIANGLES, a_Vao.GetVertexCount(), GL_UNSIGNED_INT, nullptr); + a_Vao.Unbind(); +} + +RenderPipeline::RenderPipeline() { + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_LESS); + glFrontFace(GL_CCW); +} + +} // namespace render +} // namespace td diff --git a/src/td/render/loader/GLLoader.cpp b/src/td/render/loader/GLLoader.cpp new file mode 100644 index 0000000..48b388d --- /dev/null +++ b/src/td/render/loader/GLLoader.cpp @@ -0,0 +1,99 @@ +#include + +#include + +namespace td { +namespace GL { + +VertexArray::~VertexArray() { + if (m_ID != 0) + glDeleteVertexArrays(1, &m_ID); +} + +VertexArray::VertexArray(ElementBuffer&& indicies) : m_ElementBuffer(std::move(indicies)) { + glGenVertexArrays(1, &m_ID); + Bind(); + BindElementArrayBuffer(); + // Unbind(); +} + +void VertexArray::Bind() const { + glBindVertexArray(m_ID); +} + +void VertexArray::Unbind() const { + glBindVertexArray(0); +} + +void VertexArray::BindVertexBuffer(VertexBuffer&& VertexBuffer) { + VertexBuffer.Bind(); + VertexBuffer.BindVertexAttribs(); + m_VertexBuffers.push_back(std::move(VertexBuffer)); +} + +void VertexArray::BindElementArrayBuffer() { + m_ElementBuffer.Bind(); +} + +VertexBuffer::~VertexBuffer() { + if (m_ID != 0) + glDeleteBuffers(1, &m_ID); +} + +VertexBuffer::VertexBuffer(const std::vector& data, unsigned int stride) : m_DataStride(stride) { + glGenBuffers(1, &m_ID); + Bind(); + glBufferData(GL_ARRAY_BUFFER, static_cast(data.size() * sizeof(float)), nullptr, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, static_cast(data.size() * sizeof(float)), data.data()); + Unbind(); +} + +void VertexBuffer::Bind() const { + glBindBuffer(GL_ARRAY_BUFFER, m_ID); +} + +void VertexBuffer::Unbind() const { + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void VertexBuffer::AddVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset) { + VertexAttribPointer pointer { + .m_Index = index, + .m_Size = coordinateSize, + .m_Offset = offset + }; + m_VertexAttribs.push_back(pointer); +} + +void VertexBuffer::BindVertexAttribs() const { + for (const VertexAttribPointer& pointer : m_VertexAttribs) { + glVertexAttribPointer(pointer.m_Index, static_cast(pointer.m_Size), GL_FLOAT, false, m_DataStride * sizeof(float), + reinterpret_cast(static_cast(pointer.m_Offset))); + glEnableVertexAttribArray(pointer.m_Index); + } +} + +ElementBuffer::ElementBuffer(const std::vector& indicies) { + m_TriangleCount = indicies.size(); + glGenBuffers(1, &m_ID); + Bind(); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast(indicies.size() * sizeof(unsigned int)), nullptr, GL_STATIC_DRAW); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast(indicies.size() * sizeof(unsigned int)), indicies.data()); + Unbind(); +} + +ElementBuffer::~ElementBuffer() { + if (m_ID != 0) + glDeleteBuffers(1, &m_ID); +} + +void ElementBuffer::Bind() const { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ID); +} + +void ElementBuffer::Unbind() const { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +} // namespace GL +} // namespace td \ No newline at end of file diff --git a/src/td/render/loader/WorldLoader.cpp b/src/td/render/loader/WorldLoader.cpp new file mode 100644 index 0000000..9854c4c --- /dev/null +++ b/src/td/render/loader/WorldLoader.cpp @@ -0,0 +1,218 @@ +#include + +#include +#include + +#include + +namespace td { +namespace render { + +namespace WorldLoader { + +const static int POSITION_VERTEX_SIZE = 3; +const static int TEXTURE_VERTEX_SIZE = 2; + +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; + + std::int32_t chunkX = coords.x * td::game::Chunk::ChunkWidth; + std::int32_t chunkY = coords.y * td::game::Chunk::ChunkHeight; + + for (int tileY = 0; tileY < td::game::Chunk::ChunkHeight; tileY++) { + for (int tileX = 0; tileX < td::game::Chunk::ChunkWidth; tileX++) { + int tileNumber = tileY * td::game::Chunk::ChunkWidth + tileX; + td::game::TileIndex tileIndex = chunk->GetTileIndex(tileNumber); + td::game::TilePtr tile = world->GetTilePtr(tileIndex); + + if (tile == nullptr) + continue; + + positions.insert( + positions.end(), {static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY + 1), + + static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY + 1), + static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY + 1)}); + + const td::Color* tileColor = world->GetTileColor(tile); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= tileColor->r << 24; + color |= tileColor->g << 16; + color |= tileColor->b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + } + } + + for (int spawnColor = 0; spawnColor < 2; spawnColor++) { + const game::Spawn& spawn = world->GetTeam(TeamColor(spawnColor)).GetSpawn(); + float fromX = spawn.GetTopLeft().GetX(), toX = spawn.GetBottomRight().GetX(); + float fromY = spawn.GetTopLeft().GetY(), toY = spawn.GetBottomRight().GetY(); + + positions.insert(positions.end(), {fromX, 0, fromY, fromX, 0, toY, toX, 0, fromY, fromX, 0, toY, toX, 0, toY, toX, 0, fromY}); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= world->GetSpawnColor(TeamColor(spawnColor)).r << 24; + color |= world->GetSpawnColor(TeamColor(spawnColor)).g << 16; + color |= world->GetSpawnColor(TeamColor(spawnColor)).b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + + for (int castleColor = 0; castleColor < 2; castleColor++) { + const game::TeamCastle& castle = world->GetTeam(TeamColor(castleColor)).GetCastle(); + float fromX = castle.GetTopLeft().GetX(), toX = castle.GetBottomRight().GetX(); + float fromY = castle.GetTopLeft().GetY(), toY = castle.GetBottomRight().GetY(); + + positions.insert(positions.end(), {fromX, 0, fromY, fromX, 0, toY, toX, 0, fromY, fromX, 0, toY, toX, 0, toY, toX, 0, fromY}); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= world->GetSpawnColor(TeamColor(castleColor)).r << 24; + color |= world->GetSpawnColor(TeamColor(castleColor)).g << 16; + color |= world->GetSpawnColor(TeamColor(castleColor)).b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + + GL::VertexBuffer positionVBO(positions, POSITION_VERTEX_SIZE); + positionVBO.AddVertexAttribPointer(0, POSITION_VERTEX_SIZE, 0); + GL::VertexBuffer colorVBO(colors, 1); + colorVBO.AddVertexAttribPointer(1, 1, 0); + + std::vector indexes(positions.size() / 3, 0); + for (size_t i = 0; i < indexes.size(); i++) { + indexes[i] = i + 1; + } + + GL::ElementBuffer indexVBO(indexes); + + GL::VertexArray worldVao(std::move(indexVBO)); // each pos = 3 vertecies + worldVao.Bind(); + worldVao.BindVertexBuffer(std::move(positionVBO)); + worldVao.BindVertexBuffer(std::move(colorVBO)); + worldVao.Unbind(); + return worldVao; +} + +GL::VertexArray LoadTileSelectModel() { + std::vector positions = { + -0.5f, + -0.5f, + -1.0f, + + 0.5f, + -0.5f, + -1.0f, + + 0.0f, + 0.5f, + -1.0f, + + 1, + .01, + 1, + + 0, + .01, + 1, + + 0, + 1, + 1, + }; + + int color = 255 << 24 | 255 << 16 | 255 << 8 | 150; + float colorFloat; + + memcpy(reinterpret_cast(&colorFloat), &color, sizeof(float)); + + std::vector colors(6, colorFloat); + + GL::VertexBuffer positionVBO(positions, POSITION_VERTEX_SIZE); + positionVBO.AddVertexAttribPointer(0, POSITION_VERTEX_SIZE, 0); + GL::VertexBuffer colorVBO(colors, 1); + colorVBO.AddVertexAttribPointer(1, 1, 0); + + std::vector indexes(positions.size() / 3, 0); + for (size_t i = 0; i < indexes.size(); i++) { + indexes[i] = i + 1; + } + GL::ElementBuffer indexVBO(indexes); + + GL::VertexArray tileSelectVao(std::move(indexVBO)); + tileSelectVao.Bind(); + tileSelectVao.BindVertexBuffer(std::move(positionVBO)); + tileSelectVao.BindVertexBuffer(std::move(colorVBO)); + tileSelectVao.Unbind(); + + return tileSelectVao; +} + +RenderData LoadTowerModel(game::TowerPtr tower) { + RenderData renderData; + + float towerX, towerDX; + float towerY, towerDY; + + if (tower->GetSize() == game::TowerSize::Little) { + towerX = tower->GetCenterX() - 1.5f; + towerDX = tower->GetCenterX() + 1.5f; + + towerY = tower->GetCenterY() - 1.5f; + towerDY = tower->GetCenterY() + 1.5f; + } else { + towerX = tower->GetCenterX() - 2.5f; + towerDX = tower->GetCenterX() + 2.5f; + + towerY = tower->GetCenterY() - 2.5f; + towerDY = tower->GetCenterY() + 2.5f; + } + std::vector positions = {towerDX, 0.001, towerY, towerX, 0.001, towerY, towerX, 0.001, towerDY, towerDX, 0.001, towerY, + towerX, 0.001, towerDY, towerDX, 0.001, towerDY}; + + renderData.positions = positions; + + std::uint8_t towerType = static_cast(tower->GetType()); + std::uint8_t r = 10 * towerType + 40, g = 5 * towerType + 30, b = 10 * towerType + 20; + + float colorFloat; + int color = r << 24 | g << 16 | b << 8 | 255; + memcpy(&colorFloat, &color, sizeof(int)); + + std::vector colors(6, colorFloat); + renderData.colors = colors; + + return renderData; +} + + +} // namespace WorldLoader + + +} // namespace render +} // namespace td diff --git a/src/td/render/renderer/WorldRenderer.cpp b/src/td/render/renderer/WorldRenderer.cpp new file mode 100644 index 0000000..998c02a --- /dev/null +++ b/src/td/render/renderer/WorldRenderer.cpp @@ -0,0 +1,26 @@ +#include + +#include + +#include + +namespace td { +namespace render { + +WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World){ + m_WorldVao = std::make_unique(std::move(WorldLoader::LoadWorldModel(&a_World))); +} + +WorldRenderer::~WorldRenderer() {} + +void WorldRenderer::Render() { + m_Shader.Start(); + m_Shader.SetProjectionMatrix(m_Camera.GetProjectionMatrix()); + m_Shader.SetViewMatrix(m_Camera.GetViewMatrix()); + Renderer::Render(*m_WorldVao); + ImGui::ShowDemoWindow(); +} + + +} // namespace render +} // namespace td diff --git a/src/td/render/shader/EntityShader.cpp b/src/td/render/shader/EntityShader.cpp new file mode 100644 index 0000000..4b4dd06 --- /dev/null +++ b/src/td/render/shader/EntityShader.cpp @@ -0,0 +1,123 @@ +#include + +namespace td { +namespace shader { + +// TODO: update ES shaders + +#ifdef __ANDROID__ +static const char vertexSource[] = +R"(#version 300 es + +precision mediump float; + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; +uniform vec3 modelPosition; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position + modelPosition, 1.0); +} +)"; + +static const char fragmentSource[] = +R"(#version 300 es + +precision mediump float; + +flat in int pass_color; + +out vec4 out_color; + +uniform vec3 ColorEffect; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + vec3 intermediate_color = vec3(r, g, b) * ColorEffect; + out_color = vec4(intermediate_color, a); + +} +)"; +#else +static const char vertexSource[] = R"( +#version 330 + +layout(location = 0) in vec3 position; +layout(location = 1) in vec2 textureCoords; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; +uniform vec3 modelPosition; + +out vec2 pass_textureCoords; + +void main(void){ + pass_textureCoords = textureCoords; + gl_Position = projectionMatrix * viewMatrix * vec4(position + modelPosition, 1.0); +} +)"; + +static const char fragmentSource[] = R"( +#version 330 + +in vec2 pass_textureCoords; + +out vec4 out_color; + +uniform vec3 ColorEffect; +uniform sampler2D textureSampler; + +void main(void){ + + vec4 color = vec4(ColorEffect, 1.0) * texture(textureSampler, pass_textureCoords); + + if (color.a <= 0.1) + discard; + + out_color = color; + +} +)"; +#endif + +EntityShader::EntityShader() : ShaderProgram() {} + +void EntityShader::LoadShader() { + ShaderProgram::LoadProgram(vertexSource, fragmentSource); +} + +void EntityShader::GetAllUniformLocation() { + m_LocationColorEffect = static_cast(GetUniformLocation("ColorEffect")); + m_LocationViewMatrix = static_cast(GetUniformLocation("viewMatrix")); + m_LocationPosition = static_cast(GetUniformLocation("modelPosition")); + m_LocationProjectionMatrix = static_cast(GetUniformLocation("projectionMatrix")); +} + +void EntityShader::SetColorEffect(const Vec3f& color) { + LoadVector(m_LocationColorEffect, color); +} + +void EntityShader::SetProjectionMatrix(const Mat4f& proj) const { + LoadMat4(m_LocationProjectionMatrix, proj); +} + +void EntityShader::SetViewMatrix(const Mat4f& view) const { + LoadMat4(m_LocationViewMatrix, view); +} + +void EntityShader::SetModelPos(const Vec3f& pos) const { + LoadVector(m_LocationPosition, pos); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/src/td/render/shader/ShaderProgram.cpp b/src/td/render/shader/ShaderProgram.cpp new file mode 100755 index 0000000..5d89462 --- /dev/null +++ b/src/td/render/shader/ShaderProgram.cpp @@ -0,0 +1,145 @@ +/* + * ShaderProgram.cpp + * + * Created on: 31 janv. 2020 + * Author: simon + */ + +#include +#include +#include + +#include +#include +#include +#include + +namespace td { +namespace shader { + +ShaderProgram::ShaderProgram() : + m_ProgramID(0), m_VertexShaderID(0), m_FragmentShaderID(0) { +} + +ShaderProgram::~ShaderProgram() { + CleanUp(); +} + +void ShaderProgram::Start() const { + glUseProgram(m_ProgramID); +} + +void ShaderProgram::Stop() const { + glUseProgram(0); +} + +int ShaderProgram::GetUniformLocation(const std::string& uniformName) const { + const int location = glGetUniformLocation(m_ProgramID, uniformName.c_str()); + if (location == -1) { + utils::LOGD(utils::Format("Warning ! Uniform variable %s not found !", uniformName.c_str())); + } + return location; +} + +void ShaderProgram::LoadFloat(unsigned int location, float value) const { + glUniform1f(static_cast(location), value); +} + +void ShaderProgram::LoadInt(unsigned int location, int value) const { + glUniform1i(static_cast(location), value); +} + +void ShaderProgram::LoadVector(unsigned int location, + const Vec2f& vector) const { + glUniform2f(static_cast(location), vector.x, vector.y); +} + +void ShaderProgram::LoadVector(unsigned int location, + const Vec3f& vector) const { + glUniform3f(static_cast(location), vector.x, vector.y, vector.z); +} + +void ShaderProgram::LoadBoolean(unsigned int location, bool value) const { + glUniform1i(static_cast(location), value); +} + +void ShaderProgram::LoadMat4(unsigned int location, const Mat4f& mat) const { + glUniformMatrix4fv(static_cast(location), 1, false, reinterpret_cast(&mat)); +} + +void ShaderProgram::CleanUp() const { + Stop(); + glDetachShader(m_ProgramID, m_VertexShaderID); + glDetachShader(m_ProgramID, m_FragmentShaderID); + glDeleteShader(m_VertexShaderID); + glDeleteShader(m_FragmentShaderID); + glDeleteProgram(m_ProgramID); +} + +void ShaderProgram::LoadProgramFile(const std::string& vertexFile, + const std::string& fragmentFile) { + m_VertexShaderID = static_cast(LoadShaderFromFile(vertexFile, GL_VERTEX_SHADER)); + m_FragmentShaderID = static_cast(LoadShaderFromFile(fragmentFile, GL_FRAGMENT_SHADER)); + m_ProgramID = glCreateProgram(); + glAttachShader(m_ProgramID, m_VertexShaderID); + glAttachShader(m_ProgramID, m_FragmentShaderID); + glLinkProgram(m_ProgramID); + glValidateProgram(m_ProgramID); + GetAllUniformLocation(); +} + +void ShaderProgram::LoadProgram(const std::string& vertexSource, + const std::string& fragmentSource) { + m_VertexShaderID = static_cast(LoadShader(vertexSource, GL_VERTEX_SHADER)); + m_FragmentShaderID = static_cast(LoadShader(fragmentSource, GL_FRAGMENT_SHADER)); + m_ProgramID = glCreateProgram(); + glAttachShader(m_ProgramID, m_VertexShaderID); + glAttachShader(m_ProgramID, m_FragmentShaderID); + glLinkProgram(m_ProgramID); + glValidateProgram(m_ProgramID); + GetAllUniformLocation(); +} + +unsigned int ShaderProgram::LoadShader(const std::string& source, GLenum type) { + unsigned int shaderID = glCreateShader(type); + + const char* c_str = source.c_str(); + int* null = 0; + glShaderSource(shaderID, 1, &c_str, null); // @suppress("Function cannot be resolved") + glCompileShader(shaderID); + GLint compilesuccessful; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compilesuccessful); + if (compilesuccessful == false) { + GLsizei size; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &size); + std::vector shaderError(static_cast(size)); + glGetShaderInfoLog(shaderID, size, &size, shaderError.data()); + + utils::LOGE("Could not compile shader !"); + + utils::LOGE(shaderError.data()); + + utils::LOGD(utils::Format("\nShader source : \n" + "------------------------------------------------------------------------------------------------------------------------------------\n" + "%s\n" + "------------------------------------------------------------------------------------------------------------------------------------\n" + , source.c_str())); + } + return shaderID; +} + +unsigned int ShaderProgram::LoadShaderFromFile(const std::string& file, GLenum type) { + std::stringstream stream; + std::ifstream fileStream(file); + + if (fileStream) { + stream << fileStream.rdbuf(); + } else { + return 0; + } + + return LoadShader(stream.str(), type); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/src/td/render/shader/WorldShader.cpp b/src/td/render/shader/WorldShader.cpp new file mode 100644 index 0000000..469882d --- /dev/null +++ b/src/td/render/shader/WorldShader.cpp @@ -0,0 +1,102 @@ +#include + +namespace td { +namespace shader { + +// TODO: GLES Shaders + +#ifdef __ANDROID__ +static const char vertexSource[] = +R"(#version 300 es + +precision mediump float; + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); +} +)"; + +static const char fragmentSource[] = +R"(#version 300 es + +precision mediump float; + +flat in int pass_color; + +out vec4 out_color; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + out_color = vec4(r, g, b, a); + +} +)"; +#else +static const char vertexSource[] = R"( +#version 330 + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); +} +)"; + +static const char fragmentSource[] = R"( +#version 330 + +flat in int pass_color; + +out vec4 out_color; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + out_color = vec4(r, g, b, a); + +} +)"; +#endif + +WorldShader::WorldShader() : ShaderProgram() { + ShaderProgram::LoadProgram(vertexSource, fragmentSource); +} + +void WorldShader::GetAllUniformLocation() { + m_LocationProjection = static_cast(GetUniformLocation("projectionMatrix")); + m_LocationView = static_cast(GetUniformLocation("viewMatrix")); +} + +void WorldShader::SetProjectionMatrix(const Mat4f& proj) const { + LoadMat4(m_LocationProjection, proj); +} + +void WorldShader::SetViewMatrix(const Mat4f& view) const { + LoadMat4(m_LocationView, view); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/test/tdmap.tdmap2 b/test/tdmap.tdmap2 new file mode 100644 index 0000000000000000000000000000000000000000..798a860df68b2a187e39b8af964fb7967bd5402e GIT binary patch literal 595 zcmV-Z0<8Uhc$_<9%r)Od^TMYxnGll+>=2 zKR*_d_LoGA>flNlL~thqmpK?qTIK#Lm%Fnecqi_s&brQl=gxcZ6?BrF*n)q+PqNAn zIT4OAfEY8}I~%Uu3-b(jI|sX+DbMIs`~TZox|Yjr%_Umo^3QA8_)}8Xik=mp4fK5y zN%{{q4@=rF=P~|V{cQ0-vMDFvJn(%6+raA;9hUY=nUo)mdV`p+G4o4lcu(vF>?ag> zoVt8{6aKQaXp_C$Q2qu^r~1UEmuc|F%rs8B-0<68!*`#<{~Z&aeDHcjenN1U0~1*L zID1UpvG4D>@Qn8|i;SjvMV^EI7i*sp6%_h2zlil&ycc{&c~+nGYo8YKh<;0^g>3$= zn7YMj@6Q}!ozELb@N0a=^tQnt1FQZOd=Ye$U?+@&Zjv|7TKvxFgTo}s6ALa*8{P(E zzfCL$y=L#PS<2wgar*n`xpL;V>AT^SIvI hy7D Date: Wed, 16 Jul 2025 12:54:50 +0200 Subject: [PATCH 05/39] camera notify --- .gitignore | 4 ++- imgui.ini | 16 ---------- include/td/misc/ObjectNotifier.h | 36 ++++++++++++++++++++++ include/td/render/Camera.h | 11 ++++++- include/td/render/renderer/WorldRenderer.h | 5 ++- src/main.cpp | 5 +-- src/td/render/Camera.cpp | 2 ++ src/td/render/renderer/WorldRenderer.cpp | 14 +++++++-- 8 files changed, 69 insertions(+), 24 deletions(-) delete mode 100644 imgui.ini create mode 100644 include/td/misc/ObjectNotifier.h diff --git a/.gitignore b/.gitignore index fb11540..a08e139 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ build/ .DS_Store -.vscode \ No newline at end of file +.vscode + +imgui.ini \ No newline at end of file diff --git a/imgui.ini b/imgui.ini deleted file mode 100644 index c9168d1..0000000 --- a/imgui.ini +++ /dev/null @@ -1,16 +0,0 @@ -[Window][Debug##Default] -Pos=20,60 -Size=400,400 - -[Window][Dear ImGui Demo] -Pos=1116,220 -Size=927,695 - -[Window][Dear ImGui Debug Log] -Pos=60,60 -Size=670,354 - -[Window][Example: Console] -Pos=958,210 -Size=520,600 - diff --git a/include/td/misc/ObjectNotifier.h b/include/td/misc/ObjectNotifier.h new file mode 100644 index 0000000..83e076d --- /dev/null +++ b/include/td/misc/ObjectNotifier.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace utils { + +template +class ObjectNotifier { +protected: + std::vector m_Listeners; + +public: + void BindListener(Listener* listener) { + m_Listeners.push_back(listener); + } + + void UnbindListener(Listener* listener) { + auto iter = std::find(m_Listeners.begin(), m_Listeners.end(), listener); + + if (iter == m_Listeners.end()) return; + + m_Listeners.erase(iter); + } + + template + void NotifyListeners(Func function, Args... args) { + for (Listener* listener : m_Listeners) + std::bind(function, listener, args...)(); + } +}; + +} // namespace utils +} // namespace td diff --git a/include/td/render/Camera.h b/include/td/render/Camera.h index 1f621a1..6cd1907 100644 --- a/include/td/render/Camera.h +++ b/include/td/render/Camera.h @@ -1,11 +1,20 @@ #pragma once #include +#include namespace td { namespace render { -class Camera { +class ICameraListener { + public: + virtual void OnPerspectiveChange() {} + virtual void OnViewChange() {} +}; + +using CameraNotifier = utils::ObjectNotifier; + +class Camera : public CameraNotifier { private: Mat4f m_ViewMatrix; Mat4f m_ProjectionMatrix; diff --git a/include/td/render/renderer/WorldRenderer.h b/include/td/render/renderer/WorldRenderer.h index 61fd3c2..f57662d 100644 --- a/include/td/render/renderer/WorldRenderer.h +++ b/include/td/render/renderer/WorldRenderer.h @@ -8,7 +8,7 @@ namespace td { namespace render { -class WorldRenderer : public Renderer { +class WorldRenderer : public Renderer, public ICameraListener { private: const game::World& m_World; shader::WorldShader m_Shader; @@ -19,6 +19,9 @@ class WorldRenderer : public Renderer { virtual ~WorldRenderer(); virtual void Render() override; + + virtual void OnPerspectiveChange() override; + virtual void OnViewChange() override; }; } // namespace render diff --git a/src/main.cpp b/src/main.cpp index 5dd40b2..ac43d5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -194,12 +194,13 @@ int main(int argc, char** argv) { td::Display display(1920, 1080, "Tower-Defense 2"); td::render::Camera cam; - cam.SetCamPos({77, 25, 13}); - cam.UpdatePerspective(display.GetAspectRatio()); td::render::RenderPipeline renderer; renderer.AddRenderer(std::make_unique(cam, w)); + cam.SetCamPos({77, 25, 13}); + cam.UpdatePerspective(display.GetAspectRatio()); + while (!display.IsCloseRequested()) { display.PollEvents(); renderer.Render(); diff --git a/src/td/render/Camera.cpp b/src/td/render/Camera.cpp index e1bfe19..0d2a567 100644 --- a/src/td/render/Camera.cpp +++ b/src/td/render/Camera.cpp @@ -8,6 +8,7 @@ namespace render { void Camera::UpdatePerspective(float a_AspectRatio) { m_ProjectionMatrix = maths::Perspective(80.0f / 180.0f * PI, a_AspectRatio, 0.1f, 160.0f); m_InvProjectionMatrix = maths::Inverse(m_ProjectionMatrix); + NotifyListeners(&ICameraListener::OnPerspectiveChange); } void Camera::SetCamPos(const Vec3f& a_NewPos) { @@ -20,6 +21,7 @@ void Camera::SetCamPos(const Vec3f& a_NewPos) { m_CamPos = a_NewPos; m_ViewMatrix = maths::Look(m_CamPos, front, { 0, 1, 0 }); m_InvViewMatrix = maths::Transpose(maths::Inverse(m_ViewMatrix)); // why transpose ? I don't know + NotifyListeners(&ICameraListener::OnViewChange); } } // namespace render diff --git a/src/td/render/renderer/WorldRenderer.cpp b/src/td/render/renderer/WorldRenderer.cpp index 998c02a..f9b55f7 100644 --- a/src/td/render/renderer/WorldRenderer.cpp +++ b/src/td/render/renderer/WorldRenderer.cpp @@ -7,20 +7,28 @@ namespace td { namespace render { -WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World){ +WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { m_WorldVao = std::make_unique(std::move(WorldLoader::LoadWorldModel(&a_World))); + a_Camera.BindListener(this); } WorldRenderer::~WorldRenderer() {} void WorldRenderer::Render() { m_Shader.Start(); - m_Shader.SetProjectionMatrix(m_Camera.GetProjectionMatrix()); - m_Shader.SetViewMatrix(m_Camera.GetViewMatrix()); Renderer::Render(*m_WorldVao); ImGui::ShowDemoWindow(); } +void WorldRenderer::OnPerspectiveChange() { + m_Shader.Start(); + m_Shader.SetProjectionMatrix(m_Camera.GetProjectionMatrix()); +} + +void WorldRenderer::OnViewChange() { + m_Shader.Start(); + m_Shader.SetViewMatrix(m_Camera.GetViewMatrix()); +} } // namespace render } // namespace td From 966745481161e37a571fc324b4b65595f531bfa4 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 16 Jul 2025 13:38:02 +0200 Subject: [PATCH 06/39] refactor rendering --- include/td/misc/ObjectNotifier.h | 36 ------------------- include/td/misc/Signal.h | 31 ++++++++++++++++ include/td/render/Camera.h | 15 +++----- include/td/render/Renderer.h | 29 +++++++++------ include/td/render/renderer/WorldRenderer.h | 8 ++--- .../td/render/shader/CameraShaderProgram.h | 23 ++++++++++++ include/td/render/shader/ShaderProgram.h | 9 +++-- include/td/render/shader/WorldShader.h | 11 ++---- src/main.cpp | 2 +- src/td/render/Camera.cpp | 4 +-- src/td/render/Renderer.cpp | 16 ++++++++- src/td/render/renderer/WorldRenderer.cpp | 15 ++------ src/td/render/shader/CameraShaderProgram.cpp | 21 +++++++++++ src/td/render/shader/WorldShader.cpp | 15 +------- 14 files changed, 130 insertions(+), 105 deletions(-) delete mode 100644 include/td/misc/ObjectNotifier.h create mode 100644 include/td/misc/Signal.h create mode 100644 include/td/render/shader/CameraShaderProgram.h create mode 100644 src/td/render/shader/CameraShaderProgram.cpp diff --git a/include/td/misc/ObjectNotifier.h b/include/td/misc/ObjectNotifier.h deleted file mode 100644 index 83e076d..0000000 --- a/include/td/misc/ObjectNotifier.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace td { -namespace utils { - -template -class ObjectNotifier { -protected: - std::vector m_Listeners; - -public: - void BindListener(Listener* listener) { - m_Listeners.push_back(listener); - } - - void UnbindListener(Listener* listener) { - auto iter = std::find(m_Listeners.begin(), m_Listeners.end(), listener); - - if (iter == m_Listeners.end()) return; - - m_Listeners.erase(iter); - } - - template - void NotifyListeners(Func function, Args... args) { - for (Listener* listener : m_Listeners) - std::bind(function, listener, args...)(); - } -}; - -} // namespace utils -} // namespace td diff --git a/include/td/misc/Signal.h b/include/td/misc/Signal.h new file mode 100644 index 0000000..b8dbf17 --- /dev/null +++ b/include/td/misc/Signal.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace utils { + +template +class Signal : private NonCopyable { + private: + using CallBack = std::function; + + std::vector m_Callbacks; + + public: + void Connect(CallBack&& a_Callback) { + m_Callbacks.push_back(std::move(a_Callback)); + } + + void operator()(Args... args) const { + for (const CallBack& callback : m_Callbacks) { + callback(args...); + } + } +}; + +} // namespace utils +} // namespace td diff --git a/include/td/render/Camera.h b/include/td/render/Camera.h index 6cd1907..a0d386b 100644 --- a/include/td/render/Camera.h +++ b/include/td/render/Camera.h @@ -1,20 +1,12 @@ #pragma once #include -#include +#include namespace td { namespace render { -class ICameraListener { - public: - virtual void OnPerspectiveChange() {} - virtual void OnViewChange() {} -}; - -using CameraNotifier = utils::ObjectNotifier; - -class Camera : public CameraNotifier { +class Camera { private: Mat4f m_ViewMatrix; Mat4f m_ProjectionMatrix; @@ -29,6 +21,9 @@ class Camera : public CameraNotifier { float m_Pitch = -PI / 2.0f + 0.0000001f; public: + utils::Signal<> OnPerspectiveChange; + utils::Signal<> OnViewChange; + const Mat4f& GetViewMatrix() const { return m_ViewMatrix; } diff --git a/include/td/render/Renderer.h b/include/td/render/Renderer.h index 407134c..7e2bca9 100644 --- a/include/td/render/Renderer.h +++ b/include/td/render/Renderer.h @@ -1,35 +1,42 @@ #pragma once +#include #include #include -#include +#include namespace td { namespace render { -class Renderer { - protected: - Camera& m_Camera; - +class BasicRenderer { public: - Renderer(Camera& a_Camera) : m_Camera(a_Camera) {} - virtual ~Renderer() {} - virtual void Render() = 0; void Render(const GL::VertexArray& a_Vao); }; +class Renderer : public BasicRenderer { + protected: + std::unique_ptr m_Shader; + Camera& m_Camera; + + public: + Renderer(std::unique_ptr&& a_Shader, Camera& a_Camera); + + virtual ~Renderer() {} +}; + class RenderPipeline { private: - std::vector> m_Renderers; + std::vector> m_Renderers; public: RenderPipeline(); ~RenderPipeline() = default; - void AddRenderer(std::unique_ptr&& a_Renderer) { - m_Renderers.push_back(std::move(a_Renderer)); + template + void AddRenderer(Args&&... args) { + m_Renderers.push_back(std::make_unique(args ...)); } void Clear() { diff --git a/include/td/render/renderer/WorldRenderer.h b/include/td/render/renderer/WorldRenderer.h index f57662d..095f6be 100644 --- a/include/td/render/renderer/WorldRenderer.h +++ b/include/td/render/renderer/WorldRenderer.h @@ -2,16 +2,15 @@ #include #include -#include #include +#include namespace td { namespace render { -class WorldRenderer : public Renderer, public ICameraListener { +class WorldRenderer : public Renderer { private: const game::World& m_World; - shader::WorldShader m_Shader; std::unique_ptr m_WorldVao; public: @@ -19,9 +18,6 @@ class WorldRenderer : public Renderer, public ICameraListener { virtual ~WorldRenderer(); virtual void Render() override; - - virtual void OnPerspectiveChange() override; - virtual void OnViewChange() override; }; } // namespace render diff --git a/include/td/render/shader/CameraShaderProgram.h b/include/td/render/shader/CameraShaderProgram.h new file mode 100644 index 0000000..66960ce --- /dev/null +++ b/include/td/render/shader/CameraShaderProgram.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace td { +namespace shader { + +class CameraShaderProgram : public ShaderProgram { + private: + unsigned int m_LocationProjection = 0, m_LocationView = 0; + + public: + CameraShaderProgram() {} + + void SetProjectionMatrix(const Mat4f& proj) const; + void SetViewMatrix(const Mat4f& view) const; + + virtual void GetAllUniformLocation(); +}; + + +} // namespace shader +} // namespace td diff --git a/include/td/render/shader/ShaderProgram.h b/include/td/render/shader/ShaderProgram.h index 23aef9d..66db83f 100755 --- a/include/td/render/shader/ShaderProgram.h +++ b/include/td/render/shader/ShaderProgram.h @@ -1,10 +1,15 @@ #pragma once +#include #include #include -#include namespace td { + +namespace render { +class Renderer; +} // namespace render + namespace shader { class ShaderProgram { @@ -15,10 +20,10 @@ class ShaderProgram { void Start() const; void Stop() const; + protected: void LoadProgramFile(const std::string& vertexFile, const std::string& fragmentFile); void LoadProgram(const std::string& vertexSource, const std::string& fragmentSource); - protected: virtual void GetAllUniformLocation() = 0; int GetUniformLocation(const std::string& uniformName) const; diff --git a/include/td/render/shader/WorldShader.h b/include/td/render/shader/WorldShader.h index f7d743e..8765708 100644 --- a/include/td/render/shader/WorldShader.h +++ b/include/td/render/shader/WorldShader.h @@ -1,20 +1,13 @@ #pragma once -#include +#include namespace td { namespace shader { -class WorldShader : public ShaderProgram { -private: - unsigned int m_LocationProjection = 0, m_LocationView = 0; -protected: - void GetAllUniformLocation(); +class WorldShader : public CameraShaderProgram { public: WorldShader(); - - void SetProjectionMatrix(const Mat4f& proj) const; - void SetViewMatrix(const Mat4f& view) const; }; } // namespace shader diff --git a/src/main.cpp b/src/main.cpp index ac43d5b..f0a6b0a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -196,7 +196,7 @@ int main(int argc, char** argv) { td::render::Camera cam; td::render::RenderPipeline renderer; - renderer.AddRenderer(std::make_unique(cam, w)); + renderer.AddRenderer(cam, w); cam.SetCamPos({77, 25, 13}); cam.UpdatePerspective(display.GetAspectRatio()); diff --git a/src/td/render/Camera.cpp b/src/td/render/Camera.cpp index 0d2a567..260a467 100644 --- a/src/td/render/Camera.cpp +++ b/src/td/render/Camera.cpp @@ -8,7 +8,7 @@ namespace render { void Camera::UpdatePerspective(float a_AspectRatio) { m_ProjectionMatrix = maths::Perspective(80.0f / 180.0f * PI, a_AspectRatio, 0.1f, 160.0f); m_InvProjectionMatrix = maths::Inverse(m_ProjectionMatrix); - NotifyListeners(&ICameraListener::OnPerspectiveChange); + OnPerspectiveChange(); } void Camera::SetCamPos(const Vec3f& a_NewPos) { @@ -21,7 +21,7 @@ void Camera::SetCamPos(const Vec3f& a_NewPos) { m_CamPos = a_NewPos; m_ViewMatrix = maths::Look(m_CamPos, front, { 0, 1, 0 }); m_InvViewMatrix = maths::Transpose(maths::Inverse(m_ViewMatrix)); // why transpose ? I don't know - NotifyListeners(&ICameraListener::OnViewChange); + OnViewChange(); } } // namespace render diff --git a/src/td/render/Renderer.cpp b/src/td/render/Renderer.cpp index cf811eb..9c03453 100644 --- a/src/td/render/Renderer.cpp +++ b/src/td/render/Renderer.cpp @@ -5,13 +5,27 @@ namespace td { namespace render { -void Renderer::Render(const GL::VertexArray& a_Vao) { +void BasicRenderer::Render(const GL::VertexArray& a_Vao) { a_Vao.Bind(); glDrawArrays(GL_TRIANGLES, 0, a_Vao.GetVertexCount()); // glDrawElements(GL_TRIANGLES, a_Vao.GetVertexCount(), GL_UNSIGNED_INT, nullptr); a_Vao.Unbind(); } +Renderer::Renderer(std::unique_ptr&& a_Shader, Camera& a_Camera) : + m_Shader(std::move(a_Shader)), m_Camera(a_Camera) { + a_Camera.OnPerspectiveChange.Connect([this]() { + m_Shader->Start(); + m_Shader->SetProjectionMatrix(m_Camera.GetProjectionMatrix()); + }); + + a_Camera.OnViewChange.Connect([this]() { + m_Shader->Start(); + m_Shader->SetViewMatrix(m_Camera.GetViewMatrix()); + }); +} + + RenderPipeline::RenderPipeline() { glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); diff --git a/src/td/render/renderer/WorldRenderer.cpp b/src/td/render/renderer/WorldRenderer.cpp index f9b55f7..c189111 100644 --- a/src/td/render/renderer/WorldRenderer.cpp +++ b/src/td/render/renderer/WorldRenderer.cpp @@ -7,28 +7,17 @@ namespace td { namespace render { -WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { +WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(std::make_unique(), a_Camera), m_World(a_World) { m_WorldVao = std::make_unique(std::move(WorldLoader::LoadWorldModel(&a_World))); - a_Camera.BindListener(this); } WorldRenderer::~WorldRenderer() {} void WorldRenderer::Render() { - m_Shader.Start(); + m_Shader->Start(); Renderer::Render(*m_WorldVao); ImGui::ShowDemoWindow(); } -void WorldRenderer::OnPerspectiveChange() { - m_Shader.Start(); - m_Shader.SetProjectionMatrix(m_Camera.GetProjectionMatrix()); -} - -void WorldRenderer::OnViewChange() { - m_Shader.Start(); - m_Shader.SetViewMatrix(m_Camera.GetViewMatrix()); -} - } // namespace render } // namespace td diff --git a/src/td/render/shader/CameraShaderProgram.cpp b/src/td/render/shader/CameraShaderProgram.cpp new file mode 100644 index 0000000..a54aeed --- /dev/null +++ b/src/td/render/shader/CameraShaderProgram.cpp @@ -0,0 +1,21 @@ +#include + +namespace td { +namespace shader { + +void CameraShaderProgram::SetProjectionMatrix(const Mat4f& proj) const { + LoadMat4(m_LocationProjection, proj); +} + +void CameraShaderProgram::SetViewMatrix(const Mat4f& view) const { + LoadMat4(m_LocationView, view); +} + +void CameraShaderProgram::GetAllUniformLocation() { + m_LocationProjection = static_cast(GetUniformLocation("projectionMatrix")); + m_LocationView = static_cast(GetUniformLocation("viewMatrix")); +} + + +} // namespace shader +} // namespace td diff --git a/src/td/render/shader/WorldShader.cpp b/src/td/render/shader/WorldShader.cpp index 469882d..01d9e64 100644 --- a/src/td/render/shader/WorldShader.cpp +++ b/src/td/render/shader/WorldShader.cpp @@ -81,22 +81,9 @@ void main(void){ )"; #endif -WorldShader::WorldShader() : ShaderProgram() { +WorldShader::WorldShader() : CameraShaderProgram() { ShaderProgram::LoadProgram(vertexSource, fragmentSource); } -void WorldShader::GetAllUniformLocation() { - m_LocationProjection = static_cast(GetUniformLocation("projectionMatrix")); - m_LocationView = static_cast(GetUniformLocation("viewMatrix")); -} - -void WorldShader::SetProjectionMatrix(const Mat4f& proj) const { - LoadMat4(m_LocationProjection, proj); -} - -void WorldShader::SetViewMatrix(const Mat4f& view) const { - LoadMat4(m_LocationView, view); -} - } // namespace shader } // namespace td \ No newline at end of file From db4df33861acf5b9eb96002ca15faca1bb7cce04 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 16 Jul 2025 13:39:58 +0200 Subject: [PATCH 07/39] format --- src/main.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f0a6b0a..e17c847 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -167,8 +167,8 @@ class WorldApply : public td::protocol::PacketHandler { } }; -int main(int argc, char** argv) { - sp::DataBuffer buffer; +td::game::World GetWorld() { +sp::DataBuffer buffer; buffer.ReadFile("test/tdmap.tdmap2"); sp::DataBuffer buffer1 = sp::zlib::Decompress(buffer, 84); @@ -191,6 +191,13 @@ int main(int argc, char** argv) { d.Dispatch(header); d.Dispatch(data); + return w; +} + +int main(int argc, char** argv) { + td::game::World w = GetWorld(); + + // init GL context td::Display display(1920, 1080, "Tower-Defense 2"); td::render::Camera cam; From 74848fbd78f13dee525f3a2d814fa018e6a27a4b Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 17 Jul 2025 21:55:52 +0200 Subject: [PATCH 08/39] other font scaling --- src/td/input/Display.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/td/input/Display.cpp b/src/td/input/Display.cpp index ec3a2cc..ff37c6e 100644 --- a/src/td/input/Display.cpp +++ b/src/td/input/Display.cpp @@ -95,11 +95,16 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) : // Setup scaling float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); - ImGuiStyle& style = ImGui::GetStyle(); - style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this - // requires resetting Style + calling this again) - style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave - // both here for documentation purpose) + // ImGuiStyle& style = ImGui::GetStyle(); + // style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this + // // requires resetting Style + calling this again) + // style.FontSizeBase = 13 * main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave + // // both here for documentation purpose) + + + ImFontConfig cfg; + cfg.SizePixels = 13 * main_scale * 2; + io.Fonts->AddFontDefault(&cfg); // Setup Platform/Renderer backends ImGui_ImplSDL3_InitForOpenGL(m_Window, m_GLContext); From 9a3356eb168e2f5675300506aa8bf51ceafd1599 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 17 Jul 2025 21:57:06 +0200 Subject: [PATCH 09/39] renderer template shader --- include/td/render/Renderer.h | 27 ++++++++++++++++++---- include/td/render/renderer/WorldRenderer.h | 2 +- include/td/render/shader/ShaderProgram.h | 5 ---- src/td/render/Renderer.cpp | 14 ----------- src/td/render/renderer/WorldRenderer.cpp | 2 +- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/include/td/render/Renderer.h b/include/td/render/Renderer.h index 7e2bca9..fa25234 100644 --- a/include/td/render/Renderer.h +++ b/include/td/render/Renderer.h @@ -15,14 +15,14 @@ class BasicRenderer { void Render(const GL::VertexArray& a_Vao); }; +template class Renderer : public BasicRenderer { protected: - std::unique_ptr m_Shader; + std::unique_ptr m_Shader; Camera& m_Camera; public: - Renderer(std::unique_ptr&& a_Shader, Camera& a_Camera); - + Renderer(Camera& a_Camera); virtual ~Renderer() {} }; @@ -34,9 +34,9 @@ class RenderPipeline { RenderPipeline(); ~RenderPipeline() = default; - template + template void AddRenderer(Args&&... args) { - m_Renderers.push_back(std::make_unique(args ...)); + m_Renderers.push_back(std::make_unique(args...)); } void Clear() { @@ -50,5 +50,22 @@ class RenderPipeline { } }; + + + + +template +Renderer::Renderer(Camera& a_Camera) : m_Shader(std::make_unique()), m_Camera(a_Camera) { + a_Camera.OnPerspectiveChange.Connect([this]() { + m_Shader->Start(); + m_Shader->SetProjectionMatrix(m_Camera.GetProjectionMatrix()); + }); + + a_Camera.OnViewChange.Connect([this]() { + m_Shader->Start(); + m_Shader->SetViewMatrix(m_Camera.GetViewMatrix()); + }); +} + } // namespace render } // namespace td diff --git a/include/td/render/renderer/WorldRenderer.h b/include/td/render/renderer/WorldRenderer.h index 095f6be..93dfb8d 100644 --- a/include/td/render/renderer/WorldRenderer.h +++ b/include/td/render/renderer/WorldRenderer.h @@ -8,7 +8,7 @@ namespace td { namespace render { -class WorldRenderer : public Renderer { +class WorldRenderer : public Renderer { private: const game::World& m_World; std::unique_ptr m_WorldVao; diff --git a/include/td/render/shader/ShaderProgram.h b/include/td/render/shader/ShaderProgram.h index 66db83f..93d1f13 100755 --- a/include/td/render/shader/ShaderProgram.h +++ b/include/td/render/shader/ShaderProgram.h @@ -5,11 +5,6 @@ #include namespace td { - -namespace render { -class Renderer; -} // namespace render - namespace shader { class ShaderProgram { diff --git a/src/td/render/Renderer.cpp b/src/td/render/Renderer.cpp index 9c03453..cc5a032 100644 --- a/src/td/render/Renderer.cpp +++ b/src/td/render/Renderer.cpp @@ -12,20 +12,6 @@ void BasicRenderer::Render(const GL::VertexArray& a_Vao) { a_Vao.Unbind(); } -Renderer::Renderer(std::unique_ptr&& a_Shader, Camera& a_Camera) : - m_Shader(std::move(a_Shader)), m_Camera(a_Camera) { - a_Camera.OnPerspectiveChange.Connect([this]() { - m_Shader->Start(); - m_Shader->SetProjectionMatrix(m_Camera.GetProjectionMatrix()); - }); - - a_Camera.OnViewChange.Connect([this]() { - m_Shader->Start(); - m_Shader->SetViewMatrix(m_Camera.GetViewMatrix()); - }); -} - - RenderPipeline::RenderPipeline() { glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); diff --git a/src/td/render/renderer/WorldRenderer.cpp b/src/td/render/renderer/WorldRenderer.cpp index c189111..468aad3 100644 --- a/src/td/render/renderer/WorldRenderer.cpp +++ b/src/td/render/renderer/WorldRenderer.cpp @@ -7,7 +7,7 @@ namespace td { namespace render { -WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(std::make_unique(), a_Camera), m_World(a_World) { +WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { m_WorldVao = std::make_unique(std::move(WorldLoader::LoadWorldModel(&a_World))); } From b21439718bc44c4afc439f94c3ccddd8cfc544cb Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 17 Jul 2025 23:00:05 +0200 Subject: [PATCH 10/39] fix array copy --- include/td/common/Array.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/td/common/Array.h b/include/td/common/Array.h index 3f20862..50964d0 100644 --- a/include/td/common/Array.h +++ b/include/td/common/Array.h @@ -16,7 +16,7 @@ class Array { Array() : m_Data(new T[S]) {} Array(const Array& a_Other) : Array() { - std::memcpy(m_Data, a_Other.m_Data, S); + *this = a_Other; } Array(Array&& a_Other) { @@ -33,7 +33,9 @@ class Array { } Array& operator=(const Array& a_Other) { - std::memcpy(m_Data, a_Other.m_Data, S); + for (std::size_t i = 0; i < S; i++) { + m_Data[i] = a_Other.m_Data[i]; + } return *this; } From b788caafa68044db678c57680b01917dd3a2ddad Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 17 Jul 2025 23:00:16 +0200 Subject: [PATCH 11/39] render entities --- include/td/game/Mobs.h | 235 +++++++++----------- include/td/render/renderer/EntityRenderer.h | 23 ++ include/td/render/shader/EntityShader.h | 10 +- src/main.cpp | 7 + src/td/game/Mobs.cpp | 24 ++ src/td/render/loader/WorldLoader.cpp | 36 ++- src/td/render/renderer/EntityRenderer.cpp | 26 +++ src/td/render/shader/EntityShader.cpp | 25 +-- 8 files changed, 228 insertions(+), 158 deletions(-) create mode 100644 include/td/render/renderer/EntityRenderer.h create mode 100644 src/td/game/Mobs.cpp create mode 100644 src/td/render/renderer/EntityRenderer.cpp diff --git a/include/td/game/Mobs.h b/include/td/game/Mobs.h index 3a9fe37..3b299a2 100644 --- a/include/td/game/Mobs.h +++ b/include/td/game/Mobs.h @@ -6,8 +6,8 @@ #include #include -#include #include +#include namespace td { namespace game { @@ -42,8 +42,7 @@ typedef std::uint8_t MobLevel; typedef std::vector TowerImmunities; typedef std::vector EffectImmunities; -class MobStats { -private: +struct MobStats { float m_Damage; float m_Speed; Vec2f m_Size; @@ -51,27 +50,12 @@ private: std::uint16_t m_ExpCost; std::uint16_t m_MaxLife; std::uint16_t m_ExpReward; -public: - MobStats(float damage, float speed, Vec2f size, std::uint16_t moneyCost, - std::uint16_t expCost, std::uint16_t expReward, - std::uint16_t maxLife) : m_Damage(damage), m_Speed(speed), - m_Size(size), m_MoneyCost(moneyCost), m_ExpCost(expCost), - m_MaxLife(maxLife), m_ExpReward(expReward) { - } - - float GetDamage() const { return m_Damage; } - float GetMovementSpeed() const { return m_Speed; } - const Vec2f& GetSize() const { return m_Size; } - std::uint16_t GetMoneyCost() const { return m_MoneyCost; } - std::uint16_t GetExpCost() const { return m_ExpCost; } - std::uint16_t GetExpReward() const { return m_ExpReward; } - std::uint16_t GetMaxLife() const { return m_MaxLife; } }; struct EffectDuration { EffectType type; - float duration; // in seconds - Tower* tower; // the tower that gived the effect + float duration; // in seconds + Tower* tower; // the tower that gived the effect }; const MobStats* GetMobStats(MobType type, std::uint8_t level); @@ -79,15 +63,16 @@ const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level); const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level); class Mob : public utils::shape::Rectangle { -protected: + protected: float m_Health; -private: + + private: MobID m_ID; PlayerID m_Sender; MobLevel m_Level; Direction m_Direction; std::vector m_Effects; - const Tower* m_LastDamage; // the last tower that damaged the mob + const Tower* m_LastDamage; // the last tower that damaged the mob float m_HitCooldown; // utils::Timer m_EffectFireTimer; @@ -97,30 +82,55 @@ private: TeamCastle* m_CastleTarget; // utils::CooldownTimer m_AttackTimer; -public: - Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), - m_HitCooldown(0), m_CastleTarget(nullptr) { + public: + Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), m_HitCooldown(0), m_CastleTarget(nullptr) {} - } + virtual ~Mob() {} virtual MobType GetType() const = 0; - virtual void Tick(std::uint64_t delta, World* world); + virtual void Tick(std::uint64_t delta, World* world) {} - virtual bool OnDeath(World* world) { return true; } + virtual bool OnDeath(World* world) { + return true; + } - MobID GetMobID() const { return m_ID; } - const TowerImmunities& GetTowerImmunities() const { return GetMobTowerImmunities(GetType(), m_Level); } - const EffectImmunities& GetEffectImmunities() const { return GetMobEffectImmunities(GetType(), m_Level); } - PlayerID GetSender() const { return m_Sender; } - MobLevel GetLevel() const { return m_Level; } - const MobStats* GetStats() const { return GetMobStats(GetType(), m_Level); } - void SetHealth(float newHealth) { m_Health = newHealth; } - float GetHealth() const { return m_Health; } - bool IsDead() const { return m_Health <= 0; } - bool IsAlive() const { return m_Health > 0; } - const Tower* GetLastDamageTower() { return m_LastDamage; } - bool HasReachedEnemyCastle() { return m_CastleTarget != nullptr; } + MobID GetMobID() const { + return m_ID; + } + const TowerImmunities& GetTowerImmunities() const { + return GetMobTowerImmunities(GetType(), m_Level); + } + const EffectImmunities& GetEffectImmunities() const { + return GetMobEffectImmunities(GetType(), m_Level); + } + PlayerID GetSender() const { + return m_Sender; + } + MobLevel GetLevel() const { + return m_Level; + } + const MobStats* GetStats() const { + return GetMobStats(GetType(), m_Level); + } + void SetHealth(float newHealth) { + m_Health = newHealth; + } + float GetHealth() const { + return m_Health; + } + bool IsDead() const { + return m_Health <= 0; + } + bool IsAlive() const { + return m_Health > 0; + } + const Tower* GetLastDamageTower() { + return m_LastDamage; + } + bool HasReachedEnemyCastle() { + return m_CastleTarget != nullptr; + } void Damage(float dmg, const Tower* damager) { m_Health = std::max(0.0f, m_Health - dmg); @@ -129,10 +139,12 @@ public: } void Heal(float heal) { - m_Health = std::min(static_cast(GetStats()->GetMaxLife()), m_Health + heal); + m_Health = std::min(static_cast(GetStats()->m_MaxLife), m_Health + heal); } - void SetMobReachedCastle(TeamCastle* castle) { m_CastleTarget = castle; } // used when mob is in front of the castle + void SetMobReachedCastle(TeamCastle* castle) { + m_CastleTarget = castle; + } // used when mob is in front of the castle bool IsImmuneTo(TowerType type); @@ -140,19 +152,34 @@ public: void AddEffect(EffectType type, float durationSec, Tower* tower); bool HasEffect(EffectType type); - bool HasTakenDamage() { return m_HitCooldown > 0; } - - 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 - - Direction GetDirection() const { return m_Direction; } - void SetDirection(Direction dir) { m_Direction = dir; } -protected: - void InitMob() { - m_Health = static_cast(GetStats()->GetMaxLife()); - SetSize(GetStats()->GetSize().x, GetStats()->GetSize().y); + bool HasTakenDamage() { + return m_HitCooldown > 0; } -private: + + // 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())); + } + + Direction GetDirection() const { + return m_Direction; + } + void SetDirection(Direction dir) { + m_Direction = dir; + } + + protected: + void InitMob() { + m_Health = static_cast(GetStats()->m_MaxLife); + SetSize(GetStats()->m_Size.x, GetStats()->m_Size.y); + } + + private: void UpdateEffects(std::uint64_t delta, World* world); void AttackCastle(std::uint64_t delta, World* world); void Move(std::uint64_t delta, World* world); @@ -165,85 +192,42 @@ private: typedef std::shared_ptr MobPtr; -class Zombie : public Mob { -public: - Zombie(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } +template +class ConcreteMob : public Mob { + public: + ConcreteMob(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { + InitMob(); + } - virtual MobType GetType() const { return MobType::Zombie; } + virtual ~ConcreteMob() {} + + virtual void Tick(std::uint64_t delta, World* world) {} + + virtual constexpr MobType GetType() const { + return MT; + } }; -class Spider : public Mob { -public: - Spider(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Spider; } -}; - -class Skeleton : public Mob { -public: - Skeleton(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Skeleton; } -}; - -class PigMan : public Mob { -public: - PigMan(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Pigman; } -}; - -class Creeper : public Mob { -public: - Creeper(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Creeper; } -}; - -class Silverfish : public Mob { -public: - Silverfish(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Silverfish; } -}; - -class Blaze : public Mob { -public: - Blaze(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Blaze; } -}; - -class Witch : public Mob { -public: - Witch(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Witch; } -}; - -class Slime : public Mob { -public: - Slime(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Slime; } -}; - -class Giant : public Mob { -public: - Giant(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } - - virtual MobType GetType() const { return MobType::Giant; } -}; +using Zombie = ConcreteMob; +using Spider = ConcreteMob; +using Skeleton = ConcreteMob; +using PigMan = ConcreteMob; +using Creeper = ConcreteMob; +using Silverfish = ConcreteMob; +using Blaze = ConcreteMob; +using Witch = ConcreteMob; +using Slime = ConcreteMob; +using Giant = ConcreteMob; namespace MobFactory { MobPtr CreateMob(MobID id, MobType type, std::uint8_t level, PlayerID sender); std::string GetMobName(MobType type); -} +} // namespace MobFactory class MobListener { -public: + public: virtual void OnMobSpawn(Mob* mob) {} virtual void OnMobDie(Mob* mob) {} @@ -255,6 +239,5 @@ public: // typedef utils::ObjectNotifier MobNotifier; -} // namespace game -} // namespace td - +} // namespace game +} // namespace td diff --git a/include/td/render/renderer/EntityRenderer.h b/include/td/render/renderer/EntityRenderer.h new file mode 100644 index 0000000..4f6562c --- /dev/null +++ b/include/td/render/renderer/EntityRenderer.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace render { + +class EntityRenderer : public Renderer { + private: + const game::World& m_World; + std::unique_ptr m_EntityVao; + + public: + EntityRenderer(Camera& a_Camera, const game::World& a_World); + virtual ~EntityRenderer(); + + virtual void Render() override; +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/shader/EntityShader.h b/include/td/render/shader/EntityShader.h index 765d908..1c7c948 100644 --- a/include/td/render/shader/EntityShader.h +++ b/include/td/render/shader/EntityShader.h @@ -1,14 +1,12 @@ #pragma once -#include +#include namespace td { namespace shader { -class EntityShader : public ShaderProgram { +class EntityShader : public CameraShaderProgram { private: - unsigned int m_LocationProjectionMatrix = 0; - unsigned int m_LocationViewMatrix = 0; unsigned int m_LocationPosition = 0; unsigned int m_LocationColorEffect = 0; @@ -18,11 +16,7 @@ class EntityShader : public ShaderProgram { public: EntityShader(); - void LoadShader(); - void SetColorEffect(const Vec3f& color); - void SetProjectionMatrix(const Mat4f& proj) const; - void SetViewMatrix(const Mat4f& view) const; void SetModelPos(const Vec3f& pos) const; }; diff --git a/src/main.cpp b/src/main.cpp index e17c847..281f48c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,9 @@ #include #include + #include +#include namespace td { namespace game { @@ -202,8 +204,13 @@ 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); + td::render::RenderPipeline renderer; renderer.AddRenderer(cam, w); + renderer.AddRenderer(cam, w); cam.SetCamPos({77, 25, 13}); cam.UpdatePerspective(display.GetAspectRatio()); diff --git a/src/td/game/Mobs.cpp b/src/td/game/Mobs.cpp new file mode 100644 index 0000000..1cae4a7 --- /dev/null +++ b/src/td/game/Mobs.cpp @@ -0,0 +1,24 @@ +#include + +namespace td { +namespace game { + + +const MobStats* GetMobStats(MobType type, std::uint8_t level) { + static MobStats stats; + return &stats; +} + +const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level) { + static TowerImmunities ti; + return ti; +} + +const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level) { + static EffectImmunities ei; + return ei; +} + + +} // namespace game +} // namespace td diff --git a/src/td/render/loader/WorldLoader.cpp b/src/td/render/loader/WorldLoader.cpp index 9854c4c..cf1beee 100644 --- a/src/td/render/loader/WorldLoader.cpp +++ b/src/td/render/loader/WorldLoader.cpp @@ -159,9 +159,9 @@ GL::VertexArray LoadTileSelectModel() { colorVBO.AddVertexAttribPointer(1, 1, 0); std::vector indexes(positions.size() / 3, 0); - for (size_t i = 0; i < indexes.size(); i++) { - indexes[i] = i + 1; - } + // for (size_t i = 0; i < indexes.size(); i++) { + // indexes[i] = i + 1; + // } GL::ElementBuffer indexVBO(indexes); GL::VertexArray tileSelectVao(std::move(indexVBO)); @@ -211,6 +211,36 @@ RenderData LoadTowerModel(game::TowerPtr tower) { } + +GL::VertexArray LoadMobModel() { + std::vector positions = { + -0.5, 0, -0.5, + -0.5, 0, 0.5, + 0.5, 0, -0.5, + + 0.5, 0, -0.5, + -0.5, 0, 0.5, + 0.5, 0, 0.5 + }; + + std::vector indexes(positions.size() / 3, 0); + // for (size_t i = 0; i < indexes.size(); i++) { + // indexes[i] = i + 1; + // } + GL::ElementBuffer indexVBO(indexes); + + GL::VertexBuffer positionVBO(positions, POSITION_VERTEX_SIZE); + positionVBO.AddVertexAttribPointer(0, POSITION_VERTEX_SIZE, 0); + + GL::VertexArray mobVao(std::move(indexVBO)); // each pos = 1 color + mobVao.Bind(); + mobVao.BindVertexBuffer(std::move(positionVBO)); + mobVao.Unbind(); + return mobVao; +} + + + } // namespace WorldLoader diff --git a/src/td/render/renderer/EntityRenderer.cpp b/src/td/render/renderer/EntityRenderer.cpp new file mode 100644 index 0000000..1c8dac4 --- /dev/null +++ b/src/td/render/renderer/EntityRenderer.cpp @@ -0,0 +1,26 @@ +#include + +#include + +namespace td { +namespace render { + +EntityRenderer::EntityRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { + m_EntityVao = std::make_unique(std::move(WorldLoader::LoadMobModel())); + m_Shader->Start(); + m_Shader->SetColorEffect({1, 0, 1}); +} + +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()}); + Renderer::Render(*m_EntityVao); + } +} + +} // namespace render +} // namespace td diff --git a/src/td/render/shader/EntityShader.cpp b/src/td/render/shader/EntityShader.cpp index 4b4dd06..3080487 100644 --- a/src/td/render/shader/EntityShader.cpp +++ b/src/td/render/shader/EntityShader.cpp @@ -53,16 +53,12 @@ static const char vertexSource[] = R"( #version 330 layout(location = 0) in vec3 position; -layout(location = 1) in vec2 textureCoords; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform vec3 modelPosition; -out vec2 pass_textureCoords; - void main(void){ - pass_textureCoords = textureCoords; gl_Position = projectionMatrix * viewMatrix * vec4(position + modelPosition, 1.0); } )"; @@ -70,16 +66,13 @@ void main(void){ static const char fragmentSource[] = R"( #version 330 -in vec2 pass_textureCoords; - out vec4 out_color; uniform vec3 ColorEffect; -uniform sampler2D textureSampler; void main(void){ - vec4 color = vec4(ColorEffect, 1.0) * texture(textureSampler, pass_textureCoords); + vec4 color = vec4(ColorEffect, 1.0); if (color.a <= 0.1) discard; @@ -90,31 +83,21 @@ void main(void){ )"; #endif -EntityShader::EntityShader() : ShaderProgram() {} - -void EntityShader::LoadShader() { +EntityShader::EntityShader() : CameraShaderProgram() { ShaderProgram::LoadProgram(vertexSource, fragmentSource); } + void EntityShader::GetAllUniformLocation() { + CameraShaderProgram::GetAllUniformLocation(); m_LocationColorEffect = static_cast(GetUniformLocation("ColorEffect")); - m_LocationViewMatrix = static_cast(GetUniformLocation("viewMatrix")); m_LocationPosition = static_cast(GetUniformLocation("modelPosition")); - m_LocationProjectionMatrix = static_cast(GetUniformLocation("projectionMatrix")); } void EntityShader::SetColorEffect(const Vec3f& color) { LoadVector(m_LocationColorEffect, color); } -void EntityShader::SetProjectionMatrix(const Mat4f& proj) const { - LoadMat4(m_LocationProjectionMatrix, proj); -} - -void EntityShader::SetViewMatrix(const Mat4f& view) const { - LoadMat4(m_LocationViewMatrix, view); -} - void EntityShader::SetModelPos(const Vec3f& pos) const { LoadVector(m_LocationPosition, pos); } From 6d0e56eb4625760cbae04254cfeb74c8ad4e9343 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 18 Jul 2025 13:11:18 +0200 Subject: [PATCH 12/39] 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 From 090ea962d38b956b00bb2bb532850047ad67f879 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 18 Jul 2025 18:56:49 +0200 Subject: [PATCH 13/39] and again --- include/td/Maths.h | 5 + include/td/game/Mobs.h | 160 +++----------------- include/td/game/World.h | 1 + include/td/protocol/command/Commands.h | 6 +- include/td/protocol/packet/Packets.h | 6 +- include/td/render/Renderer.h | 14 +- include/td/render/renderer/EntityRenderer.h | 2 +- include/td/render/renderer/WorldRenderer.h | 3 +- include/td/simulation/CommandApply.h | 2 +- include/td/simulation/RealTimeSimulation.h | 11 +- include/td/simulation/WorldTicker.h | 4 +- src/main.cpp | 16 +- src/td/game/World.cpp | 7 +- src/td/render/renderer/EntityRenderer.cpp | 13 +- src/td/render/renderer/WorldRenderer.cpp | 6 +- src/td/simulation/CommandApply.cpp | 6 +- src/td/simulation/RealTimeSimulation.cpp | 16 +- src/td/simulation/WorldTicker.cpp | 21 ++- src/td/simulation/system/EntityMove.cpp | 2 +- 19 files changed, 115 insertions(+), 186 deletions(-) diff --git a/include/td/Maths.h b/include/td/Maths.h index c5cad53..023ea6d 100644 --- a/include/td/Maths.h +++ b/include/td/Maths.h @@ -176,6 +176,11 @@ Mat4f Look(const Vec3f& eye, const Vec3f& center, const Vec3f& up); Mat4f Inverse(const Mat4f& mat); +template +T Lerp(T v0, T v1, T t) { + return (T(1) - t) * v0 + t * v1; +} + } // namespace maths diff --git a/include/td/game/Mobs.h b/include/td/game/Mobs.h index 9f24e5f..43ad263 100644 --- a/include/td/game/Mobs.h +++ b/include/td/game/Mobs.h @@ -9,6 +9,10 @@ #include #include +#include +#include +#include + namespace td { using Vec2fp = Vec2; @@ -65,152 +69,33 @@ 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 { - protected: - float m_Health; +class MobHandler; - private: - Vec2fp m_Position; +struct MobData {}; + +class Mob : public sp::MessageBase { + public: MobID m_ID; - PlayerID m_Sender; MobLevel m_Level; + PlayerID m_Sender; + float m_Health; + Vec2fp m_Position; Direction m_Direction; std::vector m_Effects; const Tower* m_LastDamage; // the last tower that damaged the mob float m_HitCooldown; - - // utils::Timer m_EffectFireTimer; - // utils::Timer m_EffectPoisonTimer; - // utils::Timer m_EffectHealTimer; - TeamCastle* m_CastleTarget; // utils::CooldownTimer m_AttackTimer; - public: - Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), m_HitCooldown(0), m_CastleTarget(nullptr) {} + MobPtr m_Next; - virtual ~Mob() {} + Mob() {} - virtual MobType GetType() const = 0; - - virtual bool OnDeath(World* world) { - return true; - } - - Vec2fp& GetPosition() { - return m_Position; - } - - MobID GetMobID() const { - return m_ID; - } - const TowerImmunities& GetTowerImmunities() const { - return GetMobTowerImmunities(GetType(), m_Level); - } - const EffectImmunities& GetEffectImmunities() const { - return GetMobEffectImmunities(GetType(), m_Level); - } - PlayerID GetSender() const { - return m_Sender; - } - MobLevel GetLevel() const { - return m_Level; - } - const MobStats* GetStats() const { - return GetMobStats(GetType(), m_Level); - } - void SetHealth(float newHealth) { - m_Health = newHealth; - } - float GetHealth() const { - return m_Health; - } - bool IsDead() const { - return m_Health <= 0; - } - bool IsAlive() const { - return m_Health > 0; - } - const Tower* GetLastDamageTower() { - return m_LastDamage; - } - bool HasReachedEnemyCastle() { - return m_CastleTarget != nullptr; - } - - void Damage(float dmg, const Tower* damager) { - m_Health = std::max(0.0f, m_Health - dmg); - m_LastDamage = damager; - m_HitCooldown = 0.1; - } - - void Heal(float heal) { - m_Health = std::min(static_cast(GetStats()->m_MaxLife), m_Health + heal); - } - - void SetMobReachedCastle(TeamCastle* castle) { - m_CastleTarget = castle; - } // used when mob is in front of the castle - - bool IsImmuneTo(TowerType type); - - bool IsImmuneTo(EffectType type); - void AddEffect(EffectType type, float durationSec, Tower* tower); - bool HasEffect(EffectType type); - - bool HasTakenDamage() { - 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 GetTileY() { - // return GetCenterY() - static_cast(static_cast(GetCenterY())); - // } - - Direction GetDirection() const { - return m_Direction; - } - void SetDirection(Direction dir) { - m_Direction = dir; - } - - protected: - void InitMob() { - m_Health = static_cast(GetStats()->m_MaxLife); - // SetSize(GetStats()->m_Size.x, GetStats()->m_Size.y); - } - - private: - void UpdateEffects(std::uint64_t delta, World* world); - void AttackCastle(std::uint64_t delta, World* world); - void Move(std::uint64_t delta, World* world); - void Walk(std::uint64_t delta, World* world); - void MoveBack(const TeamCastle& castle, World* world); - void ChangeDirection(const WalkableTile& tile, World* world); - bool IsTouchingCastle(const TeamCastle& castle) const; - EffectDuration& GetEffect(EffectType type); + Mob& operator=(const Mob& a_Other) = default; }; -typedef std::shared_ptr MobPtr; - -template -class ConcreteMob : public Mob { - public: - ConcreteMob(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { - InitMob(); - } - - virtual ~ConcreteMob() {} - - virtual constexpr MobType GetType() const override { - return MT; - } -}; +template +using ConcreteMob = sp::ConcreteMessage; using Zombie = ConcreteMob; using Spider = ConcreteMob; @@ -223,16 +108,17 @@ using Witch = ConcreteMob; using Slime = ConcreteMob; using Giant = ConcreteMob; -namespace MobFactory { +using AllMobs = std::tuple; -MobPtr CreateMob(MobID id, MobType type, std::uint8_t level, PlayerID sender); -std::string GetMobName(MobType type); +class MobHandler : public sp::GenericHandler {}; -} // namespace MobFactory +using MobFactory = sp::MessageFactory; class MobListener { public: - virtual void OnMobSpawn(Mob* mob) {} + virtual void OnMobSpawn(Mob* mob) { + MobHandler h; + } virtual void OnMobDie(Mob* mob) {} virtual void OnMobDamage(Mob* target, float damage, Tower* damager) {} diff --git a/include/td/game/World.h b/include/td/game/World.h index da2b1f7..410a04c 100644 --- a/include/td/game/World.h +++ b/include/td/game/World.h @@ -21,6 +21,7 @@ class World { TilePalette m_TilePalette; sim::WorldSnapshot m_CurrentState; + sim::WorldSnapshot m_NextState; private: sim::WorldTicker m_Ticker; diff --git a/include/td/protocol/command/Commands.h b/include/td/protocol/command/Commands.h index 87f5c4e..a0dcdbb 100644 --- a/include/td/protocol/command/Commands.h +++ b/include/td/protocol/command/Commands.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -33,7 +33,7 @@ class CommandHandler; using CommandBase = sp::MessageBase; template -using CommandMessage = sp::ConcreteMessage; +using CommandMessage = sp::ConcreteMessage; namespace commands { @@ -51,7 +51,7 @@ using UseItemCommand = CommandMessage; using AllCommands = std::tuple; -class CommandHandler : public sp::MessageHandler {}; +class CommandHandler : public sp::GenericHandler {}; using CommandDispatcher = sp::MessageDispatcher; diff --git a/include/td/protocol/packet/Packets.h b/include/td/protocol/packet/Packets.h index 4be3b2f..544061b 100644 --- a/include/td/protocol/packet/Packets.h +++ b/include/td/protocol/packet/Packets.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include namespace td { namespace protocol { @@ -37,7 +37,7 @@ using PacketBase = sp::MessageBase; template -using PacketMessage = sp::ConcreteMessage; +using PacketMessage = sp::ConcreteMessage; namespace packets { @@ -61,7 +61,7 @@ using AllPackets = std::tuple; -class PacketHandler : public sp::MessageHandler {}; +class PacketHandler : public sp::GenericHandler {}; using PacketDispatcher = sp::MessageDispatcher; diff --git a/include/td/render/Renderer.h b/include/td/render/Renderer.h index fa25234..cb5c23b 100644 --- a/include/td/render/Renderer.h +++ b/include/td/render/Renderer.h @@ -10,7 +10,8 @@ namespace render { class BasicRenderer { public: - virtual void Render() = 0; + virtual void Render(float a_Lerp) = 0; + virtual ~BasicRenderer() {} void Render(const GL::VertexArray& a_Vao); }; @@ -24,6 +25,11 @@ class Renderer : public BasicRenderer { public: Renderer(Camera& a_Camera); virtual ~Renderer() {} + + template + float Lerp(const T& a_Mob, float a_LerpFactor, const std::function& a_MemberGetter) { + return static_cast(maths::Lerp(a_MemberGetter(a_Mob), a_MemberGetter(*a_Mob.m_Next), a_LerpFactor)); + } }; class RenderPipeline { @@ -32,7 +38,7 @@ class RenderPipeline { public: RenderPipeline(); - ~RenderPipeline() = default; + virtual ~RenderPipeline() {} template void AddRenderer(Args&&... args) { @@ -43,9 +49,9 @@ class RenderPipeline { m_Renderers.clear(); } - void Render() { + void Render(float a_Lerp) { for (auto& renderer : m_Renderers) { - renderer->Render(); + renderer->Render(a_Lerp); } } }; diff --git a/include/td/render/renderer/EntityRenderer.h b/include/td/render/renderer/EntityRenderer.h index 4f6562c..eefd91d 100644 --- a/include/td/render/renderer/EntityRenderer.h +++ b/include/td/render/renderer/EntityRenderer.h @@ -16,7 +16,7 @@ class EntityRenderer : public Renderer { EntityRenderer(Camera& a_Camera, const game::World& a_World); virtual ~EntityRenderer(); - virtual void Render() override; + virtual void Render(float a_Lerp) override; }; } // namespace render diff --git a/include/td/render/renderer/WorldRenderer.h b/include/td/render/renderer/WorldRenderer.h index 93dfb8d..ded2b24 100644 --- a/include/td/render/renderer/WorldRenderer.h +++ b/include/td/render/renderer/WorldRenderer.h @@ -10,14 +10,13 @@ namespace render { class WorldRenderer : public Renderer { private: - const game::World& m_World; std::unique_ptr m_WorldVao; public: WorldRenderer(Camera& a_Camera, const game::World& a_World); virtual ~WorldRenderer(); - virtual void Render() override; + virtual void Render(float a_Lerp) override; }; } // namespace render diff --git a/include/td/simulation/CommandApply.h b/include/td/simulation/CommandApply.h index e98c217..b16dfe2 100644 --- a/include/td/simulation/CommandApply.h +++ b/include/td/simulation/CommandApply.h @@ -14,7 +14,7 @@ class CommandApply : public protocol::CommandHandler { public: CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot); - virtual void Handle(const protocol::cdata::SpawnTroop& a_SpawnTroop) override; + virtual void Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) override; }; } // namespace sim diff --git a/include/td/simulation/RealTimeSimulation.h b/include/td/simulation/RealTimeSimulation.h index a97b03e..694f500 100644 --- a/include/td/simulation/RealTimeSimulation.h +++ b/include/td/simulation/RealTimeSimulation.h @@ -17,9 +17,18 @@ class RealTimeSimulation { std::size_t m_CurrentStep; public: + /** + * \param a_StepTime in ms + */ RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime); - void Update(); + /** + * \return the progress [0-1] between two steps + */ + float Update(); + + private: + void Step(); }; } // namespace sim diff --git a/include/td/simulation/WorldTicker.h b/include/td/simulation/WorldTicker.h index 42555e3..9d6d838 100644 --- a/include/td/simulation/WorldTicker.h +++ b/include/td/simulation/WorldTicker.h @@ -23,12 +23,12 @@ class WorldTicker { WorldTicker(); WorldSnapshot NextStep( - const game::World& a_World, const WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta); + const game::World& a_World, 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); + WorldSnapshot CreateNext(WorldSnapshot& a_PreviousState); template void AddSystem() { diff --git a/src/main.cpp b/src/main.cpp index 53f6b90..8b035d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,11 +20,11 @@ class WorldApply : public td::protocol::PacketHandler { public: WorldApply(td::game::World& a_World) : m_World(a_World) {} - void Handle(const td::protocol::pdata::WorldHeader& a_Header) override { - m_World.LoadMap(a_Header); + void Handle(const td::protocol::packets::WorldHeaderPacket& a_Header) override { + m_World.LoadMap(*a_Header); } - void Handle(const td::protocol::pdata::WorldData& a_Data) override { - m_World.LoadMap(a_Data); + void Handle(const td::protocol::packets::WorldDataPacket& a_Data) override { + m_World.LoadMap(*a_Data); } }; @@ -93,15 +93,15 @@ int main(int argc, char** argv) { renderer.AddRenderer(cam, w); renderer.AddRenderer(cam, w); - cam.SetCamPos({77, 25, 13}); + cam.SetCamPos({77, 5, 13}); cam.UpdatePerspective(display.GetAspectRatio()); - td::sim::RealTimeSimulation simulation(w, std::move(gh), 1000); + td::sim::RealTimeSimulation simulation(w, std::move(gh), 2000); while (!display.IsCloseRequested()) { display.PollEvents(); - simulation.Update(); - renderer.Render(); + float lerp = simulation.Update(); + renderer.Render(lerp); display.Update(); } diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp index fefb18b..78c515a 100644 --- a/src/td/game/World.cpp +++ b/src/td/game/World.cpp @@ -5,9 +5,7 @@ namespace td { namespace game { -World::World() : m_CurrentState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}} { - -} +World::World() : m_CurrentState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}}, m_NextState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}} {} const Color* World::GetTileColor(TilePtr tile) const { switch (tile->GetType()) { @@ -58,7 +56,8 @@ bool World::LoadMap(const protocol::pdata::WorldData& a_WorldData) { } void World::Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta) { - m_CurrentState = m_Ticker.NextStep(*this, m_CurrentState, a_LockStep, a_Delta); + m_CurrentState = m_NextState; + m_NextState = m_Ticker.NextStep(*this, m_NextState, a_LockStep, a_Delta); } } // namespace game diff --git a/src/td/render/renderer/EntityRenderer.cpp b/src/td/render/renderer/EntityRenderer.cpp index 55eaa67..bdfa53b 100644 --- a/src/td/render/renderer/EntityRenderer.cpp +++ b/src/td/render/renderer/EntityRenderer.cpp @@ -13,16 +13,19 @@ EntityRenderer::EntityRenderer(Camera& a_Camera, const game::World& a_World) : R EntityRenderer::~EntityRenderer() {} -void EntityRenderer::Render() { + + +void EntityRenderer::Render(float a_Lerp) { m_Shader->Start(); for (const auto& mob : m_World.GetMobList()) { - const auto mobCoords = mob->GetPosition(); - float x = static_cast(mobCoords.x); - float z = static_cast(mobCoords.y); + + float x = Lerp(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast(a_Mob.m_Position.x); }); + float z = Lerp(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast(a_Mob.m_Position.y); }); + m_Shader->SetModelPos({x, 1, z}); Renderer::Render(*m_EntityVao); } } -} // namespace render +} // namespace render } // namespace td diff --git a/src/td/render/renderer/WorldRenderer.cpp b/src/td/render/renderer/WorldRenderer.cpp index 468aad3..078dc5d 100644 --- a/src/td/render/renderer/WorldRenderer.cpp +++ b/src/td/render/renderer/WorldRenderer.cpp @@ -7,13 +7,13 @@ namespace td { namespace render { -WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { - m_WorldVao = std::make_unique(std::move(WorldLoader::LoadWorldModel(&a_World))); +WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera) { + m_WorldVao = std::make_unique(WorldLoader::LoadWorldModel(&a_World)); } WorldRenderer::~WorldRenderer() {} -void WorldRenderer::Render() { +void WorldRenderer::Render(float a_Lerp) { m_Shader->Start(); Renderer::Render(*m_WorldVao); ImGui::ShowDemoWindow(); diff --git a/src/td/simulation/CommandApply.cpp b/src/td/simulation/CommandApply.cpp index eeaa9e2..852ef6d 100644 --- a/src/td/simulation/CommandApply.cpp +++ b/src/td/simulation/CommandApply.cpp @@ -5,9 +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::cdata::SpawnTroop& a_SpawnTroop) { - auto zombie = std::make_shared(0, *a_SpawnTroop.m_Level, a_SpawnTroop.m_Sender); - zombie->GetPosition() = a_SpawnTroop.m_Position; +void CommandApply::Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) { + auto zombie = std::make_shared(); + zombie->m_Position = a_SpawnTroop->m_Position; m_Snapshot.m_Mobs.push_back(zombie); } diff --git a/src/td/simulation/RealTimeSimulation.cpp b/src/td/simulation/RealTimeSimulation.cpp index 6abfc42..438c424 100644 --- a/src/td/simulation/RealTimeSimulation.cpp +++ b/src/td/simulation/RealTimeSimulation.cpp @@ -16,16 +16,24 @@ RealTimeSimulation::RealTimeSimulation(game::World& a_World, GameHistory&& a_His m_History(std::move(a_History)), m_CurrentTime(0), m_LastTime(GetTime()), - m_CurrentStep(0) {} + m_CurrentStep(0) { + Step(); +} -void RealTimeSimulation::Update() { +float RealTimeSimulation::Update() { + // TODO: handle freezes (m_CurrentTime > 2 * m_StepTime) 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++; + Step(); m_CurrentTime -= m_StepTime; } + return (float) m_CurrentTime / (float) m_StepTime; +} + +void RealTimeSimulation::Step() { + m_World.Tick(m_History[m_CurrentStep], FpFloat(m_StepTime) / FpFloat(1000)); + m_CurrentStep++; } } // namespace sim diff --git a/src/td/simulation/WorldTicker.cpp b/src/td/simulation/WorldTicker.cpp index 367f90b..ae972a8 100644 --- a/src/td/simulation/WorldTicker.cpp +++ b/src/td/simulation/WorldTicker.cpp @@ -13,9 +13,9 @@ WorldTicker::WorldTicker() { } WorldSnapshot WorldTicker::NextStep( - const game::World& a_World, const WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) { + const game::World& a_World, WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) { WorldSnapshot next = CreateNext(a_PreviousState); - ApplySteps(a_World, next, a_LockStep); + ApplySteps(a_World, next, a_LockStep); // maybe swap with tick ? Tick(a_World, next, a_Delta); return next; } @@ -33,8 +33,21 @@ void WorldTicker::Tick(const game::World& a_World, WorldSnapshot& a_State, FpFlo } } -WorldSnapshot WorldTicker::CreateNext(const WorldSnapshot& a_PreviousState) { - return a_PreviousState; +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()); + for (auto& mob : a_PreviousState.m_Mobs) { + game::MobPtr newMob = std::shared_ptr(mobFactory.CreateMessage(mob->GetId()).release()); + *newMob = *mob; + mob->m_Next = newMob; + next.m_Mobs.push_back(newMob); + } + return next; } } // namespace sim diff --git a/src/td/simulation/system/EntityMove.cpp b/src/td/simulation/system/EntityMove.cpp index 4592762..798a6f8 100644 --- a/src/td/simulation/system/EntityMove.cpp +++ b/src/td/simulation/system/EntityMove.cpp @@ -5,7 +5,7 @@ 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; + mob->m_Position.x += a_Delta; } } From 705671148b7fa5aa6df930bd950a9b94ace169d1 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Tue, 22 Jul 2025 14:04:56 +0200 Subject: [PATCH 14/39] add comment --- src/td/simulation/WorldTicker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/td/simulation/WorldTicker.cpp b/src/td/simulation/WorldTicker.cpp index ae972a8..fcebd0d 100644 --- a/src/td/simulation/WorldTicker.cpp +++ b/src/td/simulation/WorldTicker.cpp @@ -41,6 +41,7 @@ WorldSnapshot WorldTicker::CreateNext(WorldSnapshot& a_PreviousState) { .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(mobFactory.CreateMessage(mob->GetId()).release()); *newMob = *mob; From 9477099cfc95dca433f73b82f5720ec36a2770e8 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 23 Jul 2025 19:18:25 +0200 Subject: [PATCH 15/39] fix warnings + cland-tidy --- .clang-tidy | 27 +++++++++++++++++++++++ include/td/game/Team.h | 2 +- include/td/game/World.h | 2 +- include/td/game/WorldTypes.h | 3 ++- include/td/misc/Shapes.h | 4 ++-- include/td/render/loader/WorldLoader.h | 2 +- include/td/simulation/WorldTicker.h | 3 ++- src/main.cpp | 2 ++ src/td/game/World.cpp | 2 +- src/td/render/loader/WorldLoader.cpp | 4 ++-- src/td/render/renderer/EntityRenderer.cpp | 2 +- src/td/render/shader/ShaderProgram.cpp | 2 +- xmake.lua | 4 ++++ 13 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 .clang-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..14453e3 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,27 @@ +Checks: >- + -*, + readability-identifier-naming, + readability-redundant-string-cstr, + readability-redundant-string-init, + readability-simplify-boolean-expr, + performance-unnecessary-value-param, + performance-unnecessary-copy-initialization, + performance-for-range-copy, + performance-implicit-conversion-in-loop, +CheckOptions: +- key: readability-identifier-naming.PrivateMemberPrefix + value: 'm_' +- key: readability-identifier-naming.ClassConstantCase + value: aNy_CasE +# an empty *Prefix needs a *Case to work +- key: readability-identifier-naming.ClassConstantPrefix + value: '' +#- key: readability-identifier-naming.PrivateMemberCase +# value: CamelCase +#- key: readability-identifier-naming.FunctionCase +# value: CamelCase +#- key: readability-identifier-naming.EnumCase +# value: camelBack + +WarningsAsErrors: '*' +FormatStyle: 'file' \ No newline at end of file diff --git a/include/td/game/Team.h b/include/td/game/Team.h index 79e5547..a73fb76 100644 --- a/include/td/game/Team.h +++ b/include/td/game/Team.h @@ -17,7 +17,7 @@ class Spawn : public utils::shape::Rectangle { private: Direction m_Direction; public: - Spawn() { + Spawn() : m_Direction(Direction::PositiveX) { SetWidth(5); SetHeight(5); } diff --git a/include/td/game/World.h b/include/td/game/World.h index 410a04c..5c591bd 100644 --- a/include/td/game/World.h +++ b/include/td/game/World.h @@ -88,7 +88,7 @@ class World { return m_CurrentState.m_Mobs; } - const Color* GetTileColor(TilePtr tile) const; + const Color* GetTileColor(const TilePtr& tile) const; Team& GetRedTeam() { return m_CurrentState.m_Teams[static_cast(TeamColor::Red)]; diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h index 4e95cd6..7bf43fb 100644 --- a/include/td/game/WorldTypes.h +++ b/include/td/game/WorldTypes.h @@ -39,6 +39,7 @@ static constexpr Color BLUE{0, 0, 255}; struct Tile { virtual TileType GetType() const = 0; + virtual ~Tile() = default; }; struct TowerTile : Tile { @@ -118,4 +119,4 @@ struct hash { return std::hash()(key.x << 16 | key.y); } }; -} // namespace std \ No newline at end of file +} // namespace std diff --git a/include/td/misc/Shapes.h b/include/td/misc/Shapes.h index 6a789f8..2367efb 100644 --- a/include/td/misc/Shapes.h +++ b/include/td/misc/Shapes.h @@ -30,7 +30,7 @@ private: Point m_Center; float m_Width, m_Height; public: - Rectangle() {} + Rectangle() : m_Center(), m_Width(0), m_Height(0) {} const Point& GetCenter() const { return m_Center; } float GetCenterX() const { return m_Center.GetX(); } @@ -92,5 +92,5 @@ public: }; } // namespace shape -} // namespace utils +} // namespace utils } // namespace td diff --git a/include/td/render/loader/WorldLoader.h b/include/td/render/loader/WorldLoader.h index b9d2482..0a5dc79 100644 --- a/include/td/render/loader/WorldLoader.h +++ b/include/td/render/loader/WorldLoader.h @@ -16,7 +16,7 @@ struct RenderData { GL::VertexArray LoadMobModel(); GL::VertexArray LoadWorldModel(const td::game::World* world); GL::VertexArray LoadTileSelectModel(); -RenderData LoadTowerModel(game::TowerPtr tower); +RenderData LoadTowerModel(const game::TowerPtr& tower); } // namespace WorldLoader diff --git a/include/td/simulation/WorldTicker.h b/include/td/simulation/WorldTicker.h index 9d6d838..6431717 100644 --- a/include/td/simulation/WorldTicker.h +++ b/include/td/simulation/WorldTicker.h @@ -13,6 +13,7 @@ namespace sim { class IWorldSystem { public: virtual void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) = 0; + virtual ~IWorldSystem() = default; }; class WorldTicker { @@ -32,7 +33,7 @@ class WorldTicker { template void AddSystem() { - m_Systems.push_back(std::move(std::make_unique())); + m_Systems.push_back(std::make_unique()); } }; diff --git a/src/main.cpp b/src/main.cpp index 8b035d4..f0c9b68 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ class WorldApply : public td::protocol::PacketHandler { private: td::game::World& m_World; + using td::protocol::PacketHandler::Handle; public: WorldApply(td::game::World& a_World) : m_World(a_World) {} @@ -23,6 +24,7 @@ class WorldApply : public td::protocol::PacketHandler { void Handle(const td::protocol::packets::WorldHeaderPacket& a_Header) override { m_World.LoadMap(*a_Header); } + void Handle(const td::protocol::packets::WorldDataPacket& a_Data) override { m_World.LoadMap(*a_Data); } diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp index 78c515a..b4c0afc 100644 --- a/src/td/game/World.cpp +++ b/src/td/game/World.cpp @@ -7,7 +7,7 @@ namespace game { World::World() : m_CurrentState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}}, m_NextState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}} {} -const Color* World::GetTileColor(TilePtr tile) const { +const Color* World::GetTileColor(const TilePtr& tile) const { switch (tile->GetType()) { case TileType::Tower: { TowerTile* towerTile = dynamic_cast(tile.get()); diff --git a/src/td/render/loader/WorldLoader.cpp b/src/td/render/loader/WorldLoader.cpp index cf1beee..7f96087 100644 --- a/src/td/render/loader/WorldLoader.cpp +++ b/src/td/render/loader/WorldLoader.cpp @@ -11,7 +11,7 @@ namespace render { namespace WorldLoader { const static int POSITION_VERTEX_SIZE = 3; -const static int TEXTURE_VERTEX_SIZE = 2; +// const static int TEXTURE_VERTEX_SIZE = 2; GL::VertexArray LoadWorldModel(const td::game::World* world) { std::vector positions; @@ -173,7 +173,7 @@ GL::VertexArray LoadTileSelectModel() { return tileSelectVao; } -RenderData LoadTowerModel(game::TowerPtr tower) { +RenderData LoadTowerModel(const game::TowerPtr& tower) { RenderData renderData; float towerX, towerDX; diff --git a/src/td/render/renderer/EntityRenderer.cpp b/src/td/render/renderer/EntityRenderer.cpp index bdfa53b..9b5d189 100644 --- a/src/td/render/renderer/EntityRenderer.cpp +++ b/src/td/render/renderer/EntityRenderer.cpp @@ -6,7 +6,7 @@ namespace td { namespace render { EntityRenderer::EntityRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { - m_EntityVao = std::make_unique(std::move(WorldLoader::LoadMobModel())); + m_EntityVao = std::make_unique(WorldLoader::LoadMobModel()); m_Shader->Start(); m_Shader->SetColorEffect({1, 0, 1}); } diff --git a/src/td/render/shader/ShaderProgram.cpp b/src/td/render/shader/ShaderProgram.cpp index 5d89462..9406bb5 100755 --- a/src/td/render/shader/ShaderProgram.cpp +++ b/src/td/render/shader/ShaderProgram.cpp @@ -109,7 +109,7 @@ unsigned int ShaderProgram::LoadShader(const std::string& source, GLenum type) { glCompileShader(shaderID); GLint compilesuccessful; glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compilesuccessful); - if (compilesuccessful == false) { + if (!compilesuccessful) { GLsizei size; glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &size); std::vector shaderError(static_cast(size)); diff --git a/xmake.lua b/xmake.lua index 6acdb45..e92b89a 100644 --- a/xmake.lua +++ b/xmake.lua @@ -10,6 +10,10 @@ set_languages("c++17") set_warnings("all") +if is_mode("release") then + set_warnings("all", "error") +end + target("Tower-Defense2") add_includedirs("include", {public = true}) set_kind("binary") From e146f490cf78ce98483acd2628b2f21f908d6d3d Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 23 Jul 2025 19:20:59 +0200 Subject: [PATCH 16/39] CommandApply: add handle methods --- include/td/simulation/CommandApply.h | 6 ++++++ src/td/simulation/CommandApply.cpp | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/td/simulation/CommandApply.h b/include/td/simulation/CommandApply.h index b16dfe2..92d57d0 100644 --- a/include/td/simulation/CommandApply.h +++ b/include/td/simulation/CommandApply.h @@ -14,7 +14,13 @@ class CommandApply : public protocol::CommandHandler { public: CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot); + virtual void Handle(const protocol::commands::EndCommand& a_End) override; + virtual void Handle(const protocol::commands::PlaceTowerCommand& a_PlaceTower) override; + virtual void Handle(const protocol::commands::PlayerJoinCommand& a_PlayerJoin) override; virtual void Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) override; + virtual void Handle(const protocol::commands::TeamChangeCommand& a_TeamChange) override; + virtual void Handle(const protocol::commands::UpgradeTowerCommand& a_UpgradeTower) override; + virtual void Handle(const protocol::commands::UseItemCommand& a_UseItem) override; }; } // namespace sim diff --git a/src/td/simulation/CommandApply.cpp b/src/td/simulation/CommandApply.cpp index 852ef6d..02a1beb 100644 --- a/src/td/simulation/CommandApply.cpp +++ b/src/td/simulation/CommandApply.cpp @@ -5,11 +5,21 @@ 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::PlaceTowerCommand& a_PlaceTower) { + m_World.CanPlaceBigTower({}, 0); +} +void CommandApply::Handle(const protocol::commands::PlayerJoinCommand& a_PlayerJoin) {} + void CommandApply::Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) { auto zombie = std::make_shared(); 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 From 05edb951505666b070f13931bca9ee5ee0387b45 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 28 Jul 2025 14:27:37 +0200 Subject: [PATCH 17/39] nuke dispatch --- include/td/protocol/Dispatcher.h | 61 ------------------------------ include/td/protocol/Dispatcher.inl | 38 ------------------- 2 files changed, 99 deletions(-) delete mode 100644 include/td/protocol/Dispatcher.h delete mode 100644 include/td/protocol/Dispatcher.inl diff --git a/include/td/protocol/Dispatcher.h b/include/td/protocol/Dispatcher.h deleted file mode 100644 index 037ce03..0000000 --- a/include/td/protocol/Dispatcher.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -/** - * \file PacketDispatcher.h - * \brief File containing the td::protocol::PacketDispatcher class - */ - -#include -#include - -#include - -namespace td { -namespace protocol { - -/** - * \class Dispatcher - * \brief Class used to dispatch things - */ -template -class Dispatcher : private NonCopyable { - private: - std::map> m_Handlers; - - public: - /** - * \brief Constructor - */ - Dispatcher() {} - - /** - * \brief Dispatch a packet - * \param packet The packet to dispatch - */ - void Dispatch(const T& packet); - - /** - * \brief Register a packet handler - * \param type The packet type - * \param handler The packet handler - */ - void RegisterHandler(T_Enum type, T_Handler& handler); - - /** - * \brief Unregister a packet handler - * \param type The packet type - * \param handler The packet handler - */ - void UnregisterHandler(T_Enum type, T_Handler& handler); - - /** - * \brief Unregister a packet handler - * \param handler The packet handler - */ - void UnregisterHandler(T_Handler& handler); -}; - -} // namespace protocol -} // namespace td - -#include "Dispatcher.inl" \ No newline at end of file diff --git a/include/td/protocol/Dispatcher.inl b/include/td/protocol/Dispatcher.inl deleted file mode 100644 index d807f9b..0000000 --- a/include/td/protocol/Dispatcher.inl +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -namespace td { -namespace protocol { - -template -void Dispatcher::Dispatch(const T& packet) { - T_Enum type = packet.GetType(); - for (auto* handler : m_Handlers[type]) - handler->Check(packet); -} - -template -void Dispatcher::RegisterHandler(T_Enum type, T_Handler& handler) { - auto found = std::find(m_Handlers[type].begin(), m_Handlers[type].end(), &handler); - if (found == m_Handlers[type].end()) - m_Handlers[type].push_back(&handler); -} - -template -void Dispatcher::UnregisterHandler(T_Enum type, T_Handler& handler) { - m_Handlers[type].erase(std::remove(m_Handlers[type].begin(), m_Handlers[type].end(), &handler), m_Handlers[type].end()); -} - -template -void Dispatcher::UnregisterHandler(T_Handler& handler) { - for (auto& pair : m_Handlers) { - if (pair.second.empty()) - continue; - - PacketType type = pair.first; - - m_Handlers[type].erase(std::remove(m_Handlers[type].begin(), m_Handlers[type].end(), &handler), m_Handlers[type].end()); - } -} - -} // namespace protocol -} // namespace td From 47b5a281fc5368c0452806bb606ce4e8406ec792 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 28 Jul 2025 14:28:03 +0200 Subject: [PATCH 18/39] refactor towers --- include/td/game/Towers.h | 273 +++++++++++---------------------------- 1 file changed, 78 insertions(+), 195 deletions(-) diff --git a/include/td/game/Towers.h b/include/td/game/Towers.h index af84e02..19dc243 100644 --- a/include/td/game/Towers.h +++ b/include/td/game/Towers.h @@ -1,12 +1,16 @@ #pragma once -#include #include +#include #include #include +#include +#include +#include + namespace td { namespace game { @@ -15,69 +19,66 @@ class Mob; typedef std::shared_ptr MobPtr; -enum class TowerType : std::uint8_t { - Archer = 0, - Ice, - Sorcerer, - Zeus, - Mage, - Artillery, - Quake, - Poison, - - Leach, - Turret, - Necromancer, - - TowerCount -}; - enum class TowerSize : std::uint8_t { - Little = 3, // 3x3 - Big = 5, // 5x5 + Little = 3, // 3x3 + Big = 5, // 5x5 }; enum class TowerPath : std::uint8_t { Top = 0, - Base, // Base Path + Base, // Base Path Bottom }; class TowerStats { -private: + private: float m_Rate; float m_Damage; std::uint8_t m_Range; -public: - TowerStats(float rate, float damage, std::uint8_t range) : m_Rate(rate), m_Damage(damage), - m_Range(range) { - } - float GetDamageRate() const { return m_Rate; } - float GetDamage() const { return m_Damage; } - std::uint8_t GetRange() const { return m_Range; } + public: + TowerStats(float rate, float damage, std::uint8_t range) : m_Rate(rate), m_Damage(damage), m_Range(range) {} + + float GetDamageRate() const { + return m_Rate; + } + float GetDamage() const { + return m_Damage; + } + std::uint8_t GetRange() const { + return m_Range; + } }; class TowerLevel { -private: + private: // 1, 2, 3, 4 std::uint8_t m_Level : 3; // 0 : base path 1 : top path (if there is bottom path) 2 : bottom path (if there is top path) TowerPath m_Path : 2; -public: + + public: TowerLevel() : m_Level(1), m_Path(TowerPath::Base) {} TowerLevel(std::uint8_t level, TowerPath path) : m_Level(level), m_Path(path) {} - std::uint8_t GetLevel() const { return m_Level; } - TowerPath GetPath() const { return m_Path; } + std::uint8_t GetLevel() const { + return m_Level; + } + TowerPath GetPath() const { + return m_Path; + } - void SetLevel(std::uint8_t level) { m_Level = level; } - void SetPath(TowerPath path) { m_Path = path; } + void SetLevel(std::uint8_t level) { + m_Level = level; + } + void SetPath(TowerPath path) { + m_Path = path; + } // operator to sort maps friend bool operator<(const TowerLevel& level, const TowerLevel& other) { return level.GetLevel() + static_cast(level.GetPath()) * 4 < - other.GetLevel() + static_cast(other.GetPath()) * 4; + other.GetLevel() + static_cast(other.GetPath()) * 4; } }; @@ -85,182 +86,64 @@ const TowerStats* GetTowerStats(TowerType type, TowerLevel level); typedef std::uint16_t TowerID; -class Tower : public utils::shape::Circle { -private: +class TowerHandler; + +class Tower : public utils::shape::Circle, public sp::MessageBase { + public: TowerID m_ID; - TowerType m_Type; TowerLevel m_Level{}; PlayerID m_Builder; -protected: - // utils::CooldownTimer m_Timer; -public: - Tower(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) : utils::shape::Circle(x + 0.5f, y + 0.5f, 0), m_ID(id), m_Type(type), m_Builder(builder) - // m_Timer(GetStats()->GetDamageRate() * 1000) - { // converting seconds to millis - SetRadius(GetStats()->GetRange()); - } + + public: + Tower() : m_ID(0), m_Level({}), m_Builder(0) {} virtual TowerType GetType() const = 0; virtual TowerSize GetSize() const = 0; virtual void Tick(std::uint64_t delta, World* world) = 0; +}; - void Upgrade(std::uint8_t level, TowerPath path) { - m_Level.SetLevel(level); - m_Level.SetPath(path); - // m_Timer.SetCooldown(GetStats()->GetDamageRate() * 1000); // converting seconds to millis - // m_Timer.Reset(); - SetRadius(GetStats()->GetRange()); +struct TowerData {}; + +template +class ConcreteTower : public sp::ConcreteMessage { + public: + using HandlerType = typename sp::ConcreteMessage::HandlerType; + + virtual TowerSize GetSize() const { + return Size; } - std::uint16_t GetID() const { return m_ID; } - const TowerLevel& GetLevel() const { return m_Level; } - const TowerStats* GetStats() const { return GetTowerStats(m_Type, m_Level); } - PlayerID GetBuilder() const { return m_Builder; } - - bool IsMobInRange(MobPtr mob); -}; - -typedef std::shared_ptr TowerPtr; - -namespace TowerFactory { - -TowerPtr CreateTower(TowerType type, TowerID id, std::int32_t x, std::int32_t y, PlayerID builder); -std::string GetTowerName(TowerType type); - -} // namespace TowerFactory - - -class TowerInfo { -private: - std::string m_Name, m_Description; - bool m_IsBigTower; -public: - TowerInfo(std::string&& name, std::string&& description, bool big) : m_Name(std::move(name)), - m_Description(std::move(description)), m_IsBigTower(big) { + virtual TowerType GetType() const { + return Type; } - const std::string& GetName() const { return m_Name; } - const std::string& GetDescription() const { return m_Description; } + virtual void Tick(std::uint64_t delta, World* world) {} - bool IsBigTower() const { return m_IsBigTower; } + virtual void Dispatch(HandlerType& handler) const override { + handler.Handle(*this); + } }; -const TowerInfo& GetTowerInfo(TowerType type); +using TowerPtr = std::shared_ptr; -// ---------- Little Towers ---------- +using ArcherTower = ConcreteTower; +using ArtilleryTower = ConcreteTower; +using IceTower = ConcreteTower; +using LeachTower = ConcreteTower; +using MageTower = ConcreteTower; +using NecromancerTower = ConcreteTower; +using PoisonTower = ConcreteTower; +using QuakeTower = ConcreteTower; +using SorcererTower = ConcreteTower; +using TurretTower = ConcreteTower; +using ZeusTower = ConcreteTower; -class LittleTower : public Tower { -public: - LittleTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {} +using AllTowers = std::tuple; - virtual TowerSize GetSize() const { return TowerSize::Little; } +using TowerFactory = sp::MessageFactory; - virtual TowerType GetType() const = 0; - virtual void Tick(std::uint64_t delta, World* world) = 0; -}; +class TowerHandler : public sp::GenericHandler {}; -class ArcherTower : public LittleTower { -public: - ArcherTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} - - constexpr static float ExplosionRadius = 1.5f; - constexpr static float FireDurationSec = 10.0f; - - virtual TowerType GetType() const { return TowerType::Archer; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class IceTower : public LittleTower { -public: - IceTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Ice; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class MageTower : public LittleTower { -public: - MageTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Mage; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class PoisonTower : public LittleTower { -public: - PoisonTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Poison; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class QuakeTower : public LittleTower { -public: - QuakeTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Quake; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class ArtilleryTower : public LittleTower { -public: - ArtilleryTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Artillery; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class SorcererTower : public LittleTower { -public: - SorcererTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Sorcerer; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class ZeusTower : public LittleTower { -public: - ZeusTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Zeus; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -// ---------- Big Towers ---------- - -class BigTower : public Tower { -public: - BigTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {} - - virtual TowerSize GetSize() const { return TowerSize::Big; } - - virtual TowerType GetType() const = 0; - virtual void Tick(std::uint64_t delta, World* world) = 0; -}; - -class TurretTower : public BigTower { -public: - TurretTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Turret; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class NecromancerTower : public BigTower { -public: - NecromancerTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Necromancer; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -class LeachTower : public BigTower { -public: - LeachTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} - - virtual TowerType GetType() const { return TowerType::Leach; } - virtual void Tick(std::uint64_t delta, World* world); -}; - -} // namespace game -} // namespace td +} // namespace game +} // namespace td From 4128b7fbb74f5ad6b37ad6aaadec1d6a359dba7a Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 28 Jul 2025 14:28:14 +0200 Subject: [PATCH 19/39] apply steps at the end --- src/td/simulation/WorldTicker.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/td/simulation/WorldTicker.cpp b/src/td/simulation/WorldTicker.cpp index fcebd0d..88f465d 100644 --- a/src/td/simulation/WorldTicker.cpp +++ b/src/td/simulation/WorldTicker.cpp @@ -3,8 +3,6 @@ #include #include -#define ADD_SYSTEM(class) std::move(std::make_unique()) - namespace td { namespace sim { @@ -15,8 +13,8 @@ WorldTicker::WorldTicker() { WorldSnapshot WorldTicker::NextStep( const game::World& a_World, WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) { WorldSnapshot next = CreateNext(a_PreviousState); - ApplySteps(a_World, next, a_LockStep); // maybe swap with tick ? Tick(a_World, next, a_Delta); + ApplySteps(a_World, next, a_LockStep); return next; } From 4f0a81d670cfa6487a1592ce044341e0ff749619 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 28 Jul 2025 14:28:29 +0200 Subject: [PATCH 20/39] add PredictComma,d --- include/td/protocol/packet/PacketData.h | 5 +++++ include/td/protocol/packet/Packets.h | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index de85032..32b954a 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -43,6 +43,11 @@ struct PlayerLeave { PlayerID m_PlayerId; }; +struct PredictCommand { + std::unique_ptr m_Command; + std::uint16_t m_FrameNumber; +}; + /** Keep alive */ struct KeepAlive { std::uint64_t m_KeepAliveId; diff --git a/include/td/protocol/packet/Packets.h b/include/td/protocol/packet/Packets.h index 544061b..9b2ebd9 100644 --- a/include/td/protocol/packet/Packets.h +++ b/include/td/protocol/packet/Packets.h @@ -27,6 +27,7 @@ enum class PacketID : std::uint8_t { PlayerJoin, PlayerLeave, PlayerLogin, + PredictCommand, WorldHeader, WorldData, }; @@ -51,15 +52,15 @@ using LoggingSuccessPacket = PacketMessage; using PlayerLeavePacket = PacketMessage; using PlayerLoginPacket = PacketMessage; +using PredictCommandPacket = PacketMessage; using WorldHeaderPacket = PacketMessage; using WorldDataPacket = PacketMessage; - } // namespace packets using AllPackets = std::tuple; + packets::PlayerLeavePacket, packets::PlayerLoginPacket, packets::PredictCommandPacket, packets::WorldHeaderPacket, packets::WorldDataPacket>; class PacketHandler : public sp::GenericHandler {}; From d0b30ba6f84f394cda4d49e19ac8b6f5788f1543 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 28 Jul 2025 14:28:37 +0200 Subject: [PATCH 21/39] process tower creation --- src/td/simulation/CommandApply.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/td/simulation/CommandApply.cpp b/src/td/simulation/CommandApply.cpp index 02a1beb..dd227af 100644 --- a/src/td/simulation/CommandApply.cpp +++ b/src/td/simulation/CommandApply.cpp @@ -6,9 +6,17 @@ 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::PlaceTowerCommand& a_PlaceTower) { - m_World.CanPlaceBigTower({}, 0); + static game::TowerFactory factory; + + auto tower = std::shared_ptr(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) { From da1586baed7ff5a2fd563dae4be6ecccdc4d031b Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 28 Jul 2025 14:32:01 +0200 Subject: [PATCH 22/39] remove unused function --- include/td/game/Towers.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/td/game/Towers.h b/include/td/game/Towers.h index 19dc243..4715855 100644 --- a/include/td/game/Towers.h +++ b/include/td/game/Towers.h @@ -82,9 +82,7 @@ class TowerLevel { } }; -const TowerStats* GetTowerStats(TowerType type, TowerLevel level); - -typedef std::uint16_t TowerID; +using TowerID = std::uint16_t; class TowerHandler; From 4c2ac7e3f046dd856fff85d467ab9cd477731d6e Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 28 Jul 2025 18:11:17 +0200 Subject: [PATCH 23/39] temp tower rendering --- include/td/Types.h | 12 ++++++++++ include/td/render/renderer/TowerRenderer.h | 23 ++++++++++++++++++ src/main.cpp | 7 +++++- src/td/render/renderer/TowerRenderer.cpp | 27 ++++++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 include/td/render/renderer/TowerRenderer.h create mode 100644 src/td/render/renderer/TowerRenderer.cpp diff --git a/include/td/Types.h b/include/td/Types.h index 497c4fa..1b91d23 100644 --- a/include/td/Types.h +++ b/include/td/Types.h @@ -83,4 +83,16 @@ 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); +inline TowerType& operator>>=(TowerType& a_Type, std::size_t a_Offset) { + return a_Type; +} + +inline TowerType operator&(const TowerType& a_Type, std::size_t a_Offset) { + return a_Type; +} + +inline TowerType& operator&=(TowerType& a_Type, std::size_t a_Offset) { + return a_Type; +} + } // namespace td diff --git a/include/td/render/renderer/TowerRenderer.h b/include/td/render/renderer/TowerRenderer.h new file mode 100644 index 0000000..55f4dc3 --- /dev/null +++ b/include/td/render/renderer/TowerRenderer.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace render { + +class TowerRenderer : public Renderer { + private: + const game::World& m_World; + std::unique_ptr m_EntityVao; + + public: + TowerRenderer(Camera& a_Camera, const game::World& a_World); + virtual ~TowerRenderer(); + + virtual void Render(float a_Lerp) override; +}; + +} // namespace render +} // namespace td diff --git a/src/main.cpp b/src/main.cpp index f0c9b68..6e3140b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include #include +#include #include @@ -74,6 +75,9 @@ td::sim::GameHistory GetCustomHistory() { auto spawn = std::make_shared(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0); gh[0].push_back(spawn); + auto tower = std::make_shared(td::TowerType::Archer, 0, td::TowerCoords{77, 13}); + gh[0].push_back(tower); + return gh; } @@ -94,11 +98,12 @@ int main(int argc, char** argv) { td::render::RenderPipeline renderer; renderer.AddRenderer(cam, w); renderer.AddRenderer(cam, w); + renderer.AddRenderer(cam, w); cam.SetCamPos({77, 5, 13}); cam.UpdatePerspective(display.GetAspectRatio()); - td::sim::RealTimeSimulation simulation(w, std::move(gh), 2000); + td::sim::RealTimeSimulation simulation(w, std::move(gh), 500); while (!display.IsCloseRequested()) { display.PollEvents(); diff --git a/src/td/render/renderer/TowerRenderer.cpp b/src/td/render/renderer/TowerRenderer.cpp new file mode 100644 index 0000000..b9ad89c --- /dev/null +++ b/src/td/render/renderer/TowerRenderer.cpp @@ -0,0 +1,27 @@ +#include + +#include + +namespace td { +namespace render { + +TowerRenderer::TowerRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { + m_EntityVao = std::make_unique(WorldLoader::LoadMobModel()); + m_Shader->Start(); + m_Shader->SetColorEffect({0, 0, 1}); +} + +TowerRenderer::~TowerRenderer() {} + + + +void TowerRenderer::Render(float a_Lerp) { + m_Shader->Start(); + for (const auto& tower : m_World.GetTowers()) { + m_Shader->SetModelPos({tower->GetCenterX(), 1, tower->GetCenterY()}); + Renderer::Render(*m_EntityVao); + } +} + +} // namespace render +} // namespace td From 56a43d7a60d8993f31627ce38d83b262c4323367 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Tue, 29 Jul 2025 15:17:56 +0200 Subject: [PATCH 24/39] update sp (illegal) --- include/td/Types.h | 12 ------------ include/td/protocol/packet/PacketSerialize.h | 3 --- src/td/Types.cpp | 2 -- src/td/protocol/packet/PacketSerialize.cpp | 2 -- 4 files changed, 19 deletions(-) diff --git a/include/td/Types.h b/include/td/Types.h index 1b91d23..497c4fa 100644 --- a/include/td/Types.h +++ b/include/td/Types.h @@ -83,16 +83,4 @@ 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); -inline TowerType& operator>>=(TowerType& a_Type, std::size_t a_Offset) { - return a_Type; -} - -inline TowerType operator&(const TowerType& a_Type, std::size_t a_Offset) { - return a_Type; -} - -inline TowerType& operator&=(TowerType& a_Type, std::size_t a_Offset) { - return a_Type; -} - } // namespace td diff --git a/include/td/protocol/packet/PacketSerialize.h b/include/td/protocol/packet/PacketSerialize.h index 81ef979..85baf49 100644 --- a/include/td/protocol/packet/PacketSerialize.h +++ b/include/td/protocol/packet/PacketSerialize.h @@ -6,13 +6,10 @@ 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/src/td/Types.cpp b/src/td/Types.cpp index 1e7508b..d4c2fdf 100644 --- a/src/td/Types.cpp +++ b/src/td/Types.cpp @@ -11,7 +11,6 @@ sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coord sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float) { auto raw = a_Float.raw_value(); - sp::ToNetwork(raw); return a_Buffer << raw; } @@ -22,7 +21,6 @@ sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, EntityCoords& a_Coords) { 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; } diff --git a/src/td/protocol/packet/PacketSerialize.cpp b/src/td/protocol/packet/PacketSerialize.cpp index 29ce7ac..1c5b365 100644 --- a/src/td/protocol/packet/PacketSerialize.cpp +++ b/src/td/protocol/packet/PacketSerialize.cpp @@ -3,7 +3,6 @@ #include namespace sp { -namespace details { template <> void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header) { @@ -111,5 +110,4 @@ void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldDa } } -} // namespace details } // namespace sp \ No newline at end of file From 2e556e0d4522616a59f091f280dad0fa008d9cf0 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 30 Jul 2025 17:52:54 +0200 Subject: [PATCH 25/39] feat: fast forward --- include/td/game/Team.h | 24 ++++++++- include/td/game/Towers.h | 6 +-- include/td/game/World.h | 29 +++++----- include/td/input/Display.h | 2 + include/td/protocol/packet/PacketData.h | 2 +- include/td/simulation/RealTimeSimulation.h | 31 ++++++++++- src/main.cpp | 12 ++++- src/td/game/World.cpp | 12 +++-- src/td/input/Display.cpp | 8 +++ src/td/simulation/CommandApply.cpp | 4 +- src/td/simulation/GameHistory.cpp | 2 +- src/td/simulation/RealTimeSimulation.cpp | 63 ++++++++++++++++++++-- 12 files changed, 164 insertions(+), 31 deletions(-) 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 From 2b8447766abca2602861784e7e7689a6bd700b2d Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 30 Jul 2025 17:56:13 +0200 Subject: [PATCH 26/39] fix gcc build --- include/td/common/Array.h | 4 ++++ include/td/protocol/packet/PacketData.h | 2 +- src/main.cpp | 2 +- src/td/simulation/GameHistory.cpp | 6 +++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/td/common/Array.h b/include/td/common/Array.h index 50964d0..87a2b88 100644 --- a/include/td/common/Array.h +++ b/include/td/common/Array.h @@ -48,6 +48,10 @@ class Array { return m_Data[a_Index]; } + const T& operator[](std::size_t a_Index) const { + return m_Data[a_Index]; + } + ~Array() { delete [] m_Data; } diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index d395ec3..32b954a 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; - std::array m_LockSteps; + Array m_LockSteps; }; struct WorldHeader { diff --git a/src/main.cpp b/src/main.cpp index 08a570a..53c18b0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -108,7 +108,7 @@ int main(int argc, char** argv) { 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{}; + td::Array steps{}; steps[0].push_back(spawn); td::protocol::packets::LockStepsPacket packet{0, steps}; simulation.HandlePacket(packet); diff --git a/src/td/simulation/GameHistory.cpp b/src/td/simulation/GameHistory.cpp index 0639723..ef8cce1 100644 --- a/src/td/simulation/GameHistory.cpp +++ b/src/td/simulation/GameHistory.cpp @@ -25,15 +25,15 @@ bool GameHistory::HasNextStep() const { return HasLockStep(m_Cursor); } -void GameHistory::FromPacket(td::protocol::pdata::LockSteps&& a_Steps) { +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)); } } -td::protocol::packets::LockStepsPacket GameHistory::ToPacket(HistorySizeType a_StartIndex) { - std::array steps; +protocol::packets::LockStepsPacket GameHistory::ToPacket(HistorySizeType a_StartIndex) { + Array steps; for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { steps[i] = GetLockStep(a_StartIndex + i); } From 02d872c49b5974ac392842b48aca0399513bd815 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 31 Jul 2025 13:48:26 +0200 Subject: [PATCH 27/39] migrating save files --- include/td/game/Team.h | 8 +- include/td/game/World.h | 4 +- include/td/game/WorldTypes.h | 20 +-- include/td/protocol/packet/PacketData.h | 2 +- include/td/protocol/packet/PacketSerialize.h | 56 ++++++- src/main.cpp | 47 ++++-- src/td/game/World.cpp | 4 +- src/td/game/WorldTypes.cpp | 33 ---- src/td/protocol/packet/ChunkSerialize.cpp | 79 +++++++++ src/td/protocol/packet/PacketSerialize.cpp | 165 ++++++++----------- src/td/render/loader/WorldLoader.cpp | 5 +- test/tdmap.tdmap | Bin 0 -> 595 bytes test/tdmap.tdmap2 | Bin 595 -> 651 bytes xmake.lua | 2 +- 14 files changed, 249 insertions(+), 176 deletions(-) create mode 100644 src/td/protocol/packet/ChunkSerialize.cpp create mode 100644 test/tdmap.tdmap 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 0000000000000000000000000000000000000000..798a860df68b2a187e39b8af964fb7967bd5402e GIT binary patch literal 595 zcmV-Z0<8Uhc$_<9%r)Od^TMYxnGll+>=2 zKR*_d_LoGA>flNlL~thqmpK?qTIK#Lm%Fnecqi_s&brQl=gxcZ6?BrF*n)q+PqNAn zIT4OAfEY8}I~%Uu3-b(jI|sX+DbMIs`~TZox|Yjr%_Umo^3QA8_)}8Xik=mp4fK5y zN%{{q4@=rF=P~|V{cQ0-vMDFvJn(%6+raA;9hUY=nUo)mdV`p+G4o4lcu(vF>?ag> zoVt8{6aKQaXp_C$Q2qu^r~1UEmuc|F%rs8B-0<68!*`#<{~Z&aeDHcjenN1U0~1*L zID1UpvG4D>@Qn8|i;SjvMV^EI7i*sp6%_h2zlil&ycc{&c~+nGYo8YKh<;0^g>3$= zn7YMj@6Q}!ozELb@N0a=^tQnt1FQZOd=Ye$U?+@&Zjv|7TKvxFgTo}s6ALa*8{P(E zzfCL$y=L#PS<2wgar*n`xpL;V>AT^SIvI hy7D`ss>W?%dk^OwH;>&6MX+r#0FK+xJ{)t(6uQIKR2E z+!KaUmw2GwcV-G^ZJtBFhiY7>;09(t^W4q@_j>cy#Ab?zqh2WQ)A|6mv*76N7xV`x z@K2X9s*cX2yG1|n5jKt01z2VsnD{rY!8{*fE2Kg{UN1hLWooqW@%}ra&!QuhJw5_E zSo9OeOCI!v58^-?MJ8KRYSJB-(yPy{aTZGn`PlOFb=c;ob^~Z0@alac`BV6z(m_19w;RwR^`HE4*e^mGeth-24UxPMmzN3I`Le7%XwA z>p=ast#_eEXgr*_zN(^HsrW>yn?(Nk`U4$4l7^rqLi+#! literal 595 zcmV-Z0<8Uhc$_<9%r)Od^TMYxnGll+>=2 zKR*_d_LoGA>flNlL~thqmpK?qTIK#Lm%Fnecqi_s&brQl=gxcZ6?BrF*n)q+PqNAn zIT4OAfEY8}I~%Uu3-b(jI|sX+DbMIs`~TZox|Yjr%_Umo^3QA8_)}8Xik=mp4fK5y zN%{{q4@=rF=P~|V{cQ0-vMDFvJn(%6+raA;9hUY=nUo)mdV`p+G4o4lcu(vF>?ag> zoVt8{6aKQaXp_C$Q2qu^r~1UEmuc|F%rs8B-0<68!*`#<{~Z&aeDHcjenN1U0~1*L zID1UpvG4D>@Qn8|i;SjvMV^EI7i*sp6%_h2zlil&ycc{&c~+nGYo8YKh<;0^g>3$= zn7YMj@6Q}!ozELb@N0a=^tQnt1FQZOd=Ye$U?+@&Zjv|7TKvxFgTo}s6ALa*8{P(E zzfCL$y=L#PS<2wgar*n`xpL;V>AT^SIvI hy7D Date: Thu, 31 Jul 2025 15:07:34 +0200 Subject: [PATCH 28/39] fix predictpacket serialization --- include/td/protocol/command/Commands.h | 4 +++- include/td/protocol/packet/PacketData.h | 2 +- include/td/protocol/packet/PacketSerialize.h | 15 --------------- src/main.cpp | 12 +++++------- src/td/protocol/packet/PacketSerialize.cpp | 11 ----------- xmake.lua | 2 +- 6 files changed, 10 insertions(+), 36 deletions(-) diff --git a/include/td/protocol/command/Commands.h b/include/td/protocol/command/Commands.h index a0dcdbb..93cc112 100644 --- a/include/td/protocol/command/Commands.h +++ b/include/td/protocol/command/Commands.h @@ -6,10 +6,10 @@ */ #include +#include #include #include #include -#include #include #include #include @@ -32,6 +32,8 @@ class CommandHandler; using CommandBase = sp::MessageBase; +using CommandPtr = std::unique_ptr; + template using CommandMessage = sp::ConcreteMessage; diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index 139947f..da5648d 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -44,7 +44,7 @@ struct PlayerLeave { }; struct PredictCommand { - std::unique_ptr m_Command; + CommandPtr m_Command; std::uint16_t m_FrameNumber; }; diff --git a/include/td/protocol/packet/PacketSerialize.h b/include/td/protocol/packet/PacketSerialize.h index b732517..83a32cb 100644 --- a/include/td/protocol/packet/PacketSerialize.h +++ b/include/td/protocol/packet/PacketSerialize.h @@ -2,20 +2,6 @@ #include -namespace sp { - -class DataBuffer; - -// temp -template <> -void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::PredictCommand& a_Header); - -template <> -DataBuffer WriteMessage(const td::protocol::pdata::PredictCommand& a_Header); - -} // namespace sp - - namespace td { template @@ -49,7 +35,6 @@ 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); diff --git a/src/main.cpp b/src/main.cpp index 66a7d2a..4e05e79 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,22 +1,20 @@ #include -#include -#include -#include - +#include #include +#include #include #include - #include #include #include - #include +#include +#include #include +#include #include -#include class WorldApply : public td::protocol::PacketHandler { private: diff --git a/src/td/protocol/packet/PacketSerialize.cpp b/src/td/protocol/packet/PacketSerialize.cpp index 7fe85f6..426b65b 100644 --- a/src/td/protocol/packet/PacketSerialize.cpp +++ b/src/td/protocol/packet/PacketSerialize.cpp @@ -1,17 +1,6 @@ #include #include -namespace sp { - -// temp -template<> -void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::PredictCommand& a_Header){} - -template<> -DataBuffer WriteMessage(const td::protocol::pdata::PredictCommand& a_Header){} - -} // namespace sp - namespace td { namespace game { diff --git a/xmake.lua b/xmake.lua index f87cf17..813cf64 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.1", "zlib") +add_requires("splib 2.0.2", "zlib") add_requires("libsdl3 3.2.16", "glew", "fpm", "enet6") set_languages("c++17") From 1a455a3d6b2350b8a533489e055a4bbbe58487f3 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 31 Jul 2025 18:02:14 +0200 Subject: [PATCH 29/39] refactor tile serialize --- include/td/game/Mobs.h | 5 +- include/td/game/WorldTypes.h | 76 ++++++++++++-------- include/td/protocol/command/Commands.h | 5 +- include/td/protocol/packet/PacketSerialize.h | 3 - include/td/simulation/WorldSnapshot.h | 1 + src/main.cpp | 1 - src/td/game/World.cpp | 48 ++++++++----- src/td/game/WorldTypes.cpp | 2 - src/td/protocol/packet/PacketSerialize.cpp | 58 --------------- xmake.lua | 2 +- 10 files changed, 84 insertions(+), 117 deletions(-) diff --git a/include/td/game/Mobs.h b/include/td/game/Mobs.h index 43ad263..ace8d0d 100644 --- a/include/td/game/Mobs.h +++ b/include/td/game/Mobs.h @@ -18,9 +18,6 @@ using Vec2fp = Vec2; namespace game { - -struct WalkableTile; - enum class EffectType : std::uint8_t { Slowness = 0, Stun, @@ -127,6 +124,8 @@ class MobListener { virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) {} }; +using MobList = std::vector; + // typedef utils::ObjectNotifier MobNotifier; } // namespace game diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h index 1d872ec..745aa0b 100644 --- a/include/td/game/WorldTypes.h +++ b/include/td/game/WorldTypes.h @@ -1,10 +1,11 @@ #pragma once #include -#include #include #include +#include + namespace td { namespace game { @@ -31,37 +32,44 @@ static constexpr Color RED{255, 0, 0}; static constexpr Color GREEN{0, 255, 0}; static constexpr Color BLUE{0, 0, 255}; -struct Tile { - virtual TileType GetType() const = 0; - virtual ~Tile() = default; +class TileHandler; + +using Tile = sp::MessageBase; + +template +using ConcreteTile = sp::ConcreteMessage; + +namespace data { +struct EmptyData {}; + +struct TowerTileData { + std::uint8_t m_ColorPaletteRef; + TeamColor m_TeamOwner; }; -struct TowerTile : Tile { - std::uint8_t color_palette_ref; - TeamColor team_owner; - - virtual TileType GetType() const { - return TileType::Tower; - } +struct WalkableTileData { + Direction m_Direction; }; -struct WalkableTile : Tile { - Direction direction; - - virtual TileType GetType() const { - return TileType::Walk; - } +struct DecorationTileData { + std::uint16_t m_ColorPaletteRef; }; +} // namespace data -struct DecorationTile : Tile { - std::uint16_t color_palette_ref; +using EmptyTile = ConcreteTile; +using TowerTile = ConcreteTile; +using WalkableTile = ConcreteTile; +using DecorationTile = ConcreteTile; - virtual TileType GetType() const { - return TileType::Decoration; - } -}; +using AllTiles = std::tuple; -typedef std::shared_ptr TilePtr; +using TileFactory = sp::MessageFactory; + +class TileHandler : public sp::GenericHandler{}; + +using TilePtr = std::shared_ptr>; + +// typedef std::shared_ptr TilePtr; typedef std::vector ChunkPalette; typedef std::shared_ptr WalkableTilePtr; @@ -85,6 +93,21 @@ struct Chunk { } // TODO: keep data packed + /* + 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)); + } + */ }; typedef std::shared_ptr ChunkPtr; @@ -93,17 +116,12 @@ typedef std::array TowerTileColorPalette; typedef std::vector TilePalette; -typedef std::vector MobList; - typedef std::array SpawnColorPalette; typedef std::vector TowerList; using ChunkList = std::unordered_map; -sp::DataBuffer& operator>>(sp::DataBuffer& buffer, TilePtr& tile); - - } // namespace game } // namespace td diff --git a/include/td/protocol/command/Commands.h b/include/td/protocol/command/Commands.h index 93cc112..b55102b 100644 --- a/include/td/protocol/command/Commands.h +++ b/include/td/protocol/command/Commands.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace td { namespace protocol { @@ -32,8 +33,6 @@ class CommandHandler; using CommandBase = sp::MessageBase; -using CommandPtr = std::unique_ptr; - template using CommandMessage = sp::ConcreteMessage; @@ -61,5 +60,7 @@ using CommandFactory = sp::MessageFactory; using LockStep = std::vector>; +using CommandPtr = std::unique_ptr>; + } // namespace protocol } // namespace td diff --git a/include/td/protocol/packet/PacketSerialize.h b/include/td/protocol/packet/PacketSerialize.h index 83a32cb..0f0b127 100644 --- a/include/td/protocol/packet/PacketSerialize.h +++ b/include/td/protocol/packet/PacketSerialize.h @@ -43,8 +43,5 @@ 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/include/td/simulation/WorldSnapshot.h b/include/td/simulation/WorldSnapshot.h index 2081289..3f20b64 100644 --- a/include/td/simulation/WorldSnapshot.h +++ b/include/td/simulation/WorldSnapshot.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace td { namespace sim { diff --git a/src/main.cpp b/src/main.cpp index 4e05e79..6de07e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include class WorldApply : public td::protocol::PacketHandler { diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp index 894cdaa..ba9be9a 100644 --- a/src/td/game/World.cpp +++ b/src/td/game/World.cpp @@ -5,27 +5,39 @@ namespace td { namespace game { +class ColorTileVisitor : public TileHandler { + private: + const World& m_World; + const Color* m_Result; + + public: + ColorTileVisitor(const World& a_World) : m_World(a_World), m_Result(nullptr) {} + + virtual void Handle(const EmptyTile& a_Tile) override {} + + virtual void Handle(const TowerTile& a_Tile) override { + m_Result = &m_World.GetTowerTileColorPalette()[a_Tile->m_ColorPaletteRef]; + } + + virtual void Handle(const WalkableTile& a_Tile) override { + m_Result = &m_World.GetWalkableTileColor(); + } + + virtual void Handle(const DecorationTile& a_Tile) override { + m_Result = &m_World.GetDecorationPalette()[a_Tile->m_ColorPaletteRef]; + } + + const Color* GetResult() { + return m_Result; + } +}; + World::World() : m_CurrentState(std::make_shared()), m_NextState(m_CurrentState) {} const Color* World::GetTileColor(const TilePtr& tile) const { - switch (tile->GetType()) { - case TileType::Tower: { - TowerTile* towerTile = dynamic_cast(tile.get()); - return &m_TowerPlacePalette[towerTile->color_palette_ref]; - } - case TileType::Walk: { - return &m_WalkablePalette; - } - case TileType::Decoration: { - DecorationTile* towerTile = dynamic_cast(tile.get()); - return &m_DecorationPalette[towerTile->color_palette_ref]; - break; - } - default: { - return nullptr; - } - } - return nullptr; + ColorTileVisitor visitor(*this); + tile->Dispatch(visitor); + return visitor.GetResult(); } bool World::LoadMap(const protocol::pdata::WorldHeader& a_WorldHeader) { diff --git a/src/td/game/WorldTypes.cpp b/src/td/game/WorldTypes.cpp index 09afe11..25d25f6 100644 --- a/src/td/game/WorldTypes.cpp +++ b/src/td/game/WorldTypes.cpp @@ -1,7 +1,5 @@ #include -#include - namespace td { namespace game { diff --git a/src/td/protocol/packet/PacketSerialize.cpp b/src/td/protocol/packet/PacketSerialize.cpp index 426b65b..2ba507e 100644 --- a/src/td/protocol/packet/PacketSerialize.cpp +++ b/src/td/protocol/packet/PacketSerialize.cpp @@ -15,63 +15,5 @@ sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, TeamCastle& a_Castle) { return a_Buffer; } -sp::DataBuffer& operator<<(sp::DataBuffer& buffer, const TilePtr& tile) { - buffer << tile->GetType(); - - switch (tile->GetType()) { - - case game::TileType::Tower: { - const game::TowerTile* towerTile = dynamic_cast(tile.get()); - buffer << towerTile->color_palette_ref << towerTile->team_owner; - break; - } - - 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; -} - -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/xmake.lua b/xmake.lua index 813cf64..959efb7 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.2", "zlib") +add_requires("splib 2.1.0", "zlib") add_requires("libsdl3 3.2.16", "glew", "fpm", "enet6") set_languages("c++17") From fa663d0481c655ff6be718e4f391aaccb447460d Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Thu, 31 Jul 2025 19:03:41 +0200 Subject: [PATCH 30/39] packed chunk data --- include/td/game/WorldTypes.h | 31 ++--------- include/td/protocol/command/Commands.h | 2 +- src/main.cpp | 28 +++++----- src/td/game/WorldTypes.cpp | 28 ++++++++++ src/td/protocol/packet/ChunkSerialize.cpp | 66 ++--------------------- 5 files changed, 51 insertions(+), 104 deletions(-) diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h index 745aa0b..4177225 100644 --- a/include/td/game/WorldTypes.h +++ b/include/td/game/WorldTypes.h @@ -65,7 +65,7 @@ using AllTiles = std::tuple; using TileFactory = sp::MessageFactory; -class TileHandler : public sp::GenericHandler{}; +class TileHandler : public sp::GenericHandler {}; using TilePtr = std::shared_ptr>; @@ -79,35 +79,14 @@ typedef std::uint32_t TileIndex; // 32 x 32 area struct Chunk { enum { ChunkWidth = 32, ChunkHeight = 32, ChunkSize = ChunkWidth * ChunkHeight }; - typedef std::array ChunkData; + using ChunkData = std::array; + using ChunkPackedData = std::vector; // stores index of tile palette - ChunkData m_Tiles{0}; ChunkPalette m_Palette; + ChunkPackedData m_Data; - TileIndex GetTileIndex(std::uint16_t tileNumber) const { - TileIndex chunkPaletteIndex = m_Tiles.at(tileNumber); - if (chunkPaletteIndex == 0) // index 0 means empty tile index 1 = first tile - return 0; - return m_Palette.at(chunkPaletteIndex); - } - - // TODO: keep data packed - /* - 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)); - } - */ + TileIndex GetTileIndex(std::uint16_t tileNumber) const; }; typedef std::shared_ptr ChunkPtr; diff --git a/include/td/protocol/command/Commands.h b/include/td/protocol/command/Commands.h index b55102b..77c9f1a 100644 --- a/include/td/protocol/command/Commands.h +++ b/include/td/protocol/command/Commands.h @@ -7,13 +7,13 @@ #include #include +#include #include #include #include #include #include #include -#include namespace td { namespace protocol { diff --git a/src/main.cpp b/src/main.cpp index 6de07e6..ab13d0d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,18 @@ class WorldApply : public td::protocol::PacketHandler { } }; +void Save(const td::protocol::PacketBase& header, const td::protocol::PacketBase& data) { + auto comp = std::make_shared(); + + std::ofstream fStream("test/tdmap.tdmap3"); + auto out = std::make_shared(fStream); + + sp::MessageStream stream(std::move(out), std::move(comp)); + + stream.WriteMessage(header); + stream.WriteMessage(data); +} + td::game::World GetWorld() { auto comp = std::make_shared(); @@ -53,23 +65,11 @@ td::game::World GetWorld() { d.Dispatch(*header); d.Dispatch(*data); + Save(*header, *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) { const td::FpFloat delta = td::FpFloat(1) / td::FpFloat(75); for (const auto& lockstep : a_LockSteps) { diff --git a/src/td/game/WorldTypes.cpp b/src/td/game/WorldTypes.cpp index 25d25f6..a9c2f96 100644 --- a/src/td/game/WorldTypes.cpp +++ b/src/td/game/WorldTypes.cpp @@ -3,7 +3,35 @@ namespace td { namespace game { +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); +} + +TileIndex Chunk::GetTileIndex(std::uint16_t tileNumber) const { + const std::uint8_t bitsPerTile = countBits(m_Palette.size()); + + const std::size_t startLong = (tileNumber * bitsPerTile) / BITS_IN_LONG; + const std::size_t startOffset = (tileNumber * bitsPerTile) % BITS_IN_LONG; + const std::size_t endLong = ((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG; + + const Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1); + + td::game::Chunk::ChunkData::value_type value; + if (startLong == endLong) { + value = (m_Data[startLong] >> startOffset); + } else { + int endOffset = BITS_IN_LONG - startOffset; + value = (m_Data[startLong] >> startOffset | m_Data[endLong] << endOffset); + } + value &= individualValueMask; + + return m_Palette.at(value); +} } // namespace game } // namespace td diff --git a/src/td/protocol/packet/ChunkSerialize.cpp b/src/td/protocol/packet/ChunkSerialize.cpp index 1f9fe2c..b666424 100644 --- a/src/td/protocol/packet/ChunkSerialize.cpp +++ b/src/td/protocol/packet/ChunkSerialize.cpp @@ -1,78 +1,18 @@ #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; + a_Buffer << a_Chunk->m_Data; + return a_Buffer; } 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; + return a_Buffer >> a_Chunk->m_Data; } } // namespace game From ced20ca991db25caf3aeb8ad756fd6d1ce0b811d Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 1 Aug 2025 13:21:31 +0200 Subject: [PATCH 31/39] less serialize code --- include/td/game/World.h | 2 +- include/td/game/WorldTypes.h | 2 +- include/td/protocol/command/Commands.h | 7 ++++--- include/td/protocol/packet/PacketData.h | 2 +- include/td/protocol/packet/PacketSerialize.h | 4 ++-- include/td/simulation/RealTimeSimulation.h | 2 +- src/main.cpp | 12 +++++++----- src/td/Types.cpp | 1 + src/td/protocol/packet/ChunkSerialize.cpp | 10 ---------- src/td/protocol/packet/PacketSerialize.cpp | 14 ++++++++++++++ src/td/render/loader/WorldLoader.cpp | 2 +- src/td/simulation/GameHistory.cpp | 2 +- src/td/simulation/RealTimeSimulation.cpp | 6 +++--- test/tdmap.tdmap2 | Bin 651 -> 638 bytes xmake.lua | 2 +- 15 files changed, 38 insertions(+), 30 deletions(-) diff --git a/include/td/game/World.h b/include/td/game/World.h index 93ee19f..82edaf9 100644 --- a/include/td/game/World.h +++ b/include/td/game/World.h @@ -61,7 +61,7 @@ class World { TilePtr GetTilePtr(TileIndex index) const { if (index == 0) - return nullptr; + return TilePtr(nullptr); return m_TilePalette.at(index - 1); } diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h index 4177225..60023a6 100644 --- a/include/td/game/WorldTypes.h +++ b/include/td/game/WorldTypes.h @@ -67,7 +67,7 @@ using TileFactory = sp::MessageFactory; class TileHandler : public sp::GenericHandler {}; -using TilePtr = std::shared_ptr>; +using TilePtr = sp::SerializableMessage; // typedef std::shared_ptr TilePtr; typedef std::vector ChunkPalette; diff --git a/include/td/protocol/command/Commands.h b/include/td/protocol/command/Commands.h index 77c9f1a..b9fae92 100644 --- a/include/td/protocol/command/Commands.h +++ b/include/td/protocol/command/Commands.h @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -15,6 +14,8 @@ #include #include +#include + namespace td { namespace protocol { @@ -58,9 +59,9 @@ using CommandDispatcher = sp::MessageDispatcher; using CommandFactory = sp::MessageFactory; -using LockStep = std::vector>; +using CommandPtr = sp::SerializableMessage; -using CommandPtr = std::unique_ptr>; +using LockStep = std::vector; } // namespace protocol } // namespace td diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index da5648d..656bb8d 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/protocol/packet/PacketSerialize.h b/include/td/protocol/packet/PacketSerialize.h index 0f0b127..29c2331 100644 --- a/include/td/protocol/packet/PacketSerialize.h +++ b/include/td/protocol/packet/PacketSerialize.h @@ -40,8 +40,8 @@ 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& a_Buffer, const Spawn& a_Spawn); +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Spawn& a_Spawn); } // namespace game } // namespace td diff --git a/include/td/simulation/RealTimeSimulation.h b/include/td/simulation/RealTimeSimulation.h index 2fe4db2..e77f42b 100644 --- a/include/td/simulation/RealTimeSimulation.h +++ b/include/td/simulation/RealTimeSimulation.h @@ -28,7 +28,7 @@ class RealTimeSimulation { * \brief Replay constructor * \param a_StepTime in ms */ - RealTimeSimulation(game::World& a_World, const GameHistory& a_History, std::uint64_t a_StepTime); + RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime); /** * \brief Live update constructor (continuous game updates) diff --git a/src/main.cpp b/src/main.cpp index ab13d0d..2466406 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,10 +82,12 @@ td::sim::GameHistory GetCustomHistory() { td::sim::GameHistory gh(MAX_COUNT); - auto spawn = std::make_shared(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0); + auto spawn = td::protocol::CommandPtr( + std::make_shared(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0)); gh[0].push_back(spawn); - auto tower = std::make_shared(td::TowerType::Archer, 0, td::TowerCoords{77, 13}); + auto tower = td::protocol::CommandPtr( + std::make_shared(td::TowerType::Archer, 0, td::TowerCoords{77, 13})); gh[0].push_back(tower); return gh; @@ -116,9 +118,9 @@ int main(int argc, char** argv) { 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); - td::Array steps{}; + auto spawn = td::protocol::CommandPtr( + 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); diff --git a/src/td/Types.cpp b/src/td/Types.cpp index d4c2fdf..25ce84f 100644 --- a/src/td/Types.cpp +++ b/src/td/Types.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace td { diff --git a/src/td/protocol/packet/ChunkSerialize.cpp b/src/td/protocol/packet/ChunkSerialize.cpp index b666424..28d20d7 100644 --- a/src/td/protocol/packet/ChunkSerialize.cpp +++ b/src/td/protocol/packet/ChunkSerialize.cpp @@ -3,17 +3,7 @@ namespace td { namespace game { -sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const ChunkPtr& a_Chunk) { - a_Buffer << a_Chunk->m_Palette; - a_Buffer << a_Chunk->m_Data; - return a_Buffer; -} -sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, ChunkPtr& a_Chunk) { - a_Chunk = std::make_shared(); - a_Buffer >> a_Chunk->m_Palette; - return a_Buffer >> a_Chunk->m_Data; -} } // 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 2ba507e..b119ca6 100644 --- a/src/td/protocol/packet/PacketSerialize.cpp +++ b/src/td/protocol/packet/PacketSerialize.cpp @@ -1,6 +1,8 @@ #include #include +#include + namespace td { namespace game { @@ -15,5 +17,17 @@ sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, TeamCastle& a_Castle) { return a_Buffer; } +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Spawn& a_Spawn) { + return a_Buffer << a_Spawn.GetCenterX() << a_Spawn.GetCenterY(); +} + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Spawn& a_Spawn) { + float x, y; + a_Buffer >> x >> y; + a_Spawn.SetCenter({x, y}); + return a_Buffer; +} + + } // namespace game } // namespace td diff --git a/src/td/render/loader/WorldLoader.cpp b/src/td/render/loader/WorldLoader.cpp index fe1a7a5..fa478ce 100644 --- a/src/td/render/loader/WorldLoader.cpp +++ b/src/td/render/loader/WorldLoader.cpp @@ -27,7 +27,7 @@ GL::VertexArray LoadWorldModel(const td::game::World* world) { td::game::TileIndex tileIndex = chunk->GetTileIndex(tileNumber); td::game::TilePtr tile = world->GetTilePtr(tileIndex); - if (tile == nullptr) + if (!tile) continue; positions.insert( diff --git a/src/td/simulation/GameHistory.cpp b/src/td/simulation/GameHistory.cpp index ef8cce1..14ada01 100644 --- a/src/td/simulation/GameHistory.cpp +++ b/src/td/simulation/GameHistory.cpp @@ -33,7 +33,7 @@ void GameHistory::FromPacket(protocol::pdata::LockSteps&& a_Steps) { } 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 26ec283..8696e38 100644 --- a/src/td/simulation/RealTimeSimulation.cpp +++ b/src/td/simulation/RealTimeSimulation.cpp @@ -12,7 +12,7 @@ std::uint64_t GetTime() { std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count()); } -RealTimeSimulation::RealTimeSimulation(game::World& a_World, const GameHistory& a_History, std::uint64_t a_StepTime) : +RealTimeSimulation::RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime) : m_StepTime(a_StepTime), m_World(a_World), m_CurrentTime(0), @@ -21,8 +21,8 @@ RealTimeSimulation::RealTimeSimulation(game::World& a_World, const GameHistory& 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); + for (auto&& lockstep : a_History) { + m_History.emplace_back(std::move(lockstep)); } Step(); } diff --git a/test/tdmap.tdmap2 b/test/tdmap.tdmap2 index df65e869c3c0fa40a3767f9de4eac1bf73666645..5df803c9b6507f706db6547a6399e606ef6d8c86 100644 GIT binary patch delta 608 zcmV-m0-ybh1^xsAL{E_dD}P|xF@k{sgq>zX=`0}D1LCg`8UWq|4*9nP@)CHQ?Ucc8 zQb81k4@Hs0-HlB!ZmP^RbkT&k@Bs{M?yzck0vcHv=Pq2ZW?NfYiVCSA98WzwBY zjP7(JFW`c)kKjA_pP74sxN~J=82A(TIQ$&upED0@RHaIdh#pXdwtuKj_u8-ATjoR_ z^Sa9U1uJfU1p_BezSV_;30Dl3xYYGf{idsTP|wi#(hXh1S!cRY-+XBF!A?*;zPz=#_)A8kCOmqyDt8skND7jWEF7@=ku1ZMczE7mKN#x(Js?3K7 z^Wjsewx4wqZ0v}zxql;-pYDo;1)h(b30xmwYw;AhlzVQ-J)JnC^^+C1+`F~;shT&9 znk&zvUT3lgw&%IhS}QFqaDIJbc_0i@7x+SR=*$ev+B`@7F4VeC!41rQn91ViJPvZJEgyRoZ_Ws?w|1ooN<}g#F2V ztQZMD7R3@)a*O)27SU0MNTL&c0{gzkZ>*%_QW+7EaE;Fq|2xYV9rz?{7s5QBW^AWN zEG*G>5l-4Da8;?-EAHzt<-@BkCvLbnaOu(~y*gt9{?eHRTKHW0km3w&oXbg*zg_ys u5fcgX+fn!kTR-8;JzP8a@;=;KwM`;-BauqDw?ZPDd+RRTTfYHQYm$b(KQw3n delta 621 zcmV-z0+Rjy1d9a%PG^wfOOFZC??!-mG}gPx+*|5+*KjFI+S+TZt4qk;WUrXcj$j+yei#g)f5~(pr8N!InIA(9)E39iKs$E4{4ho z;L8@3o6p+`vlq9oOfMPMp#D>55zK+}iw1&FV(Y zl;=^WHQEQ;_grbMl@=B_zqzs86NXZkc%a^QW(sC)oPC*p81` zSfcGbOxhrD(d!lW`kc9c)#k+Y=Z7v``lMIqY`|YRGe-+w3LjFOp^XcfH2K?wkBpd< zFuxrOA7SegzTCsL$wvr2kqT8eiQJDw%HiG$iEQqzdvR}-s1)uk$pd#+^R;`&7%RMH zRh9EgR^0ps24YT}e6I=z6RsF6ajEM-{kE-lp+{&uoVdQLIc#S;>Y9(WF4!s5!^y36 z$#0)aSHIA^VE)iV7}zJzhLhfq=w!G$MYWUDTEVqianaxPH2*iN91^McM5>!a{`vX? H9X^tV4d6CK diff --git a/xmake.lua b/xmake.lua index 959efb7..de05652 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.1.0", "zlib") +add_requires("splib 2.2.0", "zlib") add_requires("libsdl3 3.2.16", "glew", "fpm", "enet6") set_languages("c++17") From 51dc9103595a6cdeb1d700f9ce504681586b2634 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 1 Aug 2025 13:28:53 +0200 Subject: [PATCH 32/39] fastforward test in main --- src/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2466406..f3b902b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -111,19 +111,21 @@ int main(int argc, char** argv) { renderer.AddRenderer(cam, w); renderer.AddRenderer(cam, w); - cam.SetCamPos({77, 5, 13}); + cam.SetCamPos({77, 10, 13}); cam.UpdatePerspective(display.GetAspectRatio()); - td::sim::RealTimeSimulation simulation(w, 500); + td::sim::RealTimeSimulation simulation(w, 50); display.OnKeyDown.Connect([&simulation](SDL_Keycode key) { + static int counter = 0; if (key == SDLK_A) { auto spawn = td::protocol::CommandPtr( 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}; + td::protocol::packets::LockStepsPacket packet{counter * LOCKSTEP_BUFFER_SIZE * 3, steps}; simulation.HandlePacket(packet); + counter++; } }); From 31bb0198fc6bb336abd0cc08ccc434f949e44bf8 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 1 Aug 2025 13:38:00 +0200 Subject: [PATCH 33/39] add vsync --- src/td/input/Display.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/td/input/Display.cpp b/src/td/input/Display.cpp index d744d10..c854cd1 100644 --- a/src/td/input/Display.cpp +++ b/src/td/input/Display.cpp @@ -81,6 +81,9 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) : // WindowResizeEvent(WindowWidth, WindowHeight); + // vsync + SDL_GL_SetSwapInterval(1); + // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); From 50a6caf82eab38782c8d3f8315b90621ee7b74e1 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 1 Aug 2025 13:39:32 +0200 Subject: [PATCH 34/39] add multisampling --- src/td/input/Display.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/td/input/Display.cpp b/src/td/input/Display.cpp index c854cd1..ec32934 100644 --- a/src/td/input/Display.cpp +++ b/src/td/input/Display.cpp @@ -38,6 +38,9 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) : SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8); + m_GLContext = SDL_GL_CreateContext(m_Window); if (!m_GLContext) { @@ -46,6 +49,8 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) : int major, minor, mask; int r, g, b, a, depth; + int mBuffers, mSamples; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &mask); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); @@ -57,6 +62,9 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) : SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth); + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &mBuffers); + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &mSamples); + const char* mask_desc; if (mask & SDL_GL_CONTEXT_PROFILE_CORE) { @@ -72,6 +80,9 @@ Display::Display(int a_Width, int a_Height, const std::string& a_Title) : utils::LOG(utils::Format( "GL Context : %i.%i %s, Color : R:%i G:%i B:%i A:%i, Depth bits : %i", major, minor, mask_desc, r, g, b, a, depth)); + utils::LOG(utils::Format( + "MultiSamples : Buffers : %i, Samples : %i", mBuffers, mSamples)); + SDL_GL_MakeCurrent(m_Window, m_GLContext); GLenum error = glewInit(); From c0a560a16e3329699e74be08a044544d5b81b542 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 1 Aug 2025 13:40:11 +0200 Subject: [PATCH 35/39] change default cam pos --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index f3b902b..25ddce0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -111,7 +111,7 @@ int main(int argc, char** argv) { renderer.AddRenderer(cam, w); renderer.AddRenderer(cam, w); - cam.SetCamPos({77, 10, 13}); + cam.SetCamPos({77, 7, 13}); cam.UpdatePerspective(display.GetAspectRatio()); td::sim::RealTimeSimulation simulation(w, 50); From 731c7423467a731d14261acface8109f02fbf677 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Sun, 3 Aug 2025 18:20:21 +0200 Subject: [PATCH 36/39] begin serversimulation --- include/td/protocol/command/CommandData.h | 4 +--- include/td/protocol/packet/PacketData.h | 10 +++++++++ ...ealTimeSimulation.h => ClientSimulation.h} | 11 +++++----- include/td/simulation/ServerSimulation.h | 22 +++++++++++++++++++ src/main.cpp | 14 ++++++------ ...imeSimulation.cpp => ClientSimulation.cpp} | 22 ++++++++++--------- 6 files changed, 58 insertions(+), 25 deletions(-) rename include/td/simulation/{RealTimeSimulation.h => ClientSimulation.h} (72%) create mode 100644 include/td/simulation/ServerSimulation.h rename src/td/simulation/{RealTimeSimulation.cpp => ClientSimulation.cpp} (73%) diff --git a/include/td/protocol/command/CommandData.h b/include/td/protocol/command/CommandData.h index 1b72583..bf01602 100644 --- a/include/td/protocol/command/CommandData.h +++ b/include/td/protocol/command/CommandData.h @@ -21,10 +21,8 @@ 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/PacketData.h b/include/td/protocol/packet/PacketData.h index 656bb8d..8bdcc39 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -95,6 +95,16 @@ struct WorldData { game::ChunkList m_Chunks; }; +struct SpawnTroop { + sp::BitField m_Type; + sp::BitField m_Level; +}; + +struct PlaceTower { + TowerType m_Type; + TowerCoords m_Position; +}; + } // namespace pdata } // namespace protocol } // namespace td diff --git a/include/td/simulation/RealTimeSimulation.h b/include/td/simulation/ClientSimulation.h similarity index 72% rename from include/td/simulation/RealTimeSimulation.h rename to include/td/simulation/ClientSimulation.h index e77f42b..f58abde 100644 --- a/include/td/simulation/RealTimeSimulation.h +++ b/include/td/simulation/ClientSimulation.h @@ -9,7 +9,8 @@ namespace sim { using GameHistory = std::vector; using GameBuffer = std::vector>; -class RealTimeSimulation { +// TODO: OnEnd signal +class ClientSimulation : public protocol::PacketHandler { private: std::uint64_t m_StepTime; game::World& m_World; @@ -28,21 +29,21 @@ class RealTimeSimulation { * \brief Replay constructor * \param a_StepTime in ms */ - RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime); + ClientSimulation(game::World& a_World, 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); + ClientSimulation(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); + void Handle(const protocol::packets::LockStepsPacket& a_LockSteps) override; + void Handle(const protocol::packets::PredictCommandPacket& a_Predict) override; private: void Step(); diff --git a/include/td/simulation/ServerSimulation.h b/include/td/simulation/ServerSimulation.h new file mode 100644 index 0000000..2114fd1 --- /dev/null +++ b/include/td/simulation/ServerSimulation.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace td { +namespace sim { + +// TODO: OnEnd signal +class ServerSimulation { + private: + game::World& m_World; + std::uint64_t m_StepTime; + std::uint64_t m_CurrentTime; + + public: + ServerSimulation(game::World& a_World, std::uint64_t a_StepTime); + + void Update(); +}; + +} // namespace sim +} // namespace td diff --git a/src/main.cpp b/src/main.cpp index 25ddce0..195f47f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,14 +1,14 @@ #include +#include #include #include #include -#include #include #include #include #include -#include +#include #include #include @@ -59,8 +59,8 @@ td::game::World GetWorld() { WorldApply wa(w); td::protocol::PacketDispatcher d; - d.RegisterHandler(td::protocol::PacketID::WorldData, &wa); d.RegisterHandler(td::protocol::PacketID::WorldHeader, &wa); + d.RegisterHandler(td::protocol::PacketID::WorldData, &wa); d.Dispatch(*header); d.Dispatch(*data); @@ -83,7 +83,7 @@ td::sim::GameHistory GetCustomHistory() { td::sim::GameHistory gh(MAX_COUNT); auto spawn = td::protocol::CommandPtr( - std::make_shared(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0)); + std::make_shared(td::EntityType::Zombie, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0)); gh[0].push_back(spawn); auto tower = td::protocol::CommandPtr( @@ -114,17 +114,17 @@ int main(int argc, char** argv) { cam.SetCamPos({77, 7, 13}); cam.UpdatePerspective(display.GetAspectRatio()); - td::sim::RealTimeSimulation simulation(w, 50); + td::sim::ClientSimulation simulation(w, 50); display.OnKeyDown.Connect([&simulation](SDL_Keycode key) { static int counter = 0; if (key == SDLK_A) { auto spawn = td::protocol::CommandPtr( - std::make_shared(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0)); + std::make_shared(td::EntityType::Zombie, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0)); std::array steps{}; steps[0].push_back(spawn); td::protocol::packets::LockStepsPacket packet{counter * LOCKSTEP_BUFFER_SIZE * 3, steps}; - simulation.HandlePacket(packet); + simulation.Handle(packet); counter++; } }); diff --git a/src/td/simulation/RealTimeSimulation.cpp b/src/td/simulation/ClientSimulation.cpp similarity index 73% rename from src/td/simulation/RealTimeSimulation.cpp rename to src/td/simulation/ClientSimulation.cpp index 8696e38..7d43c36 100644 --- a/src/td/simulation/RealTimeSimulation.cpp +++ b/src/td/simulation/ClientSimulation.cpp @@ -1,18 +1,18 @@ -#include +#include #include namespace td { namespace sim { -const protocol::LockStep RealTimeSimulation::EMPTY_LOCKSTEP; +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()); } -RealTimeSimulation::RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime) : +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), @@ -27,7 +27,7 @@ RealTimeSimulation::RealTimeSimulation(game::World& a_World, GameHistory&& a_His Step(); } -RealTimeSimulation::RealTimeSimulation(game::World& a_World, std::uint64_t a_StepTime) : +ClientSimulation::ClientSimulation(game::World& a_World, std::uint64_t a_StepTime) : m_StepTime(a_StepTime), m_World(a_World), m_History(std::numeric_limits::max()), @@ -39,7 +39,7 @@ RealTimeSimulation::RealTimeSimulation(game::World& a_World, std::uint64_t a_Ste Step(); } -float RealTimeSimulation::Update() { +float ClientSimulation::Update() { // TODO: handle freezes (m_CurrentTime > 2 * m_StepTime) m_CurrentTime += GetTime() - m_LastTime; m_LastTime = GetTime(); @@ -50,7 +50,7 @@ float RealTimeSimulation::Update() { return (float)m_CurrentTime / (float)m_StepTime; } -void RealTimeSimulation::Step() { +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)); @@ -61,7 +61,7 @@ void RealTimeSimulation::Step() { m_CurrentStep++; } -void RealTimeSimulation::HandlePacket(const protocol::packets::LockStepsPacket& a_LockSteps) { +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]; @@ -69,15 +69,17 @@ void RealTimeSimulation::HandlePacket(const protocol::packets::LockStepsPacket& FastReplay(); } -void RealTimeSimulation::HandlePacket(const protocol::packets::PredictCommandPacket& a_Predict) {} +void ClientSimulation::Handle(const protocol::packets::PredictCommandPacket& a_Predict) { + +} -void RealTimeSimulation::FastForward(std::size_t a_Count) { +void ClientSimulation::FastForward(std::size_t a_Count) { for (std::size_t i = 0; i < a_Count; i++) { Step(); } } -void RealTimeSimulation::FastReplay() { +void ClientSimulation::FastReplay() { if (m_LastValidStep >= m_CurrentStep) return; From 8641ddc52565e68fbdefd5d3e0e33624029e2d82 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 4 Aug 2025 09:57:38 +0200 Subject: [PATCH 37/39] fix map save --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index de05652..833c890 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.2.0", "zlib") +add_requires("splib 2.2.2", "zlib") add_requires("libsdl3 3.2.16", "glew", "fpm", "enet6") set_languages("c++17") From 7ca374ea5366f90f4ae945a16b71ca8357647869 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 4 Aug 2025 10:00:14 +0200 Subject: [PATCH 38/39] don't write packet id in map save --- src/main.cpp | 8 ++++---- test/tdmap.tdmap2 | Bin 638 -> 634 bytes 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 195f47f..883a1c3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,8 +40,8 @@ void Save(const td::protocol::PacketBase& header, const td::protocol::PacketBase sp::MessageStream stream(std::move(out), std::move(comp)); - stream.WriteMessage(header); - stream.WriteMessage(data); + stream.WriteMessage(header, false); + stream.WriteMessage(data, false); } td::game::World GetWorld() { @@ -52,8 +52,8 @@ td::game::World GetWorld() { sp::MessageStream stream(std::move(out), std::move(comp)); - auto header = stream.ReadMessage(); - auto data = stream.ReadMessage(); + auto header = stream.ReadMessage(td::protocol::PacketID::WorldHeader); + auto data = stream.ReadMessage(td::protocol::PacketID::WorldData); td::game::World w; WorldApply wa(w); diff --git a/test/tdmap.tdmap2 b/test/tdmap.tdmap2 index 5df803c9b6507f706db6547a6399e606ef6d8c86..cc9a2237adfcec49a2afd01dc115f3b73974e182 100644 GIT binary patch delta 588 zcmV-S0<-=81o{L8Lr!>{JCO!Ef7#BrA9{NOTn?(Nos>*zbFdsgZYU_C?!Nv{=n>$qb*`7#P z;Q7#*!1W=v7Eh5&x$6epf7OXIT0dQJ%iTMhpQ(A%sJZey>bA%GVEdjct+mp^0_Qik zmixjWb%`%D2hPmEtj%-O??JoQDY${zk36@FzVq!DHm!p1=_i29s+b%e|`-%Dk z2>jz^3^mc2bXW8PA7QgtU4UiQfr?NfYiVCSA98WzwBYjP7(JFW`c)kKjA_pP74s zxN~J=82A(TIQ$&upED0@RHaIdh#pXdwx~|`+OOPO=0qOzy2|+lD{g-U1Aiw@zSV_; z30Dl3xYYGf{idsTP|wi#(hXh1S!cRY-+XBF!A?*;zPz=#_ z)A8kCOmqyDt8skND7jWEF7@=ku1ZMczE7mKN#x(Js?3K7^Wjsewx4wqZ0v}zxg(XI z?uvv3o{yXfTpwU-@f5j~dw*`oJ)JnC^^+C1+`F~;shT&9nk&zvUT3lgw&%IhS}QFq zaDIJbc_0i@7x+SR=*$ev+B`@7F4VeC!41rQn91ViJPvZJEgyRoZ_Ws?w|1ooN<}g#F2VtQZMD7R3@)a*O)27SU0M zNTL&c0{gzkZ>*%_QW+7EaE;Fq|2xYV9rz?{7s5QBW^AWNEG*G>5l-4DaH-cT?&~q- z!>cYQZn!vb>Cz{?IzeLt{?eHRTKHW0km3w&oXbg*zg_ys5fcgX+fn!kTR-8;JzP8a e@;=;KwM`;-BauqDw?ZPDd+RRTTfYHQYm$aZ|1Sst From 1ca88106acacc4ff03ae927c2953cce358795b96 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Mon, 4 Aug 2025 10:15:52 +0200 Subject: [PATCH 39/39] update splib to 2.3.0 --- src/main.cpp | 5 ++--- xmake.lua | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 883a1c3..ea61461 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -56,11 +56,10 @@ td::game::World GetWorld() { auto data = stream.ReadMessage(td::protocol::PacketID::WorldData); td::game::World w; - WorldApply wa(w); + auto wa = std::make_shared(w); td::protocol::PacketDispatcher d; - d.RegisterHandler(td::protocol::PacketID::WorldHeader, &wa); - d.RegisterHandler(td::protocol::PacketID::WorldData, &wa); + d.RegisterHandler(wa); d.Dispatch(*header); d.Dispatch(*data); diff --git a/xmake.lua b/xmake.lua index 833c890..c4cf095 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.2.2", "zlib") +add_requires("splib 2.3.0", "zlib") add_requires("libsdl3 3.2.16", "glew", "fpm", "enet6") set_languages("c++17")