/* * file com_dg_client.c - send datagrams to client * * $Id: com_dg_client.c,v 1.3 2004/05/14 10:00:33 alfie 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 "com_dg_client.h" #include "com_dgram.h" #include "net_socket.h" #include "server.h" /* * local macros */ #define CLIENT_UDP_PORT(id) (16168+(id)) /* * local types */ typedef struct { XBCommDgram dgram; unsigned id; long ping; } XBCommDgramClient; /* * local variables */ static XBCommDgramClient *commList[MAX_HOSTS] = { /* this entry is never used (server) */ NULL, /* up to 5 clients can connect */ NULL, NULL, NULL, NULL, NULL, }; static int commCount = 0; /* * calculate difference for two timevals */ static void DiffTimeVal (struct timeval *diff, const struct timeval *a, const struct timeval *b) { assert (NULL != diff); assert (NULL != a); assert (NULL != b); diff->tv_sec = a->tv_sec - b->tv_sec; if (a->tv_usec < b->tv_usec) { diff->tv_usec = 1000000 + a->tv_usec - b->tv_usec; diff->tv_sec --; } else { diff->tv_usec = a->tv_usec - b->tv_usec; } } /* DiffTimeVal */ /* * polling for datagram connections */ static void PollDatagram (const struct timeval *tv) { int i, j; struct timeval dtSnd; struct timeval dtRcv; XBBool initPingTime = XBFalse; int pingTime[MAX_HOSTS]; assert (NULL != tv); for (i = 1; i < MAX_HOSTS; i ++) { if (NULL != commList[i] && commList[i]->dgram.connected) { /* when was last datagram send */ DiffTimeVal (&dtSnd, tv, &commList[i]->dgram.lastSnd); DiffTimeVal (&dtRcv, tv, &commList[i]->dgram.lastRcv); /* send a ping to client, when no datagram was send for over 500ms */ if (dtSnd.tv_sec >= 1 || dtSnd.tv_usec > 500000) { if (! initPingTime) { pingTime[0] = -1; for (j = 1; j < MAX_HOSTS; j ++) { pingTime[j] = commList[j] ? commList[j]->ping : -1; } initPingTime = XBTrue; } Dgram_SendPingData (&commList[i]->dgram, pingTime); } /* check last chance to send datagram */ if ( ( 0 != commList[i]->dgram.lastSnd.tv_sec && dtSnd.tv_sec > LINK_LOST ) || ( 0 != commList[i]->dgram.lastRcv.tv_sec && dtRcv.tv_sec > LINK_LOST ) ) { /* inform application */ Server_NotifyError (i); } } } } /* PollDatagram */ /* * */ static void ReceivePing (XBCommDgram *dgram, unsigned clientID, unsigned short pingTime) { struct timeval tvPing; XBCommDgramClient *dComm = (XBCommDgramClient *) dgram; /* we do no evaluate pings with data */ if (0 == clientID) { assert (dComm != NULL); DiffTimeVal (&tvPing, &dgram->lastRcv, &dgram->lastSnd); dComm->ping = 1000L * tvPing.tv_sec + (tvPing.tv_usec) / 1000L; #ifdef DEBUG fprintf (stderr, "ping (%u) = %lu ms\n", dComm->id, dComm->ping); #endif /* inform application */ Server_ReceivePing (dComm->id); } } /* ReceivePing */ /* * */ static void ReceiveFinish (XBCommDgram *dgram) { XBCommDgramClient *dComm = (XBCommDgramClient *) dgram; assert (dComm != NULL); Server_ReceiveFinish (dComm->id); } /* ReceiveFinish */ /* * */ static void ReceivePlayerAction (XBCommDgram *dgram, int gameTime, const PlayerAction *playerAction) { XBCommDgramClient *dComm = (XBCommDgramClient *) dgram; assert (dComm != NULL); Server_ReceivePlayerAction (dComm->id, gameTime, playerAction); } /* ReceivePlayerAction */ /* * create datagramm connection client */ XBComm * D2C_CreateComm (unsigned id, const char *localname, XBBool fixedPort) { XBSocket *pSocket; /* sanity checks */ assert (id > 0); assert (id < MAX_HOSTS); assert (commList[id] == NULL); /* create socket */ pSocket = Net_BindUdp (localname, fixedPort ? CLIENT_UDP_PORT (id) :0); if (NULL == pSocket) { return NULL; } /* create communication data structure */ commList[id] = calloc (1, sizeof (*commList[id]) ); assert (NULL != commList[id]); /* set values */ Dgram_CommInit (&commList[id]->dgram, COMM_DgClient, pSocket, XBTrue, ReceivePing, ReceiveFinish, ReceivePlayerAction); commList[id]->id = id; /* setup polling */ if (0 == commCount) { GUI_AddPollFunction (PollDatagram); } commCount ++; /* that's all */ return &commList[id]->dgram.comm; } /* D2C_CreateComm */ /* * connect to server */ XBBool D2C_Connect (unsigned id, const char *host, unsigned short port) { /* sanity checks */ assert (id > 0); assert (id < MAX_HOSTS); assert (commList[id] != NULL); assert (commList[id]->dgram.comm.socket != NULL); /* connect socket */ if (port != 0) { /* connect to client address and port */ commList[id]->dgram.connected = Net_ConnectUdp (commList[id]->dgram.comm.socket, host, port); return commList[id]->dgram.connected; } else { /* client uses NAT we wait for his first datagram */ commList[id]->dgram.host = host; return XBTrue; } } /* D2C_Connect */ /* * get port for client */ unsigned short D2C_Port (unsigned id) { return Dgram_Port (&commList[id]->dgram); } /* D2C_Port */ /* * is client connected ? */ XBBool D2C_Connected (unsigned id) { assert (id > 0); assert (id < MAX_HOSTS); return (commList[id] != NULL); } /* D2C_Connected */ /* * last ping time */ long D2C_LastPing (unsigned id) { assert (id > 0); assert (id < MAX_HOSTS); assert (NULL != commList[id]); return commList[id]->ping; } /* D2C_Connected */ /* * disconnect given client */ void D2C_Disconnect (unsigned id) { assert (id > 0); assert (id < MAX_HOSTS); assert (commList[id] != NULL); /* we only need to shutdown the socket */ CommDelete (&commList[id]->dgram.comm); commList[id] = NULL; /* disable polling */ commCount --; if (0 == commCount) { GUI_SubtractPollFunction (PollDatagram); } } /* D2C_Disconnect */ /* * reset communication after level start */ void D2C_Reset (unsigned id) { assert (id > 0); assert (id < MAX_HOSTS); assert (commList[id] != NULL); Dgram_Reset (&commList[id]->dgram); } /* D2C_Reset */ /* * send player action to client */ void D2C_SendPlayerAction (unsigned id, int gameTime, const PlayerAction *playerAction) { assert (id > 0); assert (id < MAX_HOSTS); assert (commList[id] != NULL); Dgram_SendPlayerAction (&commList[id]->dgram, gameTime, playerAction); } /* D2C_SendPlayerAction */ /* * finish level it */ void D2C_SendFinish (unsigned id, int gameTime) { assert (id > 0); assert (id < MAX_HOSTS); assert (commList[id] != NULL); Dgram_SendFinish (&commList[id]->dgram, gameTime); } /* D2C_Finish */ /* * flush remaining datgrams */ XBBool D2C_Flush (unsigned id) { assert (id > 0); assert (id < MAX_HOSTS); assert (commList[id] != NULL); return Dgram_Flush (&commList[id]->dgram); } /* D2C_Flush */ /* * end of file com_dg_client.c */