/* * file client.c - communication interface for clients * * $Id: client.c,v 1.5 2004/06/26 03:20:15 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 "client.h" #include "atom.h" #include "com_to_server.h" #include "com_dg_server.h" #include "com_query.h" #include "cfg_level.h" #include "util.h" #include "str_util.h" #include "random.h" #ifdef WMS #include "timeval.h" #endif #include "cfg_xblast.h" /* * local macros */ #define TIME_POLL_QUERY 5 /* * local types */ typedef struct _xb_network_game_list XBNetworkGameList; struct _xb_network_game_list { XBNetworkGameList *next; XBNetworkGame game; }; /* * local variables */ static XBComm *comm = NULL; static XBComm *dgram = NULL; static XBComm **query = NULL; static XBNetworkGameList *firstGame = NULL; static XBNetworkGameList *lastGame = NULL; static XBNetworkGameList *nextGame = NULL; static unsigned numGames = 0; static int pingTime[MAX_HOSTS]; static PlayerAction playerAction[GAME_TIME+1][MAX_PLAYER]; /* * local prototypes */ static void PollDatagram (const struct timeval *tv); /* * try to connect to server */ XBBool Client_Connect (CFGGameHost *cfg) { /* inits */ memset (pingTime, 0xFF, sizeof (pingTime)); /* create communincation */ assert (comm == NULL); comm = C2S_CreateComm (cfg); if (NULL == comm) { return XBFalse; } return XBTrue; } /* Client_Connect */ /* * disconnect from server */ void Client_Disconnect () { if (comm != NULL) { CommDelete (comm); comm = NULL; } if (dgram != NULL) { /* stop polling */ GUI_SubtractPollFunction (PollDatagram); /* delete connection */ CommDelete (dgram); dgram = NULL; } } /* Client_Disconnect */ /* * */ void Client_SetDisconnected () { comm = NULL; } /* Client_SetDisonnect */ /* * */ void Client_NotifyError (void) { /* shutdown datagram connection */ if (dgram != NULL) { /* stop polling */ GUI_SubtractPollFunction (PollDatagram); /* delete connection */ CommDelete (dgram); dgram = NULL; } /* inform application */ Network_QueueEvent (XBNW_Error, 0); GUI_SendEventValue (XBE_SERVER, XBSE_ERROR); } /* Client_NotifyError */ /* * receive game config from server */ void Client_ReceiveGameConfig (unsigned id, const char *data) { CFGGameHost cfgHost; CFGGameSetup cfgLocal; CFGGameSetup cfgRemote; XBAtom atom; int numPlayers; atom = Network_ReceiveGameConfig (id, data, &numPlayers); /* check if dat is complete */ if (ATOM_INVALID != atom) { /* changes for server game config */ if (id == 0) { /* set correct hostname for server */ if (RetrieveGameHost (CT_Remote, atom, &cfgHost) ) { cfgHost.name = C2S_ServerName (comm); StoreGameHost (CT_Remote, atom, &cfgHost); } /* set local overrides to game setup */ if (RetrieveGameSetup (CT_Remote, atom, &cfgRemote) && RetrieveGameSetup (CT_Local, atomClient, &cfgLocal) ) { cfgRemote.recordDemo = cfgLocal.recordDemo; cfgRemote.bot = cfgLocal.bot; StoreGameSetup (CT_Remote, atom, &cfgRemote); } } } } /* Client_ServerGameConfig */ /* * receive player config from server */ void Client_ReceivePlayerConfig (unsigned id, unsigned player, const char *data) { /* (void) Network_ReceivePlayerConfig (id, player, data); */ (void) Network_ReceivePlayerConfig (CT_Remote, id, player, data); // XBCC } /* Client_ReceivePlayerConfig */ /* * receive level config from server */ void Client_ReceiveLevelConfig (unsigned iob, const char *data) { if (NULL != data) { AddToRemoteLevelConfig (iob, data); return; } /* yes, all data received */ Dbg_Out ("server send level config\n"); Network_QueueEvent (XBNW_LevelConfig, 0); } /* Client_ServerLevelConfig */ /* * another peer has diconnected */ void Client_ReceiveDisconnect (unsigned id) { Network_QueueEvent (XBNW_Disconnected, id); } /* Client_PeerDisconnected */ /* * server had sent datagram port */ void Client_ReceiveDgramPort (unsigned id, unsigned short port) { CFGGameHost cfgHost; XBBool usingNat = XBFalse; assert (comm != NULL); assert (dgram == NULL); /* first check if we are using NAT to get to the server */ if (id != 0 && id < MAX_HOSTS) { RetrieveGameHost (CT_Remote, atomArrayHost0[id], &cfgHost); if (0 == strcmp (cfgHost.name, C2S_ServerName (comm))) { usingNat = XBTrue; } } #ifdef DEBUG_NAT usingNat = XBTrue; #endif dgram = D2S_CreateComm (C2S_ClientName (comm), C2S_ServerName (comm), port); if (NULL == dgram) { /* error we disconnect */ CommDelete (comm); return; } if (! usingNat) { /* send port to server */ C2S_SendDgramPort (comm, D2S_Port (dgram)); } else { Dbg_Out ("using NAT\n"); /* send "any port" to notify use of n.a.t. */ C2S_SendDgramPort (comm, 0); } /* poll connection */ GUI_AddPollFunction (PollDatagram); } /* Client_SetDgramPort */ /* * server has requested game start */ void Client_ReceiveStart (unsigned id) { Network_QueueEvent (XBNW_StartGame, id); } /* Client_ReceiveStart */ /* * server hast sent new seed for random number generator */ void Client_ReceiveRandomSeed (unsigned seed) { SeedRandom (seed); } /* Client_ReceiveRandomSeed */ /* * server has requested sync */ void Client_ReceiveSync (XBNetworkEvent event) { /* TODO filter events */ Network_QueueEvent (event, 0); } /* Client_StartGame */ /* * tell server we are finished with level intro */ void Client_SendSync (XBNetworkEvent event) { if (NULL != comm) { C2S_Sync (comm, event); } } /* Client_StartLevel */ /* * server has send host state */ void Client_ReceiveHostState (unsigned id, XBBool isIn) { if (isIn) { Network_QueueEvent (XBNW_HostIsIn, id); } else { Network_QueueEvent (XBNW_HostIsOut, id); } } /* Client_ReceiveHostState */ /* * server has send team state */ void Client_ReceiveTeamState (unsigned id, unsigned team) { Network_QueueEvent (XBNW_TeamChange, id); Network_QueueEvent (XBNW_TeamChangeData, team); } /* Client_ReceiveTeamState */ /*------------------------------------------------------------------------ * * Datagrams from server * *------------------------------------------------------------------------*/ /* * server */ void Client_ReceiveFinish () { /* inform application */ GUI_SendEventValue (XBE_SERVER, XBSE_FINISH); } /* Client_ServerFinish */ /* * server has send keys for new frame */ void Client_ReceivePlayerAction (int gameTime, const PlayerAction *keys) { assert (gameTime <= GAME_TIME); /* inform application */ GUI_SendEventValue (XBE_SERVER, gameTime); /* copy keys (simple version) */ memcpy (&playerAction[gameTime][0], keys, MAX_PLAYER*sizeof (PlayerAction)); } /* Client_ServerKeys */ /* * get server keys for new frame */ void Client_GetPlayerAction (int gameTime, PlayerAction *keys) { assert (gameTime <= GAME_TIME); /* copy keys (simple version) */ memcpy (keys, &playerAction[gameTime][0], MAX_PLAYER*sizeof (PlayerAction)); } /* Client_ServerKeys */ /* * received ping time for one client */ void Client_ReceivePingTime (unsigned clientID, int _pingTime) { assert (clientID < MAX_HOSTS); /* check value */ if (pingTime[clientID] != _pingTime) { pingTime[clientID] = _pingTime; /* inform application */ Network_QueueEvent (XBNW_PingReceived, clientID); } } /* Client_ReceivePingTime */ /* * ping time of server to given host */ int Client_GetPingTime (unsigned clientID) { assert (clientID < MAX_HOSTS); return pingTime[clientID]; } /* Client_GetPingTime */ /*------------------------------------------------------------------------ * * Datagrams to server * *------------------------------------------------------------------------*/ /* * polling for datagram connections */ static void PollDatagram (const struct timeval *tv) { if (! D2S_Connected (dgram)) { D2S_SendConnect (dgram); } if (NULL != dgram && D2S_Timeout (dgram, tv)) { /* disconnect from server */ Client_NotifyError (); } } /* PollDatagram */ /* * reset datagram connection */ void Client_ResetPlayerAction (void) { if (NULL != dgram) { D2S_Reset (dgram); } } /* Client_ResetPlayerAction */ /* * send own keys to server */ void Client_SendPlayerAction (int gameTime, const PlayerAction *keys) { D2S_SendPlayerAction (dgram, gameTime, keys); } /* Client_SendKeys */ /* * send end of level acknowldgement */ void Client_FinishPlayerAction (int gameTime) { D2S_SendFinish (dgram, gameTime); } /* Client_FinishPlayerAction */ /* * flush out remaing data */ XBBool Client_FlushPlayerAction (void) { return D2S_Flush (dgram); } /* Client_FlushPlayerAction */ /*------------------------------------------------------------------------* * * query for local and remote games * *------------------------------------------------------------------------*/ /* * delete current list of network games */ static void DeleteGameList (void) { XBNetworkGameList *next; while (firstGame != NULL) { next = firstGame->next; /* delete data */ free (firstGame->game.host); free (firstGame->game.game); free (firstGame->game.version); free (firstGame); firstGame = next; } lastGame = NULL; nextGame = NULL; numGames = 0; } /* DeleteGameList */ /* * Search lan query */ void Client_StartQuery (void) { size_t numInter; const XBSocketInterface *inter; size_t i, j; assert (NULL == query); inter = Socket_GetInterfaces (&numInter); if (NULL == inter) { return; } /* alloc maximum possible pointers */ query = calloc (1 + numInter, sizeof (XBComm *)); assert (NULL != query); /* start query on each broadcast device */ for (i = 0, j = 0; i < numInter; i ++) { if (NULL != inter[i].addrBroadcast && NULL != (query[j] = Query_CreateComm (inter[i].addrDevice, 16168, inter[i].addrBroadcast) ) ) { Dbg_Out ("query %i, %s\n", i, inter[i].addrBroadcast); j ++; } } Client_RestartQuery (); } /* Client_StartQuery */ /* * XBCC Search central query */ void Client_StartCentralQuery (void) { 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); // Dbg_Out("Connecting to central %s:%i\n", centralSetup.name, 16168); /* start query on one device */ #ifdef W32 Dbg_Out("W32\n"); for (i = 0, j = 0; i < numInter; i ++) { #else Dbg_Out("Linux\n"); for (i = 1, j = 0; i < numInter; i ++) { #endif if (NULL != (query[j] = Query_CreateComm (inter[i].addrDevice, centralSetup.port, centralSetup.name) ) ) { // if (NULL != (query[j] = Query_CreateComm (inter[i].addrDevice, 16168, centralSetup.name) ) ) { Dbg_Out ("query %i, %s\n", i, centralSetup.name); j ++; } } Client_RestartQuery (); } /* Client_StartCentralQuery */ /* * restart query */ void Client_RestartQuery (void) { DeleteGameList (); if (NULL != query) { struct timeval tv; int i; gettimeofday (&tv, NULL); for (i = 0; query[i] != NULL; i ++) { Query_Send (query[i], &tv); } } } /* Client_RestartQuery */ /* * */ void Client_StopQuery (void) { size_t i; DeleteGameList (); /* delete communications */ if (NULL == query) { return; } for (i = 0; query[i] != NULL; i ++) { CommDelete (query[i]); } free (query); query = NULL; } /* Client_StopQuery */ /* * receive reply from a game server */ void Client_ReceiveReply (const char *host, unsigned short port, int ping, const char *version, const char *game, int numLives , int numWins , int frameRate) { XBNetworkGameList *ptr; /* alloc data */ ptr = calloc (1, sizeof (*ptr)); assert (NULL != ptr); /* fill in data */ ptr->game.host = DupString (host); ptr->game.port = port; ptr->game.ping = ping; ptr->game.version = DupString (version); ptr->game.game = DupString (game); ptr->game.numLives = numLives; ptr->game.numWins = numWins; ptr->game.frameRate = frameRate; /* append to list */ if (NULL == lastGame) { firstGame = lastGame = nextGame = ptr; } else { lastGame->next = ptr; lastGame = ptr; } /* inform application */ Network_QueueEvent (XBNW_NetworkGame, numGames ++); } /* Client_ReceiveReply */ /* * game info for application */ const XBNetworkGame * Client_FirstNetworkGame (unsigned index) { unsigned i; const XBNetworkGame *ptr; /* find index-th game */ nextGame = firstGame; for (i = 0; i < index; i ++) { if (nextGame == NULL) { return NULL; } nextGame = nextGame->next; } if (nextGame == NULL) { return NULL; } ptr = &nextGame->game; nextGame = nextGame->next; return ptr; } /* Client_FirstNetworkGame */ /* * game info for application */ const XBNetworkGame * Client_NextNetworkGame (void) { const XBNetworkGame *ptr; if (NULL == nextGame) { return NULL; } ptr = &nextGame->game; nextGame = nextGame->next; return ptr; } /* Client_NextNetworkGame */ /* * end of file client.c */