DEADSOFTWARE

1dfed7ddfa601eb7ad72d6e54565cc6fe6259481
[d2df-sdl.git] / src / mastersrv / master.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <enet/enet.h>
5 #include <enet/types.h>
7 #define MS_VERSION "0.2"
8 #define MS_MAXSRVS 128
9 #define MS_TIMEOUT 100
11 #define NET_CHANS 2
12 #define NET_CH_MAIN 0
13 #define NET_CH_UPD 1
14 #define NET_MAXCLIENTS 64
16 #define NET_BUFSIZE 65536
18 #define NET_MSG_ADD 200
19 #define NET_MSG_RM 201
20 #define NET_MSG_LIST 202
22 #define LC_MS_INIT "D2DF master server starting on port %d...\n"
23 #define LC_MS_ADD "\nAdded server in slot #%d:\n%s:%d\n%s\n%s (%d)\n%d/%d plrs\nproto: %d pw?: %d\n"
24 #define LC_MS_UPD "\nUpdated server #%d (%s:%d):\n%s\n%s (%d)\n%d/%d plrs\nproto: %d pw?: %d\n"
25 #define LC_MS_RM "\nRemoved server #%d (%s:%d) by request.\n"
26 #define LC_MS_TIME "\nServer #%d (%s:%d) timed out.\n"
27 #define LC_MS_LIST "\nSent server list to %x:%u (ver. %s).\n"
28 #define LC_MS_DIE "\nD2DF master server shutting down...\n"
29 #define LC_MS_CONN "\nIncoming connection from %x:%u...\n"
30 #define LC_MS_MOTD "\nMOTD: %s\n"
31 #define LC_MS_URGENT "\nURGENT: %s\n"
33 #define MS_URGENT_FILE "urgent.txt"
34 #define MS_MOTD_FILE "motd.txt"
36 struct ms_server_s {
37 enet_uint8 used;
38 char s_ip[17];
39 char s_name[256];
40 char s_map[256];
41 enet_uint8 s_pw;
42 enet_uint8 s_plrs;
43 enet_uint8 s_maxplrs;
44 enet_uint8 s_mode;
45 enet_uint8 s_protocol;
46 enet_uint16 s_port;
47 enet_uint32 ttl;
48 };
50 typedef struct ms_server_s ms_server;
52 const char ms_game_ver[] = "0.63";
53 char ms_motd[255] = "";
54 char ms_urgent[255] = "";
56 int ms_port = 25660;
57 int ms_timeout = 100000;
59 size_t b_read = 0;
60 size_t b_write = 0;
62 enet_uint8 b_send[NET_BUFSIZE];
64 ENetHost *ms_host = NULL;
65 ENetPeer *ms_peers[NET_MAXCLIENTS];
66 ms_server ms_srv[MS_MAXSRVS];
67 enet_uint8 ms_count = 0;
69 // fake servers to show on old versions of the game
70 static const ms_server ms_fake_srv[] = {
71 {
72 .used = 1,
73 .s_ip = "0.0.0.0",
74 .s_name = "! \xc2\xc0\xd8\xc0 \xca\xce\xcf\xc8\xdf \xc8\xc3\xd0\xdb "
75 "\xd3\xd1\xd2\xc0\xd0\xc5\xcb\xc0! "
76 "\xd1\xca\xc0\xd7\xc0\xc9\xd2\xc5 \xcd\xce\xc2\xd3\xde C "
77 "doom2d.org !",
78 .s_map = "! Your game is outdated. "
79 "Get latest version at doom2d.org !",
80 .s_protocol = 255,
81 },
82 {
83 .used = 1,
84 .s_ip = "0.0.0.0",
85 .s_name = "! \xcf\xd0\xce\xc1\xd0\xce\xd1\xdcTE \xcf\xce\xd0\xd2\xdb "
86 "25666 \xc8 57133 HA CEPBEPE \xcf\xc5\xd0\xc5\xc4 \xc8\xc3\xd0\xce\xc9 !",
87 .s_map = "! Forward ports 25666 and 57133 before hosting !",
88 .s_protocol = 255,
89 },
90 };
92 #define MS_FAKESRVS (sizeof(ms_fake_srv) / sizeof(ms_fake_srv[0]))
94 void i_usage () {
95 printf("Usage: d2df_master -p port_number [-t timeout_seconds]\n");
96 fflush(stdout);
97 }
100 void i_version () {
101 printf("Doom 2D Forever master server v%s\n", MS_VERSION);
102 fflush(stdout);
106 void d_error (const char *msg, int fatal) {
107 if (fatal) {
108 fprintf(stderr, "FATAL ERROR: %s\n", msg);
109 exit(EXIT_FAILURE);
110 } else {
111 fprintf(stderr, "ERROR: %s\n", msg);
116 void d_getargs (int argc, char *argv[]) {
117 if (argc < 2) {
118 i_usage();
119 exit(0);
120 return;
123 for (int i = 1; i < argc; ++i) {
124 if (!strcmp(argv[i], "-v")) {
125 i_version();
126 exit(0);
127 return;
128 } else if (!strcmp(argv[i], "-p")) {
129 if (i + 1 >= argc) {
130 d_error("Specify a port value!", 1);
131 return;
132 } else {
133 ms_port = atoi(argv[++i]);
135 } else if (!strcmp(argv[i], "-t") & (i + 1 < argc)) {
136 ms_timeout = atoi(argv[++i]) * 1000;
142 int d_readtextfile (const char *fname, char *buf, size_t max) {
143 FILE *f = fopen(fname, "r");
144 char *const end = buf + max - 1;
145 char *p = buf;
146 if (f) {
147 char ln[max];
148 char *const lend = ln + max - 1;
149 while(p < end && fgets(ln, sizeof(ln), f)) {
150 for (char *n = ln; n < lend && *n && *n != '\r' && *n != '\n'; ++n) {
151 *(p++) = *n;
152 if (p == end) break;
155 *p = '\0';
156 fclose(f);
157 return 0;
159 return 1;
163 enet_uint8 b_read_uint8 (enet_uint8 buf[], size_t *pos) {
164 return buf[(*pos)++];
168 enet_uint16 b_read_uint16 (enet_uint8 buf[], size_t *pos) {
169 enet_uint16 ret = 0;
171 ret = *(enet_uint16*)(buf + *pos);
172 *pos += sizeof(enet_uint16);
174 return ret;
178 char* b_read_dstring (enet_uint8 buf[], size_t *pos) {
179 char *ret = NULL;
181 size_t len = b_read_uint8(buf, pos);
183 ret = malloc(len + 1);
185 memmove(ret, (char*)(buf + *pos), len);
186 ret[len] = '\0';
187 *pos += len;
189 return ret;
193 void b_write_uint8 (enet_uint8 buf[], size_t *pos, enet_uint8 val) {
194 buf[(*pos)++] = val;
198 void b_write_uint16 (enet_uint8 buf[], size_t *pos, enet_uint16 val) {
199 *(enet_uint16*)(buf + *pos) = val;
200 *pos += sizeof(enet_uint16);
204 void b_write_dstring (enet_uint8 buf[], size_t *pos, const char* val) {
205 enet_uint8 len = strlen(val);
206 b_write_uint8(buf, pos, len);
208 memmove((char*)(buf + *pos), val, len);
209 *pos += len;
213 void b_write_server (enet_uint8 buf[], size_t *pos, ms_server s) {
214 b_write_dstring(b_send, pos, s.s_ip);
215 b_write_uint16 (b_send, pos, s.s_port);
216 b_write_dstring(b_send, pos, s.s_name);
217 b_write_dstring(b_send, pos, s.s_map);
218 b_write_uint8 (b_send, pos, s.s_mode);
219 b_write_uint8 (b_send, pos, s.s_plrs);
220 b_write_uint8 (b_send, pos, s.s_maxplrs);
221 b_write_uint8 (b_send, pos, s.s_protocol);
222 b_write_uint8 (b_send, pos, s.s_pw);
226 int main (int argc, char *argv[]) {
227 d_getargs(argc, argv);
229 if (enet_initialize()) {
230 d_error("Could not init ENet!", 1);
231 return EXIT_FAILURE;
234 printf(LC_MS_INIT, ms_port);
236 d_readtextfile(MS_MOTD_FILE, ms_motd, sizeof(ms_motd));
237 d_readtextfile(MS_URGENT_FILE, ms_urgent, sizeof(ms_urgent));
239 if (ms_motd[0]) printf(LC_MS_MOTD, ms_motd);
240 if (ms_urgent[0]) printf(LC_MS_URGENT, ms_urgent);
242 for (int i = 0; i < NET_MAXCLIENTS; ++i) ms_peers[i] = NULL;
244 for (int i = 0; i < MS_MAXSRVS; ++i) {
245 ms_srv[i].used = 0;
246 ms_srv[i].s_ip[0] = '\0';
247 ms_srv[i].s_name[0] = '\0';
248 ms_srv[i].s_map[0] = '\0';
249 ms_srv[i].ttl = 0;
252 ENetAddress addr;
253 addr.host = ENET_HOST_ANY;
254 addr.port = ms_port;
256 ms_host = enet_host_create(&addr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
257 if (!ms_host) {
258 d_error("Could not create host on specified port!", 1);
259 return EXIT_FAILURE;
262 atexit(enet_deinitialize);
264 ENetEvent event;
265 int shutdown = 0;
266 enet_uint8 msg = 255;
268 char ip[17];
269 enet_uint16 port = 0;
271 char *name = NULL;
272 char *map = NULL;
273 char *clientver = NULL;
274 enet_uint8 gm = 0;
275 enet_uint16 pl = 0;
276 enet_uint16 mpl = 0;
278 enet_uint8 proto = 0;
279 enet_uint8 pw = 0;
280 while (!shutdown) {
281 while (enet_host_service(ms_host, &event, 5000) > 0) {
282 switch (event.type) {
283 case ENET_EVENT_TYPE_CONNECT:
284 printf(LC_MS_CONN, event.peer->address.host, event.peer->address.port);
285 break;
286 case ENET_EVENT_TYPE_RECEIVE:
287 if (!event.peer) continue;
288 b_read = 0;
289 msg = b_read_uint8(event.packet->data, &b_read);
291 switch (msg) {
292 case NET_MSG_ADD:
293 if (!event.peer) continue;
295 enet_address_get_host_ip(&(event.peer->address), ip, 17);
296 port = b_read_uint16(event.packet->data, &b_read);
298 name = b_read_dstring(event.packet->data, &b_read);
299 map = b_read_dstring(event.packet->data, &b_read);
300 gm = b_read_uint8(event.packet->data, &b_read);
302 pl = b_read_uint8(event.packet->data, &b_read);
303 mpl = b_read_uint8(event.packet->data, &b_read);
305 proto = b_read_uint8(event.packet->data, &b_read);
306 pw = b_read_uint8(event.packet->data, &b_read);
308 for (int i = 0; i < MS_MAXSRVS; ++i) {
309 if (ms_srv[i].used) {
310 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
311 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
312 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
313 ms_srv[i].s_plrs = pl;
314 ms_srv[i].s_maxplrs = mpl;
315 ms_srv[i].s_pw = pw;
316 ms_srv[i].s_mode = gm;
318 ms_srv[i].ttl = ms_timeout;
320 printf(LC_MS_UPD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
321 break;
323 } else {
324 strncpy(ms_srv[i].s_ip, ip, sizeof(ms_srv[i].s_ip));
325 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
326 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
327 ms_srv[i].s_port = port;
328 ms_srv[i].s_plrs = pl;
329 ms_srv[i].s_maxplrs = mpl;
330 ms_srv[i].s_pw = pw;
331 ms_srv[i].s_mode = gm;
332 ms_srv[i].s_protocol = proto;
333 ms_srv[i].ttl = ms_timeout;
335 ms_srv[i].used = 1;
337 printf(LC_MS_ADD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
339 ++ms_count;
340 break;
343 free(name);
344 free(map);
345 break;
346 case NET_MSG_RM:
347 enet_address_get_host_ip(&(event.peer->address), ip, 17);
348 port = b_read_uint16(event.packet->data, &b_read);
349 for (int i = 0; i < MS_MAXSRVS; ++i) {
350 if (ms_srv[i].used) {
351 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
352 ms_srv[i].used = 0;
353 printf(LC_MS_RM, i, ip, port);
354 --ms_count;
358 break;
359 case NET_MSG_LIST:
360 if (!event.peer) continue;
362 b_write = 0;
363 b_write_uint8(b_send, &b_write, NET_MSG_LIST);
365 if (event.packet->dataLength > 2) {
366 // holy shit a fresh client
367 clientver = b_read_dstring(event.packet->data, &b_read);
368 b_write_uint8(b_send, &b_write, ms_count);
369 } else {
370 // old client, feed them bullshit first
371 b_write_uint8(b_send, &b_write, ms_count + 2);
372 for (int i = 0; i < MS_FAKESRVS; ++i)
373 b_write_server(b_send, &b_write, ms_fake_srv[i]);
376 for (int i = 0; i < MS_MAXSRVS; ++i) {
377 if (ms_srv[i].used) b_write_server(b_send, &b_write, ms_srv[i]);
380 if (clientver) {
381 // TODO: check if this client is outdated (?) and send back new verstring
382 // for now just write the same shit back
383 b_write_dstring(b_send, &b_write, clientver);
384 // write the motd and urgent message
385 b_write_dstring(b_send, &b_write, ms_motd);
386 b_write_dstring(b_send, &b_write, ms_urgent);
389 ENetPacket *p = enet_packet_create(b_send, b_write, ENET_PACKET_FLAG_RELIABLE);
390 enet_peer_send(event.peer, NET_CH_MAIN, p);
391 enet_host_flush(ms_host);
393 printf(LC_MS_LIST, event.peer->address.host, event.peer->address.port, clientver ? clientver : "<old>");
394 free(clientver);
395 clientver = NULL;
396 break;
399 enet_packet_destroy(event.packet);
400 break;
402 default:
403 break;
407 for (int i = 0; i < MS_MAXSRVS; ++i) {
408 if (ms_srv[i].used) {
409 if (--(ms_srv[i].ttl) == 0) {
410 ms_srv[i].used = 0;
411 printf(LC_MS_TIME, i, ms_srv[i].s_ip, ms_srv[i].s_port);
412 --ms_count;
418 printf(LC_MS_DIE);
420 return EXIT_SUCCESS;