1 /*
2  * file client.c - communication interface for clients
3  *
4  * $Id: client.c,v 1.69 2006/03/28 11:41:19 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 macros
28  */
29 #define TIME_POLL_QUERY 5		/* unused */
30 
31 /*
32  * local types
33  */
34 
35 /* type for list of network games received, LAN or Central */
36 typedef struct _xb_network_game_list XBNetworkGameList;
37 struct _xb_network_game_list
38 {
39 	XBNetworkGameList *next;
40 	XBNetworkGame game;
41 };
42 
43 /*
44  * local variables
45  */
46 
47 /* Client connections */
48 static XBComm *comm = NULL;		/* XBCommToServer - TCP connection to server */
49 static XBComm *dgram = NULL;	/* XBCommDgramServer - UDP connection to server */
50 static XBComm **query = NULL;	/* List of XBCommQuery's - UDP connections to LAN/Central */
51 
52 /* list of received network games, for Search LAN/Central */
53 static XBNetworkGameList *firstGame = NULL;
54 static XBNetworkGameList *lastGame = NULL;
55 static XBNetworkGameList *nextGame = NULL;
56 static unsigned numGames = 0;
57 
58 /* player actions received for game time and each in-game player */
59 /* TODO: check player counts when new clients connect ! */
60 static PlayerAction playerAction[GAME_TIME + 1][MAX_PLAYER];
61 
62 /* flags for level negotiation status */
63 static XBBool rejected;
64 static XBBool fixed;
65 
66 /* flag for async level result */
67 static XBBool async;
68 
69 /*
70  * local prototypes
71  */
72 static void PollDatagram (const struct timeval *tv);
73 
74 /****************************
75  * connecting/disconnecting *
76  ****************************/
77 
78 /*
79  * try to connect to server: join attempt
80  */
81 XBBool
Client_Connect(CFGGameHost * cfg)82 Client_Connect (CFGGameHost * cfg)
83 {
84 	/* clear network and chat data */
85 	Dbg_Client ("clearing all network and chat data\n");
86 	Network_Clear ();
87 	Chat_Clear ();
88 	Chat_Listen (XBTrue);
89 	/* create communication */
90 	Dbg_Client ("trying to join game at %s:%u\n", cfg->name, cfg->port);
91 	assert (comm == NULL);
92 	comm = C2S_CreateComm (cfg);
93 	if (NULL == comm) {
94 		Dbg_Client ("failed to connect!\n");
95 		return XBFalse;
96 	}
97 	Dbg_Client ("Connected!\n");
98 	Network_SetType (XBNT_Client);
99 	return XBTrue;
100 }								/* Client_Connect */
101 
102 /*
103  * close dgram connection to server
104  */
105 static void
Client_DgramDisconnect(void)106 Client_DgramDisconnect (void)
107 {
108 	if (dgram != NULL) {
109 		Dbg_Client ("disconnecting Dgram to server\n");
110 		/* stop polling */
111 		GUI_SubtractPollFunction (PollDatagram);
112 		Dbg_Client ("stopping to poll for pings\n");
113 		/* delete connection */
114 		CommDelete (dgram);
115 		dgram = NULL;
116 	}
117 }								/* Client_DgramDisconnect */
118 
119 /*
120  * close stream connection to server
121  */
122 static void
Client_StreamDisconnect(void)123 Client_StreamDisconnect (void)
124 {
125 	if (comm != NULL) {
126 		Dbg_Client ("disconnecting Stream to server\n");
127 		CommDelete (comm);
128 		comm = NULL;
129 	}
130 }								/* Client_StreamDisconnect */
131 
132 /*
133  * close all connections to server
134  */
135 void
Client_Disconnect(void)136 Client_Disconnect (void)
137 {
138 	Network_Clear ();
139 	Network_ClearEvents ();
140 	Client_StreamDisconnect ();
141 	Client_DgramDisconnect ();
142 	Chat_Listen (XBFalse);
143 }								/* Client_Disconnect */
144 
145 /***************************
146  * poll function for dgram *
147  ***************************/
148 
149 /*
150  * polling for datagram connections: send pings, disconnect on timeout
151  */
152 static void
PollDatagram(const struct timeval * tv)153 PollDatagram (const struct timeval *tv)
154 {
155 	if (NULL != dgram && !D2S_Connected (dgram)) {
156 		Dbg_Client ("queueing connection ping to server\n");
157 		D2S_SendConnect (dgram);
158 	}
159 	if (NULL != dgram && D2S_Timeout (dgram, tv)) {
160 		/* disconnect from server */
161 		Dbg_Client ("dgram to server timed out\n");
162 		Client_Disconnect ();
163 		Network_QueueEvent (XBNW_Error, 0);
164 		GUI_SendEventValue (XBE_SERVER, XBSE_ERROR);
165 	}
166 }								/* PollDatagram */
167 
168 /******************
169  * error handling *
170  ******************/
171 
172 /*
173  * stream event occurred
174  */
175 XBBool
Client_StreamEvent(XBClientConstants code)176 Client_StreamEvent (XBClientConstants code)
177 {
178 	switch (code) {
179 	case XBCC_IDInvalid:
180 		Dbg_Client ("invalid id received from server, ignoring\n");
181 		return XBFalse;
182 	case XBCC_DataInvalid:
183 		Dbg_Client ("invalid data received from server, ignoring\n");
184 		return XBFalse;
185 	case XBCC_COTInvalid:
186 		Dbg_Client ("invalid cot received from server, disconnecting\n");
187 		break;
188 	case XBCC_IOError:
189 		Dbg_Client ("i/o error on stream to server, disconnecting\n");
190 		break;
191 	case XBCC_ExpectedEOF:
192 		Dbg_Client ("server finishes disconnect announcement\n");
193 		return XBFalse;
194 	case XBCC_UnexpectedEOF:
195 		Dbg_Client ("unexpected server disconnect\n");
196 		break;
197 	case XBCC_StreamWaiting:
198 		/* Dbg_Client("all queued data sent to server\n"); */
199 		return XBFalse;
200 	case XBCC_StreamBusy:
201 		/* Dbg_Client("data remains to be sent to server\n"); */
202 		return XBFalse;
203 	case XBCC_StreamClosed:
204 		Dbg_Client ("connection to server removed\n");
205 		comm = NULL;
206 		return XBFalse;
207 	default:
208 		Dbg_Client ("unknown error (%u) on stream to server, disconnecting\n", code);
209 	}
210 	Client_DgramDisconnect ();
211 	Network_Clear ();
212 	Network_ClearEvents ();
213 	Chat_Listen (XBFalse);
214 	/* queue event for GUI display */
215 	Network_QueueEvent (XBNW_Error, 0);
216 	GUI_SendEventValue (XBE_SERVER, XBSE_ERROR);
217 	return XBTrue;
218 }								/* Client_StreamError */
219 
220 /*
221  * handle events on dgram
222  */
223 XBBool
Client_DgramEvent(XBClientConstants code)224 Client_DgramEvent (XBClientConstants code)
225 {
226 	switch (code) {
227 	case XBCC_Loss:
228 		Dbg_Client ("data loss from server, continuing!\n");
229 		return XBFalse;
230 	case XBCC_WriteError:
231 		Dbg_Client ("write error on dgram to server, disconnecting\n");
232 		break;
233 	case XBCC_ConnFailed:
234 		Dbg_Client ("dgram failed to connect to server, disconnecting\n");
235 		break;
236 	case XBCC_DgramClosed:
237 		Dbg_Client ("dgram to server is removed\n");
238 		dgram = NULL;
239 		return XBFalse;
240 	default:
241 		Dbg_Client ("unknown event (%u) on dgram to server, disconnecting\n", code);
242 	}
243 	Network_Clear ();
244 	Network_ClearEvents ();
245 	Chat_Listen (XBFalse);
246 	Client_StreamDisconnect ();
247 	/* queue event for GUI display */
248 	Network_QueueEvent (XBNW_Error, 0);	/* TODO: put in more info here for GUI */
249 	GUI_SendEventValue (XBE_SERVER, XBSE_ERROR);
250 	return XBTrue;
251 }								/* Client_DgramEvent */
252 
253 /******************
254  * receiving data *
255  ******************/
256 
257 /*
258  * override game config with local options
259  */
260 static void
OverrideGameConfig(void)261 OverrideGameConfig (void)
262 {
263 	CFGGameHost cfgHost;
264 	CFGGameSetup cfgLocal;
265 	CFGGameSetup cfgRemote;
266 	/* set correct server name */
267 	if (RetrieveGameHost (CT_Remote, SERVERGAMECONFIG, &cfgHost)) {
268 		cfgHost.name = C2S_ServerName (comm);
269 		Dbg_Client ("setting server name to %s\n", cfgHost.name);
270 		StoreGameHost (CT_Remote, SERVERGAMECONFIG, &cfgHost);
271 	}
272 	/* set local overrides to game setup */
273 	if (RetrieveGameSetup (CT_Remote, SERVERGAMECONFIG, &cfgRemote) &&
274 		RetrieveGameSetup (CT_Local, atomClient, &cfgLocal)) {
275 		cfgRemote.recordDemo = cfgLocal.recordDemo;
276 		cfgRemote.bot = cfgLocal.bot;
277 		cfgRemote.beep = cfgLocal.beep;
278 		cfgRemote.Music = cfgLocal.Music;
279 		StoreGameSetup (CT_Remote, SERVERGAMECONFIG, &cfgRemote);
280 	}
281 }								/* OverrideGameConfig */
282 
283 /*
284  * receive game config from server
285  */
286 XBBool
Client_ReceiveGameConfig(unsigned id,const char * line)287 Client_ReceiveGameConfig (unsigned id, const char *line)
288 {
289 	unsigned num;
290 	/* receive the data */
291 	switch (Network_ReceiveGameConfig (id, line)) {
292 	case XBGC_Unfinished:
293 		return XBFalse;
294 	case XBGC_HostInvalid:
295 		Client_Disconnect ();
296 		Network_QueueEvent (XBNW_Error, 0);
297 		Dbg_Client ("game config for too large host id #%u received, disconnect\n", id);
298 		GUI_ErrorMessage ("host id=%u too large, probably SMPF-server\n", id);
299 		return XBFalse;
300 	case XBGC_Empty:
301 		Dbg_Client ("server sent empty game config for host #%u\n", id);
302 		num = Network_CreateLocalPlayers (id);
303 		if (num != 0) {
304 			Dbg_Client ("failed to initialize local game config, disconnect\n");
305 			Client_Disconnect ();
306 			Network_QueueEvent (XBNW_Error, 0);
307 			return XBFalse;
308 		}
309 		return XBTrue;
310 	case XBGC_TooManyPlayers:
311 		Client_Disconnect ();
312 		Network_QueueEvent (XBNW_Error, 0);
313 		Dbg_Client ("too many players received, host id #%u, disconnected\n", id);
314 		GUI_ErrorMessage ("too many players received, disconnected\n", id);
315 		return XBFalse;
316 	case XBGC_NoVersion:
317 		Dbg_Client ("server sent no version, disconnect\n");
318 		Client_Disconnect ();
319 		Network_QueueEvent (XBNW_Error, 0);
320 		return XBFalse;
321 	case XBGC_Local:
322 		Dbg_Client ("server sent local game config for host #%u\n", id);
323 		if (id == 0) {
324 			OverrideGameConfig ();
325 		}
326 		num = Network_CreateLocalPlayers (id);
327 		if (num > NUM_LOCAL_PLAYER) {
328 			Dbg_Client ("failed to create local players, disconnect\n");
329 			Client_Disconnect ();
330 			Network_QueueEvent (XBNW_Error, 0);
331 			return XBFalse;
332 		}
333 		Dbg_Client ("created %u players at host #%u\n", num, id);
334 		Network_QueueEvent (XBNW_GameConfig, id);
335 		return XBTrue;
336 		break;
337 	case XBGC_Global:
338 		Dbg_Client ("server sent global game config for host #%u\n", id);
339 		if (id > 0) {
340 			Dbg_Client ("ignoring global game config from client\n");
341 			DeleteGameConfig (CT_Remote, LOCALGAMECONFIG (id));
342 			return XBFalse;
343 		}
344 		OverrideGameConfig ();
345 		num = Network_CreateGlobalPlayers (id);
346 		if (num > MAX_PLAYER) {
347 			Dbg_Client ("failed to create global players, disconnect\n");
348 			Client_Disconnect ();
349 			Network_QueueEvent (XBNW_Error, 0);
350 			return XBFalse;
351 		}
352 		Dbg_Client ("created %u global players from game config #%u\n", num, id);
353 		num = Network_GetMaskBytes ();
354 		if (num == 0) {
355 			Dbg_Client ("failed to set mask bytes, disconnect\n");
356 			Client_Disconnect ();
357 			Network_QueueEvent (XBNW_Error, 0);
358 			return XBFalse;
359 		}
360 		D2S_SetMaskBytes (dgram, num);
361 		Network_QueueEvent (XBNW_GameConfig, id);
362 		return XBTrue;
363 	case XBGC_Error:
364 		Dbg_Client ("server sent invalid game config for host #%u, disconnect\n", id);
365 		Client_Disconnect ();
366 		Network_QueueEvent (XBNW_Error, 0);
367 		return XBFalse;
368 	default:
369 		break;
370 	}
371 	return XBFalse;
372 }								/* Client_ReceiveGameConfig */
373 
374 /*
375  * receive player config from server
376  */
377 void
Client_ReceivePlayerConfig(unsigned id,unsigned player,const char * data)378 Client_ReceivePlayerConfig (unsigned id, unsigned player, const char *data)
379 {
380 	/* disconnect on range check error */
381 	if (id >= MAX_HOSTS || player >= NUM_LOCAL_PLAYER) {
382 		Dbg_Client ("receiving player config for out of range player #%u(%u), disconnect\n", id,
383 					player);
384 		Client_Disconnect ();
385 		Network_QueueEvent (XBNW_Error, 0);
386 		GUI_ErrorMessage ("cannot handle player id %u(%u)\n", id, player);
387 	}
388 	if (ATOM_INVALID != Network_ReceivePlayerConfig (CT_Remote, id, player, data)) {
389 		Dbg_Client ("received player config for #%u(%u)\n", id, player);
390 	}
391 }								/* Client_ReceivePlayerConfig */
392 
393 /*
394  * receive dgram port on server
395  */
396 void
Client_ReceiveDgramPort(unsigned id,unsigned short port)397 Client_ReceiveDgramPort (unsigned id, unsigned short port)
398 {
399 	CFGGameHost cfgSrv, cfgLoc;
400 	XBBool usingNat = XBFalse;
401 
402 	assert (comm != NULL);
403 	assert (dgram == NULL);
404 
405 	/* this is the first time we learn our id from server */
406 	Network_ReceiveLocalHostId (id);
407 	/* first check if we are using NAT to get to the server */
408 	Dbg_Client ("receiving dgram port #%u on server\n", port);
409 	if (id != 0 && id < MAX_HOSTS) {
410 		RetrieveGameHost (CT_Remote, SERVERGAMECONFIG, &cfgSrv);
411 		Dbg_Client ("server is %s\n", cfgSrv.name);
412 		RetrieveGameHost (CT_Remote, LOCALGAMECONFIG (id), &cfgLoc);
413 		Dbg_Client ("server sees me as %s\n", cfgLoc.name);
414 		Dbg_Client ("local ip for stream connection = %s\n", C2S_ClientName (comm));
415 		Dbg_Client ("peer ip for stream connection = %s\n", C2S_ServerName (comm));
416 		if (0 != strcmp (cfgLoc.name, C2S_ClientName (comm))) {
417 			Dbg_Client ("server does not see local ip -> NAT detected!\n");
418 			usingNat = XBTrue;
419 		}
420 	}
421 #ifdef DEBUG_NAT
422 	usingNat = XBTrue;
423 	Dbg_Client ("forcing NAT by compile flag\n");
424 #endif
425 	dgram = D2S_CreateComm (C2S_ClientName (comm), C2S_ServerName (comm), port);
426 	if (NULL == dgram) {
427 		Dbg_Client ("failed to open dgram connection\n");
428 		/* disconnect stream */
429 		Client_StreamDisconnect ();
430 		return;
431 	}
432 	Client_SendDgramPort (usingNat);
433 	SetHostType (XBPH_Client1 + id - 1);
434 	/* poll connection */
435 	Dbg_Client ("starting to poll for pings\n");
436 	GUI_AddPollFunction (PollDatagram);
437 }								/* Client_ReceiveDgramPort */
438 
439 /*
440  * received ping time for one client
441  */
442 void
Client_ReceivePingTime(unsigned id,int ping)443 Client_ReceivePingTime (unsigned id, int ping)
444 {
445 	Network_ReceivePing (id, ping);
446 }								/* Client_ReceivePingTime */
447 
448 /*
449  * receive request for game config
450  */
451 void
Client_ReceiveGameConfigReq(unsigned id)452 Client_ReceiveGameConfigReq (unsigned id)
453 {
454 	CFGGamePlayers cfgPlayers;
455 	CFGGameConst cfgConst;
456 	Dbg_Client ("server requests game config\n");
457 	/* copy game players and constants from Local/Client to Remote/Local */
458 	(void)RetrieveGamePlayers (CT_Local, atomClient, &cfgPlayers);
459 	StoreGamePlayers (CT_Remote, atomLocal, &cfgPlayers);
460 	(void)RetrieveGameConst (CT_Local, atomClient, &cfgConst);
461 	StoreGameConst (CT_Remote, atomLocal, &cfgConst);
462 	/* send filtered config from database */
463 	if (!C2S_SendGameConfig (comm, CT_Remote, atomLocal)) {
464 		Dbg_Client ("failed to queue game config!\n");
465 		C2S_GameDataNotAvailable (comm);
466 		return;
467 	}
468 	Dbg_Client ("successfully queued game config for %u players\n", cfgPlayers.num);
469 }								/* Client_ReceiveGameConfigReq */
470 
471 /*
472  * receive request for game config
473  */
474 void
Client_ReceivePlayerConfigReq(unsigned player)475 Client_ReceivePlayerConfigReq (unsigned player)
476 {
477 	CFGGamePlayers cfgPlayers;
478 
479 	Dbg_Client ("server requests player config for player %u(%u)\n", Network_LocalHostId (),
480 				player);
481 	/* check player index */
482 	if (player >= NUM_LOCAL_PLAYER) {
483 		Dbg_Client ("invalid player number !\n");
484 		C2S_PlayerDataNotAvailable (comm, player);
485 		return;
486 	}
487 	/* get player atom from config */
488 	if (!RetrieveGamePlayers (CT_Local, atomClient, &cfgPlayers)) {
489 		Dbg_Client ("no local players !\n");
490 		C2S_PlayerDataNotAvailable (comm, player);
491 		return;
492 	}
493 	/* check player */
494 	if (player >= cfgPlayers.num) {
495 		Dbg_Client ("only %u local players !\n", cfgPlayers.num);
496 		C2S_PlayerDataNotAvailable (comm, player);
497 		return;
498 	}
499 	if (ATOM_INVALID == cfgPlayers.player[player]) {
500 		Dbg_Client ("player config empty !\n");
501 		C2S_PlayerDataNotAvailable (comm, player);
502 		return;
503 	}
504 	/* send config from database */
505 	if (!C2S_SendPlayerConfig (comm, CT_Local, cfgPlayers.player[player], player, XBFalse)) {
506 		Dbg_Client ("failed to queue !\n");
507 		C2S_PlayerDataNotAvailable (comm, player);
508 		return;
509 	}
510 	Dbg_Client ("successfully queued player config for %u(%u)\n", Network_LocalHostId (), player);
511 }								/* Client_ReceivePlayerConfigReq */
512 
513 /*
514  * receive host state for a host
515  */
516 void
Client_ReceiveHostState(unsigned id,unsigned state)517 Client_ReceiveHostState (unsigned id, unsigned state)
518 {
519 	if (Network_ReceiveHostState (id, state)) {
520 		Dbg_Client ("received host state #%u = %u\n", id, state);
521 	}
522 	else {
523 		Dbg_Client ("received invalid host state\n");
524 	}
525 }								/* Client_ReceiveHostState */
526 
527 /*
528  * receive host state request for a host
529  */
530 void
Client_ReceiveHostStateReq(unsigned who,unsigned id,unsigned state)531 Client_ReceiveHostStateReq (unsigned who, unsigned id, unsigned state)
532 {
533 	if (Network_ReceiveHostStateReq (who, id, state)) {
534 		Dbg_Client ("received host state request #%u -> %u by %u\n", id, state, who);
535 	}
536 	else {
537 		Dbg_Client ("received invalid host state request\n");
538 	}
539 }								/* Client_ReceiveHostStateReq */
540 
541 /*
542  * receive team state for a host/player
543  */
544 void
Client_ReceiveTeamState(unsigned host,unsigned player,unsigned team)545 Client_ReceiveTeamState (unsigned host, unsigned player, unsigned team)
546 {
547 	if (Network_ReceiveTeamState (host, player, team)) {
548 		Dbg_Client ("received team state #%u(%u) = %u\n", host, player, team);
549 	}
550 	else {
551 		Dbg_Client ("received invalid team state\n");
552 	}
553 }								/* Client_ReceiveTeamState */
554 
555 /*
556  * receive team state for a host/player
557  */
558 void
Client_ReceiveTeamStateReq(unsigned who,unsigned host,unsigned player,unsigned team)559 Client_ReceiveTeamStateReq (unsigned who, unsigned host, unsigned player, unsigned team)
560 {
561 	if (Network_ReceiveTeamStateReq (who, host, player, team)) {
562 		Dbg_Client ("received team state request #%u(%u) -> %u by %u\n", host, player, team, who);
563 	}
564 	else {
565 		Dbg_Client ("received invalid team state request\n");
566 	}
567 }								/* Client_ReceiveTeamStateReq */
568 
569 /*
570  * receive level config from server
571  */
572 void
Client_ReceiveLevelConfig(unsigned iob,const char * data)573 Client_ReceiveLevelConfig (unsigned iob, const char *data)
574 {
575 	if (NULL != data) {
576 		AddToRemoteLevelConfig (iob, data);
577 		return;
578 	}
579 	/* yes, all data received */
580 	Dbg_Client ("checking remote level for version\n");
581 	Client_LevelRejection (!CheckRemoteLevelConfig ());
582 	Network_QueueEvent (XBNW_LevelConfig, 0);
583 }								/* Client_ReceiveLevelConfig */
584 
585 /*
586  * receive info about a disconnection
587  */
588 XBBool
Client_ReceiveDisconnect(unsigned id)589 Client_ReceiveDisconnect (unsigned id)
590 {
591 	if (id == 0) {
592 		Dbg_Client ("server announces disconnect, partial shutdown!\n");
593 		/* partial shutdown */
594 		Client_DgramDisconnect ();
595 		Network_Clear ();
596 		Chat_Listen (XBFalse);
597 		Network_QueueEvent (XBNW_Disconnected, id);
598 		GUI_SendEventValue (XBE_SERVER, XBSE_ERROR);
599 		return XBTrue;
600 	}
601 	if (id < MAX_HOSTS) {
602 		Dbg_Client ("host #%u disconnected\n", id);
603 		Network_ClearHost (id);
604 	}
605 	else {
606 		Dbg_Client ("host id too large, ignoring (probably SMPF server)\n");
607 		return XBFalse;
608 	}
609 	Network_QueueEvent (XBNW_Disconnected, id);
610 	return XBFalse;
611 }								/* Client_ReceiveDisconnect */
612 
613 /*
614  * receive chat line
615  */
616 void
Client_ReceiveChat(XBChat * chat)617 Client_ReceiveChat (XBChat * chat)
618 {
619 	Chat_Receive (chat);
620 }								/* Client_ReceiveChat */
621 
622 /*
623  * receive game start signal from server
624  */
625 void
Client_ReceiveStart(unsigned id)626 Client_ReceiveStart (unsigned id)
627 {
628 	Dbg_Out ("received start of game!\n");
629 	Network_QueueEvent (XBNW_StartGame, id);
630 }								/* Client_ReceiveStart */
631 
632 /*
633  * receive seed for random number generator
634  */
635 void
Client_ReceiveRandomSeed(unsigned seed)636 Client_ReceiveRandomSeed (unsigned seed)
637 {
638 	Dbg_Out ("receive seed %u\n", seed);
639 	SeedRandom (seed);
640 }								/* Client_ReceiveRandomSeed */
641 
642 /*
643  * server notified that all clients reached sync point
644  */
645 void
Client_ReceiveSync(XBNetworkEvent event)646 Client_ReceiveSync (XBNetworkEvent event)
647 {
648 	Dbg_Out ("receive sync, raising event %u\n", event);
649 	/* TODO filter events */
650 	Network_QueueEvent (event, 0);
651 }								/* Client_ReceiveSync */
652 
653 /*
654  * receive action keys for a gametime
655  */
656 void
Client_ReceivePlayerAction(int gameTime,const PlayerAction * keys)657 Client_ReceivePlayerAction (int gameTime, const PlayerAction * keys)
658 {
659 	assert (gameTime <= GAME_TIME);
660 	/* inform application */
661 	GUI_SendEventValue (XBE_SERVER, gameTime);
662 	/* copy keys (simple version) */
663 	memcpy (&playerAction[gameTime][0], keys, MAX_PLAYER * sizeof (PlayerAction));
664 }								/* Client_ReceivePlayerAction */
665 
666 /*
667  * receive level finish
668  */
669 void
Client_ReceiveFinish(void)670 Client_ReceiveFinish (void)
671 {
672 	/* inform application */
673 	Dbg_Client ("receive level finish\n");
674 	GUI_SendEventValue (XBE_SERVER, XBSE_FINISH);
675 }								/* Client_ReceiveFinish */
676 
677 /*
678  * receive async status from server
679  */
680 void
Client_ReceiveAsync(unsigned ev)681 Client_ReceiveAsync (unsigned ev)
682 {
683 	/* ev always equal to XBNW_SyncLevelResult */
684 	Dbg_Client ("receiving level result async\n");
685 	async = XBTrue;
686 	Network_QueueEvent (ev, XBTrue);
687 }								/* Client_ReceiveAsync */
688 
689 /************************
690  * local data retrieval *
691  ************************/
692 
693 /*
694  * retrieve host state for a host
695  */
696 unsigned
Client_GetHostState(unsigned id)697 Client_GetHostState (unsigned id)
698 {
699 	return Network_GetHostState (id);
700 }								/* Client_GetHostState */
701 
702 /*
703  * retrieve host state requests for a host
704  */
705 unsigned *
Client_GetHostStateReq(unsigned id)706 Client_GetHostStateReq (unsigned id)
707 {
708 	return Network_GetHostStateReq (id);
709 }								/* Client_GetHostStateReq */
710 
711 /*
712  * retrieve team state for a host/player
713  */
714 unsigned
Client_GetTeamState(unsigned id,unsigned pl)715 Client_GetTeamState (unsigned id, unsigned pl)
716 {
717 	return Network_GetTeamState (id, pl);
718 }								/* Client_GetHostState */
719 
720 /*
721  * retrieve team state requests for a host/player
722  */
723 unsigned *
Client_GetTeamStateReq(unsigned id,unsigned pl)724 Client_GetTeamStateReq (unsigned id, unsigned pl)
725 {
726 	return Network_GetTeamStateReq (id, pl);
727 }								/* Client_GetTeamStateReq */
728 
729 /*
730  * query rejection status for level config
731  */
732 XBBool
Client_RejectsLevel(void)733 Client_RejectsLevel (void)
734 {
735 	return rejected;
736 }								/* Client_RejectsLevel */
737 
738 /*
739  * query fixed status for a level config
740  */
741 XBBool
Client_FixedLevel(void)742 Client_FixedLevel (void)
743 {
744 	return fixed;
745 }								/* Client_Fixed level */
746 
747 /*
748  * get actions for given gametime
749  */
750 void
Client_GetPlayerAction(int gameTime,PlayerAction * keys)751 Client_GetPlayerAction (int gameTime, PlayerAction * keys)
752 {
753 	assert (gameTime <= GAME_TIME);
754 	/* copy keys (simple version) */
755 	memcpy (keys, &playerAction[gameTime][0], MAX_PLAYER * sizeof (PlayerAction));
756 }								/* Client_PlayerAction */
757 
758 /*
759  * get ping time of server to given host
760  */
761 int
Client_GetPingTime(unsigned clientID)762 Client_GetPingTime (unsigned clientID)
763 {
764 	return Network_GetPingTime (clientID);
765 }								/* Client_GetPingTime */
766 
767 /*
768  * query async status for level
769  */
770 XBBool
Client_LevelAsynced(void)771 Client_LevelAsynced (void)
772 {
773 	return async;
774 }								/* Client_LevelAsynced */
775 
776 /**********************
777  * local data setting *
778  **********************/
779 
780 /*
781  * manually set rejection status for a level config
782  */
783 void
Client_LevelRejection(XBBool rej)784 Client_LevelRejection (XBBool rej)
785 {
786 	Dbg_Client ("setting level rejection to %u\n", rej);
787 	rejected = rej;
788 }								/* Client_LevelRejection */
789 
790 /*
791  * manually set fixed status for a level config
792  */
793 void
Client_LevelFix(XBBool fx)794 Client_LevelFix (XBBool fx)
795 {
796 	Dbg_Client ("setting level fix to %u\n", fx);
797 	fixed = fx;
798 }								/* Client_LevelFix */
799 
800 /*
801  * reset or fix a level config
802  */
803 void
Client_ActivateLevel(unsigned how)804 Client_ActivateLevel (unsigned how)
805 {
806 	if (how == 0) {
807 		Dbg_Client ("received level reset from server\n");
808 		Client_LevelRejection (XBTrue);
809 		/* clear level config in game_client: LevelFromServer */
810 	}
811 	else {
812 		Dbg_Client ("received level activation from server\n");
813 		Client_LevelFix (XBTrue);
814 	}
815 	Network_QueueEvent (XBNW_LevelConfig, how);
816 }								/* Client_Activate level */
817 
818 /*
819  * manually set async status for level
820  */
821 void
Client_SetLevelAsync(XBBool as)822 Client_SetLevelAsync (XBBool as)
823 {
824 	Dbg_Client ("setting async to %u\n", as);
825 	async = as;
826 }								/* Client_SetLevelAsync */
827 
828 /*
829  * clear all action data
830  */
831 void
Client_ClearPlayerAction(void)832 Client_ClearPlayerAction (void)
833 {
834 	unsigned gt;
835 	int player;
836 	for (gt = 0; gt < GAME_TIME + 1; gt++) {
837 		for (player = 0; player < MAX_PLAYER; player++) {
838 			playerAction[gt][player].player = player;
839 			playerAction[gt][player].dir = GoDefault;
840 			playerAction[gt][player].bomb = XBFalse;
841 			playerAction[gt][player].special = XBFalse;
842 			playerAction[gt][player].pause = XBFalse;
843 			playerAction[gt][player].suicide = XBFalse;
844 			playerAction[gt][player].abort = ABORT_NONE;
845 		}
846 	}
847 	/* Dbg_Client("cleared action for all hosts\n"); */
848 }								/* Client_ClearPlayerAction */
849 
850 /*
851  * reset datagram connection
852  */
853 void
Client_ResetPlayerAction(void)854 Client_ResetPlayerAction (void)
855 {
856 	if (NULL != dgram) {
857 		Dbg_Client ("resetting dgram player actions\n");
858 		D2S_Reset (dgram);
859 	}
860 }								/* Client_ResetPlayerAction */
861 
862 /******************
863  * queue data out *
864  ******************/
865 
866 /*
867  * queue dgram port to server
868  */
869 void
Client_SendDgramPort(XBBool nat)870 Client_SendDgramPort (XBBool nat)
871 {
872 	assert (NULL != comm);
873 	if (!nat) {
874 		/* send port to server */
875 		C2S_SendDgramPort (comm, D2S_Port (dgram));
876 	}
877 	else {
878 		/* send "any port" to notify use of n.a.t.  */
879 		C2S_SendDgramPort (comm, 0);
880 	}
881 	Dbg_Client ("queued dgram port to server\n");
882 }								/* Client_SendDgramPort */
883 
884 /*
885  * queue game config to server
886  */
887 void
Client_SendGameConfig(void)888 Client_SendGameConfig (void)
889 {
890 	/* TODO: currently only for completeness, move from com_to_server.c */
891 }								/* Client_SendGameConfig */
892 
893 /*
894  * queue player configs to server
895  */
896 void
Client_SendPlayerConfigs(void)897 Client_SendPlayerConfigs (void)
898 {
899 	/* TODO: currently only for completeness, move from com_to_server.c */
900 }								/* Client_SendPlayerConfigs */
901 
902 /*
903  * queue a chat to server
904  */
905 void
Client_SendChat(XBChat * chat)906 Client_SendChat (XBChat * chat)
907 {
908 	if (NULL != comm) {
909 		Dbg_Client ("queueing chat line\n");
910 		C2S_SendChat (comm, chat);
911 	}
912 }								/* Client_SendChat */
913 
914 /*
915  * queue sync point
916  */
917 void
Client_SendSync(XBNetworkEvent event)918 Client_SendSync (XBNetworkEvent event)
919 {
920 	if (NULL != comm) {
921 		Dbg_Client ("queueing sync event #%u on stream\n", event);
922 		C2S_Sync (comm, event);
923 	}
924 }								/* Client_SendSync */
925 
926 /*
927  * queue local host state
928  * TODO: remove, only temporarily needed for client toggle button
929  */
930 void
Client_SendHostState(unsigned state)931 Client_SendHostState (unsigned state)
932 {
933 	if (NULL != comm) {
934 		Dbg_Client ("queueing local host state #%u on stream\n", state);
935 		C2S_SendHostState (comm, state);
936 	}
937 }								/* Client_SendHostState */
938 
939 /*
940  * queue local host state request
941  */
942 void
Client_SendHostStateReq(unsigned id,unsigned state)943 Client_SendHostStateReq (unsigned id, unsigned state)
944 {
945 	if (NULL != comm) {
946 		Dbg_Client ("queueing local host state request #%u->%u on stream\n", id, state);
947 		C2S_SendHostStateReq (comm, id, state);
948 	}
949 }								/* Client_SendHostStateReq */
950 
951 /*
952  * queue local team state request
953  */
954 void
Client_SendTeamStateReq(unsigned id,unsigned player,unsigned team)955 Client_SendTeamStateReq (unsigned id, unsigned player, unsigned team)
956 {
957 	if (NULL != comm) {
958 		Dbg_Client ("queueing team state request #%u(%u)->%u on stream\n", id, player, team);
959 		C2S_SendTeamStateReq (comm, id, player, team);
960 	}
961 }								/* Client_SendTeamStateReq */
962 
963 /*
964  * queue level negotiation status to server
965  */
966 void
Client_SendLevelCheck(void)967 Client_SendLevelCheck (void)
968 {
969 	if (NULL != comm) {
970 		Dbg_Client ("queueing LevelCheck %u on stream\n", rejected);
971 		C2S_LevelCheck (comm, rejected);
972 	}
973 }								/* Client_SendLevelCheck */
974 
975 /*
976  * queue own action at gametime to server
977  */
978 void
Client_SendPlayerAction(int gameTime,const PlayerAction * keys)979 Client_SendPlayerAction (int gameTime, const PlayerAction * keys)
980 {
981 	D2S_SendPlayerAction (dgram, gameTime, keys);
982 }								/* Client_SendPlayerAction */
983 
984 /*
985  * queue end of level to server
986  */
987 void
Client_FinishPlayerAction(int gameTime)988 Client_FinishPlayerAction (int gameTime)
989 {
990 	Dbg_Client ("queueing finish at %u on dgram\n", gameTime);
991 	D2S_SendFinish (dgram, gameTime);
992 }								/* Client_FinishPlayerAction */
993 
994 /*
995  * flush out remaining data
996  */
997 XBBool
Client_FlushPlayerAction(void)998 Client_FlushPlayerAction (void)
999 {
1000 	return D2S_Flush (dgram);
1001 }								/* Client_FlushPlayerAction */
1002 
1003 /*
1004  * queue winner team to server
1005  */
1006 void
Client_SendWinner(unsigned team)1007 Client_SendWinner (unsigned team)
1008 {
1009 	if (NULL != comm) {
1010 		Dbg_Client ("queueing winner team %u\n", team);
1011 		C2S_SendWinner (comm, team);
1012 	}
1013 }								/* Client_SendWinner */
1014 
1015 /*------------------------------------------------------------------------*
1016  *
1017  * query for local and remote games
1018  *
1019  *------------------------------------------------------------------------*/
1020 
1021 /*
1022  * delete current list of network games
1023  */
1024 static void
DeleteGameList(void)1025 DeleteGameList (void)
1026 {
1027 	XBNetworkGameList *next;
1028 
1029 	while (firstGame != NULL) {
1030 		next = firstGame->next;
1031 		/* delete data */
1032 		free (firstGame->game.host);
1033 		free (firstGame->game.game);
1034 		free (firstGame->game.version);
1035 		free (firstGame);
1036 		firstGame = next;
1037 	}
1038 	lastGame = NULL;
1039 	nextGame = NULL;
1040 	numGames = 0;
1041 }								/* DeleteGameList */
1042 
1043 /*
1044  * Search lan query
1045  * try to establish COMM_Query sockets for each interface
1046  * send query on all created sockets
1047  */
1048 void
Client_StartQuery(void)1049 Client_StartQuery (void)
1050 {
1051 	size_t numInter;
1052 	const XBSocketInterface *inter;
1053 	size_t i, j;
1054 
1055 	assert (NULL == query);
1056 	inter = Socket_GetInterfaces (&numInter);
1057 	if (NULL == inter) {
1058 		Dbg_Client ("failed to find network interfaces, cancelling LAN query\n");
1059 		return;
1060 	}
1061 	/* alloc maximum possible pointers */
1062 	query = calloc (1 + numInter, sizeof (XBComm *));
1063 	assert (NULL != query);
1064 	/* start query on each broadcast device */
1065 	for (i = 0, j = 0; i < numInter; i++) {
1066 		if (NULL != inter[i].addrBroadcast &&
1067 			NULL != (query[j] =
1068 					 Query_CreateComm (i, inter[i].addrDevice, inter[i].addrBroadcast, 16168,
1069 									   XBTrue))) {
1070 			Dbg_Client ("starting LAN query on #%lu to %s\n", (unsigned long)i, inter[i].addrBroadcast);
1071 			j++;
1072 		}
1073 		else {
1074 			Dbg_Client ("failed to start LAN query on #%lu to %s\n", (unsigned long)i, inter[i].addrBroadcast);
1075 		}
1076 	}
1077 	Client_RestartQuery ();
1078 }								/* Client_StartQuery */
1079 
1080 /*
1081  * XBCC Search central query
1082  * try to establish COMM_Query sockets for each interface
1083  * send query on all created sockets
1084  */
1085 void
Client_StartCentralQuery(void)1086 Client_StartCentralQuery (void)
1087 {
1088 	size_t numInter;
1089 	const XBSocketInterface *inter;
1090 #ifdef FULL_CENTRAL_QUERY
1091 	size_t i, j;
1092 #endif
1093 	CFGCentralSetup centralSetup;
1094 
1095 	assert (NULL == query);
1096 	inter = Socket_GetInterfaces (&numInter);
1097 	if (NULL == inter) {
1098 		Dbg_Client ("failed to find network interfaces, cancelling central query\n");
1099 		return;
1100 	}
1101 	RetrieveCentralSetup (&centralSetup);
1102 	if (NULL == centralSetup.name) {
1103 		Dbg_Client ("failed to find central data, cancelling central query\n");
1104 		return;
1105 	}
1106 #ifdef FULL_CENTRAL_QUERY
1107 	/* alloc comm for each interface plus a NULL pointer (to central) */
1108 	query = calloc (1 + numInter, sizeof (XBComm *));
1109 	assert (NULL != query);
1110 	/* start query on default device */
1111 	for (i = 0, j = 0; i < numInter; i++) {
1112 		if (NULL !=
1113 			(query[j] =
1114 			 Query_CreateComm (i, inter[i].addrDevice, centralSetup.name, centralSetup.port,
1115 							   XBFalse))) {
1116 			Dbg_Client ("starting central query on #%u to %s\n", i, centralSetup.name);
1117 			j++;
1118 		}
1119 		else {
1120 			Dbg_Client ("failed to start central query on #%u to %s\n", i, centralSetup.name);
1121 		}
1122 	}
1123 #else
1124 	/* alloc comm for default interface plus a NULL pointer (to central) */
1125 	query = calloc (1 + 1, sizeof (XBComm *));
1126 	assert (NULL != query);
1127 	/* start query on default device */
1128 	if (NULL !=
1129 		(query[0] = Query_CreateComm (0, NULL, centralSetup.name, centralSetup.port, XBFalse))) {
1130 		Dbg_Client ("starting central query to %s\n", centralSetup.name);
1131 	}
1132 	else {
1133 		Dbg_Client ("failed to start central query to %s\n", centralSetup.name);
1134 	}
1135 #endif
1136 	Client_RestartQuery ();
1137 }								/* Client_StartCentralQuery */
1138 
1139 /*
1140  * send a query on all valid COMM_Query sockets
1141  */
1142 void
Client_RestartQuery(void)1143 Client_RestartQuery (void)
1144 {
1145 	unsigned cnt = 0;
1146 	DeleteGameList ();
1147 	if (NULL != query) {
1148 		struct timeval tv;
1149 		int i;
1150 		gettimeofday (&tv, NULL);
1151 		for (i = 0; query[i] != NULL; i++) {
1152 			if (!Query_isDeleted (query[i])) {
1153 				Query_Send (query[i], &tv);
1154 				cnt++;
1155 			}
1156 		}
1157 		if (cnt > 0) {
1158 			Dbg_Client ("requeued %u central/lan queries\n", cnt);
1159 		}
1160 		else {
1161 			Dbg_Client ("no valid queries remaining, freeing memory\n");
1162 			Client_StopQuery ();
1163 		}
1164 	}
1165 }								/* Client_RestartQuery */
1166 
1167 /*
1168  * stop query to central/lan
1169  */
1170 void
Client_StopQuery(void)1171 Client_StopQuery (void)
1172 {
1173 	size_t i;
1174 
1175 	DeleteGameList ();
1176 	/* return if alredy stopped */
1177 	if (NULL == query) {
1178 		return;
1179 	}
1180 	/* delete all valid queries */
1181 	for (i = 0; query[i] != NULL; i++) {
1182 		if (!Query_isDeleted (query[i])) {
1183 			CommDelete (query[i]);
1184 		}
1185 		free (query[i]);
1186 	}
1187 	/* stop */
1188 	free (query);
1189 	query = NULL;
1190 }								/* Client_StopQuery */
1191 
1192 /*
1193  * receive a close event on a query socket
1194  */
1195 void
Client_ReceiveQueryClose(unsigned id)1196 Client_ReceiveQueryClose (unsigned id)
1197 {
1198 	Dbg_Client ("query on interface #%u is closed\n", id);	/* nothing else to do */
1199 }								/* Client_ReceiveQueryClose */
1200 
1201 /*
1202  * receive reply from a game server on lan or central
1203  */
1204 void
Client_ReceiveReply(unsigned id,const char * host,unsigned short port,int ping,const char * version,const char * game,int numLives,int numWins,int frameRate)1205 Client_ReceiveReply (unsigned id, const char *host, unsigned short port, int ping,
1206 					 const char *version, const char *game, int numLives, int numWins,
1207 					 int frameRate)
1208 {
1209 	XBNetworkGameList *ptr;
1210 
1211 	/* alloc data */
1212 	ptr = calloc (1, sizeof (*ptr));
1213 	assert (NULL != ptr);
1214 	/* fill in data */
1215 	ptr->game.id = id;
1216 	ptr->game.host = DupString (host);
1217 	ptr->game.port = port;
1218 	ptr->game.ping = ping;
1219 	ptr->game.version = DupString (version);
1220 	ptr->game.game = DupString (game);
1221 	ptr->game.numLives = numLives;
1222 	ptr->game.numWins = numWins;
1223 	ptr->game.frameRate = frameRate;
1224 	/* append to list */
1225 	if (NULL == lastGame) {
1226 		firstGame = lastGame = nextGame = ptr;
1227 	}
1228 	else {
1229 		lastGame->next = ptr;
1230 		lastGame = ptr;
1231 	}
1232 	Dbg_Client ("received network game data on interface #%u\n", id);
1233 	/* inform application */
1234 	Network_QueueEvent (XBNW_NetworkGame, numGames++);
1235 }								/* Client_ReceiveReply */
1236 
1237 #ifdef unused
1238 /*
1239  * game info for application
1240  */
1241 static const XBNetworkGame *
Client_FirstNetworkGame(unsigned index)1242 Client_FirstNetworkGame (unsigned index)
1243 {
1244 	unsigned i;
1245 	const XBNetworkGame *ptr;
1246 
1247 	/* find index-th game */
1248 	nextGame = firstGame;
1249 	for (i = 0; i < index; i++) {
1250 		if (nextGame == NULL) {
1251 			return NULL;
1252 		}
1253 		nextGame = nextGame->next;
1254 	}
1255 	if (nextGame == NULL) {
1256 		return NULL;
1257 	}
1258 	ptr = &nextGame->game;
1259 	nextGame = nextGame->next;
1260 	return ptr;
1261 }								/* Client_FirstNetworkGame */
1262 #endif
1263 
1264 /*
1265  * game info for application
1266  */
1267 const XBNetworkGame *
Client_NextNetworkGame(void)1268 Client_NextNetworkGame (void)
1269 {
1270 	const XBNetworkGame *ptr;
1271 
1272 	if (NULL == nextGame) {
1273 		return NULL;
1274 	}
1275 	ptr = &nextGame->game;
1276 	nextGame = nextGame->next;
1277 	return ptr;
1278 }								/* Client_NextNetworkGame */
1279 
1280 /*
1281  * end of file client.c
1282  */
1283