/* * file game_server.c - run game as server * * $Id: game_server.c,v 1.10 2004/07/07 10:24:20 iskywalker Exp $ * * Program XBLAST * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net) * * 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; or (at your option) * any later version * * This program is distributed in the hope that it will be entertaining, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILTY 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 "game_server.h" #include // RANDOMFIX #include "random.h" // RANDOMFIX #include "atom.h" #include "cfg_level.h" #include "bomb.h" #include "demo.h" #include "game.h" #include "geom.h" #include "intro.h" #include "level.h" #include "server.h" #include "status.h" #include "bot.h" #include "user.h" // XBCC /* * local variables */ static CFGGame serverGame; static PlayerAction serverAction[MAX_PLAYER]; static XBBool playerLinked[MAX_PLAYER]; /* * */ static void InitPlayerLink (void) { int i; assert (serverGame.players.num <= MAX_PLAYER); for (i = 0; i < serverGame.players.num; i ++) { playerLinked[i] = (serverGame.players.host[i] != XBPH_Server && serverGame.players.host[i] != XBPH_Local && serverGame.players.host[i] != XBPH_Demo); } for (; i < MAX_PLAYER; i ++) { playerLinked[i] = XBFalse; } } /* InitPlayerLink */ /* * */ static XBBool UpdatePlayerLink (void) { int i; XBBool result = XBFalse; for (i = 0; i < serverGame.players.num; i ++) { if (serverGame.players.host[i] != XBPH_Server && serverGame.players.host[i] != XBPH_Local && ! playerLinked[i] && ! player_stat[i].in_active) { player_stat[i].in_active = XBTrue; result = XBTrue; } } return result; } /* UpdatePlayerLink */ /* * wait for message from all clients */ static XBBool WaitForClientEvent (XBNetworkEvent waitEvent, XBBool needFlush) { int i,j; long num; unsigned id; XBEventData eData; XBNetworkEvent netEvent; XBBool playerWait[MAX_PLAYER]; XBBool async=XBFalse; /* for which players do we wait */ memcpy (playerWait, playerLinked, sizeof (playerWait)); /* set event handling */ GUI_SetTimer (FRAME_TIME, XBTrue); GUI_SetKeyboardMode (KB_NONE); GUI_SetMouseMode (XBFalse); do { /* how many player are not ready */ num = 0; for (i = 0; i < serverGame.players.num; i ++) { if (playerWait[i]) { num ++; } } /* update window */ GameUpdateWindow (); /* wait for next event */ if (XBE_TIMER == GUI_WaitEvent (&eData) ) { /* try to flush udp connections */ if (needFlush) { needFlush = Server_FlushPlayerAction (); } /* check for nework events */ netEvent = Network_GetEvent (&id); if (netEvent == waitEvent || netEvent == XBNW_Error || netEvent == XBNW_Disconnected) { XBPlayerHost host = XBPH_Client1 + id - 1; #ifdef DEBUG if(waitEvent>=XBNW_P0) Dbg_Out(" player won here %i remote %i\n",waitEvent,netEvent); #endif for (i = 0; i < serverGame.players.num; i ++) { if (serverGame.players.host[i] == host) { playerWait[i] = XBFalse; if (netEvent == XBNW_Error || netEvent == XBNW_Disconnected) { playerLinked[i] = XBFalse; Dbg_Out ("unlink player %d\n", i + 1); } } } }else { /* async check */ if(waitEvent>=XBNW_P0){ if(netEvent>=XBNW_P0){ for(j=0;j 0); return async; } /* WaitForClientEvent */ /* * server wants to synchronize with clients */ static XBBool SyncWithClients (XBNetworkEvent syncEvent, XBBool needFlush, XBBool showMsg) { if (showMsg) { SetMessage ("Waiting for others ...", XBTrue); } /* check if async */ if(WaitForClientEvent (syncEvent, needFlush)) { Dbg_Out(" sending level ERROR ASYNC\n"); Server_SendSync ( XBNW_ASYNC); return XBFalse; }else{ Server_SendSync (syncEvent); return XBTrue; } } /* SyncWithClients */ /* * insert keys from clients */ static void InsertClientAction (const CFGGamePlayers *cfgPlayers, PlayerAction *serverAction) { int i; assert (NULL != cfgPlayers); assert (NULL != serverAction); for (i = 0; i < cfgPlayers->num; i ++) { switch (cfgPlayers->host[i]) { case XBPH_Client1: Server_GetPlayerAction (1, i, serverAction + i); break; case XBPH_Client2: Server_GetPlayerAction (2, i, serverAction + i); break; case XBPH_Client3: Server_GetPlayerAction (3, i, serverAction + i); break; case XBPH_Client4: Server_GetPlayerAction (4, i, serverAction + i); break; case XBPH_Client5: Server_GetPlayerAction (5, i, serverAction + i); break; #ifdef SMPF case XBPH_Client6: Server_GetPlayerAction (6, i, serverAction + i); break; case XBPH_Client7: Server_GetPlayerAction (7, i, serverAction + i); break; case XBPH_Client8: Server_GetPlayerAction (8, i, serverAction + i); break; case XBPH_Client9: Server_GetPlayerAction (9, i, serverAction + i); break; case XBPH_Client10: Server_GetPlayerAction (10, i, serverAction + i); break; case XBPH_Client11: Server_GetPlayerAction (11, i, serverAction + i); break; case XBPH_Client12: Server_GetPlayerAction (12, i, serverAction + i); break; case XBPH_Client13: Server_GetPlayerAction (13, i, serverAction + i); break; case XBPH_Client14: Server_GetPlayerAction (14, i, serverAction + i); break; case XBPH_Client15: Server_GetPlayerAction (15, i, serverAction + i); break; #endif default: break; } } Server_ClearPlayerAction (); } /* InsertClientAction */ /* * Game Result */ char * CurrentResult () { int i; static char res[20]; for (i = 0; i < serverGame.players.num; i ++) { sprintf(&res[i],"%i",player_stat[i].victories); } return(&res[0]); } /* * */ static int ServerRunLevel (int numActive, const DBRoot *level) { int gameTime; int pauseStatus; int lastTeam, counter,event,winner; int frameTime; BMPlayer *ps; const char *msg; XBEventData eData; /* sanity check */ assert (level != NULL); /* necesary inits */ winner = -1; gameTime = 0; pauseStatus = -1; lastTeam = -1; frameTime = serverGame.setup.frameRate ? 1000/serverGame.setup.frameRate : 0; /* start demo recording */ if (serverGame.setup.recordDemo) { DemoInitLevel (DB_Atom (level)); } /* Config level */ ConfigLevel (level); /* show level info */ if (! LevelIntro (serverGame.players.num, level, serverGame.setup.infoTime)) { goto Exit; } /* syncshronize clients and server */ SyncWithClients (XBNW_SyncLevelIntro, XBFalse, XBTrue); /* clean up */ Server_ClearPlayerAction (); Server_ResetPlayerAction (); /* init level display */ LevelBegin (GetLevelName (level)); /* inti events */ GUI_SetTimer (frameTime, XBTrue); GUI_SetKeyboardMode (KB_XBLAST); GUI_SetMouseMode (XBFalse); /* now start */ /* GAMEONFIX */ Server_RestartNewGame(CurrentResult()); /* GAMEONFIX */ do { /* * INGAME mode */ /* ready input */ ClearPlayerAction (serverAction); /* handle all event until timer triggers */ if (! GameEventLoop (XBE_TIMER, &eData) ) { goto Exit; } /* increment game clock */ gameTime ++; /* GAMEONFIX */ if((gameTime % 1024)==0) { Server_RestartNewGame(CurrentResult()); } /* GAMEONFIX */ /* bot */ counter=0; for (ps = player_stat,counter=1; ps < player_stat + serverGame.players.num; ps ++,counter++) { if (ps->local) { break; } } if(serverGame.setup.bot||ps->bot==XBTrue){ fprintf(stderr," bot in server \n"); gestionBot (player_stat,serverAction,counter-1,serverGame.players.num); } /* handle game turn */ GameTurn (gameTime, serverGame.players.num, &numActive); /* insert any data received from clients */ InsertClientAction (&serverGame.players, serverAction); /* send all data on player actions to clients */ Server_SendPlayerAction (gameTime, serverAction); /* record demo data if neede */ if (serverGame.setup.recordDemo) { DemoRecordFrame (gameTime, serverAction); } /* evaluate player action */ (void) GameEvalAction (serverGame.players.num, serverAction); /* update window */ GameUpdateWindow (); } while (gameTime < GAME_TIME && numActive > 0 && (numActive > 1 || NumberOfExplosions () != 0) ); /* tell client game ist over */ Server_FinishPlayerAction (gameTime + 1); /* calc result for async check, XBFalse to not store the game and not cause draws or false wins */ LevelResult (gameTime, &lastTeam, serverGame.players.num, level, XBFalse); event=XBNW_P0; if (lastTeam <= MAX_PLAYER) { for (ps = player_stat,counter=1; ps < player_stat +serverGame.players.num; ps ++,counter++) { if (ps->team == lastTeam) { event=XBNW_P0+counter; winner=counter; } } } /* finisg demo file if needed */ if (serverGame.setup.recordDemo) { DemoFinishLevel (gameTime,winner); } /* wait for clients to acknowledge */ // SyncWithClients (XBNW_SyncLevelResult, XBTrue, XBFalse); /* check if all clients are sync with server, if true store level, else send async for all and not store level */ Dbg_Out("Level End wainting event %i \n",event); if(SyncWithClients (event, XBTrue, XBFalse)){ msg = LevelResult (gameTime, &lastTeam, serverGame.players.num, level, XBTrue); /* show message and winner Animation */ if (! LevelEnd (serverGame.players.num, lastTeam, msg, XBTrue) ) { lastTeam = -1; } } /* clean up */ Exit: FinishLevel (); DeleteAllExplosions (); /* fade out image */ DoFade (XBFM_BLACK_OUT, PIXH+1); /* thatīs all */ return lastTeam; } /* ServerRunLevel */ /* * send level data to clients */ static void SendLevelToClients (const DBRoot *level) { /* send level data to clients */ Server_SendLevel (level); /* TODO wait for acknowledge */ } /* SendLevelToClients */ /* * */ void RunServerGame (void) { const DBRoot *level; int lastTeam; int teamActive; int i, maxNumWins; int numActive; int reinco,winner; int pa[MAX_PLAYER]; XBBool initDone = XBFalse; XBBool centralConnect=XBFalse; // XBCC CFGCentralSetup central; // XBCC /* get setup */ if (! RetrieveGame (CT_Remote, atomArrayHost0[0], &serverGame) ) { goto Exit; } /* select levels to plays */ if (! InitLevels (&serverGame) ) { goto Exit; } /* common inits */ if (! InitGame (XBPH_Server, CT_Remote, &serverGame, serverAction)) { goto Exit; } initDone = XBTrue; /* init connection status */ InitPlayerLink (); /* network sync */ SyncWithClients (XBNW_SyncEndOfInit, XBFalse, XBFalse); /* game loop */ maxNumWins = 0; numActive = serverGame.players.num; teamActive=0; if(serverGame.setup.teamMode) { reinco=0; for (i = 0; i < serverGame.players.num; i ++) { pa[i]=!player_stat[i].in_active; if (! player_stat[i].in_active) { if(!(reinco&(1 << player_stat[i].team))) { reinco|=1 << player_stat[i].team; teamActive++; } } } if(teamActive <= 1) { GUI_ErrorMessage ("Only one team you dumb fuck!"); return; } } else { for (i = 0; i < serverGame.players.num; i ++) { pa[i]=!player_stat[i].in_active; if (! player_stat[i].in_active) { teamActive ++; } } } Dbg_Out ("There are according to reinco %i teams\n", teamActive); /* Connect to central */ Dbg_Out("Game is %s\n", serverGame.setup.rated ? "rated" : "unrated"); if(serverGame.setup.rated) { RetrieveCentralSetup (¢ral); if(User_Connect(¢ral)) { Dbg_Out("Connection to central established\n"); centralConnect=XBTrue; } else { centralConnect=XBFalse; Dbg_Out("--------------------------------------\n"); Dbg_Out("*** Unable to connect to central ***\n"); Dbg_Out("*** The game will not be registred ***\n"); Dbg_Out("--------------------------------------\n"); } } else { centralConnect=XBFalse; } do { /* load and run next level */ level = LoadLevelFile (GetNextLevel ()); Dbg_Out ("Level playing is: %s\n", GetLevelName(level)); SeedRandom(time(NULL)); SendLevelToClients (level); // lastTeam = ServerRunLevel (numActive, level); lastTeam = ServerRunLevel (teamActive, level); /* check for quick exit */ if (-1 == lastTeam) { goto Exit; } /* calculate new maxmium # of victories */ for (i = 0; i < serverGame.players.num; i ++) { if (player_stat[i].victories > maxNumWins) { maxNumWins = player_stat[i].victories; winner=i; } } /* sync before showing score board */ SyncWithClients (XBNW_SyncLevelEnd, XBFalse, XBTrue); /* send level stats to central */ if(centralConnect) { Dbg_Out ("Sending level results to central\n"); User_SendGameStat(serverGame.players.num, player_stat, pa); } /* correct number of players */ UpdatePlayerLink (); /* show scores */ if (! ShowScoreBoard (lastTeam, maxNumWins, serverGame.players.num, player_stat, XBTrue)) { goto Exit; } /* sync after showing score board */ SyncWithClients (XBNW_SyncScoreboard, XBFalse, XBTrue); /* determine number of active players */ numActive = 0; teamActive =0; reinco=0; for (i = 0; i < serverGame.players.num; i ++) { pa[i]=!player_stat[i].in_active; if (! player_stat[i].in_active) { numActive ++; if(serverGame.setup.teamMode) { if(!(reinco&(1 << player_stat[i].team))) { reinco|=1 << player_stat[i].team; teamActive++; } } else { teamActive++; } } } Dbg_Out ("%d active teams\n", teamActive); Dbg_Out ("%d active players\n", numActive); } while (numActive > 1 && teamActive > 1 && maxNumWins < serverGame.setup.numWins); /* close connection to clients */ Server_SendDisconnectAll (); /* and the winner is ... */ if (maxNumWins >= serverGame.setup.numWins) { if(centralConnect) { // XBST Dbg_Out ("Sending game results to central\n"); for (i = 0; i < serverGame.players.num; i ++) { pa[i]=1; if (player_stat[i].victories == serverGame.setup.numWins) { player_stat[i].lives=-player_stat[i].victories; } else { player_stat[i].lives=player_stat[i].victories; } } User_SendGameStat(-serverGame.players.num, player_stat, pa); } InitWinner (serverGame.players.num); ShowWinner (lastTeam, serverGame.players.num, player_stat); } else { GUI_ErrorMessage ("Not enough players left in the game"); } /* that's all */ if(centralConnect) { User_SendDisconnect(); User_Disconnect(); centralConnect=XBFalse; } FinishGame (&serverGame); return; /* * fast exit via Escape key ... */ Exit: Dbg_Out ("aborting server game\n"); Server_SendDisconnectAll (); if(centralConnect) { User_SendDisconnect(); User_Disconnect(); centralConnect=XBFalse; } if (initDone) { FinishGame (&serverGame); } return; } /* StartServerGame */ /* * end of file game_server.c */