basic packet structure

This commit is contained in:
2025-02-05 15:00:19 +01:00
parent cfeea10634
commit 65275a2cc6
35 changed files with 423 additions and 1408 deletions

View File

@@ -1,61 +0,0 @@
#pragma once
/**
* \file PacketDispatcher.h
* \brief File containing the sp::protocol::PacketDispatcher class
*/
#include <sp/common/NonCopyable.h>
#include <sp/protocol/packet/Packets.h>
#include <map>
namespace sp {
namespace protocol {
/**
* \class Dispatcher
* \brief Class used to dispatch things
*/
template <typename T_Enum, typename T_Handler, typename T>
class Dispatcher : private NonCopyable {
private:
std::map<T_Enum, std::vector<T_Handler*>> 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 sp
#include "Dispatcher.inl"

View File

@@ -1,38 +0,0 @@
#pragma once
namespace sp {
namespace protocol {
template <typename T_Enum, typename T_Handler, typename T>
void Dispatcher<T_Enum, T_Handler, T>::Dispatch(const T& packet) {
T_Enum type = packet.GetType();
for (auto* handler : m_Handlers[type])
handler->Check(packet);
}
template <typename T_Enum, typename T_Handler, typename T>
void Dispatcher<T_Enum, T_Handler, T>::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 <typename T_Enum, typename T_Handler, typename T>
void Dispatcher<T_Enum, T_Handler, T>::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 <typename T_Enum, typename T_Handler, typename T>
void Dispatcher<T_Enum, T_Handler, T>::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 sp

View File

@@ -1,53 +0,0 @@
// #pragma once
// #include <array>
// #include <string>
// #include <sp/Types.h>
// namespace sp {
// namespace protocol {
// namespace cdata {
// struct PlaceTower {
// TowerType m_Type : 4;
// PlayerID m_Placer : 4;
// TowerCoords m_Position;
// };
// struct UpgradeTower {
// TowerID 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;
// };
// struct PlayerJoin {
// PlayerID m_ID;
// std::string m_Name;
// };
// struct End {};
// } // namespace cdata
// } // namespace protocol
// } // namespace sp

View File

@@ -1,22 +0,0 @@
#pragma once
namespace sp {
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 sp

View File

@@ -1,17 +0,0 @@
#pragma once
/**
* \file CommandDispatcher.h
* \brief File containing the sp::protocol::CommandDispatcher class
*/
#include <sp/protocol/Dispatcher.h>
#include <sp/protocol/command/Commands.h>
namespace sp {
namespace protocol {
using CommandDispatcher = Dispatcher<CommandType, CommandVisitor, Command>;
} // namespace protocol
} // namespace sp

View File

@@ -1,19 +0,0 @@
#pragma once
#include <memory>
#include <sp/protocol/command/Commands.h>
namespace sp {
namespace protocol {
namespace CommandFactory {
template <typename CommandDerived, typename = typename std::enable_if<std::is_base_of<Command, CommandDerived>::value>::type>
std::shared_ptr<CommandDerived> CreateCommand() {
return std::make_shared<CommandDerived>();
}
const std::shared_ptr<Command>& CreateReadOnlyCommand(CommandType a_Type);
} // namespace CommandFactory
} // namespace protocol
} // namespace sp

View File

@@ -1,21 +0,0 @@
#pragma once
#include <memory>
#include <sp/common/DataBuffer.h>
namespace sp {
namespace protocol {
class Command;
using CommandPtr = std::shared_ptr<Command>;
namespace CommandSerializer {
DataBuffer Serialize(const Command& a_Command);
std::shared_ptr<Command> Deserialize(DataBuffer& a_Data);
} // namespace CommandSerializer
} // namespace protocol
} // namespace sp

View File

@@ -1,39 +0,0 @@
#pragma once
/**
* \file CommandVisitor.h
* \brief File containing the sp::protocol::CommandVisitor class
*/
#include <sp/protocol/command/Commands.h>
namespace sp {
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 sp

View File

@@ -1,111 +0,0 @@
#pragma once
/**
* \file Commands.h
* \brief File containing the definitions of the lockstep commands
*/
#include <sp/Types.h>
#include <sp/common/NonCopyable.h>
#include <sp/protocol/command/CommandData.h>
#include <sp/protocol/command/CommandDeclare.h>
#include <memory>
namespace sp {
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;
};
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 sp::protocol::data namespace)
*/
template <CommandType CT, typename Data>
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 SP_INSTANCIATE_COMMANDS
// before including this file
// if you want to instantiate templates
#ifdef SP_INSTANCIATE_COMMANDS
#define DeclareCommand(CommandName, ...) \
using CommandName = ConcreteCommand<CommandType::CommandName, cdata::CommandName>; \
template class ConcreteCommand<CommandType::CommandName, cdata::CommandName>;
#else
#define DeclareCommand(CommandName, ...) /** Defines the CommandName Command */ \
using CommandName = ConcreteCommand<CommandType::CommandName, cdata::CommandName>;
#endif
DeclareAllCommand()
#undef DeclareCommand
} // namespace commands
using LockStep = std::vector<std::shared_ptr<protocol::Command>>;
} // namespace protocol
} // namespace sp

View File

@@ -1,76 +0,0 @@
// #pragma once
// #include <td/Types.h>
// #include <vector>
// #include <td/protocol/command/Commands.h>
// // Make it dynamic ?
// #define LOCKSTEP_BUFFER_SIZE 10
// namespace sp {
// namespace protocol {
// struct PlayerInfo {
// PlayerID m_PlayerId;
// std::string m_PlayerName;
// };
// struct MapData {
// };
// namespace pdata {
// /** Client attempts to login (very basic) */
// struct PlayerLogin {
// std::string m_PlayerName;
// };
// /** Server indicates success */
// struct LoggingSuccess {
// PlayerID m_PlayerId;
// };
// /** Player joins the lobby */
// struct PlayerJoin {
// PlayerInfo m_Player;
// };
// /** Player leaves the lobby */
// struct PlayerLeave {
// PlayerID m_PlayerId;
// };
// /** Keep alive */
// struct KeepAlive {
// std::uint64_t m_KeepAliveId;
// };
// /** Can be used by both client and server */
// struct Disconnect {
// std::string m_Reason;
// };
// /** Chat message */
// struct ChatMessage {
// std::string m_Text;
// };
// // TODO: handle players joining in the first second
// struct BeginGame {
// MapData m_Map;
// std::vector<PlayerInfo> m_BlueTeam;
// std::vector<PlayerInfo> m_RedTeam;
// // optional, used for players joining mid game
// std::vector<LockStep> m_FirstLocksteps;
// };
// struct LockSteps {
// std::uint16_t m_FirstFrameNumber;
// std::array<LockStep, LOCKSTEP_BUFFER_SIZE> m_LockSteps;
// };
// } // namespace pdata
// } // namespace protocol
// } // namespace sp

View File

@@ -1,48 +0,0 @@
// #pragma once
// /**
// * \file PacketDeclare.h
// * \brief Holds the definitions of the packets (but not their content)
// */
// namespace sp {
// 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 sp

View File

@@ -1,17 +0,0 @@
#pragma once
/**
* \file PacketDispatcher.h
* \brief File containing the sp::protocol::PacketDispatcher class
*/
#include <sp/protocol/Dispatcher.h>
#include <sp/protocol/packet/Packets.h>
namespace sp {
namespace protocol {
using PacketDispatcher = Dispatcher<PacketType, PacketVisitor, Packet>;
} // namespace protocol
} // namespace sp

View File

@@ -1,19 +0,0 @@
#pragma once
#include <memory>
#include <sp/protocol/packet/Packets.h>
namespace sp {
namespace protocol {
namespace PacketFactory {
template <typename PacketDerived, typename = typename std::enable_if<std::is_base_of<Packet, PacketDerived>::value>::type>
std::unique_ptr<PacketDerived> CreatePacket() {
return std::make_unique<PacketDerived>();
}
const std::unique_ptr<Packet>& CreateReadOnlyPacket(PacketType a_Type);
} // namespace PacketFactory
} // namespace protocol
} // namespace sp

View File

@@ -1,21 +0,0 @@
#pragma once
#include <memory>
#include <sp/common/DataBuffer.h>
namespace sp {
namespace protocol {
class Packet;
using PacketPtr = std::unique_ptr<Packet>;
namespace PacketSerializer {
DataBuffer Serialize(const Packet& a_Packet);
std::unique_ptr<Packet> Deserialize(DataBuffer& a_Data);
} // namespace PacketSerializer
} // namespace protocol
} // namespace sp

View File

@@ -1,39 +0,0 @@
#pragma once
/**
* \file PacketVisitor.h
* \brief File containing the sp::protocol::PacketVisitor class
*/
#include <sp/protocol/packet/Packets.h>
namespace sp {
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 sp

View File

@@ -1,115 +0,0 @@
#pragma once
/**
* \file Packets.h
* \brief File containing the definitions of the packets
*/
#include <sp/common/NonCopyable.h>
#include <sp/protocol/packet/PacketData.h>
#include <sp/protocol/packet/PacketDeclare.h>
#include <cstdint>
namespace sp {
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
};
#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 sp::protocol::data namespace)
*/
template <PacketType PT, typename Data>
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 SP_INSTANCIATE_PACKETS
// before including this file
// if you want to instantiate templates
#ifdef SP_INSTANCIATE_PACKETS
#define DeclarePacket(PacketName, ...) \
using PacketName = ConcretePacket<PacketType::PacketName, pdata::PacketName>; \
template class ConcretePacket<PacketType::PacketName, pdata::PacketName>;
#else
#define DeclarePacket(PacketName, ...) /** Defines the PacketName packet */ \
using PacketName = ConcretePacket<PacketType::PacketName, pdata::PacketName>;
#endif
// DeclareAllPacket()
#undef DeclarePacket
} // namespace packets
} // namespace protocol
} // namespace sp