/* * Copyright (C) 1999 Peter Amstutz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of *the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include "../config.h" #ifdef HAVE_SYS_SELECT_H # include #else # include #endif #include "player.h" #include "relay.h" #include "game.h" #include "svrhandlers.h" #include "terrain.h" #include "packets.h" #include "weapons/weapon.h" #include "kserver.h" #include "signal.h" #include "ballistics.h" #include "cfgfile.h" #include "log.h" #include "conversions.h" #include "tcpcore.h" #define DEFAULT_PORT 8086 Gamemode_gm gm_gamemode = PREGAME; Gametype_gm gm_gametype; char gm_quit = 0; int gm_activeplayers = 0; int gm_numberPlayers = 0; int gm_currentRound = 1; int gm_totalRounds = 0; int gm_killNumber = 0; Relay_rl *relay; int totalplayers = 0; int gm_current_attacker; Player_pl *gm_myplstruct = NULL; /* keep the linker happy */ char gm_tank_damaged; char gm_activate_shots; int gm_shotgeneration = 1; int gm_death_queue[256] = { 0 }; int gm_dq_pos = 0; int gm_capital = 1000; int gm_bounty = 500; int gm_stipend = 250; int gm_iAmServer = 1; int interest = 0; Turnmodetype_gm turnmode=RANDOM; Player_pl *gm_firing_order[10]; #define HELP_TEXT "Syntax %s:\n"\ "Options:\n" \ " -fSTR configuration file to use (default %s)\n" \ " -pINT port to use (default 8086)\n" \ " -l logging level\n" void svEndRound() { int bcast = 0; Player_pl *pcur; char buf[512]; struct SetGameMode_pkt gmpkt; struct Score_pkt scpkt; /* update the scores and send them to the client */ for(pcur = pl_begin; pcur; pcur = pcur->next) { /* update the total score */ pcur->score += pcur->roundScore; scpkt.type[0] = 'U'; scpkt.type[1] = 'S'; scpkt.id = pcur->id; scpkt.roundScore = pcur->roundScore; scpkt.score = pcur->score; rlBroadcast(relay, &bcast, buf, pktPackScore(buf, &scpkt)); pcur->roundScore = 0; } gm_gamemode = POSTGAME; gmpkt.type[0] = 'G'; gmpkt.type[1] = 'M'; gmpkt.gamemode = gm_gamemode; rlBroadcast(relay, &bcast, buf, pktPackSetGameMode(buf, &gmpkt)); } void svInitialize(int argc, char **argv) { int go; int port = DEFAULT_PORT; char pset = 0, lset = 0, fset = 0; char *configfile = NULL; /* char *configfile=DEFAULT_CONFIGFILE; */ const char *game_type_str[] = { "turns", "simultaneous", NULL }; const Gametype_gm game_type_val[] = { TAKETURNS, SIMULTANEOUS }; const char* turn_type_str[]={"random","loser","winner",NULL}; const Gametype_gm turn_type_val[]={RANDOM,LOOSERFIRST,WINNERFIRST}; const char *log_str[] = { "critical", "interesting", "debug", "spam", NULL }; const Levels_log log_val[] = { CRITICAL, INTERESTING, DEBUG, SPAM }; logPrintf(INTERESTING, "King of the Hill (KOTH) Server version %s\n", VERSION); logPrintf(INTERESTING, "Copyright (C) 1999 Peter Amstutz\n"); logPrintf(INTERESTING, "Copyright (C) 2002, 2003 Allan Douglas\n"); logPrintf(INTERESTING, "KOTH comes with ABSOLUTELY NO WARRANTY\n"); logPrintf(INTERESTING, "This is free software, and you are welcome to redistribute it\n"); logPrintf(INTERESTING, "under the conditions of the GNU GPL\n"); while(1) { go = getopt(argc, argv, "hf:p:l:"); if(go == EOF) break; switch (go) { case 'f': configfile = strdup(optarg); fset = 1; break; case 'p': port = atoi(optarg); pset = 1; break; case 'l': lset = 1; if(strcmp(optarg, "critical") == 0) log_level = CRITICAL; else if(strcmp(optarg, "interesting") == 0) log_level = INTERESTING; else if(strcmp(optarg, "debug") == 0) log_level = DEBUG; else if(strcmp(optarg, "spam") == 0) log_level = SPAM; else { lset = 0; logPrintf(CRITICAL, "Bad log level %s\n", optarg); } break; case 'h': logPrintf(CRITICAL, HELP_TEXT, argv[0], DEFAULT_CONFIGFILE); exit(0); break; default: logPrintf(CRITICAL, HELP_TEXT, argv[0], DEFAULT_CONFIGFILE); exit(-1); break; } } cfg_configuration = cfgReadConfiguration(configfile); if(fset) free(configfile); if(cfg_configuration != NULL) { if(!pset) cfgLoadConfigItemInt(cfg_configuration, "server.port", &port); if(!cfgLoadConfigItemOption(cfg_configuration, "game.mode", game_type_str, (int *) game_type_val, (int *) &gm_gametype)) { logPrintf(CRITICAL, "Can't find or invalid game type in config file,\n"); logPrintf(CRITICAL, "defaulting to simultaneous.\n"); gm_gametype = SIMULTANEOUS; } cfgLoadConfigItemOption(cfg_configuration,"game.turnmode", turn_type_str, (int*)turn_type_val, (int*)&turnmode); if(!lset) cfgLoadConfigItemOption(cfg_configuration, "client.logging", log_str, (int *) log_val, (int *) &log_level); cfgLoadConfigItemInt(cfg_configuration, "tank.capital", &gm_capital); cfgLoadConfigItemInt(cfg_configuration, "tank.bounty", &gm_bounty); cfgLoadConfigItemInt(cfg_configuration, "tank.stipend", &gm_stipend); cfgLoadConfigItemInt(cfg_configuration, "game.rounds", &gm_totalRounds); cfgLoadConfigItemInt(cfg_configuration, "game.interest", &interest); } else { gm_gametype = SIMULTANEOUS; port = DEFAULT_PORT; } signal(SIGPIPE, SIG_IGN); if((relay = rlInit(port)) == NULL) { logPrintf(CRITICAL, "Can't bind listen socket\n"); exit(-1); } srandom(time(NULL)); rlRegisterHandler(relay, "NP", shNewPlayer); /* shNewPlayer() */ rlRegisterHandler(relay, "SN", shSetName); /* shSetName() */ rlRegisterHandler(relay, "MS", shMessage); /* shMessage() */ rlRegisterHandler(relay, "FS", shFireCommand); /* shFireCommand() */ rlRegisterHandler(relay, "CR", shSetReady); /* shSetReady() */ rlRegisterHandler(relay, "RS", shResync); /* shResync() */ rlSetDisconnectFunc(relay, shTheyLeft); /* shTheyLeft() */ memset(ter_data, 0, sizeof(ter_data)); pl_tankwidth = ter_sizex / TANKSCREENRATIO_X; pl_tankheight = ter_sizey / TANKSCREENRATIO_Y; balInit(); wepInit(); puts("Type 'help' for help"); } /* Pregame */ void svPregame() { Player_pl *pcur; static int lasttotalplayers = 0; struct PlayerID_pkt nrpkt; char buf[256]; int bcast = 0; gm_gamemode = PREGAME; rlRegisterHandler(relay, "BW", shBuyWeapon); rlRegisterHandler(relay, "SW", shSellWeapon); /* mark all the players (not observing) not ready */ for(pcur=pl_begin; pcur; pcur=pcur->next) { if(pcur->ready!=OBSERVER) { pcur->ready = NOTREADY; } } if(pl_begin) { for(totalplayers = 0, pcur = pl_begin, gm_gamemode = INGAME; pcur; pcur = pcur->next) { if(pcur->ready != OBSERVER) totalplayers++; if(pcur->ready == NOTREADY) { gm_gamemode = PREGAME; } } if(totalplayers == 0) gm_gamemode = PREGAME; } while(!gm_quit && gm_gamemode == PREGAME) { /* if a round is in progress and everyone left but one person ** end this round */ if(lasttotalplayers > 1 && totalplayers == 1 && gm_currentRound != 1) { /* unless we're in "infinate round mode */ if(gm_totalRounds != 0) { gm_currentRound = gm_totalRounds; /* send the new round # to the client */ nrpkt.type[0] = 'N'; nrpkt.type[1] = 'R'; nrpkt.id = gm_currentRound; rlBroadcast(relay, &bcast, buf, pktPackPlayerID(buf, &nrpkt)); svEndRound(); pl_begin->ready = SCORING; break; } } lasttotalplayers = totalplayers; rlMain(relay, NULL); svCommandLine(); if(pl_begin) { for(totalplayers = 0, pcur = pl_begin, gm_gamemode = INGAME; pcur; pcur = pcur->next) { if(pcur->ready != OBSERVER) totalplayers++; if(pcur->ready == NOTREADY) { gm_gamemode = PREGAME; } } } if(gm_gamemode == INGAME && totalplayers < 2) gm_gamemode = PREGAME; } rlRemoveHandler(relay, "BW"); rlRemoveHandler(relay, "SW"); } void svSendWind(int *who) { char buf[512]; struct PlayerID_pkt pid; pid.type[0]='W'; pid.type[1]='S'; pid.id=bal_wind; rlBroadcast(relay, who, buf, pktPackWindSpeed(buf, &pid)); } void svActivateShots(int x) { gm_activate_shots = 1; } /* qsort comparison function, loser first */ int lfcompar(const void *a, const void *b) { Player_pl **pla; Player_pl **plb; pla=(Player_pl **)a; plb=(Player_pl **)b; return((*pla)->score-(*plb)->score); } /* qsort comparison function, winner first */ int wfcompar(const void *a, const void *b) { Player_pl **pla; Player_pl **plb; pla=(Player_pl **)a; plb=(Player_pl **)b; return((*plb)->score-(*pla)->score); } /* Ingame */ void svPlaygame() { int bcast = 0; int x, i, lowest; int check[10] = { 0 }; Player_pl *pcur; struct SetTank_pkt st; struct ChangeReady_pkt cr; char buf[512]; struct PlayerID_pkt pid; struct TerrainInfo_pkt ti; struct SetGameMode_pkt gmpkt; struct Projectilelist_bal *prj; int def_armor; int player_area, player_offset; gm_gamemode = INGAME; gmpkt.type[0] = 'G'; gmpkt.type[1] = 'M'; gmpkt.gamemode = gm_gamemode; rlBroadcast(relay, &bcast, buf, pktPackSetGameMode(buf, &gmpkt)); terGenerate(0, ter_sizey / 2.5, ter_sizex, ter_sizey / 2.5, ter_depth, ter_table, ter_data); rlRegisterHandler(relay, "GT", shSendTerrain); ti.type[0] = 'N'; ti.type[1] = 'T'; ti.sizex = ter_sizex; ti.sizey = ter_sizey; ti.lerp_tweak = bal_lerp_tweak * 0xFFFF; ti.grav = bal_grav * 0xFFFF; rlBroadcast(relay, &bcast, buf, pktPackTerrainInfo(buf, &ti)); cr.type[0] = 'C'; cr.type[1] = 'R'; for(pcur = pl_begin; pcur; pcur = pcur->next) { cr.id = pcur->id; cr.r = pcur->ready; rlBroadcast(relay, &bcast, buf, pktPackChangeReady(buf, &cr)); } if(cfg_configuration != NULL) cfgLoadConfigItemInt(cfg_configuration, "tank.armor", &def_armor); else def_armor = 100; for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready == OBSERVER) continue; assert(pcur->name); /* I suppose there is a chance that this "pick and check" algorithm * could lock the server up, but the odds are sufficiently low * that I think I'll take my chances :) */ do { x = random() % totalplayers; } while(check[x]); check[x] = 1; gm_firing_order[x] = pcur; pcur->x = (ter_sizex * (x + 1)) / (totalplayers + 1); player_area = (ter_sizex / (totalplayers + 1)) - (pl_tankwidth * 2); pcur->x = pcur->x - (player_area / 2); player_offset = random() % player_area; pcur->x += player_offset; /* this isn't completely kosher, but since we just generated * the terrain we can assume that there are no overhangs or * anything to screw us up, and so we read the first (and * therefore only) element directly */ for(i = pcur->x - pl_tankwidth / 2 - pl_barrelen, lowest = ter_sizey; i < pcur->x + (pl_tankwidth / 2 + pl_barrelen); i++) { if(ter_data[i].height < lowest) lowest = ter_data[i].height; } pcur->y = lowest; for(i = pcur->x - pl_tankwidth / 2 - pl_barrelen; i < pcur->x + (pl_tankwidth / 2 + pl_barrelen); i++) { terDelSpan(&ter_data[i], lowest, ter_sizey); } logPrintf(INTERESTING, "Tank %s placed at (%i, %i)\n", pcur->name, pcur->x, pcur->y); pcur->armor = def_armor; st.type[0] = 'S'; st.type[1] = 'T'; st.id = pcur->id; st.x = pcur->x; st.y = pcur->y; st.a = pcur->fire_angle; st.v = pcur->fire_velocity; st.armor = pcur->armor; rlBroadcast(relay, &bcast, buf, pktPackSetTank(buf, &st)); /* give this player his interest before the round starts */ pcur->money += pcur->money*interest/100; } if(gm_gametype==TAKETURNS) { switch(turnmode) { case RANDOM: /* leave it left to right random for now.... */ ; break; case LOOSERFIRST: qsort(&gm_firing_order, totalplayers, sizeof(gm_firing_order[0]), lfcompar); break; case WINNERFIRST: qsort(&gm_firing_order, totalplayers, sizeof(gm_firing_order[0]), wfcompar); break; default: ; } } gm_activeplayers = totalplayers; gm_numberPlayers = totalplayers; logPrintf(DEBUG, "gm_activeplayers = %d\n", gm_activeplayers); logPrintf(DEBUG, "gm_numberPlayers = %d\n", gm_numberPlayers); gm_killNumber = 0; gm_current_attacker = 0; gm_activate_shots = 0; for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready == OBSERVER) continue; /* initialize the kill number to "last", since if you don't die ** it won't be set otherwise */ pcur->killNumber = gm_numberPlayers - 1; } balCalcWind(); svSendWind(&bcast); balSetWall(); pid.type[0] = 'W'; pid.type[1] = 'T'; pid.id = (int) bal_wall; rlBroadcast(relay, &bcast, buf, pktPackWallType(buf, &pid)); signal(SIGALRM, svActivateShots); alarm(30); while(gm_gamemode == INGAME && !gm_quit) { if(gm_gametype == TAKETURNS) { /* tell the clients a new game round has started */ pid.type[0]='U'; pid.type[1]='F'; pid.id=gm_firing_order[gm_current_attacker]->id; rlBroadcast(relay, &bcast, buf, pktPackPlayerID(buf, &pid)); } rlMain(relay, NULL); svCommandLine(); if(gm_activate_shots) { #if 0 /* Combats skew on the one hand, creates it on the other... :-( */ for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready != READY) continue; st.type[0] = 'S'; st.type[1] = 'D'; st.id = pcur->id; st.x = pcur->x; st.y = pcur->y; st.armor = pcur->armor; rlBroadcast(relay, &bcast, buf, pktPackSetTank(buf, &st)); } #endif for(prj = bal_Projectiles; prj; prj = prj->next) { if(prj->stat == HOLDING && prj->gen == gm_shotgeneration) prj->stat = INITSHOT(prj); } logPrintf(DEBUG, "-- Beginning round, gen: %i --\n", gm_shotgeneration); logPrintf(DEBUG, "wind is %i\n", bal_wind); do { gm_stuff_happening = terCalcDirtFall(); if(gm_stuff_happening) plCalcTankFall(); else gm_stuff_happening = plCalcTankFall(); if(gm_stuff_happening) balAdvanceProjectiles(); else gm_stuff_happening = balAdvanceProjectiles(); } while(gm_stuff_happening); svProcessDeathQueue(); pid.type[0] = 'A'; pid.type[1] = 'S'; pid.id = gm_shotgeneration++; rlBroadcast(relay, &bcast, buf, pktPackPlayerID(buf, &pid)); if(gm_gametype == TAKETURNS) { gm_current_attacker++; gm_current_attacker %= gm_activeplayers; } if(gm_gametype == SIMULTANEOUS) { /* tell the clients a new game round has started */ pid.type[0]='U'; pid.type[1]='F'; pid.id=gm_firing_order[gm_current_attacker]->id; rlBroadcast(relay, &bcast, buf, pktPackPlayerID(buf, &pid)); } gm_activate_shots = 0; balRecalcWind(); svSendWind(&bcast); signal(SIGALRM, svActivateShots); alarm(30); } for(pcur = pl_begin, gm_activeplayers = 0; pcur; pcur = pcur->next) if(pcur->ready == READY) gm_activeplayers++; logPrintf(SPAM, "gm_activeplayers = %d\n", gm_activeplayers); logPrintf(SPAM, "gm_numberPlayers = %d\n", gm_numberPlayers); if(gm_activeplayers <= 1) gm_gamemode = POSTGAME; } alarm(0); rlRemoveHandler(relay, "GT"); pid.type[0] = 'S'; pid.type[1] = 'M'; for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready != OBSERVER) { pcur->money += gm_stipend; pcur->roundScore += SCORE_DONE(pcur); } if(pcur->name && pcur->name[0] != 0 && pcur->tankcolor != observer_color != 0) pcur->ready = SCORING; pid.id = pcur->money; rlSend(relay, pcur->id, buf, pktPackPlayerID(buf, &pid)); } if(gm_numberPlayers < 2) { gm_currentRound = gm_totalRounds; /* send the new round # to the client */ pid.type[0] = 'N'; pid.type[1] = 'R'; pid.id = gm_currentRound; rlBroadcast(relay, &bcast, buf, pktPackPlayerID(buf, &pid)); } svEndRound(); for(i = 0; i < ter_sizex; i++) terFreeCol(ter_data[i].nexthigher); memset(ter_data, 0, sizeof(ter_data)); } void svProcessDeathQueue() { unsigned i; Player_pl *pcur, *pcur2; Weapon_wep *wp; logPrintf(DEBUG,"gm_dq_pos = %i\n", gm_dq_pos); for(i = 0; i < gm_dq_pos; i += 3) { if(gm_death_queue[i + 1] == gm_death_queue[i]) { pcur = plLookupPlayer(gm_death_queue[i]); wp = wepLookupWeaponByID(gm_death_queue[i+2]); assert(wp); if(pcur) { assert(pcur->name && wp->name); pcur->money -= gm_stipend; pcur->killNumber = gm_killNumber; gm_killNumber++; pcur->roundScore += SCORE_SUICIDE; logPrintf(INTERESTING, "%s aimed his own head with a %s\n", pcur->name, wp->name); } } else if(gm_death_queue[i + 1] == 0) { pcur = plLookupPlayer(gm_death_queue[i]); if(pcur) { assert(pcur->name); pcur2 = plLookupPlayer(gm_death_queue[i+2]); if(pcur2) { if(pcur2 != pcur) { assert(pcur2->name); logPrintf(INTERESTING, "%s was cratered by %s.\n", pcur->name, pcur2->name); pcur->killNumber = gm_killNumber; gm_killNumber++; pcur2->money += gm_bounty; pcur2->roundScore += SCORE_KILL; } else { pcur->money -= gm_stipend; pcur->killNumber = gm_killNumber; gm_killNumber++; pcur->roundScore += SCORE_SUICIDE; logPrintf(INTERESTING, "%s cratered himself\n", pcur->name); } } else { logPrintf(INTERESTING, "%s cratered.\n", pcur->name); } } } else { pcur = plLookupPlayer(gm_death_queue[i]); pcur2 = plLookupPlayer(gm_death_queue[i + 1]); wp = wepLookupWeaponByID(gm_death_queue[i+2]); assert(wp); if(pcur && pcur2) { assert(pcur->name && pcur2->name && wp->name); logPrintf(INTERESTING, "%s blew %s to bits with a %s.\n", pcur2->name, pcur->name, wp->name); pcur->killNumber = gm_killNumber; gm_killNumber++; pcur2->money += gm_bounty; pcur2->roundScore += SCORE_KILL; } } } gm_dq_pos = 0; } void svScoreTimeout(int x) { gm_gamemode = PREGAME; } /* post game... show score, etc */ void svPostgame() { struct ChangeReady_pkt cr; struct SetGameMode_pkt gmpkt; struct PlayerID_pkt nrpkt; struct Score_pkt scpkt; int bcast = 0; char buf[512]; Player_pl *pcur; struct PlayerID_pkt pid; int numPlayers=0; /* sleep for a bit to let the clients see the score, then change ** the gamemode to PREGAME to get back to it */ signal(SIGALRM, svScoreTimeout); alarm(60); while(gm_gamemode == POSTGAME) { numPlayers=0; for(pcur = pl_begin, gm_gamemode = PREGAME; pcur; pcur = pcur->next) { if(pcur->ready!= OBSERVER) { numPlayers++; if(pcur->ready == SCORING) { gm_gamemode = POSTGAME; } } } if(gm_gamemode == PREGAME) break; rlMain(relay, NULL); svCommandLine(); } signal(SIGALRM, SIG_IGN); cr.type[0] = 'C'; cr.type[1] = 'R'; /* if we're at the end of the game, update everything */ if((gm_currentRound == gm_totalRounds)|| (numPlayers <= 1)) { for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready != OBSERVER) { /* set the money back to initial capital */ pcur->money = gm_capital; pid.type[0] = 'S'; pid.type[1] = 'M'; pid.id = pcur->money; rlSend(relay, pcur->id, buf, pktPackPlayerID(buf, &pid)); /* take all the weapons away */ plClearAllWeapon(pcur->id); /* clear everyone's score */ pcur->score = 0; pcur->roundScore = 0; scpkt.type[0] = 'U'; scpkt.type[1] = 'S'; scpkt.id = pcur->id; scpkt.roundScore = pcur->roundScore; scpkt.score = pcur->score; rlBroadcast(relay, &bcast, buf, pktPackScore(buf, &scpkt)); /* Prepare players for the next round */ pcur->ready = NOTREADY; cr.id = pcur->id; cr.r = (ubyte_pkt)pcur->ready; rlBroadcast(relay, &bcast, buf, pktPackChangeReady(buf, &cr)); } } /* set the game round to the first round */ gm_currentRound = 1; } else { gm_currentRound++; /* Prepare players for the next round */ for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready != OBSERVER) { pcur->ready = NOTREADY; cr.id = pcur->id; cr.r = (ubyte_pkt)pcur->ready; rlBroadcast(relay, &bcast, buf, pktPackChangeReady(buf, &cr)); } } } /* send the new round # to the client */ nrpkt.type[0] = 'N'; nrpkt.type[1] = 'R'; nrpkt.id = gm_currentRound; rlBroadcast(relay, &bcast, buf, pktPackPlayerID(buf, &nrpkt)); gmpkt.type[0] = 'G'; gmpkt.type[1] = 'M'; gmpkt.gamemode = gm_gamemode; rlBroadcast(relay, &bcast, buf, pktPackSetGameMode(buf, &gmpkt)); } int svKickPlayer(int id, char *reason) { Player_pl *pl = plLookupPlayer(id); int bcast = 0; char buf[256]; if(!pl) return 0; if(reason && reason[0]) rlBroadcast(relay, &bcast, buf, sprintf(buf, "MSServer: Kicking %s: %s", pl->name, reason) + 1); else rlBroadcast(relay, &bcast, buf, sprintf(buf, "MSServer: Kicking %s...", pl->name) + 1); rlDisconnect(relay, id); return 1; } void svCommandLine() { while(tcpDataReady(STDIN_FILENO)) { char buf[256]; if(fgets(buf, 256, stdin) != NULL) { svCommandParse(buf); } else clearerr(stdin); /*printf("$ ");*/ } } /* TODO: Write a decent parser */ void svCommandParse(char *buf) { char command[256]; if(!buf) return; /* while(*buf && isspace(*buf)) ++buf; if(!*buf) return;*/ if(sscanf(buf, "%s", command)) { if(!strcmp("kick", command)) { int id = -1; char reason[256]; reason[0] = '\0'; sscanf(buf, "%s %i %255[^]]", command, &id, reason); if(id < 0) { puts("Invalid syntax"); svCommandHelp(); return; } if(!svKickPlayer(id, reason)) printf("Unknown id: %i\n", id); } else if(!strcmp("ls", command)) { svCommandListPlayers(); } else if(!strcmp("help", command)) { svCommandHelp(); } else { puts("Unknown command"); svCommandHelp(); } } else { /*puts("Unknown command"); svCommandHelp();*/ return; } } void svCommandListPlayers() { Player_pl *pcur; puts("Players:"); puts("id\t-\tcolor\t-\tname\t-\tstatus\n"); for(pcur = pl_begin;pcur;pcur = pcur->next) { printf("%i\t-\t%s\t-\t%s\t-\t%s\n", pcur->id, conColorToString(pcur->tankcolor), pcur->name, conReadyToString(pcur->ready)); } } void svCommandHelp() { puts("\nCommands:\n \ kick id [reason] kicks a player\n \ ls list players info\n \ help shows this message\n"); } /* Main loop */ void svDriverloop() { while(!gm_quit) { if(gm_gamemode == PREGAME) { svPregame(); } if(gm_gamemode == INGAME && !gm_quit) { svPlaygame(); } if(gm_gamemode == POSTGAME && !gm_quit) { svPostgame(); } } } void svShutdown() { } int main(int argc, char **argv) { svInitialize(argc, argv); svDriverloop(); svShutdown(); return 0; } /* * Some stub routines. */ void aihDamageReport(Player_pl * hit_pl, int srcid, int amt) { } void aihExplosionHook(Projectilepos_bal * prj) { }