]> jfr.im git - irc/gameservirc.git/blob - gameserv/tcpclient.cpp
Implemented a hashing algorithm for extremely fast searches. aClients are now placed...
[irc/gameservirc.git] / gameserv / tcpclient.cpp
1 /*
2 * This file is provided for use with the unix-socket-faq. It is public
3 * domain, and may be copied freely. There is no copyright on it. The
4 * original work was by Vic Metcalfe (vic@brutus.tlug.org), and any
5 * modifications made to that work were made with the understanding that
6 * the finished work would be in the public domain.
7 *
8 * If you have found a bug, please pass it on to me at the above address
9 * acknowledging that there will be no copyright on your work.
10 *
11 * The most recent version of this file, and the unix-socket-faq can be
12 * found at http://www.interlog.com/~vic/sock-faq/.
13 */
14
15 #include "sockhelp.h"
16 #include "options.h"
17 #include "list.h"
18 #include "aClient.h"
19 #include "extern.h"
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <fstream>
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 //#include <sys/types.h>
28 //#include <sys/wait.h>
29 //#include <errno.h>
30
31
32
33 using std::ofstream;
34 using std::ifstream;
35 using std::cerr;
36 using std::endl;
37
38 char *PACKAGE = "GameServ";
39 char *VERSION = "1.2.0 +devel";
40
41 int sock;
42 int day;
43
44 List<aClient> clients[U_TABLE_SIZE];
45
46 void save_day();
47 void load_day();
48 void prettyIntro();
49
50 // Make this a daemon
51 int daemon(int nochdir, int noclose);
52
53 // Close all file descriptors from >= fd
54 void closeall(int fd);
55
56 int main(int argc, char *argv[])
57 {
58 char buffer[1024], buf[1024];
59 int connected = 1;
60 char *cmd, *source = NULL, *conf = "gameserv.conf";
61 srand(time(NULL));
62
63 /*
64 * This needs to be fixed to work for any number of arguments in any
65 * order
66 *
67 */
68 if (argc > 1)
69 {
70 if ( argc > 2 || stricmp(argv[1], "--help") == 0)
71 {
72 cout << "Usage: gameserv [options] [configfile]" << endl;
73 cout << "Options:" << endl;
74 cout << "--help Displays this help dialogue" << endl;
75 return 1;
76 }
77 conf = new char[strlen(argv[1])];
78 strcpy(conf, argv[1]);
79 }
80
81 prettyIntro();
82
83 if (load_config_file(conf))
84 {
85 cout << "Config file loaded ok...\n"
86 << "Turning into a daemon" << endl;
87 }
88 else
89 exit(2);
90
91 // Turn into a daemon
92 if (daemon(1,0) < 0)
93 {
94 perror("Could not turn into a daemon");
95 exit(2);
96 }
97
98 ignore_pipe();
99 sock = make_connection(remoteport, SOCK_STREAM, remoteserver);
100 if (sock == -1) {
101 fprintf(stderr,"make_connection failed.\n");
102 unload_config_file();
103 return -1;
104 }
105
106 #ifdef UNREAL
107 raw("PROTOCTL NICKv2 VHP");
108 raw("PASS :%s", remotepass);
109 raw("SERVER %s 1 :%s", servername, servername);
110 raw("NICK %S 1 %d %S %s %s %d +owghraAxNt %s :%s v%s", time(NULL), gshost,
111 servername, time(NULL), gshost, PACKAGE, VERSION);
112 raw(":%S JOIN %s", c_Forest);
113 raw(":%S MODE %s +mtn", c_Forest);
114 #elif defined(BAHAMUT)
115 raw("PASS %s :TS", remotepass);
116 raw("SERVER %s 1 :%s", servername, servername);
117 raw("NICK %S 1 %d +o %s %s %s 0 :GameServ", time(NULL), gsident, gshost,
118 servername);
119 raw(":%s SJOIN %d %d %s +mnt :@%S", servername, time(NULL), time(NULL), c_Forest);
120 #elif defined(HYBRID)
121 raw("PASS %s :TS", remotepass);
122 raw("SERVER %s 1 :%s", servername, servername);
123 raw("NICK %S 1 %d +o %s %s %s :GameServ", time(NULL), gsident, gshost,
124 servername);
125 // Sending a timestamp of 1 to force ops.
126 raw(":%s SJOIN 1 %s +ntm :@%S", servername, c_Forest);
127 #elif defined(P10)
128 // Server numeric is: [] <-- must be unique
129 raw("PASS :%s", remotepass);
130 raw("SERVER %s 1 %d %d P10 []AAF :%s", servername, time(NULL), time(NULL), servername);
131 raw("[] N %S 1 %d %s %s DAqAoB %s :%S", time(NULL), gsident, gshost, gsnum);
132 raw("[] B %s %d +tnm %s:o", c_Forest, time(NULL) - 864000, gsnum);
133 #endif
134
135 #if defined(P10)
136 raw("%s T %s :%s", gsnum, c_Forest, c_ForestTopic);
137 raw("[] EB"); // End burst
138 #else
139 #ifndef HYBRID
140 raw(":%S MODE %s +o %S", c_Forest);
141 #endif
142 raw(":%S TOPIC %s :%s", c_Forest, c_ForestTopic);
143 #endif
144
145 sock_gets(sock,buffer,sizeof(buffer)-1); /* -1 added thanks to
146 David Duchene <dave@ltd.com> for pointing out the possible
147 buffer overflow resulting from the linefeed added below. */
148
149
150 #ifdef DEBUGMODE
151 log("Server: %s",buffer);
152 #endif
153
154 strcpy(boss.name, "Red Dragon");
155 strcpy(boss.weapon, "Breath of Unholy Fire");
156 boss.strength = 6667;
157 boss.gold = 2000000000;
158 boss.exp = 2000000000;
159 strcpy(boss.death, "You finally snuff out the deadly murderous "\
160 "dragon's dark flames. You have freed the land of its terror "\
161 "filled reign from above!");
162
163 init_masters();
164 load_gs_dbase();
165 load_day();
166 long int loadtime = time(NULL);
167 long int currentTime;
168 long int oldTime = loadtime;
169 bool loaded = false;
170
171 if (load_monsters() == false)
172 goto end;
173
174 while (connected) {
175 if (sock_gets(sock,buffer,sizeof(buffer)) == -1) {
176 connected = 0;
177 }
178 strcpy(buf, buffer);
179
180 #if !defined(P10)
181 if (buffer[0] == ':')
182 {
183 source = strtok(buf, " ");
184 cmd = strtok(NULL, " ");
185 }
186 else
187 cmd = strtok(buf, " ");
188 #else
189 source = strtok(buf, " ");
190 cmd = strtok(NULL, " ");
191 #endif
192
193 #ifdef DEBUGMODE
194 log("Server: %s", buffer);
195 #endif
196
197 // Wait N seconds then we're loaded.
198 if (!loaded)
199 {
200 if (time(NULL) >= welcomedelay + loadtime)
201 loaded = true;
202 }
203
204 // Save the player data every updateperiod seconds
205 currentTime = time(NULL);
206 if (currentTime - oldTime >= updateperiod)
207 {
208 oldTime = currentTime;
209 save_gs_dbase();
210 }
211
212
213 #if !defined(P10)
214 if (stricmp(cmd, "PING") == 0) {
215 char *timestamp;
216 timestamp = strtok(NULL, "");
217 raw("PONG %s", timestamp);
218 #else
219 if (stricmp(cmd, "G") == 0) {
220 char *timestamp;
221 timestamp = strtok(NULL, " ");
222 raw("[] Z [] %s 0 %s", timestamp + 1, timestamp);
223 #endif
224 #ifdef P10
225 } else if (stricmp(cmd, "EB") == 0) {
226 raw("[] EA");
227 #endif
228 } else if (stricmp(cmd, "VERSION") == 0) {
229 char *server;
230 server = strtok(NULL, " ");
231 server++;
232 raw(":%s 351 %s %s_%s. %s", servername, source+1, PACKAGE, VERSION, servername);
233 #if !defined(P10)
234 } else if (strncmp(cmd, "NICK", 4) == 0) {
235 if (buffer[0] == ':')
236 {
237 aClient *tempPtr;
238 if ((tempPtr = find((source + 1))))
239 {
240 char *nick;
241 nick = strtok(NULL, " ");
242 tempPtr->setNick(nick);
243 }
244 }
245 else
246 {
247 char *nick;
248 #else
249 } else if (stricmp(cmd, "N") == 0 && strlen(source) == 2) {
250 {
251 char *nick, *realnick;
252 realnick = strtok(NULL, " ");
253
254 for (int x = 0; x < 5; x++)
255 nick = strtok(NULL, " ");
256
257 if (nick[0] == '+')
258 {
259 #ifdef DEBUGMODE
260 log ("aClient has modes");
261 #endif
262
263 // Searching for the +r mode (extra parameter)
264 for (unsigned int count = 1; count < strlen(nick); count++)
265 {
266 if (nick[count] == 'r')
267 {
268 nick = strtok(NULL, " ");
269 break;
270 }
271 }
272 nick = strtok(NULL, " ");
273 }
274 #endif
275 aClient *newuser;
276
277 nick = strtok(NULL, " ");
278
279 #ifdef P10
280 newuser = new aClient(nick, realnick);
281 #else
282 newuser = new aClient(nick);
283 #endif
284
285
286 if (loaded)
287 #ifdef P10
288 notice(s_GameServ, nick, welcomemsg, realnick);
289 #else
290 notice(s_GameServ, nick, welcomemsg, nick);
291 #endif
292 unsigned long hv = HASH((unsigned char *) nick, U_TABLE_SIZE);
293 clients[hv].insertAtBack(newuser);
294 delete newuser;
295 }
296 #if defined(P10)
297 } else if (stricmp(cmd, "Q") == 0) {
298 #else
299 } else if (stricmp(cmd, "QUIT") == 0) {
300 #endif
301 aClient *quitter;
302 char z = source[0];
303
304 if (z == ':')
305 source++;
306
307 unsigned long hv = HASH((unsigned char *) source, U_TABLE_SIZE);
308 if ((quitter = find(source)))
309 clients[hv].remove(quitter);
310 if ((quitter = findIRCplayer(source)))
311 {
312 if (player_fight(quitter))
313 {
314 // Stop the fight on the other client
315 aClient *otherplayer = quitter->stats->battle;
316 otherplayer->stats->battle = NULL;
317 notice(s_GameServ, otherplayer->getNick(), "%s "\
318 "has quit IRC. The fight stops here.",
319 quitter->stats->name);
320 }
321 quitter->stats->battle = NULL;
322 quitter->stats->fight = NULL;
323 quitter->stats->master = NULL;
324
325 quitter->setNick("!NULL!");
326 #ifdef P10
327 quitter->setRealNick("!NULL!");
328 #endif
329 quitter->stats->user = NULL; // Unidentify them
330 }
331
332 if (z == ':')
333 source--;
334
335 #if defined(P10)
336 } else if (stricmp(cmd, "P") == 0) {
337 char *rest, *dest;
338 char *longname;
339 longname = new char[strlen(s_GameServ) + strlen(servername) + 2];
340
341 sprintf(longname, "%S@%s", servername);
342
343 dest = strtok(NULL, " ");
344 rest = strtok(NULL, "");
345 if (stricmp(dest, gsnum) == 0 || stricmp(dest, longname) == 0)
346 {
347 delete [] longname;
348 gameserv(source, rest);
349 }
350 else if (stricmp(dest, c_Forest) == 0)
351 {
352 delete [] longname;
353 forest(source, rest);
354 }
355 #else
356 } else if (stricmp(cmd, "PRIVMSG") == 0) {
357 char *rest, *dest;
358 dest = strtok(NULL, " ");
359 rest = strtok(NULL, "");
360 if (strnicmp(dest, s_GameServ, strlen(s_GameServ)) == 0)
361 gameserv(source, rest);
362 else if (stricmp(dest, c_Forest) == 0)
363 forest(source, rest);
364 #endif
365 #if defined(P10)
366 } else if (stricmp(cmd, "J") == 0) {
367 #else
368 } else if (stricmp(cmd, "JOIN") == 0) {
369 #endif
370 char *channel;
371 aClient *joiner;
372 channel = strtok(NULL, " ");
373
374 char z = source[0];
375
376 if (z == ':')
377 source++;
378
379 joiner = find(source);
380
381 if (stricmp(channel, c_Forest) == 0 && is_playing(joiner))
382 {
383 #ifdef DEBUGMODE
384 log("Player %s (IRC: %s) joined %s",
385 joiner->stats->name,
386 #ifdef P10
387 joiner->getRealNick(),
388 #else
389 joiner->getNick(),
390 #endif
391 c_Forest);
392 #endif
393 raw(":%S MODE %s +v %s", c_Forest, (source));
394 }
395
396 if (z == ':')
397 source--;
398
399 #if defined(BAHAMUT)
400 } else if (stricmp(cmd, "SJOIN") == 0) {
401 char *channel, *nick, *tmp, *rest;
402 strtok(NULL, " "); // Ignore the TS
403 #ifndef HYBRID
404 strtok(NULL, " "); // Ignore the TS
405 #endif
406 channel = strtok(NULL, " ");
407 rest = strtok(NULL, "");
408 tmp = strchr(rest, ':');
409 tmp++;
410 nick = strtok(tmp, " ");
411 while (nick != NULL)
412 {
413 if (*nick == '@')
414 nick++;
415 if (*nick == '+')
416 nick++; // Assume for users set op and voice, they
417 // are never passed as +@nick
418 if (stricmp(channel, c_Forest) == 0 && is_playing(nick))
419 raw(":%S MODE %s +v %s", channel, nick);
420
421 nick = strtok(NULL, " ");
422 }
423 #endif
424 } else {
425 #ifdef DEBUGMODE
426 log("Unrecognized Message: cmd = %s source = %s", cmd, source);
427 #endif
428 }
429 }
430
431 end:
432
433 save_gs_dbase();
434 save_day();
435
436 delete_monsters();
437 delete_masters();
438
439 #ifdef DEBUGMODE
440 log("<CLOSED>");
441 #endif
442
443 close(sock);
444 unload_config_file();
445 return 0;
446 }
447
448 aClient *find(char *nick)
449 {
450 return findbynick(nick);
451 }
452
453 aClient *find(const char *nick)
454 {
455 return findbynick(nick);
456 }
457
458 #ifdef P10
459
460 aClient *findbyrealnick(char *realnick)
461 {
462 ListNode <aClient> *newPtr;
463 unsigned long hv = HASH((unsigned char *) realnick, U_TABLE_SIZE);
464 newPtr = clients[hv].First();
465
466 aClient *client = NULL;
467
468 while (newPtr)
469 {
470 client = newPtr->getData();
471 if (stricmp(client->getRealNick(), realnick) == 0)
472 return client;
473 client = NULL;
474 newPtr = newPtr->Next();
475 }
476 return client;
477 }
478
479 #else
480
481 aClient *findbyrealnick(char *realnick)
482 {
483 return findbynick(realnick);
484 }
485
486 #endif
487
488 aClient *findbynick(char *nick)
489 {
490 ListNode <aClient> *newPtr;
491 unsigned long hv = HASH((unsigned char *) nick, U_TABLE_SIZE);
492 newPtr = clients[hv].First();
493
494 aClient *client = NULL;
495
496 while (newPtr)
497 {
498 client = newPtr->getData();
499 #ifdef P10
500 if (strcmp(client->getNick(), nick) == 0)
501 #else
502 if (stricmp(client->getNick(), nick) == 0)
503 #endif
504 return client;
505 client = NULL;
506 newPtr = newPtr->Next();
507 }
508 return client;
509 }
510
511 aClient *findIRCplayer(const char *nick)
512 {
513 ListNode <aClient> *newPtr;
514 aClient *p = NULL;
515
516 unsigned long hv = HASH((unsigned char *) nick, U_TABLE_SIZE);
517 for (newPtr = players[hv].First(); newPtr; newPtr = newPtr->Next())
518 {
519 p = newPtr->getData();
520 #ifdef P10
521 if (strcmp(p->getNick(), nick) == 0)
522 #else
523 if (stricmp(p->getNick(), nick) == 0)
524 #endif
525 return p;
526 p = NULL;
527 }
528 return NULL;
529 }
530 aClient *findplayer(const char *name)
531 {
532 ListNode <aClient> *newPtr;
533 Player *p = NULL;
534 unsigned long hv = HASH((unsigned char *) name, U_TABLE_SIZE);
535 for (newPtr = players[hv].First(); newPtr; newPtr = newPtr->Next())
536 {
537 p = newPtr->getData()->stats;
538 if (stricmp(p->name, name) == 0)
539 return newPtr->getData();
540 p = NULL;
541 }
542 return NULL;
543 }
544
545 aClient *findbynick(const char *nick)
546 {
547 ListNode <aClient> *newPtr;
548 unsigned long hv = HASH((unsigned char *) nick, U_TABLE_SIZE);
549 newPtr = clients[hv].First();
550
551 aClient *client = NULL;
552
553 while (newPtr)
554 {
555 client = newPtr->getData();
556 #ifdef P10
557 if (strcmp(client->getNick(), nick) == 0)
558 #else
559 if (stricmp(client->getNick(), nick) == 0)
560 #endif
561 return client;
562 client = NULL;
563 newPtr = newPtr->Next();
564 }
565 return client;
566 }
567
568 void load_day()
569 {
570 ifstream infile;
571
572 infile.open(".gsday");
573
574 if (infile.fail())
575 {
576 #ifdef DEBUGMODE
577 log("Error opening .gsday");
578 #endif
579
580 generate:
581 #ifdef DEBUGMODE
582 log("Generating new day");
583 #endif
584 struct tm *tm;
585 time_t ti;
586 time(&ti);
587 tm = localtime(&ti);
588
589 day = tm->tm_mday;
590
591 save_day();
592 return;
593 }
594
595 infile >> day;
596 infile.close();
597 if (day < 1 || day > 31)
598 goto generate;
599 }
600
601 void save_day()
602 {
603 ofstream outfile;
604
605 outfile.open(".gsday");
606
607 if (outfile.fail())
608 {
609 log("Error creating new file .gsday");
610 return;
611 }
612
613 outfile << day << endl;
614
615 outfile.close();
616 }
617
618 /* daemon() - detach process from user and disappear into the background
619 * returns -1 on failure, but you can't do much except exit in that case
620 * since we may already have forked. This is based on the BSD version,
621 * so the caller is responsible for things like the umask, etc.
622 */
623
624 /* believed to work on all Posix systems */
625
626 int daemon(int nochdir, int noclose)
627 {
628 pid_t pid;
629 switch (pid = fork())
630 {
631 case 0: break;
632 case -1: return -1;
633 default: _exit(0); /* exit the original process */
634 }
635
636 if (setsid() < 0) /* shoudn't fail */
637 return -1;
638
639 /* dyke out this switch if you want to acquire a control tty in */
640 /* the future -- not normally advisable for daemons */
641
642 switch (pid = fork())
643 {
644 case 0: break;
645 case -1: return -1;
646 default:
647 ofstream outfile;
648 outfile.open(pidfile);
649 if (outfile.fail())
650 cerr << "Unable to open " << pidfile << endl;
651 outfile << pid << endl;
652 outfile.close();
653
654 _exit(0);
655 }
656
657 if (!nochdir)
658 chdir("/");
659
660 if (!noclose)
661 {
662 closeall(0);
663 open("/dev/null",O_RDWR);
664 dup(0); dup(0);
665 }
666
667 return 0;
668 }
669
670
671 /* closeall() -- close all FDs >= a specified value */
672
673 void closeall(int fd)
674 {
675 int fdlimit = sysconf(_SC_OPEN_MAX);
676
677 while (fd < fdlimit)
678 close(fd++);
679 }
680
681 void prettyIntro()
682 {
683 cout << endl;
684 cout << " GGGG AAA MM MM EEEEEEE SSSSS EEEEEEE RRRRRR VV VV " << endl;
685 cout << " GG GG AAAAA MMM MMM EE SS EE RR RR VV VV " << endl;
686 cout << "GG AA AA MM MM MM EEEEE SSSSS EEEEE RRRRRR VV VV " << endl;
687 cout << "GG GGG AAAAAAA MM MM EE SS EE RR RR VV VV " << endl;
688 cout << "G G AA AA MM MM EEEEEEE SSSSS EEEEEEE RR RR VVV" << endl;
689 cout << " GGGGG V\n\n" << endl;
690 cout << "Version: " << VERSION << endl;
691 }