DEADSOFTWARE

ca1c129895cb851f83c68d026288706b462bf011
[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>
6 #include <time.h>
8 #define MS_VERSION "0.2"
9 #define MS_MAXSRVS 128
10 #define MS_TIMEOUT 100
12 #define NET_CHANS 2
13 #define NET_CH_MAIN 0
14 #define NET_CH_UPD 1
15 #define NET_MAXCLIENTS 64
17 #define NET_BUFSIZE 65536
19 #define NET_MSG_ADD 200
20 #define NET_MSG_RM 201
21 #define NET_MSG_LIST 202
23 #define LC_MS_INIT "D2DF master server starting on port %d...\n"
24 #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"
25 #define LC_MS_UPD "\nUpdated server #%d (%s:%d):\n%s\n%s (%d)\n%d/%d plrs\nproto: %d pw?: %d\n"
26 #define LC_MS_RM "\nRemoved server #%d (%s:%d) by request.\n"
27 #define LC_MS_TIME "\nServer #%d (%s:%d) timed out.\n"
28 #define LC_MS_LIST "\nSent server list to %x:%u (ver. %s).\n"
29 #define LC_MS_DIE "\nD2DF master server shutting down...\n"
30 #define LC_MS_CONN "\nIncoming connection from %x:%u...\n"
31 #define LC_MS_MOTD "\nMOTD: %s\n"
32 #define LC_MS_URGENT "\nURGENT: %s\n"
34 #define MS_URGENT_FILE "urgent.txt"
35 #define MS_MOTD_FILE "motd.txt"
37 struct ms_server_s {
38 enet_uint8 used;
39 char s_ip[17];
40 char s_name[256];
41 char s_map[256];
42 enet_uint8 s_pw;
43 enet_uint8 s_plrs;
44 enet_uint8 s_maxplrs;
45 enet_uint8 s_mode;
46 enet_uint8 s_protocol;
47 enet_uint16 s_port;
48 time_t deathtime;
49 };
51 typedef struct ms_server_s ms_server;
53 const char ms_game_ver[] = "0.63";
54 char ms_motd[255] = "";
55 char ms_urgent[255] = "";
57 int ms_port = 25660;
58 int ms_timeout = 100;
60 size_t b_read = 0;
61 size_t b_write = 0;
63 enet_uint8 b_send[NET_BUFSIZE];
65 ENetHost *ms_host = NULL;
66 ENetPeer *ms_peers[NET_MAXCLIENTS];
67 ms_server ms_srv[MS_MAXSRVS];
68 enet_uint8 ms_count = 0;
70 // fake servers to show on old versions of the game
71 static const ms_server ms_fake_srv[] = {
72 {
73 .used = 1,
74 .s_ip = "0.0.0.0",
75 .s_name = "! \xc2\xc0\xd8\xc0 \xca\xce\xcf\xc8\xdf \xc8\xc3\xd0\xdb "
76 "\xd3\xd1\xd2\xc0\xd0\xc5\xcb\xc0! "
77 "\xd1\xca\xc0\xd7\xc0\xc9\xd2\xc5 \xcd\xce\xc2\xd3\xde C "
78 "doom2d.org !",
79 .s_map = "! Your game is outdated. "
80 "Get latest version at doom2d.org !",
81 .s_protocol = 255,
82 },
83 {
84 .used = 1,
85 .s_ip = "0.0.0.0",
86 .s_name = "! \xcf\xd0\xce\xc1\xd0\xce\xd1\xdcTE \xcf\xce\xd0\xd2\xdb "
87 "25666 \xc8 57133 HA CEPBEPE \xcf\xc5\xd0\xc5\xc4 \xc8\xc3\xd0\xce\xc9 !",
88 .s_map = "! Forward ports 25666 and 57133 before hosting !",
89 .s_protocol = 255,
90 },
91 };
93 #define MS_FAKESRVS (sizeof(ms_fake_srv) / sizeof(ms_fake_srv[0]))
95 void i_usage () {
96 printf("Usage: d2df_master -p port_number [-t timeout_seconds]\n");
97 fflush(stdout);
98 }
101 void i_version () {
102 printf("Doom 2D Forever master server v%s\n", MS_VERSION);
103 fflush(stdout);
107 void d_error (const char *msg, int fatal) {
108 if (fatal) {
109 fprintf(stderr, "FATAL ERROR: %s\n", msg);
110 exit(EXIT_FAILURE);
111 } else {
112 fprintf(stderr, "ERROR: %s\n", msg);
117 void d_getargs (int argc, char *argv[]) {
118 if (argc < 2) {
119 i_usage();
120 exit(0);
121 return;
124 for (int i = 1; i < argc; ++i) {
125 if (!strcmp(argv[i], "-v")) {
126 i_version();
127 exit(0);
128 return;
129 } else if (!strcmp(argv[i], "-p")) {
130 if (i + 1 >= argc) {
131 d_error("Specify a port value!", 1);
132 return;
133 } else {
134 ms_port = atoi(argv[++i]);
136 } else if (!strcmp(argv[i], "-t") & (i + 1 < argc)) {
137 ms_timeout = atoi(argv[++i]);
143 int d_readtextfile (const char *fname, char *buf, size_t max) {
144 FILE *f = fopen(fname, "r");
145 char *const end = buf + max - 1;
146 char *p = buf;
147 if (f) {
148 char ln[max];
149 char *const lend = ln + max - 1;
150 while(p < end && fgets(ln, sizeof(ln), f)) {
151 for (char *n = ln; n < lend && *n && *n != '\r' && *n != '\n'; ++n) {
152 *(p++) = *n;
153 if (p == end) break;
156 *p = '\0';
157 fclose(f);
158 return 0;
160 return 1;
164 enet_uint8 b_read_uint8 (enet_uint8 buf[], size_t *pos) {
165 return buf[(*pos)++];
169 enet_uint16 b_read_uint16 (enet_uint8 buf[], size_t *pos) {
170 enet_uint16 ret = 0;
172 ret = *(enet_uint16*)(buf + *pos);
173 *pos += sizeof(enet_uint16);
175 return ret;
179 char* b_read_dstring (enet_uint8 buf[], size_t *pos) {
180 char *ret = NULL;
182 size_t len = b_read_uint8(buf, pos);
184 ret = malloc(len + 1);
186 memmove(ret, (char*)(buf + *pos), len);
187 ret[len] = '\0';
188 *pos += len;
190 return ret;
194 void b_write_uint8 (enet_uint8 buf[], size_t *pos, enet_uint8 val) {
195 buf[(*pos)++] = val;
199 void b_write_uint16 (enet_uint8 buf[], size_t *pos, enet_uint16 val) {
200 *(enet_uint16*)(buf + *pos) = val;
201 *pos += sizeof(enet_uint16);
205 void b_write_dstring (enet_uint8 buf[], size_t *pos, const char* val) {
206 enet_uint8 len = strlen(val);
207 b_write_uint8(buf, pos, len);
209 memmove((char*)(buf + *pos), val, len);
210 *pos += len;
214 void b_write_server (enet_uint8 buf[], size_t *pos, ms_server s) {
215 b_write_dstring(b_send, pos, s.s_ip);
216 b_write_uint16 (b_send, pos, s.s_port);
217 b_write_dstring(b_send, pos, s.s_name);
218 b_write_dstring(b_send, pos, s.s_map);
219 b_write_uint8 (b_send, pos, s.s_mode);
220 b_write_uint8 (b_send, pos, s.s_plrs);
221 b_write_uint8 (b_send, pos, s.s_maxplrs);
222 b_write_uint8 (b_send, pos, s.s_protocol);
223 b_write_uint8 (b_send, pos, s.s_pw);
227 int main (int argc, char *argv[]) {
228 d_getargs(argc, argv);
230 if (enet_initialize()) {
231 d_error("Could not init ENet!", 1);
232 return EXIT_FAILURE;
235 printf(LC_MS_INIT, ms_port);
237 d_readtextfile(MS_MOTD_FILE, ms_motd, sizeof(ms_motd));
238 d_readtextfile(MS_URGENT_FILE, ms_urgent, sizeof(ms_urgent));
240 if (ms_motd[0]) printf(LC_MS_MOTD, ms_motd);
241 if (ms_urgent[0]) printf(LC_MS_URGENT, ms_urgent);
243 for (int i = 0; i < NET_MAXCLIENTS; ++i) ms_peers[i] = NULL;
245 for (int i = 0; i < MS_MAXSRVS; ++i) {
246 ms_srv[i].used = 0;
247 ms_srv[i].s_ip[0] = '\0';
248 ms_srv[i].s_name[0] = '\0';
249 ms_srv[i].s_map[0] = '\0';
250 ms_srv[i].deathtime = 0;
253 ENetAddress addr;
254 addr.host = ENET_HOST_ANY;
255 addr.port = ms_port;
257 ms_host = enet_host_create(&addr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
258 if (!ms_host) {
259 d_error("Could not create host on specified port!", 1);
260 return EXIT_FAILURE;
263 atexit(enet_deinitialize);
265 ENetEvent event;
266 int shutdown = 0;
267 enet_uint8 msg = 255;
269 char ip[17];
270 enet_uint16 port = 0;
272 char *name = NULL;
273 char *map = NULL;
274 char *clientver = NULL;
275 enet_uint8 gm = 0;
276 enet_uint16 pl = 0;
277 enet_uint16 mpl = 0;
279 enet_uint8 proto = 0;
280 enet_uint8 pw = 0;
281 while (!shutdown) {
282 while (enet_host_service(ms_host, &event, 5000) > 0) {
283 switch (event.type) {
284 case ENET_EVENT_TYPE_CONNECT:
285 printf(LC_MS_CONN, event.peer->address.host, event.peer->address.port);
286 break;
287 case ENET_EVENT_TYPE_RECEIVE:
288 if (!event.peer) continue;
289 b_read = 0;
290 msg = b_read_uint8(event.packet->data, &b_read);
292 switch (msg) {
293 case NET_MSG_ADD:
294 if (!event.peer) continue;
296 enet_address_get_host_ip(&(event.peer->address), ip, 17);
297 port = b_read_uint16(event.packet->data, &b_read);
299 name = b_read_dstring(event.packet->data, &b_read);
300 map = b_read_dstring(event.packet->data, &b_read);
301 gm = b_read_uint8(event.packet->data, &b_read);
303 pl = b_read_uint8(event.packet->data, &b_read);
304 mpl = b_read_uint8(event.packet->data, &b_read);
306 proto = b_read_uint8(event.packet->data, &b_read);
307 pw = b_read_uint8(event.packet->data, &b_read);
309 for (int i = 0; i < MS_MAXSRVS; ++i) {
310 if (ms_srv[i].used) {
311 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
312 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
313 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
314 ms_srv[i].s_plrs = pl;
315 ms_srv[i].s_maxplrs = mpl;
316 ms_srv[i].s_pw = pw;
317 ms_srv[i].s_mode = gm;
319 ms_srv[i].deathtime = time(NULL) + ms_timeout;
321 printf(LC_MS_UPD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
322 break;
324 } else {
325 strncpy(ms_srv[i].s_ip, ip, sizeof(ms_srv[i].s_ip));
326 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
327 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
328 ms_srv[i].s_port = port;
329 ms_srv[i].s_plrs = pl;
330 ms_srv[i].s_maxplrs = mpl;
331 ms_srv[i].s_pw = pw;
332 ms_srv[i].s_mode = gm;
333 ms_srv[i].s_protocol = proto;
334 ms_srv[i].deathtime = time(NULL) + ms_timeout;
336 ms_srv[i].used = 1;
338 printf(LC_MS_ADD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
340 ++ms_count;
341 break;
344 free(name);
345 free(map);
346 break;
347 case NET_MSG_RM:
348 enet_address_get_host_ip(&(event.peer->address), ip, 17);
349 port = b_read_uint16(event.packet->data, &b_read);
350 for (int i = 0; i < MS_MAXSRVS; ++i) {
351 if (ms_srv[i].used) {
352 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
353 ms_srv[i].used = 0;
354 printf(LC_MS_RM, i, ip, port);
355 --ms_count;
359 break;
360 case NET_MSG_LIST:
361 if (!event.peer) continue;
363 b_write = 0;
364 b_write_uint8(b_send, &b_write, NET_MSG_LIST);
366 if (event.packet->dataLength > 2) {
367 // holy shit a fresh client
368 clientver = b_read_dstring(event.packet->data, &b_read);
369 b_write_uint8(b_send, &b_write, ms_count);
370 } else {
371 // old client, feed them bullshit first
372 b_write_uint8(b_send, &b_write, ms_count + 2);
373 for (int i = 0; i < MS_FAKESRVS; ++i)
374 b_write_server(b_send, &b_write, ms_fake_srv[i]);
377 for (int i = 0; i < MS_MAXSRVS; ++i) {
378 if (ms_srv[i].used) b_write_server(b_send, &b_write, ms_srv[i]);
381 if (clientver) {
382 // TODO: check if this client is outdated (?) and send back new verstring
383 // for now just write the same shit back
384 b_write_dstring(b_send, &b_write, clientver);
385 // write the motd and urgent message
386 b_write_dstring(b_send, &b_write, ms_motd);
387 b_write_dstring(b_send, &b_write, ms_urgent);
390 ENetPacket *p = enet_packet_create(b_send, b_write, ENET_PACKET_FLAG_RELIABLE);
391 enet_peer_send(event.peer, NET_CH_MAIN, p);
392 enet_host_flush(ms_host);
394 printf(LC_MS_LIST, event.peer->address.host, event.peer->address.port, clientver ? clientver : "<old>");
395 free(clientver);
396 clientver = NULL;
397 break;
400 enet_packet_destroy(event.packet);
401 break;
403 default:
404 break;
408 time_t now = time(NULL);
409 for (int i = 0; i < MS_MAXSRVS; ++i) {
410 if (ms_srv[i].used) {
411 if (ms_srv[i].deathtime <= now) {
412 ms_srv[i].used = 0;
413 printf(LC_MS_TIME, i, ms_srv[i].s_ip, ms_srv[i].s_port);
414 --ms_count;
420 printf(LC_MS_DIE);
422 return EXIT_SUCCESS;