diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..e993027 --- /dev/null +++ b/.clang-format @@ -0,0 +1,37 @@ +Language: Cpp +BasedOnStyle: LLVM + +AlignAfterOpenBracket: DontAlign +BreakConstructorInitializers: AfterColon +ConstructorInitializerAllOnOneLineOrOnePerLine: true +PointerAlignment: Left +SortIncludes: true +SpacesBeforeTrailingComments: 2 +UseTab: Always +MaxEmptyLinesToKeep: 5 + +TabWidth: 4 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +IndentWidth: 4 +IndentCaseLabels: true + +ColumnLimit: 135 +AlwaysBreakTemplateDeclarations: Yes + +AllowShortFunctionsOnASingleLine: Empty +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true \ No newline at end of file diff --git a/include/td/Types.h b/include/td/Types.h new file mode 100644 index 0000000..2b66115 --- /dev/null +++ b/include/td/Types.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +namespace td { + +enum class Team : std::uint8_t { + Blue = 0, + Red, +}; + +enum class CastleType : std::uint8_t { + Archer = 0, + Leach, + Artillery, + Mage, + Ice, + Poison, + Quake, + Sorcerer, + Zeus, + Necromancer, + Turret, +}; + +enum class EntityType : std::uint8_t { + Zombie = 0, + Spider, + Pigman, + Skeleton, + Creeper, + Silverfish, + Blaze, + Witch, + Slime, + Giant, + Wither, + // And passive animals +}; + +enum class ShopItem : std::uint8_t { + Goldmine = 0, + Inferno, + Meteor, + Zeus, + Freeze, + Speed, + Heal, +}; + +using CastleID = std::uint16_t; +using PlayerID = std::uint8_t; +using EntityID = std::uint16_t; + +struct CastleCoords { + std::int16_t x; + std::int16_t y; +}; + +struct EntityCoords { + fpm::fixed_16_16 x; + fpm::fixed_16_16 y; +}; + +} // namespace td diff --git a/include/td/protocol/ByteBuffer.h b/include/td/protocol/ByteBuffer.h new file mode 100644 index 0000000..076843c --- /dev/null +++ b/include/td/protocol/ByteBuffer.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace protocol { + +class PlayerInfo; + +class ByteBuffer { + private: + godot::PackedByteArray m_Buffer; + std::size_t m_ReadOffset; + + public: + class ReadError : public std::runtime_error { + public: + ReadError(const std::string& msg) : std::runtime_error(msg) {} + }; + + ByteBuffer(godot::PackedByteArray&& a_Buffer) : m_Buffer(std::move(a_Buffer)), m_ReadOffset(0) {} + ByteBuffer() : m_ReadOffset(0) { + m_Buffer.resize(0); + } + + const godot::PackedByteArray& GetByteArray() const { + return m_Buffer; + } + + godot::PackedByteArray& GetByteArray() { + return m_Buffer; + } + + // Integers + ByteBuffer& operator<<(int8_t a_Data); + ByteBuffer& operator>>(int8_t& a_Data); + ByteBuffer& operator<<(uint8_t a_Data); + ByteBuffer& operator>>(uint8_t& a_Data); + + ByteBuffer& operator<<(int16_t a_Data); + ByteBuffer& operator>>(int16_t& a_Data); + ByteBuffer& operator<<(uint16_t a_Data); + ByteBuffer& operator>>(uint16_t& a_Data); + + ByteBuffer& operator<<(int32_t a_Data); + ByteBuffer& operator>>(int32_t& a_Data); + ByteBuffer& operator<<(uint32_t a_Data); + ByteBuffer& operator>>(uint32_t& a_Data); + + ByteBuffer& operator<<(int64_t a_Data); + ByteBuffer& operator>>(int64_t& a_Data); + ByteBuffer& operator<<(uint64_t a_Data); + ByteBuffer& operator>>(uint64_t& a_Data); + + ByteBuffer& operator<<(float a_Data); + ByteBuffer& operator>>(float& a_Data); + ByteBuffer& operator<<(double a_Data); + ByteBuffer& operator>>(double& a_Data); + + ByteBuffer& operator<<(const godot::String& a_Data); + ByteBuffer& operator>>(godot::String& a_Data); + + ByteBuffer& operator<<(const godot::Vector3& a_Data); + ByteBuffer& operator>>(godot::Vector3& a_Data); + + template + ByteBuffer& operator<<(const std::vector& a_Data) { + *this << static_cast(a_Data.size()); + for (const T& data : a_Data) { + *this << data; + } + return *this; + } + + template + ByteBuffer& operator>>(std::vector& a_Data) { + std::uint32_t arraySize; + *this >> arraySize; + a_Data.resize(arraySize); + for (std::uint32_t i = 0; i < arraySize; i++) { + *this >> a_Data[i]; + } + return *this; + } + + ByteBuffer& operator<<(const PlayerInfo& a_Data); + ByteBuffer& operator>>(PlayerInfo& a_Data); +}; + +} // namespace protocol +} // namespace td diff --git a/include/td/protocol/PacketData.h b/include/td/protocol/PacketData.h new file mode 100644 index 0000000..98c28f8 --- /dev/null +++ b/include/td/protocol/PacketData.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace protocol { + +struct PlayerInfo { + PlayerID m_PlayerId; + godot::String m_PlayerName; +}; + +namespace data { + +struct PlayerLogin { + godot::String m_PlayerName; +}; + +struct UpdateHealth { + float m_NewHealth; +}; + +struct LoggingSuccess { + PlayerID m_PlayerId; +}; + +struct PlayerDeath {}; + +struct PlayerJoin { + PlayerInfo m_Player; +}; + +struct PlayerLeave { + PlayerID m_PlayerId; +}; + +struct PlayerStats {}; + +struct PlayerList { + std::vector m_Players; +}; + +struct ServerConfig {}; + +struct ServerTps {}; + +struct UpdateGameState {}; + +struct KeepAlive { + std::uint64_t m_KeepAliveId; +}; + +struct Disconnect {}; + +struct ChatMessage { + godot::String m_Text; +}; + +struct PlayerPositionAndRotation { + PlayerID m_Player; + godot::Vector3 m_Position; + godot::Vector3 m_Rotation; + godot::Vector3 m_Velocity; +}; + +struct PlayerShoot { + PlayerID m_Sender; + godot::Vector3 m_Position; + godot::Vector3 m_Rotation; + godot::Vector3 m_Velocity; +}; + +} // namespace data +} // namespace protocol +} // namespace td diff --git a/include/td/protocol/PacketDeclare.h b/include/td/protocol/PacketDeclare.h new file mode 100644 index 0000000..35adbe4 --- /dev/null +++ b/include/td/protocol/PacketDeclare.h @@ -0,0 +1,55 @@ +#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(Disconnect, Reliable, Both) \ + DeclarePacket(KeepAlive, Reliable, Both) \ + DeclarePacket(LoggingSuccess, Reliable, Server) \ + DeclarePacket(PlayerDeath, Reliable, Server) \ + DeclarePacket(PlayerJoin, Reliable, Server) \ + DeclarePacket(PlayerLeave, Reliable, Server) \ + DeclarePacket(PlayerList, Reliable, Server) \ + DeclarePacket(PlayerLogin, Reliable, Client) \ + DeclarePacket(PlayerPositionAndRotation, Unreliable, Both) \ + DeclarePacket(PlayerShoot, Reliable, Both) \ + DeclarePacket(PlayerStats, Reliable, Server) \ + DeclarePacket(ServerConfig, Reliable, Server) \ + DeclarePacket(ServerTps, Reliable, Server) \ + DeclarePacket(UpdateGameState, Reliable, Server) \ + DeclarePacket(UpdateHealth, Reliable, Client) + + +} // namespace protocol +} // namespace td \ No newline at end of file diff --git a/include/td/protocol/PacketDispatcher.h b/include/td/protocol/PacketDispatcher.h new file mode 100644 index 0000000..225b777 --- /dev/null +++ b/include/td/protocol/PacketDispatcher.h @@ -0,0 +1,58 @@ +#pragma once + +/** + * \file PacketDispatcher.h + * \brief File containing the blitz::protocol::PacketDispatcher class + */ + +#include +#include + +#include + +namespace td { +namespace protocol { + +class PacketHandler; + +/** + * \class PacketDispatcher + * \brief Class used to dispatch packets + */ +class PacketDispatcher : private NonCopyable { + private: + std::map> m_Handlers; + + public: + /** + * \brief Constructor + */ + PacketDispatcher() {} + + /** + * \brief Dispatch a packet + * \param packet The packet to dispatch + */ + void Dispatch(const Packet& packet); + + /** + * \brief Register a packet handler + * \param type The packet type + * \param handler The packet handler + */ + void RegisterHandler(PacketType type, PacketHandler& handler); + /** + * \brief Unregister a packet handler + * \param type The packet type + * \param handler The packet handler + */ + void UnregisterHandler(PacketType type, PacketHandler& handler); + /** + * \brief Unregister a packet handler + * \param handler The packet handler + */ + void UnregisterHandler(PacketHandler& handler); +}; + +} // namespace protocol +} // namespace td diff --git a/include/td/protocol/PacketFactory.h b/include/td/protocol/PacketFactory.h new file mode 100644 index 0000000..5b65c8d --- /dev/null +++ b/include/td/protocol/PacketFactory.h @@ -0,0 +1,19 @@ +#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/PacketHandler.h b/include/td/protocol/PacketHandler.h new file mode 100644 index 0000000..f98680a --- /dev/null +++ b/include/td/protocol/PacketHandler.h @@ -0,0 +1,34 @@ +#pragma once + +/** + * \file PacketHandler.h + * \brief File containing the blitz::protocol::PacketHandler class + */ + +#include +#include + +namespace td { +namespace protocol { + +class PacketDispatcher; + +#define DeclarePacket(PacketName, ...) virtual void Visit(const packets::PacketName&); virtual void HandlePacket(const packets::PacketName&) {} + +/** + * \class PacketHandler + * \brief Class used to handle packets + */ +class PacketHandler : public PacketVisitor { + public: + PacketHandler() {} + ~PacketHandler() {} + + DeclareAllPacket() + +}; + +#undef DeclarePacket + +} // namespace protocol +} // namespace td \ No newline at end of file diff --git a/include/td/protocol/PacketSender.h b/include/td/protocol/PacketSender.h new file mode 100644 index 0000000..e190dd8 --- /dev/null +++ b/include/td/protocol/PacketSender.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +namespace td { + +class NetworkInterface; + +namespace protocol { + +#define DeclarePacket(PacketName, Reliability, ...) void Visit(const protocol::packets::PacketName& a_Packet) override; + + + + + +/////////////////////// +/* PacketBroadcaster */ +/////////////////////// +class PacketBroadcaster : public protocol::PacketVisitor { + private: + NetworkInterface& m_NetworkInterface; + + public: + PacketBroadcaster(NetworkInterface& a_NetworkInterface) : m_NetworkInterface(a_NetworkInterface) {} + + void BroadcastPacket(const protocol::Packet& a_Packet) { + Check(a_Packet); + } + + DeclareAllPacket() +}; + + + + + + +////////////////// +/* PacketSender */ +////////////////// +class PacketSender : public protocol::PacketVisitor { + private: + NetworkInterface& m_NetworkInterface; + PeerID m_PeerId; + + public: + PacketSender(PeerID a_PeerId, NetworkInterface& a_NetworkInterface) : m_PeerId(a_PeerId), m_NetworkInterface(a_NetworkInterface) {} + + void SendPacket(const protocol::Packet& a_Packet) { + Check(a_Packet); + } + + DeclareAllPacket() +}; + +#undef DeclarePacket + +} // namespace protocol +} // namespace td diff --git a/include/td/protocol/PacketSerializer.h b/include/td/protocol/PacketSerializer.h new file mode 100644 index 0000000..5172af7 --- /dev/null +++ b/include/td/protocol/PacketSerializer.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace protocol { + +using PacketPtr = std::unique_ptr; + +namespace PacketSerializer { + +godot::PackedByteArray Serialize(const Packet& a_Packet); + +std::unique_ptr Deserialize(godot::PackedByteArray& a_Data); + +} +} // namespace protocol +} // namespace td diff --git a/include/td/protocol/PacketVisitor.h b/include/td/protocol/PacketVisitor.h new file mode 100644 index 0000000..60dbd1b --- /dev/null +++ b/include/td/protocol/PacketVisitor.h @@ -0,0 +1,39 @@ +#pragma once + +/** + * \file PacketVisitor.h + * \brief File containing the blitz::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/Packets.h b/include/td/protocol/Packets.h new file mode 100644 index 0000000..b039ffd --- /dev/null +++ b/include/td/protocol/Packets.h @@ -0,0 +1,112 @@ +#pragma once + +/** + * \file Packets.h + * \brief File containing the definitions of the packets + */ + +#include +#include +#include + +namespace td { +namespace protocol { + +class PacketVisitor; + +/** A Packet id is 8 bits wide */ +using PacketID = std::uint8_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 +}; + + +#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; +}; + + + + + +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 blitz::protocol::data namespace) + */ +template +class ConcretePacket : public Packet { + public: + /** The type of the struct holding the data */ + using PacketDataType = Data; + + /** 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 BLITZ_INSTANCIATE_PACKETS +// before including this file +// if you want to instantiate templates +#ifdef BLITZ_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 + +} // namespace protocol +} // namespace td diff --git a/include/td/protocol/command/CommandData.h b/include/td/protocol/command/CommandData.h new file mode 100644 index 0000000..e26511a --- /dev/null +++ b/include/td/protocol/command/CommandData.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace td { +namespace protocol { + + +struct LockStep { + std::uint8_t m_CommandNumber; + std::vector m_Commands; +}; + +struct LockSteps { + std::uint16_t m_FirstFrameNumber; + std::array m_LockSteps; +}; + + +namespace data { + + +struct PlaceTower { + CastleType m_Type : 4; + PlayerID m_Placer : 4; + CastleCoords m_Position; +}; + +struct UpgradeTower { + CastleID m_Tower : 12; + std::uint8_t m_Upgrade : 4; +}; + +struct SpawnTroop { + EntityType m_Type : 5; + std::uint8_t m_Level : 3; + EntityCoords m_Position; + PlayerID m_Sender; +}; + +struct UseItem { + ShopItem m_Item : 4; + PlayerID m_User : 4; + EntityCoords m_Position; +}; + +struct TeamChange { + PlayerID m_Player : 7; + Team m_NewTeam : 1; +}; + + + +} // namespace data + + +} // namespace protocol +} // namespace td diff --git a/xmake.lua b/xmake.lua index fda12d7..258e5a7 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,8 +1,12 @@ add_rules("mode.debug", "mode.release") +add_requires("fpm") + target("Tower-Defense2") + add_includedirs("include") set_kind("binary") add_files("src/*.cpp") + add_packages("fpm") -- -- If you want to known more usage about xmake, please see https://xmake.io