-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathKCPNet.h
211 lines (175 loc) · 7.34 KB
/
KCPNet.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//
// _ _______ ______ _ _ _
// | | / / __ \| ___ \ \ | | | |
// | |/ /| / \/| |_/ / \| | ___| |_
// | \| | | __/| . ` |/ _ \ __|
// | |\ \ \__/\| | | |\ | __/ |_
// \_| \_/\____/\_| \_| \_/\___|\__|
//
//
// Created by Unit-X @ Edgeware AB on 2020-09-30.
//
// C++ wrapper around KCP
// All payload data is little endian.
#ifndef KCP_CPP_KCPNET_H
#define KCP_CPP_KCPNET_H
#include "ikcp.h"
#include "kissnet/kissnet.hpp"
#include <any>
#include <thread>
#include <unordered_map>
#include <functional>
#include <mutex>
#include <vector>
#include <atomic>
// Time preamble
#define TIME_PREAMBLE_V1 0x000100010ff00ff0
// Time constants server
#define MAX_DELAY_DIFF_MS 20
#define MIN_COLLECTED_TIME_POINTS 5
#define MAX_SAVED_TIME_POINTS 100
#define TIME_PACKETS_BURST_DISTANCE_MS 100
#define TIME_PACKETS_NORMAL_DISTANCE_MS 1000
// Time constants client
// The maximum adjustment + and - in microseconds per second allowed
#define MAX_TIME_DRIFT_PPM 500
// Count the HEART_BEAT every x ms.
#define HEART_BEAT_DISTANCE 500
// Time out after HEART_BEAT_DISTANCE ms * HEART_BEAT_TIME_OUT milliseconds
#define HEART_BEAT_TIME_OUT 10
struct KCPTimePacket{
uint64_t timePreamble = TIME_PREAMBLE_V1; //Version 1 preamble
int64_t t1 = 0;
int64_t t2 = 0;
int64_t t3 = 0;
int64_t t4 = 0;
int64_t correction = 0;
int64_t correctionActive = 0;
};
static_assert(sizeof(KCPTimePacket) == 56, "KCPTimePacket is not the expected size");
class KCPSettings {
public:
bool mNoDelay = false; // No delay mode. False: Off / True: On.
int mInterval = 10; // KCP update interval in ms
int mResend = 0; // Retransmit when missed mResend number ACK (Default value is 0)
bool mFlow = true; // Flow control, False: Off / True: On.
int mMtu = 1472; // Maximum payload in a single UDP datagram
int mSndWnd = 32; // Send window size
int mRcvWnd = 32; // Receive window size //The doc says 32 the code says 128
};
// Optional context passed to the callbacks
class KCPContext {
public:
explicit KCPContext (std::string lKey): mKCPSocket(lKey) {
}
std::any mObject = nullptr; // For safe object lifecycles
void* mUnsafePointer = nullptr; // Lightweight alternative for unsafe pointers
uint64_t mValue = 0; // Generic 64-bit variable
uint64_t mID = 10; //KCP ID to be used
KCPSettings mSettings;
std::string mKCPSocket;
};
//------------------------------------------------------------------------------------------
//
// KCP Client
//
//------------------------------------------------------------------------------------------
class KCPNetClient {
public:
explicit KCPNetClient();
virtual ~KCPNetClient();
int sendData(const char* pData, size_t lSize);
int configureKCP(KCPSettings &rSettings,
const std::function<void(const char*, size_t, KCPContext*)> &rGotData,
const std::function<void(KCPContext*)> &rDisconnect,
const std::string& lIP = "",
uint16_t lPort = 0,
uint32_t lID = 0,
std::shared_ptr<KCPContext> pCTX = nullptr);
int64_t getNetworkTimeus(); //Network time in us
void udpOutputClient(const char *pBuf, int lSize); //Method used by the bridge function
// delete copy and move constructors and assign operators
KCPNetClient(KCPNetClient const &) = delete; // Copy construct
KCPNetClient(KCPNetClient &&) = delete; // Move construct
KCPNetClient &operator=(KCPNetClient const &) = delete; // Copy assign
KCPNetClient &operator=(KCPNetClient &&) = delete; // Move assign
protected:
std::shared_ptr<KCPContext> mCTX = nullptr;
private:
void netWorkerClient(const std::function<void(const char*, size_t, KCPContext*)> &rDisconnect);
void kcpNudgeWorkerClient(const std::function<void(KCPContext*)> &rGotData);
#ifdef _WIN32
bool mHasSentData = false;
#endif
std::mutex mKCPNetMtx;
ikcpcb *mKCP = nullptr; // The KCP handle for client mode
kissnet::udp_socket mKissnetSocket;
bool mNetworkThreadRunning = false;
bool mNudgeThreadRunning = false;
bool mNudgeThreadActive = false;
uint64_t mConnectionTimeOut = HEART_BEAT_TIME_OUT;
uint64_t mHeartBeatIntervalTrigger = 0;
bool mFirstTimeDelivery = true;
int64_t mLastDeliveredTime;
std::atomic<int64_t> mCurrentCorrectionTarget = 0;
std::atomic<int64_t> mCurrentCorrection = 0;
std::atomic<bool> mGotCorrection = false;
};
//------------------------------------------------------------------------------------------
//
// KCP Server
//
//------------------------------------------------------------------------------------------
class KCPNetServer {
public:
class KCPServerData {
public:
virtual ~KCPServerData() {
if (mKCPServer) ikcp_release(mKCPServer);
}
KCPNetServer* mWeakKCPNetServer = nullptr;
ikcpcb* mKCPServer = nullptr;
std::shared_ptr<KCPContext> mKCPContext = nullptr;
kissnet::addr_collection mDestination;
uint64_t mConnectionTimeOut = HEART_BEAT_TIME_OUT;
bool mGotStableTime = false;
bool mClientGotCorrection = false;
std::vector<std::pair<int64_t, int64_t>> mListOfDelayAndCompensation;
int64_t mCurrentCorrection = 0;
};
explicit KCPNetServer();
virtual ~KCPNetServer();
int sendData(const char* pData, size_t lSize, KCPContext* pCTX);
int configureKCP(const std::function<void(const char*, size_t, KCPContext*)> &rGotData,
const std::function<void(KCPContext*)> &rDisconnect,
const std::function<std::shared_ptr<KCPContext>(std::string, uint16_t, std::shared_ptr<KCPContext>&)> &rValidate,
const std::string& lIP = "",
uint16_t lport = 0,
std::shared_ptr<KCPContext> pCTX = nullptr);
// Method used by the bridge function
void udpOutputServer(const char *pBuf, int lSize, KCPServerData* lCTX);
// delete copy and move constructors and assign operators
KCPNetServer(KCPNetServer const &) = delete; // Copy construct
KCPNetServer(KCPNetServer &&) = delete; // Move construct
KCPNetServer &operator=(KCPNetServer const &) = delete; // Copy assign
KCPNetServer &operator=(KCPNetServer &&) = delete; // Move assign
bool mDropAll = false;
protected:
std::shared_ptr<KCPContext> mCTX = nullptr;
private:
int configureInternal(KCPSettings &rSettings, KCPContext *pCtx);
void netWorkerServer(const std::function<void(const char*, size_t, KCPContext*)> &rGotData,
const std::function<std::shared_ptr<KCPContext>(std::string, uint16_t, std::shared_ptr<KCPContext>&)> &rValidate);
void kcpNudgeWorkerServer(const std::function<void(KCPContext*)> &rDisconnect);
void sendTimePacket(KCPServerData &rServerData);
std::mutex mKCPMapMtx;
std::unordered_map<std::string, std::unique_ptr<KCPServerData>> mKCPMap;
kissnet::udp_socket mKissnetSocket;
bool mNetworkThreadRunning = false;
bool mNudgeThreadRunning = false;
bool mNudgeThreadActive = false;
uint64_t mHeartBeatIntervalTrigger = 0;
uint64_t mSendTimeIntervalTriggerLow = 0;
uint64_t mSendTimeIntervalTriggerHi = 0;
};
#endif //KCP_CPP_KCPNET_H