From 871caf90569f81d292867fce0fcda3468335de08 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Fri, 18 Oct 2024 15:36:00 +0200 Subject: [PATCH] finish serialize --- include/td/Types.h | 6 +- include/td/protocol/command/CommandData.h | 8 +- include/td/protocol/command/CommandFactory.h | 6 +- .../td/protocol/command/CommandSerializer.h | 4 +- src/td/protocol/command/CommandFactory.cpp | 10 +- src/td/protocol/command/CommandSerializer.cpp | 228 ++++++++++++++++++ src/td/protocol/packet/PacketSerializer.cpp | 27 ++- .../protocol/command/CommandFactory_test.cpp | 17 ++ .../command/CommandSerializer_test.cpp | 51 ++++ 9 files changed, 337 insertions(+), 20 deletions(-) create mode 100644 src/td/protocol/command/CommandSerializer.cpp create mode 100644 test/blitz/protocol/command/CommandFactory_test.cpp create mode 100644 test/blitz/protocol/command/CommandSerializer_test.cpp diff --git a/include/td/Types.h b/include/td/Types.h index 4a2c6d0..b057bc2 100644 --- a/include/td/Types.h +++ b/include/td/Types.h @@ -10,7 +10,7 @@ enum class Team : std::uint8_t { Red, }; -enum class CastleType : std::uint8_t { +enum class TowerType : std::uint8_t { Archer = 0, Leach, Artillery, @@ -49,11 +49,11 @@ enum class ShopItem : std::uint8_t { Heal, }; -using CastleID = std::uint16_t; +using TowerID = std::uint16_t; using PlayerID = std::uint8_t; using EntityID = std::uint16_t; -struct CastleCoords { +struct TowerCoords { std::int16_t x; std::int16_t y; }; diff --git a/include/td/protocol/command/CommandData.h b/include/td/protocol/command/CommandData.h index d7c7689..f4e0aac 100644 --- a/include/td/protocol/command/CommandData.h +++ b/include/td/protocol/command/CommandData.h @@ -1,8 +1,8 @@ #pragma once +#include #include #include -#include namespace td { namespace protocol { @@ -11,13 +11,13 @@ namespace cdata { struct PlaceTower { - CastleType m_Type : 4; + TowerType m_Type : 4; PlayerID m_Placer : 4; - CastleCoords m_Position; + TowerCoords m_Position; }; struct UpgradeTower { - CastleID m_Tower : 12; + TowerID m_Tower : 12; std::uint8_t m_Upgrade : 4; }; diff --git a/include/td/protocol/command/CommandFactory.h b/include/td/protocol/command/CommandFactory.h index e758b2c..3bae063 100644 --- a/include/td/protocol/command/CommandFactory.h +++ b/include/td/protocol/command/CommandFactory.h @@ -8,11 +8,11 @@ namespace protocol { namespace CommandFactory { template ::value>::type> -std::unique_ptr CreateCommand() { - return std::make_unique(); +std::shared_ptr CreateCommand() { + return std::make_shared(); } -const std::unique_ptr& CreateReadOnlyCommand(CommandType a_Type); +const std::shared_ptr& CreateReadOnlyCommand(CommandType a_Type); } // namespace CommandFactory } // namespace protocol diff --git a/include/td/protocol/command/CommandSerializer.h b/include/td/protocol/command/CommandSerializer.h index 5b9b6e6..181fcb3 100644 --- a/include/td/protocol/command/CommandSerializer.h +++ b/include/td/protocol/command/CommandSerializer.h @@ -8,13 +8,13 @@ namespace protocol { class Command; -using CommandPtr = std::unique_ptr; +using CommandPtr = std::shared_ptr; namespace CommandSerializer { DataBuffer Serialize(const Command& a_Command); -std::unique_ptr Deserialize(DataBuffer& a_Data); +std::shared_ptr Deserialize(DataBuffer& a_Data); } // namespace CommandSerializer } // namespace protocol diff --git a/src/td/protocol/command/CommandFactory.cpp b/src/td/protocol/command/CommandFactory.cpp index d1e8a87..0dbdc9f 100644 --- a/src/td/protocol/command/CommandFactory.cpp +++ b/src/td/protocol/command/CommandFactory.cpp @@ -8,15 +8,13 @@ namespace td { namespace protocol { namespace CommandFactory { -using CommandCreator = std::function()>; +using CommandCreator = std::function()>; -#define DeclareCommand(CommandName, ...) std::make_unique(), +#define DeclareCommand(CommandName, ...) std::make_shared(), -static std::array, static_cast(CommandType::COMMAND_COUNT)> Commands = { - DeclareAllCommand() -}; +static std::array, static_cast(CommandType::COMMAND_COUNT)> Commands = {DeclareAllCommand()}; -const std::unique_ptr& CreateReadOnlyCommand(CommandType a_Type) { +const std::shared_ptr& CreateReadOnlyCommand(CommandType a_Type) { assert(a_Type < CommandType::COMMAND_COUNT); return Commands[static_cast(a_Type)]; } diff --git a/src/td/protocol/command/CommandSerializer.cpp b/src/td/protocol/command/CommandSerializer.cpp new file mode 100644 index 0000000..13864a6 --- /dev/null +++ b/src/td/protocol/command/CommandSerializer.cpp @@ -0,0 +1,228 @@ +#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(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/packet/PacketSerializer.cpp b/src/td/protocol/packet/PacketSerializer.cpp index 144ad24..0fc785a 100644 --- a/src/td/protocol/packet/PacketSerializer.cpp +++ b/src/td/protocol/packet/PacketSerializer.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -212,11 +213,33 @@ void Deserializer::DeserializePacketData(pdata::ChatMessage& a_Packet) { void Serializer::SerializePacketData(const pdata::LockSteps& a_Packet) { - m_Buffer << a_Packet.m_FirstFrameNumber << a_Packet.m_LockSteps; + 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 >> a_Packet.m_LockSteps; + 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"); + } + } + } } diff --git a/test/blitz/protocol/command/CommandFactory_test.cpp b/test/blitz/protocol/command/CommandFactory_test.cpp new file mode 100644 index 0000000..9ce58cb --- /dev/null +++ b/test/blitz/protocol/command/CommandFactory_test.cpp @@ -0,0 +1,17 @@ +#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 new file mode 100644 index 0000000..ef75f8e --- /dev/null +++ b/test/blitz/protocol/command/CommandSerializer_test.cpp @@ -0,0 +1,51 @@ +#include + +#include +#include + +namespace tp = td::protocol; + +template +static int TestCommand() { + 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 std::memcmp(&command.m_Data, &command2->m_Data, sizeof(Command_Data_T)); +} + +#define DeclareCommand(Command, ...) TestCommand(); + +static int TestAllCommands() { + DeclareAllCommand() + + return TD_TEST_SUCCESSFUL; +} + +static int TestNewTeam() { + tp::commands::TeamChange tc; + tc.m_Data.m_Player = 69; + tc.m_Data.m_NewTeam = td::Team::Red; + + td::DataBuffer db = tp::CommandSerializer::Serialize(tc); + + auto packet = tp::CommandSerializer::Deserialize(db); + + tp::commands::TeamChange* tc2 = dynamic_cast(packet.get()); + + return TD_TEST_SUCCESSFUL; +} + +int main() { + TestNewTeam(); + return TestAllCommands(); +} \ No newline at end of file