1 /*
2  * file com_query.c - client queryes for local network game
3  *
4  * $Id: com_query.c,v 1.19 2006/02/17 19:52:31 fzago Exp $
5  *
6  * Program XBLAST
7  * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net)
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published
11  * by the Free Software Foundation; either version 2; or (at your option)
12  * any later version
13  *
14  * This program is distributed in the hope that it will be entertaining,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.
21  * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #include "xblast.h"
25 
26 /*
27  * local types
28  */
29 typedef struct
30 {
31 	XBCommBrowse browse;
32 	char *addrRemote;			/* address to connect to */
33 	unsigned short port;		/* port of remote address */
34 	unsigned char serial;		/* datagram id */
35 	struct timeval tvSend;		/* time of last send */
36 	unsigned id;				/* interface id */
37 	XBBool broadcast;			/* broadcast flag */
38 	XBBool deleted;				/* deletion flag */
39 } XBCommQuery;
40 
41 /*
42  * handle replies, from either a server or from central
43  */
44 static void
HandleReply(XBCommQuery * qComm,const XBBrowseTeleReply * tele,const char * host)45 HandleReply (XBCommQuery * qComm, const XBBrowseTeleReply * tele, const char *host)
46 {
47 	long msec;
48 	struct timeval tv;
49 	char version[32];
50 
51 	/* calculate ping time */
52 	gettimeofday (&tv, NULL);
53 	msec =
54 		(tv.tv_sec - qComm->tvSend.tv_sec) * 1000L + (tv.tv_usec - qComm->tvSend.tv_usec) / 1000L;
55 	/* create remote version string */
56 	sprintf (version, "V%u.%u.%u", (unsigned)tele->version[0], (unsigned)tele->version[1],
57 			 (unsigned)tele->version[2]);
58 	/* check if it is response to latest query */
59 	if (tele->any.serial != qComm->serial) {
60 		Dbg_Query ("received reply ignored, got serial %u, expected serial %u\n", tele->any.serial,
61 				   qComm->serial);
62 		return;
63 	}
64 	/* check version and source, inform client */
65 	if (((tele->version[0] == VERSION_MAJOR) && (tele->version[1] == VERSION_MINOR))
66 		|| (tele->frameRate == 0)) {
67 		/* versions match or message */
68 		if (strlen (tele->host) <= 0) {
69 			/* server reply: host entry is empty/NULL */
70 			Dbg_Query ("received reply for #%u - game \"%s\" at %s:%hu (%ld msec)\n",
71 					   tele->any.serial, tele->game, host, tele->port, msec);
72 			Client_ReceiveReply (qComm->id, host, tele->port, msec, version, tele->game,
73 								 tele->numLives, tele->numWins, tele->frameRate);
74 		}
75 		else {
76 			/* central reply: host entry not empty */
77 			Dbg_Query ("received reply for #%u - game \"%s\" at %s:%hu (from central %ld msec)\n",
78 					   tele->any.serial, tele->game, tele->host, tele->port, msec);
79 			Client_ReceiveReply (qComm->id, tele->host, tele->port, msec, version, tele->game,
80 								 tele->numLives, tele->numWins, tele->frameRate);
81 		}
82 	}
83 	else {
84 		/* versions don't match and not message */
85 		if (strlen (tele->host) <= 0) {
86 			/* server reply */
87 			Dbg_Query
88 				("received reply for #%u - game \"%s\" at %s:%hu (%ld msec), WRONG VERSION %s\n",
89 				 tele->any.serial, tele->game, host, tele->port, msec, version);
90 		}
91 		else {
92 			/* central reply */
93 			Dbg_Query
94 				("received reply for #%u - game \"%s\" at %s:%hu (from central %ld msec), WRONG VERSION %s\n",
95 				 tele->any.serial, tele->game, tele->host, tele->port, msec, version);
96 		}
97 	}
98 }								/* HandleReply */
99 
100 /*
101  * handle received data
102  */
103 static void
ReceiveQuery(XBCommBrowse * bComm,const XBBrowseTele * tele,const char * host,unsigned short port)104 ReceiveQuery (XBCommBrowse * bComm, const XBBrowseTele * tele, const char *host,
105 			  unsigned short port)
106 {
107 	assert (NULL != bComm);
108 	assert (NULL != tele);
109 	assert (NULL != host);
110 	switch (tele->type) {
111 	case XBBT_Reply:
112 		HandleReply ((XBCommQuery *) bComm, &tele->reply, host);
113 		break;
114 	default:
115 		Dbg_Query ("receiving invalid signal %u, ignoring\n", tele->type);
116 		break;
117 	}
118 }								/* ReceiveQuery */
119 
120 /*
121  * handle browse event
122  */
123 static XBBool
EventQuery(XBCommBrowse * bComm,XBBrowseEvent ev)124 EventQuery (XBCommBrowse * bComm, XBBrowseEvent ev)
125 {
126 #if defined(DEBUG_QUERY) || defined(WMS)
127 	XBCommQuery *qComm = (XBCommQuery *) bComm;
128 	unsigned id = qComm->id;
129 #endif
130 	switch (ev) {
131 	case XBBE_Wait:
132 		Dbg_Query ("all data sent on #%u\n", id);
133 		break;
134 	case XBBE_Dgram:
135 		Dbg_Query ("received invalid datagram on #%u, ignoring\n", id);
136 		break;
137 	case XBBE_Browse:
138 		Dbg_Query ("received invalid browse on #%u, ignoring\n", id);
139 		break;
140 	case XBBE_Write:
141 		Dbg_Query ("new data waits to be sent on #%u\n", id);
142 		break;
143 	case XBBE_Close:
144 		Dbg_Query ("browse shutdown complete for #%u\n", id);
145 		break;
146 	default:
147 		Dbg_Query ("unknown browse event on #%u, ignoring!\n", ev);
148 	}
149 	return XBFalse;
150 }								/* EventQuery */
151 
152 /*
153  * Delete handler for COMM_Query sockets
154  * frees contents of structure but not the structure itself
155  * deleted entry indicates state of the structure
156  */
157 static XBCommResult
DeleteQuery(XBComm * comm)158 DeleteQuery (XBComm * comm)
159 {
160 	XBCommQuery *qComm = (XBCommQuery *) comm;
161 	assert (NULL != qComm);
162 	assert (!qComm->deleted);
163 	Browse_Finish (&qComm->browse);
164 	assert (NULL != qComm->addrRemote);
165 	free (qComm->addrRemote);
166 	/* mark as deleted, do *not* free qComm yet */
167 	qComm->deleted = XBTrue;
168 	/* inform client */
169 	Client_ReceiveQueryClose (qComm->id);
170 	return XCR_OK;
171 }								/* DeleteQuery  */
172 
173 /*
174  * create broadcast socket to query for network games/central games
175  */
176 XBComm *
Query_CreateComm(unsigned id,const char * local,const char * remote,unsigned short port,XBBool broadcast)177 Query_CreateComm (unsigned id, const char *local, const char *remote, unsigned short port,
178 				  XBBool broadcast)
179 {
180 	XBSocket *pSocket;
181 	XBCommQuery *qComm;
182 
183 	assert (NULL != remote);
184 	/* create socket */
185 	pSocket = Net_BindUdp (local, 0);
186 	if (NULL == pSocket) {
187 		Dbg_Query ("failed to establish query socket on %s to %s:%u\n",
188 				   (local == NULL) ? "auto" : local, remote, port);
189 		return NULL;
190 	}
191 	Dbg_Query ("established udp socket on %s:%u\n", Socket_HostName (pSocket, XBFalse),
192 			   Socket_HostPort (pSocket, XBFalse));
193 	/* create communication data structure */
194 	qComm = calloc (1, sizeof (*qComm));
195 	assert (NULL != qComm);
196 	/* set values */
197 	Browse_CommInit (&qComm->browse, COMM_Query, pSocket, ReceiveQuery, EventQuery, DeleteQuery);
198 	qComm->addrRemote = DupString (remote);
199 	qComm->port = port;
200 	qComm->serial = 0;
201 	qComm->tvSend.tv_sec = 0;
202 	qComm->tvSend.tv_usec = 0;
203 	qComm->id = id;
204 	qComm->broadcast = broadcast;
205 	qComm->deleted = XBFalse;
206 	/* that's all ? */
207 	return &qComm->browse.comm;
208 }								/* Query_CreateComm */
209 
210 /*
211  * return deletion flag
212  */
213 XBBool
Query_isDeleted(XBComm * comm)214 Query_isDeleted (XBComm * comm)
215 {
216 	XBCommQuery *qComm = (XBCommQuery *) comm;
217 	assert (comm->type == COMM_Query);
218 	assert (NULL != qComm);
219 	return qComm->deleted;
220 }								/* Query_isDeleted */
221 
222 /*
223  * query central/lan for games
224  */
225 void
Query_Send(XBComm * comm,const struct timeval * tv)226 Query_Send (XBComm * comm, const struct timeval *tv)
227 {
228 	XBBrowseTeleQuery tele;
229 	XBCommQuery *qComm = (XBCommQuery *) comm;
230 
231 	assert (NULL != qComm);
232 	assert (NULL != tv);
233 	/* create browse datagram */
234 	tele.any.type = XBBT_Query;
235 	tele.any.serial = ++qComm->serial;
236 	/* queue data */
237 	Browse_Send (&qComm->browse, qComm->addrRemote, qComm->port, qComm->broadcast, &tele.any);
238 	/* mark time */
239 	qComm->tvSend = *tv;
240 	Dbg_Query ("queued query #%u to %s:%hu\n", qComm->serial, qComm->addrRemote, qComm->port);
241 }								/* Query_Send */
242 
243 /*
244  * end of file com_query.c
245  */
246