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