FastEngine 0.9.4
A multiplayer oriented 2D engine made with Vulkan.
Loading...
Searching...
No Matches
C_protocol.hpp
1/*
2 * Copyright 2025 Guillaume Guillet
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _FGE_C_PROTOCOL_HPP_INCLUDED
18#define _FGE_C_PROTOCOL_HPP_INCLUDED
19
20#include "FastEngine/fge_extern.hpp"
21#include "FastEngine/network/C_identity.hpp"
22#include "FastEngine/network/C_packet.hpp"
23#include <memory>
24#include <optional>
25#include <queue>
26#include <vector>
27
28#define FGE_NET_HEADER_DO_NOT_DISCARD_FLAG 0x8000
29#define FGE_NET_HEADER_DO_NOT_REORDER_FLAG 0x4000
30#define FGE_NET_HEADER_COMPRESSED_FLAG 0x2000
31#define FGE_NET_HEADER_DO_NOT_FRAGMENT_FLAG 0x1000
32#define FGE_NET_HEADER_FLAGS_MASK 0xF000
33#define FGE_NET_HEADER_FLAGS_COUNT 4
34
35#define FGE_NET_ID_MAX ((~FGE_NET_HEADER_FLAGS_MASK) - 1)
36
37#define FGE_NET_INTERNAL_ID_MAX 1024
38#define FGE_NET_INTERNAL_ID_START 1
39
40#define FGE_NET_CUSTOM_ID_MAX FGE_NET_ID_MAX
41#define FGE_NET_CUSTOM_ID_START (FGE_NET_INTERNAL_ID_MAX + 1)
42
43#define FGE_NET_BAD_ID 0
44
45#define FGE_NET_PACKET_CACHE_DELAY_FACTOR 1.2f
46#define FGE_NET_PACKET_CACHE_MAX 100
47#define FGE_NET_PACKET_CACHE_MIN_LATENCY_MS 10
48
49#define FGE_NET_DEFAULT_REALM 0
50#define FGE_NET_DEFAULT_PACKET_REORDERER_CACHE_SIZE 5
51#define FGE_NET_PACKET_REORDERER_CACHE_COMPUTE(_clientReturnRate, _serverTickRate) \
52 (((_clientReturnRate) * FGE_NET_PACKET_CACHE_DELAY_FACTOR / (_serverTickRate) + 1) * 2)
53
54#define FGE_NET_HANDSHAKE_STRING "FGE:HANDSHAKE:AZCgMVg4d4Sl2xYvZcqXqljIOqSrKX6H"
55
56namespace fge
57{
58
59class Compressor;
60
61} // namespace fge
62
63namespace fge::net
64{
65
66class Client;
67
68using Timestamp = uint16_t;
69
84class FGE_API ProtocolPacket : public Packet, public std::enable_shared_from_this<ProtocolPacket>
85{
86public:
97
98 struct Option
99 {
100 constexpr explicit Option(Options option, std::size_t argument = 0) :
101 _option(option),
102 _argument(argument)
103 {}
104
106 std::size_t _argument;
107 };
108
109 using IdType = uint16_t;
110 using RealmType = uint16_t;
111 using CounterType = uint16_t;
112
113 struct Header
114 {
115 IdType _id;
116 RealmType _realm;
117 CounterType _counter;
118 CounterType _lastCounter;
119 };
120
121 constexpr static std::size_t HeaderSize = sizeof(IdType) + sizeof(RealmType) + sizeof(CounterType) * 2;
122 constexpr static std::size_t IdPosition = 0;
123 constexpr static std::size_t RealmPosition = sizeof(IdType);
124 constexpr static std::size_t CounterPosition = sizeof(IdType) + sizeof(RealmType);
125 constexpr static std::size_t LastCounterPosition = sizeof(IdType) + sizeof(RealmType) + sizeof(CounterType);
126
127 inline ProtocolPacket(Packet const& pck,
128 Identity const& id,
129 std::size_t fluxIndex = 0,
130 std::size_t fluxLifetime = 0);
131 inline ProtocolPacket(Packet&& pck, Identity const& id, std::size_t fluxIndex = 0, std::size_t fluxLifetime = 0);
132 inline ProtocolPacket(IdType header,
133 RealmType realmId = FGE_NET_DEFAULT_REALM,
134 CounterType countId = 0,
135 CounterType lastCountId = 0);
136
137 inline ProtocolPacket(Packet const& r);
138 inline ProtocolPacket(Packet&& r) noexcept;
139
140 inline ProtocolPacket(ProtocolPacket const& r);
141 inline ProtocolPacket(ProtocolPacket&& r) noexcept;
142
143 inline ~ProtocolPacket() override = default;
144
145 [[nodiscard]] inline Packet& packet() noexcept { return *this; }
146 [[nodiscard]] inline Packet const& packet() const noexcept { return *this; }
147
148 [[nodiscard]] inline bool haveCorrectHeader() const;
149 [[nodiscard]] inline bool haveCorrectHeaderSize() const;
150 [[nodiscard]] inline std::optional<IdType> retrieveHeaderId() const;
151 [[nodiscard]] inline std::optional<IdType> retrieveFlags() const;
152 [[nodiscard]] inline std::optional<IdType> retrieveFullHeaderId() const;
153 [[nodiscard]] inline std::optional<RealmType> retrieveRealm() const;
154 [[nodiscard]] inline std::optional<CounterType> retrieveCounter() const;
155 [[nodiscard]] inline std::optional<CounterType> retrieveLastCounter() const;
156 [[nodiscard]] inline std::optional<Header> retrieveHeader() const;
157
158 [[nodiscard]] inline bool isFragmented() const;
159
160 inline ProtocolPacket& setHeader(Header const& header);
161 inline ProtocolPacket& setHeaderId(IdType id);
162
163 inline ProtocolPacket& setFlags(IdType flags);
164 inline ProtocolPacket& addFlags(IdType flags);
165 inline ProtocolPacket& removeFlags(IdType flags);
166 [[nodiscard]] inline bool checkFlags(IdType flags) const;
167 inline ProtocolPacket& doNotDiscard();
168 inline ProtocolPacket& doNotReorder();
169 inline ProtocolPacket& doNotFragment();
170
171 inline ProtocolPacket& setRealm(RealmType realm);
172 inline ProtocolPacket& setCounter(CounterType counter);
173 inline ProtocolPacket& setLastReorderedPacketCounter(CounterType counter);
174
175 inline void setTimestamp(Timestamp timestamp);
176 [[nodiscard]] inline Timestamp getTimeStamp() const;
177 [[nodiscard]] inline Identity const& getIdentity() const;
178
179 [[nodiscard]] inline std::vector<Option> const& options() const;
180 [[nodiscard]] inline std::vector<Option>& options();
181
182 [[nodiscard]] bool compress(Compressor& compressor);
183 [[nodiscard]] bool decompress(Compressor& compressor);
184
185 inline void markForEncryption();
186 inline void unmarkForEncryption();
187 [[nodiscard]] inline bool isMarkedForEncryption() const;
188
189 inline void markAsLocallyReordered();
190 inline void unmarkAsLocallyReordered();
191 [[nodiscard]] inline bool isMarkedAsLocallyReordered() const;
192
193 inline void markAsCached();
194 inline void unmarkAsCached();
195 [[nodiscard]] inline bool isMarkedAsCached() const;
196
204 void applyOptions(Client const& client);
212
223 [[nodiscard]] inline bool checkFluxLifetime(std::size_t fluxSize);
224 inline std::size_t getFluxIndex() const;
225 inline std::size_t bumpFluxIndex(std::size_t fluxSize);
226
227 [[nodiscard]] std::vector<std::unique_ptr<ProtocolPacket>> fragment(uint16_t mtu) const;
228
229private:
230 Identity g_identity{};
231 Timestamp g_timestamp{0};
232
233 std::size_t g_fluxIndex{0};
234 std::size_t g_fluxLifetime{0};
235
236 bool g_markedForEncryption{false};
237 bool g_markedAsLocallyReordered{false};
238 bool g_markedAsCached{false};
239
240 std::vector<Option> g_options;
241};
242
243using TransmitPacketPtr = std::unique_ptr<ProtocolPacket>;
244using ReceivedPacketPtr = std::unique_ptr<ProtocolPacket>;
245
246template<class... Args>
247[[nodiscard]] inline TransmitPacketPtr CreatePacket(Args&&... args)
248{
249 return std::make_unique<ProtocolPacket>(std::forward<Args>(args)...);
250}
251[[nodiscard]] inline TransmitPacketPtr CreatePacket()
252{
253 return std::make_unique<ProtocolPacket>(FGE_NET_BAD_ID);
254}
255
256enum InternalProtocolIds : ProtocolPacket::IdType
257{
258 NET_INTERNAL_ID_MTU_ASK = FGE_NET_INTERNAL_ID_START,
259 NET_INTERNAL_ID_MTU_ASK_RESPONSE,
260 NET_INTERNAL_ID_MTU_TEST,
261 NET_INTERNAL_ID_MTU_TEST_RESPONSE,
262 NET_INTERNAL_ID_MTU_FINAL,
263
264 NET_INTERNAL_ID_FRAGMENTED_PACKET,
265
266 NET_INTERNAL_ID_FGE_HANDSHAKE,
267 NET_INTERNAL_ID_CRYPT_HANDSHAKE,
268
269 NET_INTERNAL_ID_RETURN_PACKET,
270
271 NET_INTERNAL_ID_DISCONNECT
272};
273
274[[nodiscard]] inline TransmitPacketPtr CreateDisconnectPacket()
275{
276 auto packet = std::make_unique<ProtocolPacket>(NET_INTERNAL_ID_DISCONNECT);
277 packet->doNotDiscard().doNotReorder();
278 return packet;
279}
280
282{
283 uint8_t _fragmentTotal;
284};
285
286class PacketDefragmentation
287{
288public:
289 PacketDefragmentation() = default;
290 ~PacketDefragmentation() = default;
291
292 enum class Results
293 {
294 RETRIEVABLE,
295 WAITING,
296 DISCARDED
297 };
298 struct Result
299 {
300 Results _result;
301 ProtocolPacket::RealmType _id;
302 };
303
304 void clear();
305
306 [[nodiscard]] Result process(ReceivedPacketPtr&& packet);
307 [[nodiscard]] ReceivedPacketPtr retrieve(ProtocolPacket::RealmType id, Identity const& client);
308
309private:
310 struct Data
311 {
312 Data(ProtocolPacket::RealmType id, ProtocolPacket::CounterType total) :
313 _id(id),
314 _count(1),
315 _fragments(total)
316 {}
317
318 ProtocolPacket::RealmType _id;
319 decltype(InternalFragmentedPacketData::_fragmentTotal) _count;
320 std::vector<ReceivedPacketPtr> _fragments;
321 };
322 std::vector<Data> g_data;
323};
324
332class FGE_API PacketReorderer
333{
334public:
335 enum class Stats
336 {
337 OLD_REALM,
338 OLD_COUNTER,
339 WAITING_NEXT_REALM,
340 WAITING_NEXT_COUNTER,
341 RETRIEVABLE
342 };
343
344 PacketReorderer() = default;
345 PacketReorderer(PacketReorderer const& r) = delete;
346 PacketReorderer(PacketReorderer&& r) noexcept = default;
347 ~PacketReorderer() = default;
348
349 PacketReorderer& operator=(PacketReorderer const& r) = delete;
350 PacketReorderer& operator=(PacketReorderer&& r) noexcept = default;
351
352 void clear();
353
354 void push(ReceivedPacketPtr&& packet);
355 [[nodiscard]] static Stats checkStat(ReceivedPacketPtr const& packet,
356 ProtocolPacket::CounterType currentCounter,
357 ProtocolPacket::RealmType currentRealm);
358 [[nodiscard]] bool isForced() const;
359 [[nodiscard]] std::optional<Stats> checkStat(ProtocolPacket::CounterType currentCounter,
360 ProtocolPacket::RealmType currentRealm) const;
361 [[nodiscard]] ReceivedPacketPtr pop();
362
363 [[nodiscard]] bool isEmpty() const;
364
365 void setMaximumSize(std::size_t size);
366 [[nodiscard]] std::size_t getMaximumSize() const;
367
368private:
369 struct FGE_API Data
370 {
371 explicit Data(ReceivedPacketPtr&& packet);
372 Data(Data const& r) = delete;
373 Data(Data&& r) noexcept;
374 ~Data();
375
376 Data& operator=(Data const& r) = delete;
377 Data& operator=(Data&& r) noexcept;
378
379 [[nodiscard]] Stats checkStat(ProtocolPacket::CounterType currentCounter,
380 ProtocolPacket::RealmType currentRealm) const;
381
382 ReceivedPacketPtr _packet;
383 ProtocolPacket::CounterType _counter;
384 ProtocolPacket::CounterType _lastCounter;
385 ProtocolPacket::RealmType _realm;
386
387 struct Compare
388 {
389 [[nodiscard]] constexpr bool operator()(Data const& l, Data const& r) const
390 {
391 if (l._realm == r._realm)
392 {
393 return l._counter > r._counter;
394 }
395 return l._realm > r._realm;
396 }
397 };
398 };
399
400 std::priority_queue<Data, std::vector<Data>, Data::Compare> g_cache;
401 std::size_t g_cacheSize{FGE_NET_DEFAULT_PACKET_REORDERER_CACHE_SIZE};
402 bool g_forceRetrieve{false};
403};
404
405class FGE_API PacketCache
406{
407public:
408 struct Label
409 {
410 constexpr Label() = default;
411 constexpr Label(ProtocolPacket::CounterType counter, ProtocolPacket::RealmType realm) :
412 _counter(counter),
413 _realm(realm)
414 {}
415
416 ProtocolPacket::CounterType _counter{0};
417 ProtocolPacket::RealmType _realm{0};
418
419 [[nodiscard]] constexpr bool operator==(Label const& r) const
420 {
421 return this->_counter == r._counter && this->_realm == r._realm;
422 }
423 };
424
425 PacketCache() = default;
426 PacketCache(PacketCache const& r) = delete;
427 PacketCache(PacketCache&& r) noexcept = default;
428 ~PacketCache() = default;
429
430 PacketCache& operator=(PacketCache const& r) = delete;
431 PacketCache& operator=(PacketCache&& r) noexcept = default;
432
433 void clear();
434 [[nodiscard]] bool isEmpty() const;
435
436 //Transmit
437 void push(TransmitPacketPtr const& packet);
438
439 //Receive
440 void acknowledgeReception(std::span<Label> labels);
441
442 //Check for unacknowledged packet
443 [[nodiscard]] bool check(std::chrono::steady_clock::time_point const& timePoint,
444 std::chrono::milliseconds clientDelay);
445 [[nodiscard]] TransmitPacketPtr pop();
446
447private:
448 struct Data
449 {
450 Data() = default;
451 explicit Data(TransmitPacketPtr&& packet);
452
453 Data& operator=(TransmitPacketPtr&& packet);
454
455 TransmitPacketPtr _packet;
456 Label _label;
457 std::chrono::steady_clock::time_point _time{};
458 };
459
460 //Circular buffer
461 std::vector<Data> g_cache{FGE_NET_PACKET_CACHE_MAX};
462 std::size_t g_start{0};
463 std::size_t g_end{0};
464};
465
466} // namespace fge::net
467
468#include "C_protocol.inl"
469
470#endif // _FGE_C_PROTOCOL_HPP_INCLUDED
Definition C_compressor.hpp:29
Class that represent the identity of a client.
Definition C_client.hpp:220
Definition C_packet.hpp:52
void applyOptions()
Apply packet options to the packet.
bool checkFluxLifetime(std::size_t fluxSize)
Check if the flux lifetime is reached.
Definition C_protocol.inl:341
void applyOptions(Client const &client)
Apply packet options to the packet.
Options
Options to pass to the network thread when sending a packet.
Definition C_protocol.hpp:92
@ UPDATE_FULL_TIMESTAMP
The full timestamp of the packet will be updated when sending.
Definition C_protocol.hpp:94
@ UPDATE_CORRECTION_LATENCY
The latency of the packet will be updated with the corrector latency from the Client.
Definition C_protocol.hpp:95
@ UPDATE_TIMESTAMP
The timestamp of the packet will be updated when sending.
Definition C_protocol.hpp:93
uint16_t Timestamp
An timestamp represent modulated current time in milliseconds.
Definition C_client.hpp:62
A class to represent a client or server identity with an IP address and a port.
Definition C_identity.hpp:31
Definition C_protocol.hpp:282
Definition C_protocol.hpp:409
Definition C_protocol.hpp:299
Definition C_protocol.hpp:388
Definition C_protocol.hpp:114
Options _option
The option to send the packet with.
Definition C_protocol.hpp:105
std::size_t _argument
The option argument.
Definition C_protocol.hpp:106