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 (¢ralSetup);
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