/* * file server.c - communication interface for the server * * $Id: server.c,v 1.5 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 "server.h" #ifdef WMS #include "timeval.h" #endif #include "cfg_player.h" #include "com_listen.h" #include "com_to_client.h" #include "com_dg_client.h" #include "com_reply.h" #include "random.h" #include "com_newgame.h" // XBCC #include "cfg_xblast.h" // XBCC /* * local variables */ static XBComm *listenComm = NULL; static XBComm *replyComm = NULL; static XBComm **query = NULL; // XBCC static XBBool fixedUdpPort = XBFalse; static XBBool allowNat = XBFalse; static PlayerAction playerAction[MAX_HOSTS][MAX_PLAYER]; /* * Start to listen for clients */ XBBool Server_StartListen (CFGGameHost *cfg) { CFGGame cfgGame; CFGPlayer cfgPlayer; int i; /* prepare database */ /* Step 1: game setup */ if (RetrieveGame (CT_Local, atomServer, &cfgGame) ) { StoreGame (CT_Remote, atomLocal, &cfgGame); } /* Step 2: player data */ for (i = 0; i < cfgGame.players.num; i ++) { Network_SetPlayer (0, i, cfgGame.players.player[i]); if (ATOM_INVALID != cfgGame.players.player[i]) { if (RetrievePlayer (CT_Local, cfgGame.players.player[i], cfgGame.players.teamColor[i], &cfgPlayer)) { StorePlayer (CT_Remote, cfgGame.players.player[i], &cfgPlayer); } } } for (; i < NUM_LOCAL_PLAYER; i ++) { Network_SetPlayer (0, i, ATOM_INVALID); } /* listen on tcp-port for clients */ assert (listenComm == NULL); listenComm = CommCreateListen (cfg, XBFalse); // XBCC not central if (NULL == listenComm) { return XBFalse; } /* allow client to browse for games */ if (cfg->browseLan) { assert (NULL == replyComm); replyComm = Reply_CreateComm (16168, cfg, &cfgGame.setup); if (NULL == replyComm) { Dbg_Out ("failed to open reply socket\n"); } } if (query!=NULL) { Server_StopNewGame(); // see Server_StopListen } if (cfg->central) { Server_StartCentralNewGame(cfg, &cfgGame.setup); } /* flag for fixed udp-ports */ fixedUdpPort = cfg->fixedUdpPort; /* flag to allow clients using NAT */ allowNat = cfg->allowNat; /* that'S all */ return XBTrue; } /* Server_StartListen */ /* * delete port for listening */ void Server_StopListen (void) { /* delete listen port */ assert (NULL != listenComm); CommDelete (listenComm); listenComm = NULL; /* delete reply socket */ if (NULL != replyComm) { CommDelete (replyComm); replyComm = NULL; } if (query!=NULL) { Server_CloseNewGame(); // Server_StopNewGame(); // for some reason if closed now socket does not flush } } /* Server_StopListen */ /* * disconnect from clients */ void Server_SendDisconnect (unsigned clientID) { assert (clientID > 0); assert (clientID < MAX_HOSTS); /* disconnect from client */ if (S2C_Connected (clientID) ) { S2C_Disconnect (clientID); } if (D2C_Connected (clientID) ) { D2C_Disconnect (clientID); } } /* Server_SendDisconnect */ /* * disconnect from clients */ void Server_SendDisconnectAll (void) { unsigned clientID; /* disconnect from client */ for (clientID = 1 ; clientID < MAX_HOSTS; clientID ++) { Server_SendDisconnect (clientID); } } /* Server_SendDisconnectAll */ /* * a client has connected */ void Server_Accept (unsigned id, const char *hostName, unsigned port) { CFGGameHost cfg; unsigned client; int player; XBAtom atom; assert (hostName != NULL); assert (id > 0); assert (id < MAX_HOSTS); Dbg_Out ("client adr=%s:%u id=%u connected\n", hostName, port, id); /* clear host entry in database */ DeleteGameConfig (CT_Remote, atomArrayHost0[id]); /* store in database */ memset (&cfg, 0, sizeof (cfg)); cfg.name = hostName; cfg.port = port; StoreGameHost (CT_Remote, atomArrayHost0[id], &cfg); /* create message */ Network_QueueEvent (XBNW_Accepted, id); /* 2. send configuration of other clients to this client */ for (client = 0; client < MAX_HOSTS; client ++) { if (client == 0 || S2C_Connected (client) ) { /* 2a game players */ S2C_SendGameConfig (id, client, client ? atomArrayHost0[client] : atomLocal); /* detailed player setup */ for (player = 0; player < NUM_LOCAL_PLAYER; player ++) { atom = Network_GetPlayer (client, player); if (ATOM_INVALID != atom) { S2C_SendPlayerConfig (id, client, player, atom); // XBCC } } } } /* 3. create udp connection for client */ D2C_CreateComm (id, S2C_LocalName (id), fixedUdpPort); S2C_SendDgramPort (id, D2C_Port (id)); /* 4. query client player config */ S2C_QueryGameConfig (id); } /* Server_ClientAccepted */ /* * game config retrieved from client */ void Server_ReceiveGameConfig (unsigned id, const char *line) { XBAtom atom; unsigned client; int player, numPlayers; atom = Network_ReceiveGameConfig (id, line, &numPlayers); /* check if data is complete */ if (ATOM_INVALID != atom) { for (client = 1; client < MAX_HOSTS; client ++) { if (S2C_Connected (client) ) { /* 1. send game config to other clients */ S2C_SendGameConfig (client, id, atom); } } /* 2. query player config */ for (player = 0; player < numPlayers; player ++) { DeletePlayerConfig (CT_Demo, Network_GetPlayer (id, player)); S2C_QueryPlayerConfig (id, player); } } } /* Server_ReceiveGameConfig */ /* * player config received from client */ void Server_ReceivePlayerConfig (unsigned id, int player, const char *line) { unsigned client; XBAtom atom; /* store player for config */ atom = Network_ReceivePlayerConfig (CT_Remote, id, player, line); // XBCC /* if atom is valid, data is complete */ if (ATOM_INVALID != atom) { /* 2. send player config to other clients */ for (client = 1; client < MAX_HOSTS; client ++) { if (S2C_Connected (client) ) { S2C_SendPlayerConfig (client, id, player, atom); } } } } /* Server_ClientPlayerConfig */ /* * a client has connected */ void Server_ReceiveDisconnect (unsigned id) { unsigned clientID; /* send disconnect message to other clients */ for (clientID = 1 ; clientID < MAX_HOSTS; clientID ++) { if (clientID != id && S2C_Connected (clientID) ) { S2C_HostDisconnected (clientID, id); } } /* delete datagram connection */ if (D2C_Connected (id) ) { D2C_Disconnect (id); } /* create message */ Network_QueueEvent (XBNW_Disconnected, id); Dbg_Out ("client id=%u disconnected\n", id); } /* Server_ClientAccepted */ /* * client has sent dgram port */ void Server_ReceiveDgramPort (unsigned id, unsigned short port) { /* set port for datagram conection */ if (0 != port || allowNat) { D2C_Connect (id, S2C_HostName (id), port); } else { Server_SendDisconnect (id); } } /* Server_ClientDgramPort */ /* * start game on client */ void Server_SendStart (unsigned id) { /* send game config to client */ S2C_SendGameConfig (id, 0, atomArrayHost0[0]); /* send start signal to client */ S2C_StartGame (id); } /* Server_StartClient */ /* * send level data to clients */ void Server_SendLevel (const DBRoot *level) { unsigned client; unsigned seed = GetRandomSeed (); for (client = 1; client < MAX_HOSTS; client ++) { if (S2C_Connected (client) ) { S2C_SendRandomSeed (client, seed); S2C_SendLevelConfig (client, level); } } } /* Server_ClientLevelConfig */ /* * reset datagrams connections for new level */ void Server_ResetPlayerAction (void) { unsigned client; for (client = 1; client < MAX_HOSTS; client ++) { if (D2C_Connected (client) ) { D2C_Reset (client); } } } /* Server_SendPlayerAction */ /* * send player action to client */ void Server_SendPlayerAction (int gameTime, const PlayerAction *playerAction) { unsigned client; for (client = 1; client < MAX_HOSTS; client ++) { if (D2C_Connected (client) ) { D2C_SendPlayerAction (client, gameTime, playerAction); } } } /* Server_SendPlayerAction */ /* * send finish player actions (= end of level) to clients */ void Server_FinishPlayerAction (int gameTime) { unsigned client; for (client = 1; client < MAX_HOSTS; client ++) { if (D2C_Connected (client) ) { D2C_SendFinish (client, gameTime); } } } /* Server_SendPlayerAction */ /* * flush last player actions */ XBBool Server_FlushPlayerAction (void) { XBBool result; unsigned client; result = XBTrue; for (client = 1; client < MAX_HOSTS; client ++) { if (D2C_Connected (client) ) { if (! D2C_Flush (client)) { result = XBFalse; } } } return result; } /* Server_FlushPlayerAction */ /* * client is ready to start current level */ void Server_ReceiveSync (unsigned id, XBNetworkEvent event) { /* inform application */ Network_QueueEvent (event, id); } /* Server_ClientStartLevel */ /* * send all clients start level message */ void Server_SendSync (XBNetworkEvent event) { unsigned clientID; /* to all connected client */ for (clientID = 1 ; clientID < MAX_HOSTS; clientID ++) { if (S2C_Connected (clientID) ) { S2C_Sync (clientID, event); } } } /* Server_SendStartLevel */ /* * send host state to clients */ void Server_SendHostState (unsigned id, XBBool isIn) { unsigned clientID; for (clientID = 1 ; clientID < MAX_HOSTS; clientID ++) { if (S2C_Connected (clientID) ) { S2C_SendHostState (clientID, id, isIn); } } } /* Server_SendHostState */ /* * send team state to clients */ void Server_SendTeamState (unsigned id, unsigned team) { unsigned clientID; for (clientID = 1 ; clientID < MAX_HOSTS; clientID ++) { if (S2C_Connected (clientID) ) { S2C_SendTeamState (clientID, id, team); } } } /* Server_SendHostState */ /* * received level finish from clients */ void Server_ReceiveFinish (unsigned id) { } /* Server_ReceiveFinish */ /* * received keys from one client */ void Server_ReceivePlayerAction (unsigned id, int gameTime, const PlayerAction *keys) { int i; assert (id > 0); assert (id < MAX_HOSTS); assert (playerAction != NULL); for (i = 0; i < MAX_PLAYER; i ++) { if (keys[i].dir != GoDefault) { playerAction[id][i].dir = keys[i].dir; } if (keys[i].bomb) { playerAction[id][i].bomb = XBTrue; } if (keys[i].special) { playerAction[id][i].special = XBTrue; } if (keys[i].pause) { playerAction[id][i].pause = XBTrue; } if (keys[i].abort != ABORT_NONE) { playerAction[id][i].abort = keys[i].abort; } /* Skywalker */ if (keys[i].laola) { playerAction[id][i].laola = XBTrue; }else { playerAction[id][i].laola = XBFalse; if (keys[i].looser ) { playerAction[id][i].looser = XBTrue; } else{playerAction[id][i].looser = XBFalse; } } /* */ } } /* Server_ClientKeys */ /* * */ void Server_GetPlayerAction (unsigned id, int player, PlayerAction *action) { assert (id > 0); assert (id < MAX_HOSTS); assert (player < MAX_PLAYER); assert (playerAction != NULL); /* copy data */ *action = playerAction[id][player]; } /* Server_GetPlayerAction */ /* * clear player action data */ void Server_ClearPlayerAction (void) { unsigned id; int player; for (id = 0; id < MAX_HOSTS; id ++) { for (player = 0; player < MAX_PLAYER; player ++) { playerAction[id][player].player = player; playerAction[id][player].dir = GoDefault; playerAction[id][player].bomb = XBFalse; playerAction[id][player].special = XBFalse; playerAction[id][player].pause = XBFalse; playerAction[id][player].abort = ABORT_NONE; } } } /* Server_ClearPlayerAction */ /* * */ void Server_NotifyError (unsigned clientID) { assert (clientID > 0); assert (clientID < MAX_HOSTS); if (S2C_Connected (clientID) ) { S2C_Disconnect (clientID); } if (D2C_Connected (clientID) ) { D2C_Disconnect (clientID); } /* inform application */ Network_QueueEvent (XBNW_Error, clientID); } /* Client_NotifyError */ /* * last ping time of client */ int Server_GetPingTime (unsigned clientID) { assert (clientID > 0); assert (clientID < MAX_HOSTS); if (! D2C_Connected (clientID) ) { return -1; } return (int) D2C_LastPing (clientID); } /* Server_PingTime */ /* * ping recieved => inform application */ void Server_ReceivePing (unsigned clientID) { Network_QueueEvent (XBNW_PingReceived, clientID); } /* Server_ReceivePing */ /* * XBCC Search central query */ void Server_StartCentralNewGame (const CFGGameHost *cfg, const CFGGameSetup *setup) { size_t numInter; const XBSocketInterface *inter; size_t i, j; CFGCentralSetup centralSetup; assert (NULL == query); inter = Socket_GetInterfaces (&numInter); if (NULL == inter) { return; } RetrieveCentralSetup (¢ralSetup); if (NULL == centralSetup.name) { return; } /* alloc 1 pointer (to central) */ query = calloc (1 + numInter, sizeof (XBComm *)); assert (NULL != query); Dbg_Out("Connecting to central %s:%i\n", centralSetup.name, centralSetup.port); /* start query on one device */ Dbg_Out("numInter = %d\n",numInter); /* FIXXX used to be i=1 worked on linux */ #ifdef W32 Dbg_Out("W32\n"); for (i = 0, j = 0; (j == 0) && (i < numInter); i ++) { #else Dbg_Out("Linux\n"); for (i = 1, j = 0; (j == 0) && (i < numInter); i ++) { #endif if (NULL != (query[j] = NewGame_CreateComm (inter[i].addrDevice, centralSetup.port, centralSetup.name, cfg, setup) ) ) { Dbg_Out ("query %i, %s\n", i, centralSetup.name); j ++; } } // Server_RestartNewGame ()x; } /* Server_StartCentralQuery */ /* * restart newgame */ /* GAMEONFIX */ void Server_RestartNewGame (const char *busy) { if (NULL != query) { struct timeval tv; int i; gettimeofday (&tv, NULL); for (i = 0; query[i] != NULL; i ++) { NewGame_Send (query[i], &tv, busy); } } } /* Server_RestartQuery */ /* GAMEONFIX */ /* * restart close newgame */ void Server_CloseNewGame (void) { if (NULL != query) { struct timeval tv; int i; gettimeofday (&tv, NULL); for (i = 0; query[i] != NULL; i ++) { NewGame_Close (query[i], &tv); } } } /* Server_RestartQuery */ /* * */ void Server_StopNewGame (void) { size_t i; Dbg_Out("Close connection to central\n"); /* delete communications */ if (NULL == query) { return; } for (i = 0; query[i] != NULL; i ++) { CommDelete (query[i]); } free (query); query = NULL; } /* Server_StopQuery */ /* * end of file server.c */