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

@@ -2,12 +2,12 @@
/**
* \file PacketDispatcher.h
* \brief File containing the sp::protocol::PacketDispatcher class
* \brief File containing the td::protocol::PacketDispatcher class
*/
#include <sp/common/NonCopyable.h>
#include <sp/protocol/packet/Packets.h>
#include <vector>
#include <map>
namespace sp {
@@ -56,6 +56,6 @@ class Dispatcher : private NonCopyable {
};
} // namespace protocol
} // namespace sp
} // namespace td
#include "Dispatcher.inl"

View File

@@ -28,11 +28,11 @@ void Dispatcher<T_Enum, T_Handler, T>::UnregisterHandler(T_Handler& handler) {
if (pair.second.empty())
continue;
PacketType type = pair.first;
auto 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
} // namespace td

141
include/sp/GenericHandler.h Normal file
View File

@@ -0,0 +1,141 @@
#pragma once
#include <tuple>
namespace sp
{
// This class is inspired by https://arobenko.gitbooks.io/comms-protocols-cpp/content/
// TCommon is common interface class for all the messages
// TAll is all the message types, that need to be handled, bundled in std::tuple
template <typename TCommon, typename TAll>
class GenericHandler;
// Big boy to process packets 20 by 20, preventing needlessly copying vtable many times at each inheritance stage
template <typename TCommon,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename T11, typename T12, typename T13, typename T14, typename T15,
typename T16, typename T17, typename T18, typename T19, typename T20,
typename... TRest>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T11, T13, T14, T15, T16, T17, T18, T19, T20, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >
{
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T4& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T5& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T6& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T7& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T8& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T9& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T10& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T11& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T12& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T13& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T14& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T15& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T16& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T17& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T18& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T19& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T20& msg) { this->Handle(static_cast<TCommon&>(msg)); }
};
// 10 by 10
template <typename TCommon,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10,
typename... TRest>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >
{
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T4& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T5& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T6& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T7& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T8& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T9& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T10& msg) { this->Handle(static_cast<TCommon&>(msg)); }
};
// 5 by 5
template <typename TCommon,
typename T1, typename T2, typename T3, typename T4, typename T5,
typename... TRest>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4, T5, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >
{
using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
public:
using Base::Handle; // Don't hide all Handle() functions from base classes
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T4& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T5& msg) { this->Handle(static_cast<TCommon&>(msg)); }
};
// Deal with rest with 4 types
template <typename TCommon, typename T1, typename T2, typename T3, typename T4>
class GenericHandler<TCommon, std::tuple<T1, T2, T3, T4> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T4& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(TCommon&) { } //Nothing to do
};
// Deal with rest with 3 types
template <typename TCommon, typename T1, typename T2, typename T3>
class GenericHandler<TCommon, std::tuple<T1, T2, T3> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T3& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(TCommon&) { } //Nothing to do
};
// Deal with rest with 2 types
template <typename TCommon, typename T1, typename T2>
class GenericHandler<TCommon, std::tuple<T1, T2> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(T2& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(TCommon&) { } //Nothing to do
};
// Deal with rest with 1 type
template <typename TCommon, typename T1>
class GenericHandler<TCommon, std::tuple<T1> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(T1& msg) { this->Handle(static_cast<TCommon&>(msg)); }
virtual void Handle(TCommon&) { } //Nothing to do
};
// Deal with rest with 0 type
template <typename TCommon>
class GenericHandler<TCommon, std::tuple<> >
{
public:
virtual ~GenericHandler() {}
virtual void Handle(TCommon&) { } //Nothing to do
};
} // sp

38
include/sp/Message.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include <memory>
#include <vector>
#include <sp/common/DataBuffer.h>
namespace sp {
class Handler;
class Message {
public:
virtual ~Message() {}
void Write(DataBuffer& buffer) const {
buffer << GetId();
WriteImpl(buffer);
}
void Read(const DataBuffer& buffer) {
ReadImpl(buffer);
}
void Dispatch(Handler& handler) {
DispatchImpl(handler);
}
virtual int GetId() const = 0;
virtual std::string_view GetName() const = 0;
protected:
virtual void DispatchImpl(Handler& handler) = 0;
virtual void WriteImpl(DataBuffer& buffer) const = 0;
virtual void ReadImpl(const DataBuffer& buffer) = 0;
};
} // namespace sp

18
include/sp/MessageBase.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <sp/Message.h>
namespace sp {
template <typename TDerived>
class MessageBase : public Message {
protected:
virtual void DispatchImpl(Handler& handler) override {
handler.Handle(static_cast<TDerived&>(*this));
}
virtual void WriteImpl(DataBuffer& buffer) const = 0;
virtual void ReadImpl(const DataBuffer& buffer) = 0;
};
} // namespace sp

47
include/sp/Templates.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include <cstdint>
namespace sp {
/// @brief Default case, see field_index specialization for implementation details
template <size_t N, typename T, template <size_t, typename> typename U, typename = void>
static constexpr size_t field_index = 0;
/// @brief A templated size_t that counts the number of existing classes U with a "field_name" member
/// @tparam N Current counter for recursion (user should always call it with 0)
/// @tparam T Can be any type, must be different for each field we want to be counted later (used because we can't have a empty
/// template<> specialization nested in a class)
/// @tparam U The templated class that will be searched for match
template <size_t N, typename T, template <size_t, typename> typename U>
static constexpr size_t field_index<N, T, U, std::void_t<decltype(U<N, T>::field_name)>> = 1 + field_index<N + 1, T, U>;
/// @brief Concat multiple tuples in one big tuple
/// @tparam ...input_t Multiple std::tuple types to concat
template <typename... input_t>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<input_t>()...));
template <typename T, typename Tuple>
constexpr bool tuple_contains_type = false;
template <typename T, typename... Ts>
constexpr bool tuple_contains_type<T, std::tuple<Ts...>> = std::disjunction_v<std::is_same<T, Ts>...>;
template <typename T, typename Tuple>
constexpr int get_tuple_index = 0;
template <typename T, typename... Rest>
constexpr int get_tuple_index<T, std::tuple<T, Rest...>> = 0;
template <typename T, typename First, typename... Rest>
constexpr int get_tuple_index<T, std::tuple<First, Rest...>> = 1 + get_tuple_index<T, std::tuple<Rest...>>;
// Template black magic to loop at compile time
template <std::size_t... indices, class LoopBody>
void loop_impl(std::index_sequence<indices...>, LoopBody&& loop_body) {
(loop_body(std::integral_constant<std::size_t, indices>{}), ...);
}
template <std::size_t N, class LoopBody>
void loop(LoopBody&& loop_body) {
loop_impl(std::make_index_sequence<N>{}, std::forward<LoopBody>(loop_body));
}
} // namespace sp

View File

@@ -0,0 +1,54 @@
/*
* Created by William Swanson in 2012.
*
* I, William Swanson, dedicate this work to the public domain.
* I waive all rights to the work worldwide under copyright law,
* including all related and neighboring rights,
* to the extent allowed by law.
*
* You can copy, modify, distribute and perform the work,
* even for commercial purposes, all without asking permission.
*/
#ifndef MAP_H_INCLUDED
#define MAP_H_INCLUDED
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define MAP_END(...)
#define MAP_OUT
#define MAP_COMMA ,
#define MAP_GET_END2() 0, MAP_END
#define MAP_GET_END1(...) MAP_GET_END2
#define MAP_GET_END(...) MAP_GET_END1
#define MAP_NEXT0(test, next, ...) next MAP_OUT
#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next)
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
/**
* Applies the function macro `f` to each of the remaining parameters.
*/
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
/**
* Applies the function macro `f` to each of the remaining parameters and
* inserts commas between the results.
*/
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
#endif

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