Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-1-2-2013-Decompilation
Path: blob/main/Server/Server.cpp
817 views
1
#include <cstdlib>
2
#include <deque>
3
#include <iostream>
4
#include <list>
5
#include <map>
6
#include <memory>
7
#include <set>
8
#include <utility>
9
#include "asio.hpp"
10
11
#define DATA_MAX 0x1000
12
13
typedef unsigned int uint;
14
15
using asio::ip::tcp;
16
17
struct CodedData {
18
unsigned char header;
19
uint64_t code;
20
uint roomcode;
21
union {
22
unsigned char bytes[DATA_MAX];
23
int ints[DATA_MAX / sizeof(int)];
24
} data;
25
};
26
27
//----------------------------------------------------------------------
28
29
typedef std::deque<CodedData> data_queue;
30
31
//----------------------------------------------------------------------
32
33
class player
34
{
35
public:
36
virtual ~player() {}
37
virtual void deliver(const CodedData &msg) = 0;
38
uint64_t code = 0;
39
uint roomcode = 0;
40
bool hasPartner = true; // to prevent ==0 joins
41
};
42
43
typedef std::shared_ptr<player> player_ptr;
44
std::set<uint64_t> codes;
45
46
//----------------------------------------------------------------------
47
48
bool debug = false;
49
bool verbose = false;
50
51
inline uint randomint()
52
{
53
uint res = 0;
54
for (int i = 0; i < 4; i++) {
55
res |= (rand() & 0xFF) << (i * 8);
56
}
57
if (!res)
58
res++;
59
return res;
60
}
61
62
class game_room
63
{
64
public:
65
game_room(){};
66
game_room(std::string &name) : game_name(name), set(true){};
67
void join(player_ptr participant)
68
{
69
participants.insert(participant);
70
if (debug)
71
std::cout << "[" + game_name + "] "
72
<< "New player " << std::hex << participant->code << std::endl;
73
}
74
75
void leave(player_ptr participant)
76
{
77
participants.erase(participant);
78
rooms.erase(participant->roomcode);
79
codes.erase(participant->code);
80
if (debug)
81
std::cout << "[" + game_name + "] "
82
<< "Player " << std::hex << participant->code << " in room " << participant->roomcode << " left" << std::endl;
83
}
84
85
void deliver(const CodedData &msg, player_ptr sender)
86
{
87
for (auto &participant : participants) {
88
if (participant->code != sender->code && participant->roomcode == msg.roomcode) {
89
CodedData s(msg);
90
//s.code = participant->code;
91
participant->deliver(s);
92
}
93
}
94
}
95
96
bool meet(const CodedData msg, player_ptr searcher)
97
{
98
if (searcher->hasPartner)
99
return true; // YOU HAVE ONE ALREADY
100
for (auto &participant : participants) {
101
if (participant->code != searcher->code && participant->roomcode == msg.roomcode && !participant->hasPartner) {
102
participant->hasPartner = true;
103
searcher->hasPartner = true;
104
CodedData send;
105
send.code = participant->code;
106
send.header = 0x80;
107
send.roomcode = msg.roomcode;
108
participant->deliver(send);
109
return true;
110
}
111
}
112
return false;
113
}
114
115
std::set<uint> rooms;
116
std::set<player_ptr> participants;
117
std::string game_name = "(NO GAME)";
118
bool set = false;
119
};
120
121
game_room blank_room;
122
std::vector<game_room> rooms;
123
124
//----------------------------------------------------------------------
125
126
class player_connection : public player, public std::enable_shared_from_this<player_connection>
127
{
128
public:
129
player_connection(tcp::socket socket) : socket_(std::move(socket))
130
{
131
do {
132
code = randomint() | ((uint64_t)randomint() << 32);
133
} while (codes.find(code) != codes.end());
134
codes.insert(code);
135
}
136
137
void start()
138
{
139
// don't join any room YET
140
if (debug)
141
std::cout << "New game-less player " << std::hex << code << std::endl;
142
do_read();
143
}
144
145
void deliver(const CodedData &msg)
146
{
147
write_msgs_.push_back(msg);
148
do_write();
149
}
150
151
private:
152
inline void send_to_self(const CodedData &msg = CodedData())
153
{
154
auto self(shared_from_this());
155
CodedData send(msg);
156
send.code = self->code;
157
send.roomcode = self->roomcode;
158
deliver(send);
159
}
160
inline void send_error(unsigned char code = 0)
161
{
162
auto self(shared_from_this());
163
CodedData send;
164
send.header = 0x81;
165
send.data.bytes[0] = code;
166
send_to_self(send);
167
}
168
169
game_room &get_room()
170
{
171
auto self(shared_from_this());
172
if (self->roomID == -1)
173
return blank_room;
174
return rooms[self->roomID];
175
}
176
177
void do_read()
178
{
179
auto self(shared_from_this());
180
asio::async_read(socket_, asio::buffer(&read_msg_, sizeof(CodedData)), [this, self](std::error_code ec, std::size_t /*length*/) {
181
if (ec)
182
return do_read();
183
if (debug && verbose)
184
std::cout << "[" + self->get_room().game_name + "] "
185
<< "Reading data with header " << std::uppercase << std::hex << (int)read_msg_.header << " from player " << std::hex
186
<< read_msg_.code << " in room " << std::hex << read_msg_.roomcode << " to player " << self->code << std::endl;
187
CodedData send;
188
189
switch (read_msg_.header) {
190
case 0x00: { // give me my codes, set the room
191
if (self->get_room().set) {
192
self->send_error(1); // already in a room
193
break;
194
}
195
std::string name((char *)&read_msg_.data.bytes[12], read_msg_.data.ints[2]);
196
for (int i = 0; i < rooms.size(); i++)
197
if (rooms[i].game_name == name)
198
self->roomID = i;
199
if (!self->get_room().set) {
200
auto newroom = game_room(name);
201
rooms.push_back(newroom);
202
if (debug)
203
std::cout << "Game " << name << " created" << std::endl;
204
self->roomID = rooms.size() - 1;
205
}
206
self->get_room().join(self);
207
208
if (self->get_room().rooms.find(read_msg_.roomcode) != self->get_room().rooms.end()) {
209
// joining
210
read_msg_.header = 0x02; // You can now play as Luigi!
211
self->roomcode = read_msg_.roomcode;
212
send_to_self(read_msg_);
213
}
214
else if (read_msg_.roomcode) {
215
send_error(0); // roomcode doesn't exist
216
}
217
else {
218
int roomcodeMask = read_msg_.data.ints[0];
219
int roomcodeBase = read_msg_.data.ints[1];
220
int tries = 0;
221
do {
222
self->socket_.async_wait(asio::socket_base::wait_read, [](asio::error_code) {});
223
self->roomcode = (randomint() & ~roomcodeMask) | roomcodeBase;
224
} while (self->get_room().rooms.find(self->roomcode) != self->get_room().rooms.end());
225
226
send.roomcode = self->roomcode;
227
send.header = 0x01; // this is code and you are MARIO. NOT LUIGI. YOU **SUCK**
228
send_to_self(send);
229
}
230
self->hasPartner = false;
231
self->get_room().rooms.insert(self->roomcode);
232
if (debug)
233
std::cout << "[" + self->get_room().game_name + "] "
234
<< "Player " << std::hex << self->code << " is now in room " << std::hex << self->roomcode << std::endl;
235
break;
236
}
237
case 0x01: { // try to join player with code
238
if (get_room().meet(read_msg_, self)) {
239
send.header = 0x80;
240
send_to_self(send);
241
}
242
break;
243
}
244
case 0x11: // reliable send
245
case 0x10: { // data
246
get_room().deliver(read_msg_, self);
247
break;
248
}
249
250
case 0xFF: { // leave/quit, sent to both players
251
get_room().leave(self);
252
get_room().deliver(read_msg_, self);
253
break;
254
}
255
}
256
// let's check rooms and get rid of unused ones while we're here
257
/*std::set<int> codes;
258
for (auto &p : get_room().participants) {
259
codes.push_back(p->roomcode);
260
}
261
for (auto &c : rooms) {
262
if (!std::count(codes.begin(), codes.end(), c)) {
263
rooms.erase(std::find(rooms.begin(), rooms.end(), c));
264
}
265
}//*/
266
if (!write_msgs_.empty()) {
267
socket_.async_wait(asio::socket_base::wait_write, [this](std::error_code) { do_write(); });
268
}
269
do_read();
270
});
271
}
272
273
void do_write()
274
{
275
auto self(shared_from_this());
276
if (self->writing || write_msgs_.empty())
277
return;
278
asio::async_write(socket_, asio::buffer(&write_msgs_.front(), sizeof(CodedData)), [this, self](std::error_code ec, std::size_t /*length*/) {
279
self->writing = true;
280
if (!ec && !write_msgs_.empty()) {
281
auto msg = write_msgs_.front();
282
if (debug && verbose)
283
std::cout << "[" + self->get_room().game_name + "] "
284
<< "Sending data with header " << std::uppercase << std::hex << (int)msg.header << " to player " << std::hex << msg.code
285
<< " in room " << std::hex << msg.roomcode << " from player " << self->code << std::endl;
286
if (msg.header == 0x11) {
287
CodedData sent;
288
sent.header = 0x1F;
289
send_to_self(sent);
290
}
291
write_msgs_.pop_front();
292
self->writing = false;
293
do_write();
294
}
295
else if (ec) {
296
get_room().leave(shared_from_this());
297
}
298
self->writing = false;
299
});
300
}
301
302
tcp::socket socket_;
303
uint roomID = -1;
304
CodedData read_msg_;
305
data_queue write_msgs_;
306
bool writing = false;
307
};
308
309
//----------------------------------------------------------------------
310
311
class server
312
{
313
public:
314
server(asio::io_context &io_context, const tcp::endpoint &endpoint) : acceptor_(io_context, endpoint) { do_accept(); }
315
316
private:
317
void do_accept()
318
{
319
acceptor_.async_accept([this](std::error_code ec, tcp::socket socket) {
320
if (!ec) {
321
std::make_shared<player_connection>(std::move(socket))->start();
322
}
323
324
do_accept();
325
});
326
}
327
328
tcp::acceptor acceptor_;
329
};
330
331
//----------------------------------------------------------------------
332
333
int main(int argc, char *argv[])
334
{
335
srand((uint)time(0));
336
try {
337
if (argc < 2) {
338
std::cerr << "Pass ports as arguments. (Server [port] [port] [port]...)";
339
return 1;
340
}
341
342
asio::io_context io_context;
343
344
std::list<server> servers;
345
for (int i = 1; i < argc; ++i) {
346
if (std::string(argv[i]) == "debug") {
347
debug = true;
348
continue;
349
}
350
if (std::string(argv[i]) == "verbose") {
351
debug = true;
352
verbose = true;
353
continue;
354
}
355
tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[i]));
356
servers.emplace_back(io_context, endpoint);
357
std::cout << "Port " << argv[i] << " ready" << std::endl;
358
}
359
360
std::cout << "Starting..." << std::endl;
361
io_context.run();
362
} catch (std::exception &e) {
363
std::cerr << "Exception: " << e.what() << "\n";
364
}
365
366
return 0;
367
}
368