FastEngine 0.9.5
A multiplayer oriented 2D engine made with Vulkan.
Loading...
Searching...
No Matches
C_protocol.hpp
1/*
2 * Copyright 2026 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 2.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 ((static_cast<unsigned>(static_cast<float>(_clientReturnRate) * FGE_NET_PACKET_CACHE_DELAY_FACTOR / \
53 static_cast<float>(_serverTickRate)) + \
54 1) * \
55 2)
56
57#define FGE_NET_HANDSHAKE_STRING "FGE:HANDSHAKE:AZCgMVg4d4Sl2xYvZcqXqljIOqSrKX6H"
58
59namespace fge
60{
61
62class Compressor;
63
64} // namespace fge
65
66namespace fge::net
67{
68class NetFluxUdp;
69class Client;
70
71using Timestamp = uint16_t;
72
87class FGE_API ProtocolPacket : public Packet, public std::enable_shared_from_this<ProtocolPacket>
88{
89public:
94 enum class Options
95 {
96 UPDATE_TIMESTAMP,
97 UPDATE_FULL_TIMESTAMP,
98 UPDATE_CORRECTION_LATENCY
99 };
100
101 struct Option
102 {
103 constexpr explicit Option(Options option, std::size_t argument = 0) :
104 _option(option),
105 _argument(argument)
106 {}
107
109 std::size_t _argument;
110 };
111
112 using IdType = uint16_t;
113 using RealmType = uint16_t;
114 using CounterType = uint16_t;
115
116 struct Header
117 {
118 IdType _id;
119 RealmType _realm;
120 CounterType _counter;
121 CounterType _lastCounter;
122 };
123
124 constexpr static std::size_t HeaderSize = sizeof(IdType) + sizeof(RealmType) + sizeof(CounterType) * 2;
125 constexpr static std::size_t IdPosition = 0;
126 constexpr static std::size_t RealmPosition = sizeof(IdType);
127 constexpr static std::size_t CounterPosition = sizeof(IdType) + sizeof(RealmType);
128 constexpr static std::size_t ReorderedCounterPosition = sizeof(IdType) + sizeof(RealmType) + sizeof(CounterType);
129
130 inline ProtocolPacket(Packet const& pck,
131 Identity const& id,
132 std::size_t fluxIndex = 0,
133 std::size_t fluxLifetime = 0);
134 inline ProtocolPacket(Packet&& pck, Identity const& id, std::size_t fluxIndex = 0, std::size_t fluxLifetime = 0);
135 inline ProtocolPacket(IdType header,
136 RealmType realmId = FGE_NET_DEFAULT_REALM,
137 CounterType countId = 0,
138 CounterType lastCountId = 0);
139
140 inline ProtocolPacket(Packet const& r);
141 inline ProtocolPacket(Packet&& r) noexcept;
142
143 inline ProtocolPacket(ProtocolPacket const& r);
144 inline ProtocolPacket(ProtocolPacket&& r) noexcept;
145
146 inline ~ProtocolPacket() override = default;
147
148 [[nodiscard]] inline Packet& packet() noexcept { return *this; }
149 [[nodiscard]] inline Packet const& packet() const noexcept { return *this; }
150
151 [[nodiscard]] inline bool haveCorrectHeader() const;
152 [[nodiscard]] inline bool haveCorrectHeaderSize() const;
153 [[nodiscard]] inline std::optional<IdType> retrieveHeaderId() const;
154 [[nodiscard]] inline std::optional<IdType> retrieveFlags() const;
155 [[nodiscard]] inline std::optional<IdType> retrieveFullHeaderId() const;
156 [[nodiscard]] inline std::optional<RealmType> retrieveRealm() const;
157 [[nodiscard]] inline std::optional<CounterType> retrieveCounter() const;
158 [[nodiscard]] inline std::optional<CounterType> retrieveReorderedCounter() const;
159 [[nodiscard]] inline std::optional<Header> retrieveHeader() const;
160
161 [[nodiscard]] inline bool isFragmented() const;
162
163 inline ProtocolPacket& setHeader(Header const& header);
164 inline ProtocolPacket& setHeaderId(IdType id);
165
166 inline ProtocolPacket& setFlags(IdType flags);
167 inline ProtocolPacket& addFlags(IdType flags);
168 inline ProtocolPacket& removeFlags(IdType flags);
169 [[nodiscard]] inline bool checkFlags(IdType flags) const;
170 inline ProtocolPacket& doNotDiscard();
171 inline ProtocolPacket& doNotReorder();
172 inline ProtocolPacket& doNotFragment();
173
174 inline ProtocolPacket& setRealm(RealmType realm);
175 inline ProtocolPacket& setCounter(CounterType counter);
176 inline ProtocolPacket& setReorderedCounter(CounterType counter);
177
178 inline void setTimestamp(Timestamp timestamp);
179 [[nodiscard]] inline Timestamp getTimeStamp() const;
180 [[nodiscard]] inline Identity const& getIdentity() const;
181
182 [[nodiscard]] inline std::vector<Option> const& options() const;
183 [[nodiscard]] inline std::vector<Option>& options();
184
185 [[nodiscard]] bool compress(Compressor& compressor);
186 [[nodiscard]] bool decompress(Compressor& compressor);
187
188 inline void markForEncryption();
189 inline void unmarkForEncryption();
190 [[nodiscard]] inline bool isMarkedForEncryption() const;
191
192 inline void markAsLocallyReordered();
193 inline void unmarkAsLocallyReordered();
194 [[nodiscard]] inline bool isMarkedAsLocallyReordered() const;
195
196 inline void markAsCached();
197 inline void unmarkAsCached();
198 [[nodiscard]] inline bool isMarkedAsCached() const;
199
207 void applyOptions(Client const& client);
215
226 [[nodiscard]] inline bool checkFluxLifetime(std::size_t fluxSize);
227 inline std::size_t getFluxIndex() const;
228 inline std::size_t bumpFluxIndex(std::size_t fluxSize);
229
230 [[nodiscard]] std::vector<std::unique_ptr<ProtocolPacket>> fragment(uint16_t mtu) const;
231
232private:
233 Identity g_identity{};
234 Timestamp g_timestamp{0};
235
236 std::size_t g_fluxIndex{0};
237 std::size_t g_fluxLifetime{0};
238
239 bool g_markedForEncryption{false};
240 bool g_markedAsLocallyReordered{false};
241 bool g_markedAsCached{false};
242
243 std::vector<Option> g_options;
244};
245
246using TransmitPacketPtr = std::unique_ptr<ProtocolPacket>;
247using ReceivedPacketPtr = std::unique_ptr<ProtocolPacket>;
248
249template<class... Args>
250[[nodiscard]] inline TransmitPacketPtr CreatePacket(Args&&... args)
251{
252 return std::make_unique<ProtocolPacket>(std::forward<Args>(args)...);
253}
254[[nodiscard]] inline TransmitPacketPtr CreatePacket()
255{
256 return std::make_unique<ProtocolPacket>(FGE_NET_BAD_ID);
257}
258
259enum InternalProtocolIds : ProtocolPacket::IdType
260{
261 NET_INTERNAL_ID_MTU_ASK = FGE_NET_INTERNAL_ID_START,
262 NET_INTERNAL_ID_MTU_ASK_RESPONSE,
263 NET_INTERNAL_ID_MTU_TEST,
264 NET_INTERNAL_ID_MTU_TEST_RESPONSE,
265 NET_INTERNAL_ID_MTU_FINAL,
266
267 NET_INTERNAL_ID_FRAGMENTED_PACKET,
268
269 NET_INTERNAL_ID_FGE_HANDSHAKE,
270 NET_INTERNAL_ID_CRYPT_HANDSHAKE,
271
272 NET_INTERNAL_ID_RETURN_PACKET,
273
274 NET_INTERNAL_ID_DISCONNECT
275};
276
277[[nodiscard]] inline TransmitPacketPtr CreateDisconnectPacket()
278{
279 auto packet = std::make_unique<ProtocolPacket>(NET_INTERNAL_ID_DISCONNECT);
280 packet->doNotDiscard().doNotReorder();
281 return packet;
282}
283
285{
286 uint8_t _fragmentTotal;
287};
288
289class PacketDefragmentation
290{
291public:
292 PacketDefragmentation() = default;
293 PacketDefragmentation(PacketDefragmentation const& r) = delete;
294 PacketDefragmentation(PacketDefragmentation&& r) noexcept = default;
295 ~PacketDefragmentation() = default;
296
297 PacketDefragmentation& operator=(PacketDefragmentation const& r) = delete;
298 PacketDefragmentation& operator=(PacketDefragmentation&& r) noexcept = default;
299
300 enum class Results
301 {
302 RETRIEVABLE,
303 WAITING,
304 DISCARDED
305 };
306 struct Result
307 {
308 Results _result;
309 ProtocolPacket::RealmType _id;
310 };
311
312 void clear();
313
314 [[nodiscard]] Result process(ReceivedPacketPtr&& packet);
315 [[nodiscard]] ReceivedPacketPtr retrieve(ProtocolPacket::RealmType id, Identity const& client);
316
317private:
318 struct Data
319 {
320 Data(ProtocolPacket::RealmType id, ProtocolPacket::CounterType total) :
321 _id(id),
322 _count(1),
323 _fragments(total)
324 {}
325
326 ProtocolPacket::RealmType _id;
327 decltype(InternalFragmentedPacketData::_fragmentTotal) _count;
328 std::vector<ReceivedPacketPtr> _fragments;
329 };
330 std::vector<Data> g_data;
331};
332
340class FGE_API PacketReorderer
341{
342public:
343 enum class Stats
344 {
345 OLD_REALM,
346 OLD_COUNTER,
347 WAITING_NEXT_REALM,
348 WAITING_NEXT_COUNTER,
349 RETRIEVABLE
350 };
351
352 PacketReorderer() = default;
353 PacketReorderer(PacketReorderer const& r) = delete;
354 PacketReorderer(PacketReorderer&& r) noexcept = default;
355 ~PacketReorderer() = default;
356
357 PacketReorderer& operator=(PacketReorderer const& r) = delete;
358 PacketReorderer& operator=(PacketReorderer&& r) noexcept = default;
359
360 void clear();
361
362 bool process(Client& client, NetFluxUdp& flux, bool ignoreRealm);
363
364 void push(ReceivedPacketPtr&& packet);
365
366 [[nodiscard]] static Stats checkStat(ReceivedPacketPtr const& packet, Client const& client, bool ignoreRealm);
367 [[nodiscard]] static Stats checkStat(ReceivedPacketPtr const& packet,
368 ProtocolPacket::CounterType peerCounter,
369 ProtocolPacket::CounterType peerReorderedCounter,
370 ProtocolPacket::RealmType peerRealm,
371 bool ignoreRealm);
372 [[nodiscard]] std::optional<Stats> checkStat(Client const& client, bool ignoreRealm) const;
373 [[nodiscard]] std::optional<Stats> checkStat(ProtocolPacket::CounterType peerCounter,
374 ProtocolPacket::CounterType peerReorderedCounter,
375 ProtocolPacket::RealmType peerRealm,
376 bool ignoreRealm) const;
377
378 [[nodiscard]] ReceivedPacketPtr pop();
379
380 [[nodiscard]] bool isEmpty() const;
381 [[nodiscard]] bool isForced() const;
382
383 void setMaximumSize(std::size_t size);
384 [[nodiscard]] std::size_t getMaximumSize() const;
385
386private:
387 struct FGE_API Data
388 {
389 explicit Data(ReceivedPacketPtr&& packet);
390 Data(Data const& r) = delete;
391 Data(Data&& r) noexcept;
392 ~Data();
393
394 Data& operator=(Data const& r) = delete;
395 Data& operator=(Data&& r) noexcept;
396
397 [[nodiscard]] Stats checkStat(ProtocolPacket::CounterType peerCounter,
398 ProtocolPacket::CounterType peerReorderedCounter,
399 ProtocolPacket::RealmType peerRealm,
400 bool ignoreRealm) const;
401
402 ReceivedPacketPtr _packet;
403 ProtocolPacket::CounterType _counter;
404 ProtocolPacket::CounterType _reorderedCounter;
405 ProtocolPacket::RealmType _realm;
406
407 struct Compare
408 {
409 [[nodiscard]] constexpr bool operator()(Data const& l, Data const& r) const
410 {
411 if (l._realm == r._realm)
412 {
413 return l._reorderedCounter > r._reorderedCounter;
414 }
415 return l._realm > r._realm;
416 }
417 };
418 };
419
420 std::priority_queue<Data, std::vector<Data>, Data::Compare> g_cache;
421 std::size_t g_cacheSize{FGE_NET_DEFAULT_PACKET_REORDERER_CACHE_SIZE};
422 bool g_forceRetrieve{false};
423};
424
425class FGE_API PacketCache
426{
427public:
428 struct Label
429 {
430 constexpr Label() = default;
431 constexpr Label(ProtocolPacket::CounterType counter, ProtocolPacket::RealmType realm) :
432 _counter(counter),
433 _realm(realm)
434 {}
435
436 ProtocolPacket::CounterType _counter{0};
437 ProtocolPacket::RealmType _realm{0};
438
439 [[nodiscard]] constexpr bool operator==(Label const& r) const
440 {
441 return this->_counter == r._counter && this->_realm == r._realm;
442 }
443
444 struct Hash
445 {
446 [[nodiscard]] inline std::size_t operator()(Label const& label) const
447 {
448 static_assert(sizeof(label._counter) == sizeof(label._realm) && sizeof(label._counter) == 2,
449 "ProtocolPacket::CounterType and ProtocolPacket::RealmType must be 16 bits");
450 return std::hash<std::size_t>()(static_cast<std::size_t>(label._counter) << 16 |
451 static_cast<std::size_t>(label._realm));
452 }
453 };
454 };
455
456 PacketCache();
457 PacketCache(PacketCache const& r) = delete;
458 PacketCache(PacketCache&& r) noexcept;
459 ~PacketCache() = default;
460
461 PacketCache& operator=(PacketCache const& r) = delete;
462 PacketCache& operator=(PacketCache&& r) noexcept;
463
464 void clear();
465 [[nodiscard]] bool isEmpty() const;
466 [[nodiscard]] bool isEnabled() const;
467 [[nodiscard]] bool isAlarmed() const;
468 void enable(bool enable);
469
470 //Transmit
471 void push(TransmitPacketPtr const& packet);
472
473 //Receive
474 void acknowledgeReception(std::span<Label> labels);
475
476 //Check for unacknowledged packet
477 bool process(std::chrono::steady_clock::time_point const& timePoint, Client& client);
478
479private:
480 struct Data
481 {
482 Data() = default;
483 explicit Data(TransmitPacketPtr&& packet);
484
485 Data& operator=(TransmitPacketPtr&& packet);
486
487 TransmitPacketPtr _packet;
488 Label _label;
489 std::chrono::steady_clock::time_point _time{};
490 unsigned int _tryCount{0};
491 };
492
493 mutable std::mutex g_mutex;
494
495 std::vector<Data> g_cache;
496
497 bool g_alarm{false};
498 bool g_enable{false};
499};
500
501} // namespace fge::net
502
503#include "C_protocol.inl"
504
505#endif // _FGE_C_PROTOCOL_HPP_INCLUDED
Definition C_compressor.hpp:29
Class that represent the identity of a client.
Definition C_client.hpp:239
A network flux.
Definition C_server.hpp:114
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:95
uint16_t Timestamp
An timestamp represent modulated current time in milliseconds.
Definition C_client.hpp:64
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:285
Definition C_protocol.hpp:445
Definition C_protocol.hpp:429
Definition C_protocol.hpp:307
Definition C_protocol.hpp:408
Definition C_protocol.hpp:117
Options _option
The option to send the packet with.
Definition C_protocol.hpp:108
std::size_t _argument
The option argument.
Definition C_protocol.hpp:109