DEADSOFTWARE

Net: Master now supports MOTD and message boxes
[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[256] = "";
54 char ms_urgent[256] = "";
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 if (f) {
145 fgets(buf, max, f);
146 fclose(f);
147 return 0;
149 return 1;
153 enet_uint8 b_read_uint8 (enet_uint8 buf[], size_t *pos) {
154 return buf[(*pos)++];
158 enet_uint16 b_read_uint16 (enet_uint8 buf[], size_t *pos) {
159 enet_uint16 ret = 0;
161 ret = *(enet_uint16*)(buf + *pos);
162 *pos += sizeof(enet_uint16);
164 return ret;
168 char* b_read_dstring (enet_uint8 buf[], size_t *pos) {
169 char *ret = NULL;
171 size_t len = b_read_uint8(buf, pos);
173 ret = malloc(len + 1);
175 memmove(ret, (char*)(buf + *pos), len);
176 ret[len] = '\0';
177 *pos += len;
179 return ret;
183 void b_write_uint8 (enet_uint8 buf[], size_t *pos, enet_uint8 val) {
184 buf[(*pos)++] = val;
188 void b_write_uint16 (enet_uint8 buf[], size_t *pos, enet_uint16 val) {
189 *(enet_uint16*)(buf + *pos) = val;
190 *pos += sizeof(enet_uint16);
194 void b_write_dstring (enet_uint8 buf[], size_t *pos, const char* val) {
195 enet_uint8 len = strlen(val);
196 b_write_uint8(buf, pos, len);
198 memmove((char*)(buf + *pos), val, len);
199 *pos += len;
203 void b_write_server (enet_uint8 buf[], size_t *pos, ms_server s) {
204 b_write_dstring(b_send, pos, s.s_ip);
205 b_write_uint16 (b_send, pos, s.s_port);
206 b_write_dstring(b_send, pos, s.s_name);
207 b_write_dstring(b_send, pos, s.s_map);
208 b_write_uint8 (b_send, pos, s.s_mode);
209 b_write_uint8 (b_send, pos, s.s_plrs);
210 b_write_uint8 (b_send, pos, s.s_maxplrs);
211 b_write_uint8 (b_send, pos, s.s_protocol);
212 b_write_uint8 (b_send, pos, s.s_pw);
216 int main (int argc, char *argv[]) {
217 d_getargs(argc, argv);
219 if (enet_initialize()) {
220 d_error("Could not init ENet!", 1);
221 return EXIT_FAILURE;
224 printf(LC_MS_INIT, ms_port);
226 d_readtextfile(MS_MOTD_FILE, ms_motd, sizeof(ms_motd));
227 d_readtextfile(MS_URGENT_FILE, ms_urgent, sizeof(ms_urgent));
229 if (ms_motd[0]) printf(LC_MS_MOTD, ms_motd);
230 if (ms_urgent[0]) printf(LC_MS_URGENT, ms_urgent);
232 for (int i = 0; i < NET_MAXCLIENTS; ++i) ms_peers[i] = NULL;
234 for (int i = 0; i < MS_MAXSRVS; ++i) {
235 ms_srv[i].used = 0;
236 ms_srv[i].s_ip[0] = '\0';
237 ms_srv[i].s_name[0] = '\0';
238 ms_srv[i].s_map[0] = '\0';
239 ms_srv[i].ttl = 0;
242 ENetAddress addr;
243 addr.host = ENET_HOST_ANY;
244 addr.port = ms_port;
246 ms_host = enet_host_create(&addr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
247 if (!ms_host) {
248 d_error("Could not create host on specified port!", 1);
249 return EXIT_FAILURE;
252 atexit(enet_deinitialize);
254 ENetEvent event;
255 int shutdown = 0;
256 enet_uint8 msg = 255;
258 char ip[17];
259 enet_uint16 port = 0;
261 char *name = NULL;
262 char *map = NULL;
263 char *clientver = NULL;
264 enet_uint8 gm = 0;
265 enet_uint16 pl = 0;
266 enet_uint16 mpl = 0;
268 enet_uint8 proto = 0;
269 enet_uint8 pw = 0;
270 while (!shutdown) {
271 while (enet_host_service(ms_host, &event, 1) > 0) {
272 switch (event.type) {
273 case ENET_EVENT_TYPE_CONNECT:
274 printf(LC_MS_CONN, event.peer->address.host, event.peer->address.port);
275 break;
276 case ENET_EVENT_TYPE_RECEIVE:
277 if (!event.peer) continue;
278 b_read = 0;
279 msg = b_read_uint8(event.packet->data, &b_read);
281 switch (msg) {
282 case NET_MSG_ADD:
283 if (!event.peer) continue;
285 enet_address_get_host_ip(&(event.peer->address), ip, 17);
286 port = b_read_uint16(event.packet->data, &b_read);
288 name = b_read_dstring(event.packet->data, &b_read);
289 map = b_read_dstring(event.packet->data, &b_read);
290 gm = b_read_uint8(event.packet->data, &b_read);
292 pl = b_read_uint8(event.packet->data, &b_read);
293 mpl = b_read_uint8(event.packet->data, &b_read);
295 proto = b_read_uint8(event.packet->data, &b_read);
296 pw = b_read_uint8(event.packet->data, &b_read);
298 for (int i = 0; i < MS_MAXSRVS; ++i) {
299 if (ms_srv[i].used) {
300 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
301 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
302 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
303 ms_srv[i].s_plrs = pl;
304 ms_srv[i].s_maxplrs = mpl;
305 ms_srv[i].s_pw = pw;
306 ms_srv[i].s_mode = gm;
308 ms_srv[i].ttl = ms_timeout;
310 printf(LC_MS_UPD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
311 break;
313 } else {
314 strncpy(ms_srv[i].s_ip, ip, sizeof(ms_srv[i].s_ip));
315 strncpy(ms_srv[i].s_map, map, sizeof(ms_srv[i].s_map));
316 strncpy(ms_srv[i].s_name, name, sizeof(ms_srv[i].s_name));
317 ms_srv[i].s_port = port;
318 ms_srv[i].s_plrs = pl;
319 ms_srv[i].s_maxplrs = mpl;
320 ms_srv[i].s_pw = pw;
321 ms_srv[i].s_mode = gm;
322 ms_srv[i].s_protocol = proto;
323 ms_srv[i].ttl = ms_timeout;
325 ms_srv[i].used = 1;
327 printf(LC_MS_ADD, i, ip, port, name, map, gm, pl, mpl, proto, pw);
329 ++ms_count;
330 break;
333 free(name);
334 free(map);
335 break;
336 case NET_MSG_RM:
337 enet_address_get_host_ip(&(event.peer->address), ip, 17);
338 port = b_read_uint16(event.packet->data, &b_read);
339 for (int i = 0; i < MS_MAXSRVS; ++i) {
340 if (ms_srv[i].used) {
341 if ((strncmp(ip, ms_srv[i].s_ip, 16) == 0) && (ms_srv[i].s_port == port)) {
342 ms_srv[i].used = 0;
343 printf(LC_MS_RM, i, ip, port);
344 --ms_count;
348 break;
349 case NET_MSG_LIST:
350 if (!event.peer) continue;
352 b_write = 0;
353 b_write_uint8(b_send, &b_write, NET_MSG_LIST);
355 if (event.packet->dataLength > 2) {
356 // holy shit a fresh client
357 clientver = b_read_dstring(event.packet->data, &b_read);
358 b_write_uint8(b_send, &b_write, ms_count);
359 } else {
360 // old client, feed them bullshit first
361 b_write_uint8(b_send, &b_write, ms_count + 2);
362 for (int i = 0; i < MS_FAKESRVS; ++i)
363 b_write_server(b_send, &b_write, ms_fake_srv[i]);
366 for (int i = 0; i < MS_MAXSRVS; ++i) {
367 if (ms_srv[i].used) b_write_server(b_send, &b_write, ms_srv[i]);
370 if (clientver) {
371 // TODO: check if this client is outdated (?) and send back new verstring
372 // for now just write the same shit back
373 b_write_dstring(b_send, &b_write, clientver);
374 // write the motd and urgent message
375 b_write_dstring(b_send, &b_write, ms_motd);
376 b_write_dstring(b_send, &b_write, ms_urgent);
379 ENetPacket *p = enet_packet_create(b_send, b_write, ENET_PACKET_FLAG_RELIABLE);
380 enet_peer_send(event.peer, NET_CH_MAIN, p);
381 enet_host_flush(ms_host);
383 printf(LC_MS_LIST, event.peer->address.host, event.peer->address.port, clientver ? clientver : "<old>");
384 free(clientver);
385 clientver = NULL;
386 break;
389 enet_packet_destroy(event.packet);
390 break;
392 default:
393 break;
397 for (int i = 0; i < MS_MAXSRVS; ++i) {
398 if (ms_srv[i].used) {
399 if (--(ms_srv[i].ttl) == 0) {
400 ms_srv[i].used = 0;
401 printf(LC_MS_TIME, i, ms_srv[i].s_ip, ms_srv[i].s_port);
402 --ms_count;
408 printf(LC_MS_DIE);
410 return EXIT_SUCCESS;