|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00031 #include <global.h> 00032 00033 static fd_set tmp_read, tmp_exceptions, tmp_write; 00034 00036 typedef void (*func_uint8_int_ns)(char *, int, socket_struct *); 00037 00039 struct client_cmd_mapping 00040 { 00042 char *cmdname; 00043 00045 func_uint8_int_ns cmdproc; 00046 }; 00047 00049 typedef void (*func_uint8_int_pl)(char *, int, player *); 00050 00052 struct player_cmd_mapping 00053 { 00055 char *cmdname; 00056 00058 func_uint8_int_pl cmdproc; 00059 00061 int flags; 00062 }; 00063 00065 static const struct player_cmd_mapping player_commands[] = 00066 { 00067 {"ex", ExamineCmd, 0}, 00068 {"ap", ApplyCmd, 0}, 00069 {"mv", MoveCmd, 0}, 00070 {"reply", ReplyCmd, 0}, 00071 {"cm", PlayerCmd, 0}, 00072 {"lock", (func_uint8_int_pl) LockItem, 0}, 00073 {"mark", (func_uint8_int_pl) MarkItem, 0}, 00074 {"/fire", command_fire_old, 0}, 00075 {"fire", (func_uint8_int_pl) command_fire, 0}, 00076 {"nc", command_new_char, 0}, 00077 {"qs", (func_uint8_int_pl) QuickSlotCmd, 0}, 00078 {"shop", ShopCmd, 0}, 00079 {"qlist", QuestListCmd, 0}, 00080 {"mp", (func_uint8_int_pl) command_move_path, 0}, 00081 {"rd", (func_uint8_int_pl) cmd_ready, 0}, 00082 {"pc", (func_uint8_int_pl) cmd_password_change, 0}, 00083 {NULL, NULL, 0} 00084 }; 00085 00087 static const struct client_cmd_mapping client_commands[] = 00088 { 00089 {"addme", AddMeCmd}, 00090 {"askface", SendFaceCmd}, 00091 {"setup", SetUp}, 00092 {"version", VersionCmd}, 00093 {"rf", RequestFileCmd}, 00094 {"clr", command_clear_cmds}, 00095 {"setsound", SetSound}, 00096 {"upf", cmd_request_update}, 00097 {"ka", cmd_keepalive}, 00098 {NULL, NULL} 00099 }; 00100 00105 static int check_client_command(socket_struct *ns) 00106 { 00107 unsigned char *data; 00108 int i, data_len; 00109 00110 for (i = 0; client_commands[i].cmdname; i++) 00111 { 00112 if ((int) strlen(client_commands[i].cmdname) <= ns->inbuf.len - 2 && !strncmp((char *) ns->inbuf.buf + 2, client_commands[i].cmdname, strlen(client_commands[i].cmdname))) 00113 { 00114 /* Pre-process the command. */ 00115 data = (unsigned char *) strchr((char *) ns->inbuf.buf + 2, ' '); 00116 00117 if (data) 00118 { 00119 *data = '\0'; 00120 data++; 00121 data_len = ns->inbuf.len - (data - ns->inbuf.buf); 00122 } 00123 else 00124 { 00125 data_len = 0; 00126 } 00127 00128 client_commands[i].cmdproc((char *) data, data_len, ns); 00129 00130 /* We have successfully added this client. */ 00131 if (ns->addme) 00132 { 00133 ns->addme = 0; 00134 } 00135 00136 return 1; 00137 } 00138 } 00139 00140 return 0; 00141 } 00142 00146 static void fill_command_buffer(socket_struct *ns) 00147 { 00148 int rr; 00149 00150 do 00151 { 00152 if ((rr = SockList_ReadCommand(&ns->readbuf, &ns->inbuf))) 00153 { 00154 /* Terminate buffer - useful for string data. */ 00155 ns->inbuf.buf[ns->inbuf.len] = '\0'; 00156 00157 if (check_client_command(ns)) 00158 { 00159 if (ns->status == Ns_Dead) 00160 { 00161 return; 00162 } 00163 } 00164 else 00165 { 00166 memcpy(ns->cmdbuf.buf + ns->cmdbuf.len, ns->inbuf.buf, rr); 00167 ns->cmdbuf.len += rr; 00168 } 00169 } 00170 } 00171 while (rr); 00172 } 00173 00179 static int check_command(socket_struct *ns, player *pl) 00180 { 00181 int i, len = 0; 00182 unsigned char *data; 00183 00184 /* Terminate buffer - useful for string data */ 00185 ns->inbuf.buf[ns->inbuf.len] = '\0'; 00186 00187 /* First, break out beginning word. There are at least 00188 * a few commands that do not have any parameters. If 00189 * we get such a command, don't worry about trying 00190 * to break it up. */ 00191 data = (unsigned char *) strchr((char *) ns->inbuf.buf + 2, ' '); 00192 00193 if (data) 00194 { 00195 *data = '\0'; 00196 data++; 00197 len = ns->inbuf.len - (data - ns->inbuf.buf); 00198 } 00199 else 00200 { 00201 len = 0; 00202 } 00203 00204 for (i = 0; client_commands[i].cmdname; i++) 00205 { 00206 if (strcmp((char *) ns->inbuf.buf + 2, client_commands[i].cmdname) == 0) 00207 { 00208 client_commands[i].cmdproc((char *) data, len, ns); 00209 ns->inbuf.len = 0; 00210 00211 /* We have successfully added this connect! */ 00212 if (ns->addme) 00213 { 00214 ns->addme = 0; 00215 } 00216 00217 return 1; 00218 } 00219 } 00220 00221 /* Only valid players can use these commands */ 00222 if (pl) 00223 { 00224 for (i = 0; player_commands[i].cmdname; i++) 00225 { 00226 if (strcmp((char *) ns->inbuf.buf + 2, player_commands[i].cmdname) == 0) 00227 { 00228 player_commands[i].cmdproc((char *) data, len, pl); 00229 ns->inbuf.len = 0; 00230 return 1; 00231 } 00232 } 00233 } 00234 00235 LOG(llevSystem, "Bad command from client ('%s') (%s)\n", ns->inbuf.buf + 2, STRING_SAFE((char *) data)); 00236 return 0; 00237 } 00238 00247 void handle_client(socket_struct *ns, player *pl) 00248 { 00249 int cmd_count = 0; 00250 00251 /* Loop through this - maybe we have several complete packets here. */ 00252 while (1) 00253 { 00254 /* If it is a player, and they don't have any speed left, we 00255 * return, and will parse the data when they do have time. */ 00256 if (ns->status == Ns_Zombie || ns->status == Ns_Dead || (pl && pl->state == ST_PLAYING && pl->ob != NULL && pl->ob->speed_left < 0)) 00257 { 00258 return; 00259 } 00260 00261 if (!SockList_ReadCommand(&ns->cmdbuf, &ns->inbuf)) 00262 { 00263 return; 00264 } 00265 00266 /* Reset idle counter. */ 00267 if (pl && pl->state == ST_PLAYING) 00268 { 00269 ns->login_count = 0; 00270 ns->keepalive = 0; 00271 } 00272 00273 if (check_command(ns, pl)) 00274 { 00275 if (cmd_count++ <= 8 && ns->status != Ns_Dead) 00276 { 00277 continue; 00278 } 00279 } 00280 00281 return; 00282 } 00283 } 00284 00289 void watchdog() 00290 { 00291 static int fd = -1; 00292 static struct sockaddr_in insock; 00293 00294 if (fd == -1) 00295 { 00296 struct protoent *protoent; 00297 00298 if ((protoent = getprotobyname("udp")) == NULL || (fd = socket(PF_INET, SOCK_DGRAM, protoent->p_proto)) == -1) 00299 { 00300 return; 00301 } 00302 00303 insock.sin_family = AF_INET; 00304 insock.sin_port = htons((unsigned short) 13325); 00305 insock.sin_addr.s_addr = inet_addr("127.0.0.1"); 00306 } 00307 00308 sendto(fd, (void *) &fd, 1, 0, (struct sockaddr *) &insock, sizeof(insock)); 00309 } 00310 00315 void remove_ns_dead_player(player *pl) 00316 { 00317 if (pl == NULL || pl->ob->type == DEAD_OBJECT) 00318 { 00319 return; 00320 } 00321 00322 if (pl->state == ST_PLAYING) 00323 { 00324 /* Trigger the global LOGOUT event */ 00325 trigger_global_event(GEVENT_LOGOUT, pl->ob, pl->socket.host); 00326 statistics_player_logout(pl); 00327 00328 if (!pl->dm_stealth) 00329 { 00330 new_draw_info_format(NDI_ALL, COLOR_DK_ORANGE, NULL, "%s left the game.", query_name(pl->ob, NULL)); 00331 } 00332 00333 /* If this player is in a party, leave the party */ 00334 if (pl->party) 00335 { 00336 command_party(pl->ob, "leave"); 00337 } 00338 00339 strncpy(pl->killer, "left", MAX_BUF - 1); 00340 hiscore_check(pl->ob, 1); 00341 00342 /* Be sure we have closed container when we leave */ 00343 container_unlink(pl, NULL); 00344 00345 save_player(pl->ob, 0); 00346 00347 if (!QUERY_FLAG(pl->ob, FLAG_REMOVED)) 00348 { 00349 leave_map(pl->ob); 00350 } 00351 00352 if (pl->ob->map) 00353 { 00354 if (pl->ob->map->in_memory == MAP_IN_MEMORY) 00355 { 00356 pl->ob->map->timeout = MAP_TIMEOUT(pl->ob->map); 00357 } 00358 00359 pl->ob->map = NULL; 00360 } 00361 } 00362 00363 LOG(llevInfo, "Logout %s from IP %s\n", pl->ob->name, pl->socket.host); 00364 00365 /* To avoid problems with inventory window */ 00366 pl->ob->type = DEAD_OBJECT; 00367 free_player(pl); 00368 } 00369 00374 static int is_fd_valid(int fd) 00375 { 00376 #ifndef WIN32 00377 return fcntl(fd, F_GETFL) != -1 || errno != EBADF; 00378 #else 00379 return 1; 00380 #endif 00381 } 00382 00386 #define FREE_SOCKET(i) \ 00387 free_newsocket(&init_sockets[(i)]); \ 00388 init_sockets[(i)].status = Ns_Avail; \ 00389 socket_info.nconns--; 00390 00396 void doeric_server() 00397 { 00398 int i, pollret, rr; 00399 struct sockaddr_in addr; 00400 socklen_t addrlen = sizeof(addr); 00401 player *pl, *next; 00402 00403 #if CS_LOGSTATS 00404 if ((time(NULL) - cst_lst.time_start) >= CS_LOGTIME) 00405 { 00406 write_cs_stats(); 00407 } 00408 #endif 00409 00410 FD_ZERO(&tmp_read); 00411 FD_ZERO(&tmp_write); 00412 FD_ZERO(&tmp_exceptions); 00413 00414 for (i = 0; i < socket_info.allocated_sockets; i++) 00415 { 00416 if (init_sockets[i].status == Ns_Add && !is_fd_valid(init_sockets[i].fd)) 00417 { 00418 LOG(llevDebug, "doeric_server(): Invalid waiting fd %d\n", i); 00419 init_sockets[i].status = Ns_Dead; 00420 } 00421 00422 if (init_sockets[i].status == Ns_Dead) 00423 { 00424 FREE_SOCKET(i); 00425 } 00426 else if (init_sockets[i].status == Ns_Zombie) 00427 { 00428 if (init_sockets[i].login_count++ >= 1000000 / MAX_TIME) 00429 { 00430 init_sockets[i].status = Ns_Dead; 00431 } 00432 } 00433 else if (init_sockets[i].status != Ns_Avail) 00434 { 00435 if (init_sockets[i].status > Ns_Wait) 00436 { 00437 if (init_sockets[i].keepalive++ >= (uint32) SOCKET_KEEPALIVE_TIMEOUT * (1000000 / max_time)) 00438 { 00439 LOG(llevInfo, "Keepalive: disconnecting %s: %d\n", init_sockets[i].host ? init_sockets[i].host : "(unknown ip?)", init_sockets[i].fd); 00440 FREE_SOCKET(i); 00441 continue; 00442 } 00443 } 00444 00445 FD_SET((uint32) init_sockets[i].fd, &tmp_read); 00446 FD_SET((uint32) init_sockets[i].fd, &tmp_write); 00447 FD_SET((uint32) init_sockets[i].fd, &tmp_exceptions); 00448 } 00449 } 00450 00451 /* Go through the players. Let the loop set the next pl value, since 00452 * we may remove some. */ 00453 for (pl = first_player; pl != NULL; ) 00454 { 00455 if (pl->socket.status != Ns_Dead && !is_fd_valid(pl->socket.fd)) 00456 { 00457 LOG(llevDebug, "doeric_server(): Invalid file descriptor for player %s [%s]: %d\n", (pl->ob && pl->ob->name) ? pl->ob->name : "(unnamed player?)", (pl->socket.host) ? pl->socket.host : "(unknown ip?)", pl->socket.fd); 00458 pl->socket.status = Ns_Dead; 00459 } 00460 00461 if (pl->socket.status != Ns_Dead && pl->socket.socket_version >= 1052 && pl->socket.keepalive++ >= (uint32) SOCKET_KEEPALIVE_TIMEOUT * (1000000 / max_time)) 00462 { 00463 LOG(llevInfo, "Keepalive: disconnecting %s [%s]: %d\n", (pl->ob && pl->ob->name) ? pl->ob->name : "(unnamed player?)", (pl->socket.host) ? pl->socket.host : "(unknown ip?)", pl->socket.fd); 00464 pl->socket.status = Ns_Dead; 00465 } 00466 00467 if (pl->socket.status == Ns_Dead) 00468 { 00469 player *npl = pl->next; 00470 00471 remove_ns_dead_player(pl); 00472 pl = npl; 00473 } 00474 else if (pl->socket.status == Ns_Zombie) 00475 { 00476 if (pl->socket.login_count++ >= 1000000 / MAX_TIME) 00477 { 00478 pl->socket.status = Ns_Dead; 00479 } 00480 } 00481 else 00482 { 00483 FD_SET((uint32) pl->socket.fd, &tmp_read); 00484 FD_SET((uint32) pl->socket.fd, &tmp_write); 00485 FD_SET((uint32) pl->socket.fd, &tmp_exceptions); 00486 pl = pl->next; 00487 } 00488 } 00489 00490 socket_info.timeout.tv_sec = 0; 00491 socket_info.timeout.tv_usec = 0; 00492 00493 pollret = select(socket_info.max_filedescriptor, &tmp_read, &tmp_write, &tmp_exceptions, &socket_info.timeout); 00494 00495 if (pollret == -1) 00496 { 00497 LOG(llevDebug, "doeric_server(): select failed: %s\n", strerror_local(errno)); 00498 return; 00499 } 00500 00501 /* Following adds a new connection */ 00502 if (pollret && FD_ISSET(init_sockets[0].fd, &tmp_read)) 00503 { 00504 int newsocknum = 0; 00505 00506 /* If this is the case, all sockets are currently in use */ 00507 if (socket_info.allocated_sockets <= socket_info.nconns) 00508 { 00509 init_sockets = realloc(init_sockets, sizeof(socket_struct) * (socket_info.nconns + 1)); 00510 00511 if (!init_sockets) 00512 { 00513 LOG(llevError, "doeric_server(): Out of memory\n"); 00514 } 00515 00516 newsocknum = socket_info.allocated_sockets; 00517 socket_info.allocated_sockets++; 00518 init_sockets[newsocknum].status = Ns_Avail; 00519 } 00520 else 00521 { 00522 int j; 00523 00524 for (j = 1; j < socket_info.allocated_sockets; j++) 00525 { 00526 if (init_sockets[j].status == Ns_Avail) 00527 { 00528 newsocknum = j; 00529 break; 00530 } 00531 } 00532 } 00533 00534 init_sockets[newsocknum].fd = accept(init_sockets[0].fd, (struct sockaddr *) &addr, &addrlen); 00535 00536 if (init_sockets[newsocknum].fd == -1) 00537 { 00538 LOG(llevDebug, "doeric_server(): accept failed: %s\n", strerror_local(errno)); 00539 } 00540 else 00541 { 00542 char buf[MAX_BUF]; 00543 long ip = ntohl(addr.sin_addr.s_addr); 00544 00545 snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255); 00546 00547 if (checkbanned(NULL, buf)) 00548 { 00549 LOG(llevSystem, "Ban: Banned IP tried to connect: %s\n", buf); 00550 #ifndef WIN32 00551 close(init_sockets[newsocknum].fd); 00552 #else 00553 shutdown(init_sockets[newsocknum].fd, SD_BOTH); 00554 closesocket(init_sockets[newsocknum].fd); 00555 #endif 00556 init_sockets[newsocknum].fd = -1; 00557 } 00558 else 00559 { 00560 init_connection(&init_sockets[newsocknum], buf); 00561 socket_info.nconns++; 00562 } 00563 } 00564 } 00565 00566 /* Check for any exceptions/input on the sockets */ 00567 if (pollret) 00568 { 00569 for (i = 1; i < socket_info.allocated_sockets; i++) 00570 { 00571 if (init_sockets[i].status == Ns_Avail) 00572 { 00573 continue; 00574 } 00575 00576 if (FD_ISSET(init_sockets[i].fd, &tmp_exceptions)) 00577 { 00578 FREE_SOCKET(i); 00579 continue; 00580 } 00581 00582 if (FD_ISSET(init_sockets[i].fd, &tmp_read)) 00583 { 00584 rr = SockList_ReadPacket(&init_sockets[i], MAXSOCKBUF_IN - 1); 00585 00586 if (rr < 0) 00587 { 00588 LOG(llevInfo, "Drop connection: %s\n", STRING_SAFE(init_sockets[i].host)); 00589 init_sockets[i].status = Ns_Dead; 00590 } 00591 else 00592 { 00593 fill_command_buffer(&init_sockets[i]); 00594 } 00595 } 00596 00597 if (init_sockets[i].status == Ns_Avail) 00598 { 00599 continue; 00600 } 00601 00602 if (init_sockets[i].status == Ns_Dead) 00603 { 00604 FREE_SOCKET(i); 00605 continue; 00606 } 00607 00608 if (FD_ISSET(init_sockets[i].fd, &tmp_write)) 00609 { 00610 socket_buffer_write(&init_sockets[i]); 00611 } 00612 00613 if (init_sockets[i].status == Ns_Dead) 00614 { 00615 FREE_SOCKET(i); 00616 } 00617 } 00618 } 00619 00620 /* This does roughly the same thing, but for the players now */ 00621 for (pl = first_player; pl; pl = next) 00622 { 00623 next = pl->next; 00624 00625 /* Kill players if we have problems */ 00626 if (pl->socket.status == Ns_Dead || FD_ISSET(pl->socket.fd, &tmp_exceptions)) 00627 { 00628 remove_ns_dead_player(pl); 00629 continue; 00630 } 00631 00632 if (FD_ISSET(pl->socket.fd, &tmp_read)) 00633 { 00634 rr = SockList_ReadPacket(&pl->socket, MAXSOCKBUF_IN - 1); 00635 00636 if (rr < 0) 00637 { 00638 LOG(llevInfo, "Drop connection: %s (%s)\n", STRING_OBJ_NAME(pl->ob), STRING_SAFE(pl->socket.host)); 00639 pl->socket.status = Ns_Dead; 00640 } 00641 else 00642 { 00643 fill_command_buffer(&pl->socket); 00644 } 00645 } 00646 00647 /* Perhaps something was bad in handle_client(), or player has 00648 * left the game. */ 00649 if (pl->socket.status == Ns_Dead) 00650 { 00651 remove_ns_dead_player(pl); 00652 continue; 00653 } 00654 00655 if (FD_ISSET(pl->socket.fd, &tmp_write)) 00656 { 00657 socket_buffer_write(&pl->socket); 00658 } 00659 } 00660 } 00661 00664 void doeric_server_write() 00665 { 00666 player *pl, *next; 00667 uint32 update_below; 00668 00669 /* This does roughly the same thing, but for the players now */ 00670 for (pl = first_player; pl; pl = next) 00671 { 00672 next = pl->next; 00673 00674 if (pl->socket.status == Ns_Dead || FD_ISSET(pl->socket.fd, &tmp_exceptions)) 00675 { 00676 remove_ns_dead_player(pl); 00677 continue; 00678 } 00679 00680 esrv_update_stats(pl); 00681 party_update_who(pl); 00682 00683 if (pl->update_skills) 00684 { 00685 esrv_update_skills(pl); 00686 pl->update_skills = 0; 00687 } 00688 00689 draw_client_map(pl->ob); 00690 00691 if (pl->ob->map && (update_below = GET_MAP_UPDATE_COUNTER(pl->ob->map, pl->ob->x, pl->ob->y)) != pl->socket.update_tile) 00692 { 00693 esrv_draw_look(pl->ob); 00694 pl->socket.update_tile = update_below; 00695 } 00696 00697 if (FD_ISSET(pl->socket.fd, &tmp_write)) 00698 { 00699 socket_buffer_write(&pl->socket); 00700 } 00701 } 00702 }
1.7.4