]> jfr.im git - irc/gameservirc.git/blame - gameserv/tcpclient.cpp
Updated TODO and fixed the hashing algorithm for P10 numerics + others' nickname...
[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];
85ce9d3e 59 int connected = 1;
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
85ce9d3e 98 ignore_pipe();
324ab87f 99 sock = make_connection(remoteport, SOCK_STREAM, remoteserver);
85ce9d3e 100 if (sock == -1) {
101 fprintf(stderr,"make_connection failed.\n");
102 unload_config_file();
103 return -1;
104 }
105
c7340cbd 106#ifdef UNREAL
85ce9d3e 107 raw("PROTOCTL NICKv2 VHP");
108 raw("PASS :%s", remotepass);
c7340cbd 109 raw("SERVER %s 1 :%s", servername, servername);
173302fe 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);
85ce9d3e 112 raw(":%S JOIN %s", c_Forest);
c7340cbd 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);
581ec09e 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);
e1c41a84 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);
c7340cbd 133#endif
e1c41a84 134
135#if defined(P10)
136 raw("%s T %s :%s", gsnum, c_Forest, c_ForestTopic);
fc9d2643 137 raw("[] EB"); // End burst
581ec09e 138#else
139#ifndef HYBRID
85ce9d3e 140 raw(":%S MODE %s +o %S", c_Forest);
581ec09e 141#endif
c7340cbd 142 raw(":%S TOPIC %s :%s", c_Forest, c_ForestTopic);
e1c41a84 143#endif
85ce9d3e 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
9f8c2acc 150 #ifdef DEBUGMODE
151 log("Server: %s",buffer);
152 #endif
153
425d7369 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
ab4f4ec0 163 init_masters();
ad7dfaa0 164 load_gs_dbase();
abbfcdb9 165 load_day();
922daad7 166 long int loadtime = time(NULL);
167 long int currentTime;
168 long int oldTime = loadtime;
5963944b 169 bool loaded = false;
44ea29f7 170
4dde2ed9 171 if (load_monsters() == false)
172 goto end;
173
85ce9d3e 174 while (connected) {
175 if (sock_gets(sock,buffer,sizeof(buffer)) == -1) {
176 connected = 0;
177 }
178 strcpy(buf, buffer);
179
e1c41a84 180 #if !defined(P10)
85ce9d3e 181 if (buffer[0] == ':')
182 {
183 source = strtok(buf, " ");
184 cmd = strtok(NULL, " ");
185 }
186 else
187 cmd = strtok(buf, " ");
e1c41a84 188 #else
189 source = strtok(buf, " ");
190 cmd = strtok(NULL, " ");
191 #endif
85ce9d3e 192
9f8c2acc 193 #ifdef DEBUGMODE
194 log("Server: %s", buffer);
195 #endif
5963944b 196
bf2cabcd 197 // Wait N seconds then we're loaded.
5963944b 198 if (!loaded)
199 {
922daad7 200 if (time(NULL) >= welcomedelay + loadtime)
5963944b 201 loaded = true;
202 }
203
922daad7 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
e1c41a84 212
213 #if !defined(P10)
85ce9d3e 214 if (stricmp(cmd, "PING") == 0) {
0a1518fa 215 char *timestamp;
216 timestamp = strtok(NULL, "");
217 raw("PONG %s", timestamp);
e1c41a84 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
fc9d2643 224 #ifdef P10
225 } else if (stricmp(cmd, "EB") == 0) {
226 raw("[] EA");
227 #endif
0501fe18 228 } else if (stricmp(cmd, "VERSION") == 0) {
229 char *server;
230 server = strtok(NULL, " ");
231 server++;
2e9db9c4 232 raw(":%s 351 %s %s_%s. %s", servername, source+1, PACKAGE, VERSION, servername);
ba2a880f 233 #if !defined(P10)
85ce9d3e 234 } else if (strncmp(cmd, "NICK", 4) == 0) {
235 if (buffer[0] == ':')
236 {
237 aClient *tempPtr;
28f552b8 238 if ((tempPtr = find((source + 1))))
85ce9d3e 239 {
240 char *nick;
59dc3990 241 unsigned long oldhv, newhv;
85ce9d3e 242 nick = strtok(NULL, " ");
59dc3990 243 oldhv = HASH((unsigned char *) tempPtr->getNick(),
244 U_TABLE_SIZE);
245 newhv = HASH((unsigned char *) nick, U_TABLE_SIZE);
85ce9d3e 246 tempPtr->setNick(nick);
59dc3990 247 clients[oldhv].remove(tempPtr);
248 clients[newhv].insertAtBack(tempPtr);
85ce9d3e 249 }
250 }
251 else
252 {
253 char *nick;
ba2a880f 254 #else
255 } else if (stricmp(cmd, "N") == 0 && strlen(source) == 2) {
256 {
ce61cdfa 257 char *nick, *realnick;
258 realnick = strtok(NULL, " ");
ba2a880f 259
ce61cdfa 260 for (int x = 0; x < 5; x++)
ba2a880f 261 nick = strtok(NULL, " ");
fc9d2643 262
ba2a880f 263 if (nick[0] == '+')
fc9d2643 264 {
265 #ifdef DEBUGMODE
266 log ("aClient has modes");
267 #endif
268
269 // Searching for the +r mode (extra parameter)
270 for (unsigned int count = 1; count < strlen(nick); count++)
271 {
272 if (nick[count] == 'r')
273 {
274 nick = strtok(NULL, " ");
275 break;
276 }
277 }
278 nick = strtok(NULL, " ");
279 }
ba2a880f 280 #endif
85ce9d3e 281 aClient *newuser;
ba2a880f 282
85ce9d3e 283 nick = strtok(NULL, " ");
ba2a880f 284
ce61cdfa 285 #ifdef P10
286 newuser = new aClient(nick, realnick);
287 #else
288 newuser = new aClient(nick);
289 #endif
290
291
5963944b 292 if (loaded)
ce61cdfa 293 #ifdef P10
294 notice(s_GameServ, nick, welcomemsg, realnick);
295 #else
bf2cabcd 296 notice(s_GameServ, nick, welcomemsg, nick);
ce61cdfa 297 #endif
7996e5fd 298 unsigned long hv = HASH((unsigned char *) nick, U_TABLE_SIZE);
299 clients[hv].insertAtBack(newuser);
85ce9d3e 300 delete newuser;
301 }
ba2a880f 302 #if defined(P10)
303 } else if (stricmp(cmd, "Q") == 0) {
304 #else
85ce9d3e 305 } else if (stricmp(cmd, "QUIT") == 0) {
ba2a880f 306 #endif
85ce9d3e 307 aClient *quitter;
fc9d2643 308 char z = source[0];
309
310 if (z == ':')
311 source++;
312
7996e5fd 313 unsigned long hv = HASH((unsigned char *) source, U_TABLE_SIZE);
fc9d2643 314 if ((quitter = find(source)))
7996e5fd 315 clients[hv].remove(quitter);
fc9d2643 316 if ((quitter = findIRCplayer(source)))
ee38284f 317 {
7f17db99 318 if (player_fight(quitter))
319 {
320 // Stop the fight on the other client
321 aClient *otherplayer = quitter->stats->battle;
322 otherplayer->stats->battle = NULL;
323 notice(s_GameServ, otherplayer->getNick(), "%s "\
324 "has quit IRC. The fight stops here.",
325 quitter->stats->name);
326 }
327 quitter->stats->battle = NULL;
328 quitter->stats->fight = NULL;
329 quitter->stats->master = NULL;
330
ee38284f 331 quitter->setNick("!NULL!");
a5316c52 332 #ifdef P10
333 quitter->setRealNick("!NULL!");
334 #endif
ee38284f 335 quitter->stats->user = NULL; // Unidentify them
336 }
fc9d2643 337
338 if (z == ':')
339 source--;
340
ba2a880f 341 #if defined(P10)
342 } else if (stricmp(cmd, "P") == 0) {
343 char *rest, *dest;
344 char *longname;
345 longname = new char[strlen(s_GameServ) + strlen(servername) + 2];
346
347 sprintf(longname, "%S@%s", servername);
85ce9d3e 348
ba2a880f 349 dest = strtok(NULL, " ");
350 rest = strtok(NULL, "");
351 if (stricmp(dest, gsnum) == 0 || stricmp(dest, longname) == 0)
352 {
353 delete [] longname;
354 gameserv(source, rest);
355 }
356 else if (stricmp(dest, c_Forest) == 0)
357 {
358 delete [] longname;
359 forest(source, rest);
360 }
361 #else
85ce9d3e 362 } else if (stricmp(cmd, "PRIVMSG") == 0) {
363 char *rest, *dest;
364 dest = strtok(NULL, " ");
365 rest = strtok(NULL, "");
ad7dfaa0 366 if (strnicmp(dest, s_GameServ, strlen(s_GameServ)) == 0)
85ce9d3e 367 gameserv(source, rest);
368 else if (stricmp(dest, c_Forest) == 0)
369 forest(source, rest);
ba2a880f 370 #endif
f2072f1a 371 #if defined(P10)
372 } else if (stricmp(cmd, "J") == 0) {
373 #else
85ce9d3e 374 } else if (stricmp(cmd, "JOIN") == 0) {
f2072f1a 375 #endif
85ce9d3e 376 char *channel;
f2072f1a 377 aClient *joiner;
85ce9d3e 378 channel = strtok(NULL, " ");
f2072f1a 379
380 char z = source[0];
381
382 if (z == ':')
383 source++;
384
385 joiner = find(source);
386
387 if (stricmp(channel, c_Forest) == 0 && is_playing(joiner))
388 {
389 #ifdef DEBUGMODE
390 log("Player %s (IRC: %s) joined %s",
391 joiner->stats->name,
392 #ifdef P10
393 joiner->getRealNick(),
394 #else
395 joiner->getNick(),
396 #endif
397 c_Forest);
398 #endif
399 raw(":%S MODE %s +v %s", c_Forest, (source));
400 }
401
402 if (z == ':')
403 source--;
c7340cbd 404
405 #if defined(BAHAMUT)
406 } else if (stricmp(cmd, "SJOIN") == 0) {
581ec09e 407 char *channel, *nick, *tmp, *rest;
c7340cbd 408 strtok(NULL, " "); // Ignore the TS
581ec09e 409#ifndef HYBRID
c7340cbd 410 strtok(NULL, " "); // Ignore the TS
581ec09e 411#endif
c7340cbd 412 channel = strtok(NULL, " ");
581ec09e 413 rest = strtok(NULL, "");
414 tmp = strchr(rest, ':');
415 tmp++;
416 nick = strtok(tmp, " ");
417 while (nick != NULL)
418 {
419 if (*nick == '@')
420 nick++;
421 if (*nick == '+')
422 nick++; // Assume for users set op and voice, they
423 // are never passed as +@nick
424 if (stricmp(channel, c_Forest) == 0 && is_playing(nick))
425 raw(":%S MODE %s +v %s", channel, nick);
426
427 nick = strtok(NULL, " ");
428 }
429#endif
85ce9d3e 430 } else {
9f8c2acc 431 #ifdef DEBUGMODE
432 log("Unrecognized Message: cmd = %s source = %s", cmd, source);
433 #endif
85ce9d3e 434 }
435 }
4dde2ed9 436
437 end:
438
c8ada07e 439 save_gs_dbase();
abbfcdb9 440 save_day();
4dde2ed9 441
c8ada07e 442 delete_monsters();
443 delete_masters();
444
9f8c2acc 445 #ifdef DEBUGMODE
446 log("<CLOSED>");
447 #endif
448
85ce9d3e 449 close(sock);
450 unload_config_file();
451 return 0;
452}
453
454aClient *find(char *nick)
455{
456 return findbynick(nick);
457}
458
459aClient *find(const char *nick)
460{
461 return findbynick(nick);
462}
463
ce61cdfa 464#ifdef P10
465
466aClient *findbyrealnick(char *realnick)
467{
468 ListNode <aClient> *newPtr;
7996e5fd 469 unsigned long hv = HASH((unsigned char *) realnick, U_TABLE_SIZE);
470 newPtr = clients[hv].First();
ce61cdfa 471
472 aClient *client = NULL;
473
474 while (newPtr)
475 {
476 client = newPtr->getData();
477 if (stricmp(client->getRealNick(), realnick) == 0)
478 return client;
479 client = NULL;
480 newPtr = newPtr->Next();
481 }
482 return client;
483}
4e5760fd 484
485#else
486
487aClient *findbyrealnick(char *realnick)
488{
489 return findbynick(realnick);
490}
491
ce61cdfa 492#endif
85ce9d3e 493
494aClient *findbynick(char *nick)
495{
496 ListNode <aClient> *newPtr;
7996e5fd 497 unsigned long hv = HASH((unsigned char *) nick, U_TABLE_SIZE);
498 newPtr = clients[hv].First();
85ce9d3e 499
500 aClient *client = NULL;
501
502 while (newPtr)
503 {
504 client = newPtr->getData();
23ec3ff4 505 #ifdef P10
23ec3ff4 506 if (strcmp(client->getNick(), nick) == 0)
507 #else
508 if (stricmp(client->getNick(), nick) == 0)
509 #endif
85ce9d3e 510 return client;
511 client = NULL;
512 newPtr = newPtr->Next();
513 }
514 return client;
515}
516
ee38284f 517aClient *findIRCplayer(const char *nick)
518{
519 ListNode <aClient> *newPtr;
520 aClient *p = NULL;
521
7996e5fd 522 unsigned long hv = HASH((unsigned char *) nick, U_TABLE_SIZE);
523 for (newPtr = players[hv].First(); newPtr; newPtr = newPtr->Next())
ee38284f 524 {
525 p = newPtr->getData();
23ec3ff4 526 #ifdef P10
527 if (strcmp(p->getNick(), nick) == 0)
528 #else
529 if (stricmp(p->getNick(), nick) == 0)
530 #endif
ee38284f 531 return p;
532 p = NULL;
533 }
534 return NULL;
535}
0a1518fa 536aClient *findplayer(const char *name)
537{
538 ListNode <aClient> *newPtr;
539 Player *p = NULL;
7996e5fd 540 unsigned long hv = HASH((unsigned char *) name, U_TABLE_SIZE);
541 for (newPtr = players[hv].First(); newPtr; newPtr = newPtr->Next())
0a1518fa 542 {
543 p = newPtr->getData()->stats;
544 if (stricmp(p->name, name) == 0)
545 return newPtr->getData();
546 p = NULL;
547 }
548 return NULL;
549}
550
85ce9d3e 551aClient *findbynick(const char *nick)
552{
553 ListNode <aClient> *newPtr;
7996e5fd 554 unsigned long hv = HASH((unsigned char *) nick, U_TABLE_SIZE);
555 newPtr = clients[hv].First();
85ce9d3e 556
557 aClient *client = NULL;
558
559 while (newPtr)
560 {
561 client = newPtr->getData();
23ec3ff4 562 #ifdef P10
563 if (strcmp(client->getNick(), nick) == 0)
564 #else
565 if (stricmp(client->getNick(), nick) == 0)
566 #endif
85ce9d3e 567 return client;
568 client = NULL;
569 newPtr = newPtr->Next();
570 }
571 return client;
572}
573
abbfcdb9 574void load_day()
44ea29f7 575{
576 ifstream infile;
577
abbfcdb9 578 infile.open(".gsday");
44ea29f7 579
580 if (infile.fail())
581 {
9f8c2acc 582 #ifdef DEBUGMODE
abbfcdb9 583 log("Error opening .gsday");
9f8c2acc 584 #endif
585
44ea29f7 586 generate:
9f8c2acc 587 #ifdef DEBUGMODE
abbfcdb9 588 log("Generating new day");
9f8c2acc 589 #endif
abbfcdb9 590 struct tm *tm;
591 time_t ti;
592 time(&ti);
593 tm = localtime(&ti);
594
595 day = tm->tm_mday;
596
597 save_day();
44ea29f7 598 return;
599 }
600
abbfcdb9 601 infile >> day;
44ea29f7 602 infile.close();
abbfcdb9 603 if (day < 1 || day > 31)
44ea29f7 604 goto generate;
605}
606
abbfcdb9 607void save_day()
44ea29f7 608{
609 ofstream outfile;
610
abbfcdb9 611 outfile.open(".gsday");
44ea29f7 612
613 if (outfile.fail())
614 {
abbfcdb9 615 log("Error creating new file .gsday");
44ea29f7 616 return;
617 }
618
abbfcdb9 619 outfile << day << endl;
44ea29f7 620
621 outfile.close();
622}
623
ce61cdfa 624/* daemon() - detach process from user and disappear into the background
625 * returns -1 on failure, but you can't do much except exit in that case
626 * since we may already have forked. This is based on the BSD version,
627 * so the caller is responsible for things like the umask, etc.
628 */
629
630/* believed to work on all Posix systems */
631
632int daemon(int nochdir, int noclose)
633{
634 pid_t pid;
635 switch (pid = fork())
636 {
637 case 0: break;
638 case -1: return -1;
639 default: _exit(0); /* exit the original process */
640 }
641
642 if (setsid() < 0) /* shoudn't fail */
643 return -1;
644
645 /* dyke out this switch if you want to acquire a control tty in */
646 /* the future -- not normally advisable for daemons */
647
648 switch (pid = fork())
649 {
650 case 0: break;
651 case -1: return -1;
652 default:
653 ofstream outfile;
69ae096c 654 outfile.open(pidfile);
ce61cdfa 655 if (outfile.fail())
69ae096c 656 cerr << "Unable to open " << pidfile << endl;
ce61cdfa 657 outfile << pid << endl;
658 outfile.close();
659
660 _exit(0);
661 }
662
663 if (!nochdir)
664 chdir("/");
665
666 if (!noclose)
667 {
668 closeall(0);
669 open("/dev/null",O_RDWR);
670 dup(0); dup(0);
671 }
672
673 return 0;
674}
675
676
677/* closeall() -- close all FDs >= a specified value */
678
679void closeall(int fd)
680{
681 int fdlimit = sysconf(_SC_OPEN_MAX);
682
683 while (fd < fdlimit)
684 close(fd++);
685}
686
15838737 687void prettyIntro()
688{
689cout << endl;
690cout << " GGGG AAA MM MM EEEEEEE SSSSS EEEEEEE RRRRRR VV VV " << endl;
691cout << " GG GG AAAAA MMM MMM EE SS EE RR RR VV VV " << endl;
692cout << "GG AA AA MM MM MM EEEEE SSSSS EEEEE RRRRRR VV VV " << endl;
693cout << "GG GGG AAAAAAA MM MM EE SS EE RR RR VV VV " << endl;
694cout << "G G AA AA MM MM EEEEEEE SSSSS EEEEEEE RR RR VVV" << endl;
695cout << " GGGGG V\n\n" << endl;
696cout << "Version: " << VERSION << endl;
697}