Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-1-2-2013-Decompilation
Path: blob/main/RSDKv4/Networking.cpp
817 views
1
#include "RetroEngine.hpp"
2
#if RETRO_USE_NETWORKING
3
4
#include <cstdlib>
5
#include <deque>
6
#include <iostream>
7
#include <thread>
8
#include <chrono>
9
#if RETRO_PLATFORM == RETRO_ANDROID
10
#define ASIO_NO_TYPEID
11
#endif
12
#include <asio.hpp>
13
14
char networkHost[64];
15
char networkGame[7] = "SONIC2";
16
int networkPort = 50;
17
int dcError = 0;
18
float lastPing = 0;
19
20
bool waitingForPing = false;
21
22
uint64_t lastTime = 0;
23
24
using asio::ip::udp;
25
26
typedef std::deque<ServerPacket> DataQueue;
27
28
class NetworkSession
29
{
30
public:
31
bool running = false;
32
NetworkSession(asio::io_context &io_context, const udp::endpoint &endpoint)
33
: io_context(io_context), socket(io_context), endpoint(endpoint), timer(io_context)
34
{
35
socket.open(udp::v4());
36
}
37
38
~NetworkSession() {}
39
40
void write(const ServerPacket &msg, bool repeat = false)
41
{
42
ServerPacket sent(msg);
43
sent.player = code;
44
sent.room = room;
45
write_msgs_.push_back(sent);
46
if (repeat) {
47
this->repeat = sent;
48
}
49
}
50
51
void start()
52
{
53
repeat.header = 0x80;
54
running = true;
55
ServerPacket sent;
56
sent.header = CL_REQUEST_CODE;
57
sent.room = 0x1F2F3F4F;
58
write_msgs_.push_back(sent);
59
}
60
61
void close()
62
{
63
if (running)
64
running = false;
65
if (socket.is_open())
66
socket.close();
67
}
68
69
NetworkSession &operator=(const NetworkSession &s)
70
{
71
close();
72
memcpy(this, &s, sizeof(NetworkSession));
73
return *this;
74
}
75
76
uint code = 0;
77
uint partner = 0;
78
uint room = 0;
79
80
bool awaitingReceive = false;
81
bool verifyReceived = false;
82
83
bool retried = true;
84
uint retries = 0;
85
86
ServerPacket repeat;
87
88
void run()
89
{
90
// do we write anything
91
while (!write_msgs_.empty()) {
92
ServerPacket *send = &write_msgs_.front();
93
StrCopy(send->game, networkGame);
94
socket.send_to(asio::buffer(send, sizeof(ServerPacket)), endpoint);
95
write_msgs_.pop_front();
96
if (send->header == 0xFF)
97
session->running = false;
98
}
99
if (!awaitingReceive)
100
do_read();
101
if (repeat.header != 0x80 && retried) {
102
retried = false;
103
timer.expires_from_now(asio::chrono::seconds(1));
104
timer.async_wait([&](const asio::error_code &) {
105
retried = true;
106
repeat.room = room;
107
StrCopy(repeat.game, networkGame);
108
if (retries++ == 10)
109
socket.send_to(asio::buffer(&repeat, sizeof(ServerPacket)), endpoint);
110
});
111
}
112
else if (repeat.header == 0x80)
113
retries = 0;
114
if (retries > 10) {
115
switch (repeat.header) {
116
case 0x01: {
117
dcError = 4;
118
vsPlaying = false;
119
session->running = false;
120
break;
121
}
122
case 0x00: {
123
if (!room) {
124
dcError = 4;
125
vsPlaying = false;
126
session->running = false;
127
}
128
break;
129
}
130
}
131
}
132
133
io_context.poll();
134
io_context.restart();
135
}
136
137
void leave()
138
{
139
ServerPacket send;
140
send.header = CL_LEAVE;
141
vsPlaying = false;
142
write(send);
143
}
144
145
private:
146
void do_read()
147
{
148
if (awaitingReceive)
149
return;
150
awaitingReceive = true;
151
socket.async_receive(asio::buffer(&read_msg_, sizeof(ServerPacket)), [&](const asio::error_code &ec, size_t bytes) {
152
awaitingReceive = false; // async, not threaded. this is safe
153
if (ec || !session->running)
154
return;
155
// it's ok to use preformace counter; we're in a different thread and slowdown is safe
156
lastPing = ((SDL_GetPerformanceCounter() - lastTime) * 1000.0 / SDL_GetPerformanceFrequency());
157
lastTime = SDL_GetPerformanceCounter();
158
waitingForPing = false;
159
if (!code) {
160
if (read_msg_.header == SV_CODES && read_msg_.player) {
161
code = read_msg_.player;
162
}
163
return;
164
}
165
166
switch (read_msg_.header) {
167
case SV_CODES: {
168
if (vsPlaying)
169
return;
170
room = read_msg_.room;
171
if (read_msg_.data.multiData.type > 2) {
172
dcError = 3;
173
leave();
174
return;
175
}
176
177
if (read_msg_.data.multiData.type - 1) {
178
partner = *read_msg_.data.multiData.data;
179
Receive2PVSMatchCode(room);
180
repeat.header = 0x80;
181
return;
182
}
183
break;
184
}
185
case SV_NEW_PLAYER: {
186
if (partner)
187
return;
188
repeat.header = 0x80;
189
vsPlayerID = 0;
190
partner = read_msg_.player;
191
Receive2PVSMatchCode(room);
192
return;
193
}
194
case SV_DATA_VERIFIED:
195
// fallthrough
196
case SV_DATA: {
197
Receive2PVSData(&read_msg_.data.multiData);
198
return;
199
}
200
case SV_RECEIVED: {
201
if (repeat.header == CL_DATA_VERIFIED)
202
repeat.header = CL_QUERY_VERIFICATION;
203
return;
204
}
205
case SV_VERIFY_CLEAR: {
206
repeat.header = 0x80;
207
return;
208
}
209
case SV_NO_ROOM: {
210
leave();
211
dcError = 5;
212
return;
213
}
214
case SV_LEAVE: {
215
if (read_msg_.player != partner)
216
return;
217
leave();
218
dcError = 1;
219
return;
220
}
221
}
222
});
223
}
224
225
asio::io_context &io_context;
226
asio::steady_timer timer;
227
udp::socket socket;
228
udp::endpoint endpoint;
229
ServerPacket read_msg_;
230
DataQueue write_msgs_;
231
232
bool writing = false;
233
234
int attempts;
235
};
236
237
std::shared_ptr<NetworkSession> session;
238
asio::io_context io_context;
239
std::thread loopThread;
240
241
void InitNetwork()
242
{
243
try {
244
udp::resolver resolver(io_context);
245
asio::error_code ec;
246
auto endpoint = *resolver.resolve(udp::v4(), networkHost, std::to_string(networkPort), ec).begin();
247
session.reset();
248
auto newsession = std::make_shared<NetworkSession>(io_context, endpoint);
249
session.swap(newsession);
250
} catch (std::exception &e) {
251
Engine.onlineActive = false;
252
PrintLog("Failed to initialize networking: %s", e.what());
253
}
254
}
255
256
void networkLoop()
257
{
258
try {
259
session->start();
260
while (session->running) session->run();
261
session->close();
262
} catch (std::exception &e) {
263
std::cerr << "Exception: " << e.what() << "\n";
264
}
265
}
266
267
void RunNetwork()
268
{
269
if (loopThread.joinable()) {
270
DisconnectNetwork();
271
InitNetwork();
272
}
273
loopThread = std::thread(networkLoop);
274
}
275
276
void SendData(bool verify)
277
{
278
ServerPacket send;
279
send.header = CL_DATA + verify;
280
send.data.multiData = multiplayerDataOUT;
281
session->write(send, verify);
282
}
283
284
void DisconnectNetwork(bool finalClose)
285
{
286
if (session->running)
287
session->leave();
288
if (loopThread.joinable())
289
loopThread.join();
290
291
if (finalClose) {
292
if (session)
293
session.reset();
294
}
295
}
296
297
void SendServerPacket(ServerPacket &send, bool repeat) { session->write(send, repeat); }
298
int GetRoomCode() { return session->room; }
299
void SetRoomCode(int code) { session->room = code; }
300
301
void SetNetworkGameName(int *a1, const char *name) { StrCopy(networkGame, name); }
302
#endif
303