1 /*
2  * file server.c - communication interface for the server
3  *
4  * $Id: server.c,v 1.79 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 variables
28  */
29 
30 /* Server connections */
31 static XBComm *listenComm = NULL;	/* XBCommListen - TCP listen for clients */
32 static XBComm *replyComm = NULL;	/* XBCommReply - UDP LAN reply */
33 static XBComm **query = NULL;	/* XBCommNewGame - UDP connection to central */
34 
35 /* default server options */
36 static CFGGame cfgGame;
37 
38 /* player actions received for expected gametime */
39 static PlayerAction playerAction[MAX_HOSTS][MAX_PLAYER];
40 
41 /* level negotiation states per host */
42 static unsigned levelStatus[MAX_HOSTS];
43 
44 /* levelwinners per host */
45 static unsigned levelWinner[MAX_HOSTS];
46 static char levelWinnerStr[3 * MAX_HOSTS + 1];
47 static unsigned currentWinner = TEAM_UNDEF;
48 
49 /* server flag, unneeded ? */
50 static XBBool isServer = XBFalse;
51 
52 /***************************
53  * starting/closing server *
54  ***************************/
55 
56 /*
57  * start listening for clients
58  *
59  * should always be start for server!
60  */
61 XBBool
Server_StartListen(CFGGameHost * cfg)62 Server_StartListen (CFGGameHost * cfg)
63 {
64 	unsigned char p;
65 	/* init network/chat */
66 	Dbg_Server ("clearing all network and chat data\n");
67 	Network_Clear ();
68 	Network_ClearEvents ();
69 	Chat_Clear ();
70 	/* listen on tcp-port for clients */
71 	assert (listenComm == NULL);
72 	listenComm = CommCreateListen (cfg, XBFalse);
73 	if (NULL == listenComm) {
74 		Dbg_Server ("failed to listen for clients on TCP port %u\n", cfg->port);
75 		return XBFalse;
76 	}
77 	Dbg_Server ("listening for clients on port %u\n", cfg->port);
78 	/* receive local host id */
79 	Network_ReceiveLocalHostId (0);
80 	/* get game config data from database Local/Server */
81 	if (!RetrieveGame (CT_Local, atomServer, &cfgGame)) {
82 		Dbg_Server ("failed to get local game config!\n");
83 		Network_Clear ();
84 		Network_ClearEvents ();
85 		return XBFalse;
86 	}
87 	/* simulate game config receive from network */
88 	StoreGame (CT_Remote, SERVERGAMECONFIG, &cfgGame);
89 	/* now receive terminator like client */
90 	if (!Server_ReceiveGameConfig (0, NULL)) {
91 		Dbg_Server ("listen failed, local game config error\n");
92 		Network_Clear ();
93 		Network_ClearEvents ();
94 		return XBFalse;
95 	}
96 	/* receive local player configs */
97 	for (p = 0; p < cfgGame.players.num; p++) {
98 		if (!Server_ReceivePlayerConfig (0, p, NULL)) {
99 			Dbg_Server ("failed to setup local player #%u\n", p);
100 			Network_Clear ();
101 			Network_ClearEvents ();
102 			return XBFalse;
103 		}
104 	}
105 	/* receive ping */
106 	Server_ReceivePing (0, 0);
107 	/* allow clients to browse for games */
108 	if (cfg->browseLan) {
109 		assert (NULL == replyComm);
110 		replyComm = Reply_CreateComm (16168, cfg, &cfgGame.setup);
111 		if (NULL == replyComm) {
112 			Dbg_Server ("failed to open reply socket, not visible in LAN\n");
113 		}
114 	}
115 	Dbg_Server ("listening for LAN queries on UDP port %u\n", 16168);
116 	/* force stop on old query sockets - unlikely that they are still active, but not impossible */
117 	if (query != NULL) {
118 		Dbg_Server ("removing old query sockets\n");
119 		Server_StopNewGame ();
120 	}
121 	/* registering game at central */
122 	if (cfg->central) {
123 		Dbg_Server ("attempting to register game\n");
124 		Server_StartCentralNewGame (cfg, &cfgGame.setup);
125 	}
126 	Dbg_Server ("marking as server\n");
127 	isServer = XBTrue;			/* needed for old code, remove some time */
128 	assert (XBNT_None == Network_GetType ());
129 	Network_SetType (XBNT_Server);
130 	/* listen for local chat keys */
131 	Chat_Listen (XBTrue);
132 	/* that's all */
133 	return XBTrue;
134 }								/* Server_StartListen */
135 
136 /*
137  * remove listen ports
138  */
139 void
Server_StopListen(void)140 Server_StopListen (void)
141 {
142 	/* delete listen port */
143 	assert (NULL != listenComm);
144 	CommFinishListen (listenComm);
145 	/* should trigger Server_ReceiveListenClose() */
146 	assert (listenComm == NULL);
147 	/* delete reply socket */
148 	if (NULL != replyComm) {
149 		CommDelete (replyComm);
150 		replyComm = NULL;
151 		Dbg_Server ("stopped listening for LAN queries\n");
152 	}
153 }								/* Server_StopListen */
154 
155 /*
156  * listen socket is closed down
157  */
158 void
Server_ReceiveListenClose(XBBool exp)159 Server_ReceiveListenClose (XBBool exp)
160 {
161 	listenComm = NULL;
162 	if (exp) {
163 		Dbg_Server ("stopped listening for clients\n");
164 	}
165 	else {
166 		Dbg_Server ("unexpected shutdown of listen socket\n");
167 		/* do something appropriate */
168 	}
169 }								/* Server_ReceiveListenClose */
170 
171 /****************************
172  * connecting/disconnecting *
173  ****************************/
174 
175 /*
176  * a client has connected to server
177  */
178 void
Server_Accept(unsigned id,const char * hostName,unsigned port)179 Server_Accept (unsigned id, const char *hostName, unsigned port)
180 {
181 	CFGGameHost cfg;
182 	time_t ltime;
183 
184 	assert (hostName != NULL);
185 	assert (id > 0);
186 	assert (id < MAX_HOSTS);
187 	/* produce beep if wanted */
188 	time (&ltime);
189 	if (cfgGame.host.beep) {
190 		SND_Beep ();
191 	}
192 	fprintf (stderr, "\n%s client adr=%s:%u id=%u connected\n", ctime (&ltime), hostName, port, id);
193 	/* store in database */
194 	Dbg_Server ("setting up initial game config for host #%u\n", id);
195 	memset (&cfg, 0, sizeof (cfg));
196 	cfg.name = hostName;
197 	cfg.port = port;
198 	StoreGameHost (CT_Remote, LOCALGAMECONFIG (id), &cfg);
199 	/* attempt to start dgram */
200 	if (NULL == D2C_CreateComm (id, S2C_LocalName (id), cfgGame.host.fixedUdpPort)) {
201 		Dbg_Server ("failed to create udp socket for host #%u, disconnecting !!\n", id);
202 		Server_StreamDisconnect (id);
203 		return;
204 	}
205 	Dbg_Server ("created udp socket for host #%u on port %u\n", id, D2C_Port (id));
206 	/* respond to client now */
207 	Server_SendAllConfigs (id);
208 	Server_SendDgramPort (id);
209 	Server_QueryGameConfig (id);
210 	Network_QueueEvent (XBNW_Accepted, id);
211 }								/* Server_Accept */
212 
213 /*
214  * initiate stream shut down - send the disconnect signal and mark waiting EOF
215  * the stream and its host slot will be freed on receivng eof/error
216  */
217 void
Server_StreamDisconnect(unsigned clientID)218 Server_StreamDisconnect (unsigned clientID)
219 {
220 	assert (clientID > 0);
221 	assert (clientID < MAX_HOSTS);
222 	if (S2C_Connected (clientID)) {
223 		Dbg_Server ("queueing TCP disconnect sequence to host #%u\n", clientID);
224 		S2C_Disconnect (clientID);
225 	}
226 }								/* Server_StreamDisconnect */
227 
228 /*
229  * makes dgram slot available for new clients
230  * the dgram data structure is completely removed
231  */
232 void
Server_DgramDisconnect(unsigned clientID)233 Server_DgramDisconnect (unsigned clientID)
234 {
235 	assert (clientID > 0);
236 	assert (clientID < MAX_HOSTS);
237 	if (D2C_Connected (clientID)) {
238 		Dbg_Server ("disconnecting UDP to host #%u\n", clientID);
239 		D2C_Disconnect (clientID);
240 	}
241 }								/* Server_DgramDisconnect */
242 
243 /*
244  * active freeing of the client slot
245  * informs other clients and local GUI
246  */
247 void
Server_SendDisconnect(unsigned clientID)248 Server_SendDisconnect (unsigned clientID)
249 {
250 	if (clientID > 0) {
251 		Server_StreamDisconnect (clientID);
252 		Server_DgramDisconnect (clientID);
253 		Server_SendDisconnectInfo (clientID);
254 		Network_ClearHost (clientID);
255 		Network_QueueEvent (XBNW_Disconnected, clientID);
256 	}
257 	else {
258 		Server_SendDisconnectAll ();
259 	}
260 }								/* Server_SendDisconnect */
261 
262 /*
263  * active shutdown of server
264  * all slots are cleared
265  */
266 void
Server_SendDisconnectAll(void)267 Server_SendDisconnectAll (void)
268 {
269 	unsigned clientID;
270 	/* disconnect from clients */
271 	for (clientID = 1; clientID < MAX_HOSTS; clientID++) {
272 		Server_StreamDisconnect (clientID);
273 		Server_DgramDisconnect (clientID);
274 	}
275 	if (NULL != query) {
276 		Dbg_Server ("queuing close to central, mark for shutdown after send\n");
277 		Server_CloseNewGame ();
278 	}
279 	if (listenComm == NULL) {
280 		Network_SetType (XBNT_None);
281 	}
282 	Network_Clear ();
283 	Network_ClearEvents ();
284 	Dbg_Server ("*** all data completely cleared ***\n");
285 	Chat_Listen (XBFalse);
286 }								/* Server_SendDisconnectAll */
287 
288 /*
289  * disconnect all clients that are out
290  */
291 static void
Server_SendDisconnectOut(void)292 Server_SendDisconnectOut (void)
293 {
294 	unsigned id;
295 	for (id = 1; id < MAX_HOSTS; id++) {
296 		if (!Network_HostIsIn (id)) {
297 			Dbg_Server ("disconnecting host #%i, is out\n", id);
298 			Server_SendDisconnect (id);
299 		}
300 	}
301 }								/* Server_SendDisconnectOut */
302 
303 /***************
304  * comm events *
305  ***************/
306 
307 /*
308  * handle event on stream
309  */
310 XBBool
Server_StreamEvent(unsigned clientID,XBServerConstants code)311 Server_StreamEvent (unsigned clientID, XBServerConstants code)
312 {
313 	assert (clientID > 0);
314 	assert (clientID < MAX_HOSTS);
315 	switch (code) {
316 	case XBSC_IDInvalid:
317 		Dbg_Server ("invalid id received from host #%u, ignoring\n", clientID);
318 		return XBFalse;
319 	case XBSC_DataInvalid:
320 		Dbg_Server ("invalid data received from host #%u, ignoring\n", clientID);
321 		return XBFalse;
322 	case XBSC_COTInvalid:
323 		Dbg_Server ("invalid cot received from host #%u, sending disconnect\n", clientID);
324 		Server_StreamDisconnect (clientID);
325 		Server_SendDisconnectInfo (clientID);
326 		return XBFalse;
327 	case XBSC_MissingData:
328 		Dbg_Server ("host #%u has not requested data\n", clientID);
329 		return XBFalse;
330 	case XBSC_IOError:
331 		Dbg_Server ("i/o error on stream to host #%u, disconnecting\n", clientID);
332 		Server_SendDisconnectInfo (clientID);
333 		break;
334 	case XBSC_UnexpectedEOF:
335 		Dbg_Server ("unexpected eof on stream to host #%u, disconnecting\n", clientID);
336 		Server_SendDisconnectInfo (clientID);
337 		break;
338 	case XBSC_StreamWaiting:
339 		Dbg_Server ("all queued data sent to host #%u\n", clientID);
340 		return XBFalse;
341 	case XBSC_StreamBusy:
342 		/* Dbg_Server("data waits to be sent to host #%u\n", clientID); */
343 		return XBFalse;
344 	case XBSC_StreamClosed:
345 		Dbg_Server ("stream to host #%u fully disconnected\n", clientID);
346 		return XBFalse;
347 	default:
348 		Dbg_Server ("unknown event (%u) on stream to host #%u, disconnecting\n", code, clientID);
349 	}
350 	/* remove dgram if still there */
351 	Server_DgramDisconnect (clientID);
352 	/* clear host data */
353 	Network_ClearHost (clientID);
354 	/* queue event for GUI display */
355 	Network_QueueEvent (XBNW_Error, clientID);	/* more info for GUI */
356 	return XBTrue;
357 }								/* Server_StreamEvent */
358 
359 /*
360  * handle event on dgram
361  */
362 XBBool
Server_DgramEvent(unsigned clientID,XBServerConstants code)363 Server_DgramEvent (unsigned clientID, XBServerConstants code)
364 {
365 	assert (clientID > 0);
366 	assert (clientID < MAX_HOSTS);
367 	switch (code) {
368 	case XBSC_GameTime:
369 		Dbg_Server ("future gametimes from host #%u received, ignoring\n", clientID);
370 		return XBFalse;
371 	case XBSC_WriteError:
372 		Dbg_Server ("write error on dgram to host #%u, disconnecting\n", clientID);
373 		break;
374 	case XBSC_ConnFailed:
375 		Dbg_Server ("dgram failed to connect to host #%u, disconnecting\n", clientID);
376 		break;
377 	case XBSC_Timeout:
378 		Dbg_Server ("dgram to host #%u timed out, disconnecting\n", clientID);
379 		Server_DgramDisconnect (clientID);
380 		break;
381 	case XBSC_DgramClosed:
382 		Dbg_Server ("dgram to host #%u removed\n", clientID);
383 		return XBFalse;
384 	default:
385 		Dbg_Server ("unknown event (%u) on dgram to host #%u, disconnecting\n", code, clientID);
386 	}
387 	/* initiate disconnection of stream */
388 	Server_StreamDisconnect (clientID);
389 	Server_SendDisconnectInfo (clientID);
390 	/* clear host data */
391 	Network_ClearHost (clientID);
392 	/* queue event for GUI display */
393 	Network_QueueEvent (XBNW_Error, clientID);
394 	return XBTrue;
395 }								/* Server_DgramDisconnect */
396 
397 /******************
398  * receiving data *
399  ******************/
400 
401 /*
402  * game config received from host
403  */
404 XBBool
Server_ReceiveGameConfig(unsigned id,const char * line)405 Server_ReceiveGameConfig (unsigned id, const char *line)
406 {
407 	unsigned num;
408 	assert (id < MAX_HOSTS);
409 	/* receive the data */
410 	switch (Network_ReceiveGameConfig (id, line)) {
411 	case XBGC_Unfinished:
412 		return XBFalse;
413 	case XBGC_Empty:
414 		Dbg_Server ("host #%u sent empty game config, disconnect\n", id);
415 		Server_SendDisconnect (id);
416 		return XBFalse;
417 	case XBGC_TooManyPlayers:
418 		Dbg_Server ("host #%u sent too many players, disconnect\n", id);
419 		Server_SendDisconnect (id);
420 		return XBFalse;
421 	case XBGC_NoVersion:
422 		Dbg_Server ("host #%u sent no version, disconnect\n", id);
423 		Server_SendDisconnect (id);
424 		return XBFalse;
425 	case XBGC_Local:
426 		Dbg_Server ("host #%u sent local game config\n", id);
427 		num = Network_CreateLocalPlayers (id);
428 		if (num >= NUM_LOCAL_PLAYER) {
429 			Dbg_Server ("failed to create local players, disconnect\n");
430 			Server_SendDisconnect (id);
431 			return XBFalse;
432 		}
433 		Dbg_Server ("created %u players at host #%u\n", num, id);
434 		Server_SendGameConfig (id);
435 		Server_QueryPlayerConfigs (id, num);
436 		Server_ReceiveHostStateReq (0, id, XBHS_Wait);
437 		Network_QueueEvent (XBNW_GameConfig, id);
438 		return XBTrue;
439 	case XBGC_Global:
440 		Dbg_Server ("host #%u sent global game config\n", id);
441 		if (id > 0) {
442 			Dbg_Server ("ignoring global game config from client\n");
443 			DeleteGameConfig (CT_Remote, LOCALGAMECONFIG (id));
444 			return XBFalse;
445 		}
446 		num = Network_CreateGlobalPlayers (id);
447 		if (num > MAX_PLAYER) {
448 			Dbg_Server ("failed to create global players, stop\n");
449 			Server_SendDisconnect (id);
450 			Network_QueueEvent (XBNW_Error, id);
451 			return XBFalse;
452 		}
453 		num = Network_GetMaskBytes ();
454 		if (num == 0) {
455 			Dbg_Server ("failed to set mask bytes, stop\n");
456 			Server_SendDisconnect (id);
457 			Network_QueueEvent (XBNW_Error, id);
458 			return XBFalse;
459 		}
460 		D2C_SetMaskBytes (num);
461 		Dbg_Server ("created %u global players from game config #%u\n", num, id);
462 		return XBTrue;
463 	case XBGC_Error:
464 		Dbg_Server ("host #%u sent invalid game config, disconnect\n", id);
465 		Server_SendDisconnect (id);
466 		return XBFalse;
467 	default:
468 		break;
469 	}
470 	return XBFalse;
471 }								/* Server_ReceiveGameConfig */
472 
473 /*
474  * player config received from client
475  */
476 XBBool
Server_ReceivePlayerConfig(unsigned id,int player,const char * line)477 Server_ReceivePlayerConfig (unsigned id, int player, const char *line)
478 {
479 	XBAtom atom, name, full;
480 	CFGPlayer cfgPlayer;
481 	if (id == 0) {
482 		Dbg_Server ("local player config transfer #%u\n", player);
483 		full = Network_GetPlayer (0, player);
484 		if (full == ATOM_INVALID) {
485 			Dbg_Server ("local player #%u not registered\n", player);
486 			return XBFalse;
487 		}
488 		name = Network_GetPlayer2 (0, player);
489 		if (name == ATOM_INVALID) {
490 			Dbg_Server ("local player name #%u not registered\n", player);
491 			return XBFalse;
492 		}
493 		if (!RetrievePlayer (CT_Local, name, cfgGame.players.teamColor[player], &cfgPlayer)) {
494 			Dbg_Server ("incomplete data for player #%u\n", player);
495 			return XBFalse;
496 		}
497 		StorePlayer (CT_Remote, full, &cfgPlayer);
498 		line = NULL;
499 	}
500 	/* store player for config */
501 	atom = Network_ReceivePlayerConfig (CT_Remote, id, player, line);
502 	/* if atom is valid, data is complete */
503 	if (ATOM_INVALID != atom) {
504 		Dbg_Server ("player config for host #%u(%u) received\n", id, player);
505 		/* send player config of id to other clients */
506 		Server_SendPlayerConfig (id, player);
507 		/* initial team state */
508 		Server_ReceiveTeamStateReq (0, id, player, cfgGame.setup.teamMode ? XBTS_Red : XBTS_None);
509 		return XBTrue;
510 	}
511 	return XBFalse;
512 }								/* Server_ReceivePlayerConfig */
513 
514 /*
515  * client has sent dgram port
516  */
517 void
Server_ReceiveDgramPort(unsigned id,unsigned short port)518 Server_ReceiveDgramPort (unsigned id, unsigned short port)
519 {
520 	/* set port for datagram conection */
521 	if (0 != port || cfgGame.host.allowNat) {
522 		if (port == 0) {
523 			Dbg_Server ("client at %s reports NAT, setting up dgram\n", S2C_HostName (id));
524 		}
525 		else {
526 			Dbg_Server ("received dgram port %u for host #%u, connecting dgram\n", port, id);
527 		}
528 		if (!D2C_Connect (id, S2C_HostName (id), port)) {
529 			Dbg_Server ("failed to connect dgram\n");
530 			Server_SendDisconnect (id);
531 		}
532 	}
533 	else {
534 		Dbg_Server ("client has NAT, disallowed\n");
535 		Server_SendDisconnect (id);
536 	}
537 }								/* Server_ReceiveDgramPort */
538 
539 /*
540  * receive and resend host state changes, local use only
541  */
542 static void
Server_ReceiveHostState(unsigned id,XBHostState state)543 Server_ReceiveHostState (unsigned id, XBHostState state)
544 {
545 	if (Network_ReceiveHostState (id, state)) {
546 		Server_SendHostState (id, state);
547 	}
548 	else {
549 		Dbg_Server ("received invalid host state\n");
550 	}
551 }								/* Server_ReceiveHostState */
552 
553 /*
554  * return if host state request from id for host to state is granted
555  */
556 static XBBool
Server_GrantsHostRequest(unsigned id,unsigned host,XBHostState state)557 Server_GrantsHostRequest (unsigned id, unsigned host, XBHostState state)
558 {
559 	/* never grant the None request */
560 	if (state == XBHS_None) {
561 		return XBFalse;
562 	}
563 	/* distinguish server and client requests */
564 	if (id == 0) {
565 		/* grant any server request unless request equals current */
566 		return (Network_GetHostState (host) != state);
567 	}
568 	else if (id == host) {
569 		/* grant certain client requests for self */
570 		switch (Network_GetHostState (host)) {
571 		case XBHS_Ready:
572 			return (state == XBHS_In);
573 		case XBHS_In:
574 			return (state == XBHS_Ready);
575 		default:
576 			return XBFalse;
577 		}
578 	}
579 	return XBFalse;
580 }								/* Server_GrantsHostRequest */
581 
582 /*
583  * receive host state request from id for host to state
584  */
585 void
Server_ReceiveHostStateReq(unsigned id,unsigned host,XBHostState state)586 Server_ReceiveHostStateReq (unsigned id, unsigned host, XBHostState state)
587 {
588 	XBHostState *req;
589 	/* translate In to Srv for server */
590 	if (host == 0 && state == XBHS_In) {
591 		state = XBHS_Server;
592 	}
593 	/* check if request is valid */
594 	if (Network_ReceiveHostStateReq (id, host, state)) {
595 		/* first send request data to all clients */
596 		Server_SendHostStateReq (id, host, state);
597 		/* check if request is granted */
598 		if (Server_GrantsHostRequest (id, host, state)) {
599 			Dbg_Server ("granting host request #%u->%u from #%u\n", host, state, id);
600 			Server_ReceiveHostState (host, state);
601 		}
602 		/* reprocess host's request */
603 		req = Network_GetHostStateReq (host);
604 		if (Server_GrantsHostRequest (host, host, req[host])) {
605 			Dbg_Server ("granting reprocessed host request #%u->%u from #%u\n", host, req[host],
606 						host);
607 			Server_ReceiveHostState (host, req[host]);
608 		}
609 		/* clients agree on a host state */
610 		if (Network_HostReqClientsAgree (host, state)) {
611 			/* all clients!=host request out for host, grant unless host = server */
612 			if (state == XBHS_Out && id != 0) {
613 				Dbg_Server ("granting disconnect request of clients for host #%u!!\n", host);
614 				Server_SendDisconnect (host);
615 				return;
616 			}
617 		}
618 		/* check if all clients are ready */
619 		if (Network_ClientsReady () && Network_GetHostState (0) == XBHS_Ready) {
620 			Dbg_Server ("all hosts are ready, starting game!!\n");
621 			Network_QueueEvent (XBNW_StartGame, 0);
622 		}
623 	}
624 	else {
625 		Dbg_Server ("received invalid host state request\n");
626 	}
627 }								/* Server_ReceiveHostStateReq */
628 
629 /*
630  * receive and resend team state change, local use only
631  */
632 static void
Server_ReceiveTeamState(unsigned id,unsigned player,XBTeamState state)633 Server_ReceiveTeamState (unsigned id, unsigned player, XBTeamState state)
634 {
635 	if (Network_ReceiveTeamState (id, player, state)) {
636 		Server_SendTeamState (id, player, state);
637 	}
638 	else {
639 		Dbg_Server ("received invalid team state\n");
640 	}
641 }								/* Server_ReceiveTeamState */
642 
643 /*
644  * return if team request from id for host to state is granted
645  */
646 static XBBool
Server_GrantsTeamRequest(unsigned id,unsigned host,unsigned player,XBTeamState state)647 Server_GrantsTeamRequest (unsigned id, unsigned host, unsigned player, XBTeamState state)
648 {
649 	if (id == 0) {
650 		/* grant any server request for server */
651 		Dbg_Server ("granting team request #%u: #%u(%u)->%u\n", id, host, player, state);
652 		return XBTrue;
653 	}
654 	else if (Network_TeamReqClientsAgree (host, player, state)) {
655 		/* grant client request if they agree */
656 		Dbg_Server ("granting joint team request #%u(%u)->%u\n", host, player, state);
657 		return XBTrue;
658 	}
659 	return XBFalse;
660 }								/* Server_GrantsTeamRequest */
661 
662 /*
663  * receive team state request from id for host:player to state
664  */
665 void
Server_ReceiveTeamStateReq(unsigned id,unsigned host,unsigned player,XBTeamState state)666 Server_ReceiveTeamStateReq (unsigned id, unsigned host, unsigned player, XBTeamState state)
667 {
668 	unsigned i, p;
669 	/* first check for valid request */
670 	if (!Network_ReceiveTeamStateReq (id, host, player, state)) {
671 		Dbg_Server ("received invalid team state request\n");
672 	}
673 	/* send valid request data to all hosts */
674 	Server_SendTeamStateReq (id, host, player, state);
675 	/* process request */
676 	if (!Server_GrantsTeamRequest (id, host, player, state)) {
677 		Dbg_Server ("team request #%u: #%u(%u)->%u denied\n", id, host, player, state);
678 		return;
679 	}
680 	/* check if player is requested out, no mode change */
681 	if (state == XBTS_Out) {
682 		Dbg_Server ("setting player %u(%u) out\n", host, player);
683 		Server_ReceiveTeamState (host, player, state);
684 		return;
685 	}
686 	/* check for change to chaos mode */
687 	if (cfgGame.setup.teamMode && (state == XBTS_None)) {
688 		/* change to chaos, first set new team defaults */
689 		Network_SetDefaultTeams (host, player);
690 		/* set requested host/player explicitly to none */
691 		Server_ReceiveTeamState (host, player, XBTS_None);
692 		/* set all players with valid team to none */
693 		for (i = 0; i < MAX_HOSTS; i++) {
694 			for (p = 0; p < NUM_LOCAL_PLAYER; p++) {
695 				state = Network_GetTeamState (i, p);
696 				if (state > XBTS_None && state < XBTS_Out) {
697 					Server_ReceiveTeamState (i, p, XBTS_None);
698 				}
699 			}
700 		}
701 		Dbg_Server ("changed to chaos mode!\n");
702 		cfgGame.setup.teamMode = XBFalse;
703 		return;
704 	}
705 	/* check for change to team mode */
706 	if (!cfgGame.setup.teamMode && (state > XBTS_None)) {
707 		/* change to default teams */
708 		for (i = 0; i < MAX_HOSTS; i++) {
709 			for (p = 0; p < NUM_LOCAL_PLAYER; p++) {
710 				if (Network_GetTeamState (i, p) == XBTS_None) {
711 					Server_ReceiveTeamState (i, p, Network_GetDefaultTeam (i, p));
712 				}
713 			}
714 		}
715 		Dbg_Server ("changed to team mode!\n");
716 		cfgGame.setup.teamMode = XBTrue;
717 		return;
718 	}
719 	/* no team mode change */
720 	Dbg_Server ("setting player %u(%u) = %u\n", host, player, state);
721 	Server_ReceiveTeamState (host, player, state);
722 }								/* Server_ReceiveTeamStateReq */
723 
724 /*
725  * server received chat from a client
726  */
727 void
Server_ReceiveChat(XBChat * chat)728 Server_ReceiveChat (XBChat * chat)
729 {
730 	Chat_Receive (chat);
731 	Server_SendChat (chat);
732 }								/* Server_ReceiveChat */
733 
734 /*
735  * ping received, tell network
736  */
737 void
Server_ReceivePing(unsigned id,int ping)738 Server_ReceivePing (unsigned id, int ping)
739 {
740 	Dbg_Server ("received ping from host #%u\n", id);
741 	Network_ReceivePing (id, ping);
742 }								/* Server_ReceivePing */
743 
744 /*
745  * client has reached a sync point
746  */
747 void
Server_ReceiveSync(unsigned id,XBNetworkEvent event)748 Server_ReceiveSync (unsigned id, XBNetworkEvent event)
749 {
750 	/* inform application */
751 	Dbg_Server ("host #%u reached sync point %u\n", id, event);
752 	Network_QueueEvent (event, id);
753 }								/* Server_ReceiveSync */
754 
755 /*
756  * receive a level check result from a client
757  */
758 void
Server_ReceiveLevelCheck(unsigned id,unsigned stat)759 Server_ReceiveLevelCheck (unsigned id, unsigned stat)
760 {
761 	Dbg_Server ("receiving level status at %u = %u\n", id, stat);
762 	levelStatus[id] = stat;
763 	Network_QueueEvent (XBNW_LevelConfig, id);
764 }								/* Server_ReceiveLevelCheck */
765 
766 /*
767  * received keys from one client
768  */
769 void
Server_ReceivePlayerAction(unsigned id,int gameTime,const PlayerAction * keys)770 Server_ReceivePlayerAction (unsigned id, int gameTime, const PlayerAction * keys)
771 {
772 	int i;
773 
774 	assert (id > 0);
775 	assert (id < MAX_HOSTS);
776 	assert (playerAction != NULL);
777 
778 	/* Dbg_Server("received action from host #%u\n", id); */
779 	for (i = 0; i < MAX_PLAYER; i++) {
780 		if (keys[i].dir != GoDefault) {
781 			playerAction[id][i].dir = keys[i].dir;
782 		}
783 		if (keys[i].bomb) {
784 			playerAction[id][i].bomb = XBTrue;
785 		}
786 		if (keys[i].special) {
787 			playerAction[id][i].special = XBTrue;
788 		}
789 		if (keys[i].pause) {
790 			playerAction[id][i].pause = XBTrue;
791 		}
792 		if (keys[i].abort != ABORT_NONE) {
793 			playerAction[id][i].abort = keys[i].abort;
794 		}
795 		if (keys[i].suicide) {
796 			playerAction[id][i].suicide = XBTrue;
797 		}
798 		/* Skywalker */
799 		if (keys[i].laola) {
800 			playerAction[id][i].laola = XBTrue;
801 		}
802 		else {
803 			playerAction[id][i].laola = XBFalse;
804 			if (keys[i].looser) {
805 				playerAction[id][i].looser = XBTrue;
806 			}
807 			else {
808 				playerAction[id][i].looser = XBFalse;
809 			}
810 		}
811 		/* */
812 	}
813 }								/* Server_ReceivePlayerAction */
814 
815 /*
816  * received level finish from clients
817  */
818 void
Server_ReceiveFinish(unsigned id)819 Server_ReceiveFinish (unsigned id)
820 {
821 	Dbg_Server ("FINISH from host #%u\n", id);
822 }								/* Server_ReceiveFinish */
823 
824 /*
825  * receive the winner team from a client
826  */
827 void
Server_ReceiveWinnerTeam(unsigned id,unsigned team)828 Server_ReceiveWinnerTeam (unsigned id, unsigned team)
829 {
830 	assert (id < MAX_HOSTS);
831 	Dbg_Server ("receiving winner at %u = %u\n", id, team);
832 	/* update winner table */
833 	levelWinner[id] = team;
834 	/* update string */
835 	switch (team) {
836 	case TEAM_NOWINNER:
837 		memcpy (levelWinnerStr + 3 * id, "DR", 2);
838 		break;
839 	case TEAM_LOCALASYNC:
840 		memcpy (levelWinnerStr + 3 * id, "LA", 2);
841 		break;
842 	default:
843 		memcpy (levelWinnerStr + 3 * id, TempString ("%2x", team), 2);
844 		break;
845 	}
846 	/* update current winner */
847 	if (currentWinner == TEAM_UNDEF) {
848 		currentWinner = team;
849 	}
850 	else if (currentWinner != team) {
851 		currentWinner = TEAM_ASYNC;
852 	}
853 	/* queue event for game_server sync */
854 	Network_QueueEvent (XBNW_SyncLevelResult, id);
855 }								/* Server_ReceiveWinnerTeam */
856 
857 /************************
858  * local data retrieval *
859  ************************/
860 
861 /*
862  * get server flag
863  */
864 XBBool
GetIsServer(void)865 GetIsServer (void)
866 {
867 	return isServer;
868 }								/* GetIsServer */
869 
870 /*
871  * set server flag
872  */
873 void
SetIsServer(XBBool value)874 SetIsServer (XBBool value)
875 {
876 	isServer = value;
877 }								/* SetIsServer */
878 
879 /*
880  * retrieve host state from client
881  */
882 XBHostState
Server_GetHostState(unsigned id)883 Server_GetHostState (unsigned id)
884 {
885 	return Network_GetHostState (id);
886 }								/* Server_GetHostState */
887 
888 /*
889  * retrieve host state requests for client
890  */
891 XBHostState *
Server_GetHostStateReq(unsigned id)892 Server_GetHostStateReq (unsigned id)
893 {
894 	return Network_GetHostStateReq (id);
895 }								/* Server_GetHostStateReq */
896 
897 /*
898  * retrieve team state form client, player
899  */
900 XBTeamState
Server_GetTeamState(unsigned id,unsigned player)901 Server_GetTeamState (unsigned id, unsigned player)
902 {
903 	return Network_GetTeamState (id, player);
904 }								/* Server_GetTeamState */
905 
906 /*
907  * retrieve host state requests from client
908  */
909 XBTeamState *
Server_GetTeamStateReq(unsigned id,unsigned player)910 Server_GetTeamStateReq (unsigned id, unsigned player)
911 {
912 	return Network_GetTeamStateReq (id, player);
913 }								/* Server_GetTeamStateReq */
914 
915 /*
916  * last ping time of client
917  */
918 int
Server_GetPingTime(unsigned clientID)919 Server_GetPingTime (unsigned clientID)
920 {
921 	return Network_GetPingTime (clientID);
922 }								/* Server_GetPingTime */
923 
924 /*
925  * check level status list for rejections
926  */
927 XBBool
Server_LevelApproved(void)928 Server_LevelApproved (void)
929 {
930 	unsigned i;
931 	for (i = 0; i < MAX_HOSTS; i++) {
932 		if (levelStatus[i] != 255) {
933 			if (levelStatus[i] == 0) {
934 				Dbg_Server ("level rejected by clients\n");
935 				return XBFalse;
936 			}
937 		}
938 	}
939 	Dbg_Server ("level approved by clients\n");
940 	return XBTrue;
941 }								/* Server_LevelApproved */
942 
943 /*
944  * read player keys from local buffer
945  */
946 void
Server_GetPlayerAction(unsigned id,int player,PlayerAction * action)947 Server_GetPlayerAction (unsigned id, int player, PlayerAction * action)
948 {
949 	assert (id > 0);
950 	assert (id < MAX_HOSTS);
951 	assert (player < MAX_PLAYER);
952 	assert (playerAction != NULL);
953 	/* copy data */
954 	*action = playerAction[id][player];
955 }								/* Server_GetPlayerAction */
956 
957 /*
958  * check winner list for async
959  */
960 XBBool
Server_LevelAsync(void)961 Server_LevelAsync (void)
962 {
963 	if (currentWinner != TEAM_ASYNC) {
964 		Dbg_Server ("level results match!\n");
965 		return XBFalse;
966 	}
967 	else {
968 		Dbg_Server ("level results asynced (%s)!\n", levelWinnerStr);
969 		return XBTrue;
970 	}
971 }								/* Server_LevelAsync */
972 
973 /**********************
974  * local data setting *
975  **********************/
976 
977 /*
978  * clear level status list for level checking
979  */
980 void
Server_ClearLevelStatus(void)981 Server_ClearLevelStatus (void)
982 {
983 	unsigned i;
984 	Dbg_Server ("clearing level negotiation status\n");
985 	for (i = 0; i < MAX_HOSTS; i++) {
986 		levelStatus[i] = 255;
987 	}							/* undefined value */
988 }								/* Server_ClearLevelStatus */
989 
990 /*
991  * set the level status for a single client
992  */
993 void
Server_SetLevelStatus(unsigned id,XBBool val)994 Server_SetLevelStatus (unsigned id, XBBool val)
995 {
996 	assert (id < MAX_HOSTS);
997 	levelStatus[id] = val ? 1 : 0;
998 	Dbg_Server ("level is %s by %u\n", val ? "accepted" : "rejected", id);
999 }								/* Server_SetLevelStatus */
1000 
1001 /*
1002  * reset datagrams connections for new level
1003  */
1004 void
Server_ResetPlayerAction(void)1005 Server_ResetPlayerAction (void)
1006 {
1007 	unsigned client;
1008 
1009 	for (client = 1; client < MAX_HOSTS; client++) {
1010 		if (D2C_Connected (client)) {
1011 			D2C_Reset (client);
1012 		}
1013 	}
1014 	Dbg_Server ("reset frames for all hosts\n");
1015 }								/* Server_ResetPlayerAction */
1016 
1017 /*
1018  * clear player action data
1019  */
1020 void
Server_ClearPlayerAction(void)1021 Server_ClearPlayerAction (void)
1022 {
1023 	unsigned id;
1024 	int player;
1025 
1026 	for (id = 0; id < MAX_HOSTS; id++) {
1027 		for (player = 0; player < MAX_PLAYER; player++) {
1028 			playerAction[id][player].player = player;
1029 			playerAction[id][player].dir = GoDefault;
1030 			playerAction[id][player].bomb = XBFalse;
1031 			playerAction[id][player].special = XBFalse;
1032 			playerAction[id][player].pause = XBFalse;
1033 			playerAction[id][player].suicide = XBFalse;
1034 			playerAction[id][player].abort = ABORT_NONE;
1035 		}
1036 	}
1037 	/* Dbg_Server("cleared action for all hosts\n"); */
1038 }								/* Server_ClearPlayerAction */
1039 
1040 /*
1041  * clear the winner list for async checking
1042  */
1043 void
Server_ClearLevelWinners(void)1044 Server_ClearLevelWinners (void)
1045 {
1046 	unsigned i;
1047 	Dbg_Server ("clearing winner list\n");
1048 	for (i = 0; i < MAX_HOSTS; i++) {
1049 		levelWinner[i] = TEAM_UNDEF;
1050 		currentWinner = TEAM_UNDEF;
1051 		sprintf (levelWinnerStr + 3 * i, "-- ");
1052 	}
1053 }								/* Server_ClearLevelWinners */
1054 
1055 /******************
1056  * queue data out *
1057  ******************/
1058 
1059 /*
1060  * queue all current game configs to client
1061  */
1062 void
Server_SendAllConfigs(unsigned id)1063 Server_SendAllConfigs (unsigned id)
1064 {
1065 	unsigned client;
1066 	unsigned player;
1067 	unsigned rclient;
1068 	XBHostState *hReq;
1069 	XBTeamState *tReq;
1070 	XBAtom atom;
1071 	assert (id > 0);
1072 	assert (id < MAX_HOSTS);
1073 	for (client = 0; client < MAX_HOSTS; client++) {
1074 		if (client == 0 || S2C_Connected (client)) {
1075 			/* send game configs to client */
1076 			S2C_SendGameConfig (id, client, LOCALGAMECONFIG (client));
1077 			S2C_SendHostState (id, client, Network_GetHostState (client));
1078 			hReq = Network_GetHostStateReq (client);
1079 			for (rclient = 0; rclient < MAX_HOSTS; rclient++) {
1080 				if (rclient == 0 || S2C_Connected (rclient)) {
1081 					S2C_SendHostStateReq (id, rclient, client, hReq[rclient]);
1082 				}
1083 			}
1084 			/* send player configs to client */
1085 			for (player = 0; player < NUM_LOCAL_PLAYER; player++) {
1086 				atom = Network_GetPlayer (client, player);
1087 				if (ATOM_INVALID != atom) {
1088 					S2C_SendPlayerConfig (id, client, player, atom);
1089 					S2C_SendTeamState (id, client, player, Network_GetTeamState (client, player));
1090 					tReq = Network_GetTeamStateReq (client, player);
1091 					for (rclient = 0; rclient < MAX_HOSTS; rclient++) {
1092 						if (rclient == 0 || S2C_Connected (rclient)) {
1093 							S2C_SendTeamStateReq (id, rclient, client, player, tReq[rclient]);
1094 						}
1095 					}
1096 				}
1097 			}
1098 			Dbg_Server ("Queued data for host #%u to %u\n", client, id);
1099 		}
1100 	}
1101 }								/* Server_SendAllConfigs */
1102 
1103 /*
1104  * queue dgram port to client
1105  */
1106 void
Server_SendDgramPort(unsigned id)1107 Server_SendDgramPort (unsigned id)
1108 {
1109 	assert (id > 0);
1110 	assert (id < MAX_HOSTS);
1111 	S2C_SendDgramPort (id, D2C_Port (id));
1112 	Dbg_Server ("queueing dgram port number to host #%u\n", id);
1113 }								/* Server_SendDgramPort */
1114 
1115 /*
1116  * queue request for game config to client
1117  */
1118 void
Server_QueryGameConfig(unsigned id)1119 Server_QueryGameConfig (unsigned id)
1120 {
1121 	assert (id > 0);
1122 	assert (id < MAX_HOSTS);
1123 	S2C_QueryGameConfig (id);
1124 	Dbg_Server ("queueing game config request to host #%u\n", id);
1125 }								/* Server_QueryGameConfig */
1126 
1127 /*
1128  * queue game config of client to all hosts
1129  */
1130 void
Server_SendGameConfig(unsigned id)1131 Server_SendGameConfig (unsigned id)
1132 {
1133 	unsigned client;
1134 	XBAtom atom;
1135 	assert (id < MAX_HOSTS);
1136 	atom = (id == 0) ? SERVERGAMECONFIG : LOCALGAMECONFIG (id);
1137 	for (client = 1; client < MAX_HOSTS; client++) {
1138 		if (S2C_Connected (client)) {
1139 			S2C_SendGameConfig (client, id, atom);
1140 		}
1141 	}
1142 	Dbg_Server ("queueing game config #%u to clients\n", id);
1143 }								/* Server_SendGameConfig */
1144 
1145 /*
1146  * queue request for player config to client
1147  */
1148 void
Server_QueryPlayerConfigs(unsigned id,unsigned cnt)1149 Server_QueryPlayerConfigs (unsigned id, unsigned cnt)
1150 {
1151 	unsigned player;
1152 	assert (id < MAX_HOSTS);
1153 	if (id > 0) {
1154 		for (player = 0; player < cnt; player++) {
1155 			DeletePlayerConfig (CT_Demo, Network_GetPlayer (id, player));
1156 			S2C_QueryPlayerConfig (id, player);
1157 		}
1158 		Dbg_Server ("queueing %u player config requests to host #%u\n", cnt, id);
1159 	}
1160 }								/* Server_QueryPlayerConfigs */
1161 
1162 /*
1163  * queue player config of client id:player to all hosts
1164  */
1165 void
Server_SendPlayerConfig(unsigned id,unsigned player)1166 Server_SendPlayerConfig (unsigned id, unsigned player)
1167 {
1168 	unsigned client;
1169 	XBAtom atom;
1170 	assert (id < MAX_HOSTS);
1171 	atom = Network_GetPlayer (id, player);
1172 	assert (ATOM_INVALID != atom);
1173 	for (client = 1; client < MAX_HOSTS; client++) {
1174 		if (S2C_Connected (client)) {
1175 			S2C_SendPlayerConfig (client, id, player, atom);
1176 		}
1177 	}
1178 	Dbg_Server ("queueing player config #%u:%u to hosts\n", id, player);
1179 }								/* Server_SendPlayerConfig */
1180 
1181 /*
1182  * requeue a received chat to connected clients
1183  */
1184 void
Server_SendChat(XBChat * chat)1185 Server_SendChat (XBChat * chat)
1186 {
1187 	unsigned client;
1188 	for (client = 1; client < MAX_HOSTS; client++) {
1189 		if (S2C_Connected (client)) {
1190 			S2C_SendChat (client, chat);
1191 		}
1192 	}
1193 	Dbg_Server ("requeuing chat to clients\n");
1194 }								/* Server_SendChat */
1195 
1196 /*
1197  * server system message on chat
1198  */
1199 void
Server_SysChat(const char * txt)1200 Server_SysChat (const char *txt)
1201 {
1202 	XBChat *chat = Chat_CreateSys ();
1203 	assert (NULL != txt);
1204 	Chat_SetText (chat, txt);
1205 	Server_ReceiveChat (chat);
1206 }								/* Server_SysChat */
1207 
1208 /*
1209  * queue host state to all clients and sync local data
1210  */
1211 void
Server_SendHostState(unsigned id,XBHostState state)1212 Server_SendHostState (unsigned id, XBHostState state)
1213 {
1214 	unsigned clientID;
1215 	assert (id < MAX_HOSTS);
1216 	Network_ReceiveHostState (id, state);
1217 	for (clientID = 1; clientID < MAX_HOSTS; clientID++) {
1218 		if (S2C_Connected (clientID)) {
1219 			S2C_SendHostState (clientID, id, state);
1220 		}
1221 	}
1222 	Dbg_Server ("queued host state for host #%u=%u to hosts\n", id, state);
1223 }								/* Server_SendHostState */
1224 
1225 /*
1226  * queue team state to all clients and sync local data
1227  */
1228 void
Server_SendTeamState(unsigned id,unsigned player,XBTeamState team)1229 Server_SendTeamState (unsigned id, unsigned player, XBTeamState team)
1230 {
1231 	unsigned clientID;
1232 	assert (id < MAX_HOSTS);
1233 	assert (player < MAX_HOSTS);
1234 	Network_ReceiveTeamState (id, player, team);
1235 	for (clientID = 1; clientID < MAX_HOSTS; clientID++) {
1236 		if (S2C_Connected (clientID)) {
1237 			S2C_SendTeamState (clientID, id, player, team);
1238 		}
1239 	}
1240 	Dbg_Server ("queued team state for host #%u(%u)=%u to hosts\n", id, player, team);
1241 }								/* Server_SendTeamState */
1242 
1243 /*
1244  * queue host state request from id for host to state and sync local data
1245  */
1246 void
Server_SendHostStateReq(unsigned id,unsigned host,XBHostState state)1247 Server_SendHostStateReq (unsigned id, unsigned host, XBHostState state)
1248 {
1249 	unsigned clientID;
1250 	assert (id < MAX_HOSTS);
1251 	assert (host < MAX_HOSTS);
1252 	for (clientID = 1; clientID < MAX_HOSTS; clientID++) {
1253 		if (S2C_Connected (clientID)) {
1254 			S2C_SendHostStateReq (clientID, id, host, state);
1255 		}
1256 	}
1257 	Dbg_Server ("queued host request #%u->%u by #%u to hosts\n", host, state, id);
1258 }								/* Server_SendHostStateReq */
1259 
1260 /*
1261  * queue team state request from id for host:player to state and sync local data
1262  */
1263 void
Server_SendTeamStateReq(unsigned id,unsigned host,unsigned player,XBTeamState team)1264 Server_SendTeamStateReq (unsigned id, unsigned host, unsigned player, XBTeamState team)
1265 {
1266 	unsigned clientID;
1267 	assert (id < MAX_HOSTS);
1268 	assert (host < MAX_HOSTS);
1269 	assert (player < MAX_HOSTS);
1270 	for (clientID = 1; clientID < MAX_HOSTS; clientID++) {
1271 		if (S2C_Connected (clientID)) {
1272 			S2C_SendTeamStateReq (clientID, id, host, player, team);
1273 		}
1274 	}
1275 	Dbg_Server ("queued requested team state for player #%u(%u)->%u by #%u to hosts\n", host,
1276 				player, team, id);
1277 }								/* Server_SendTeamStateReq */
1278 
1279 /*
1280  * queue disconnect info to clients
1281  */
1282 void
Server_SendDisconnectInfo(unsigned clientID)1283 Server_SendDisconnectInfo (unsigned clientID)
1284 {
1285 	unsigned id;
1286 	unsigned cnt = 0;
1287 	for (id = 1; id < MAX_HOSTS; id++) {
1288 		if (S2C_Connected (id)) {
1289 			cnt++;
1290 			S2C_HostDisconnected (id, clientID);
1291 		}
1292 	}
1293 	if (cnt) {
1294 		Dbg_Server ("queued disconnect of host #%u to %u hosts\n", clientID, cnt);
1295 	}
1296 }								/* SendDisconnectInfo */
1297 
1298 /*
1299  * queue start game to single client
1300  */
1301 void
Server_SendStart(void)1302 Server_SendStart (void)
1303 {
1304 	unsigned client;
1305 	Server_StopListen ();
1306 	Server_SendDisconnectOut ();
1307 	Dbg_Server ("disconnecting all hosts that are out\n");
1308 	Server_SendGameConfig (0);
1309 	for (client = 1; client < MAX_HOSTS; client++) {
1310 		if (S2C_Connected (client)) {
1311 			S2C_StartGame (client);
1312 			Dbg_Server ("queued start to host #%u\n", client);
1313 		}
1314 	}
1315 }								/* Server_SendStart */
1316 
1317 /*
1318  * queue random seed to clients
1319  */
1320 void
Server_SendRandomSeed(void)1321 Server_SendRandomSeed (void)
1322 {
1323 	unsigned client;
1324 	unsigned seed = GetRandomSeed ();
1325 	for (client = 1; client < MAX_HOSTS; client++) {
1326 		if (S2C_Connected (client)) {
1327 			S2C_SendRandomSeed (client, seed);
1328 		}
1329 	}
1330 	Dbg_Server ("queued random seed %u to hosts\n", seed);
1331 }								/* Server_SendRandomSeed */
1332 
1333 /*
1334  * queue level data to all clients
1335  */
1336 void
Server_SendLevel(const DBRoot * level)1337 Server_SendLevel (const DBRoot * level)
1338 {
1339 	unsigned client;
1340 	for (client = 1; client < MAX_HOSTS; client++) {
1341 		if (S2C_Connected (client)) {
1342 			S2C_SendLevelConfig (client, level);
1343 		}
1344 	}
1345 	Dbg_Server ("queued level data to hosts\n");
1346 }								/* Server_SendLevel */
1347 
1348 /*
1349  * queue level activation to all clients
1350  */
1351 void
Server_SendLevelActivate(void)1352 Server_SendLevelActivate (void)
1353 {
1354 	unsigned clientID;
1355 	/* to all connected client */
1356 	for (clientID = 1; clientID < MAX_HOSTS; clientID++) {
1357 		if (S2C_Connected (clientID)) {
1358 			S2C_SendLevelActivate (clientID);
1359 		}
1360 	}
1361 	Dbg_Server ("queued level activation to hosts\n");
1362 }								/* Server_SendLevelActivate */
1363 
1364 /*
1365  * queue level reset to all clients
1366  */
1367 void
Server_SendLevelReset(void)1368 Server_SendLevelReset (void)
1369 {
1370 	unsigned clientID;
1371 	/* to all connected client */
1372 	for (clientID = 1; clientID < MAX_HOSTS; clientID++) {
1373 		if (S2C_Connected (clientID)) {
1374 			S2C_SendLevelReset (clientID);
1375 		}
1376 	}
1377 	Dbg_Server ("queued level reset to hosts\n");
1378 }								/* Server_SendLevelReset */
1379 
1380 /*
1381  * queue player action to client
1382  */
1383 void
Server_SendPlayerAction(int gameTime,const PlayerAction * playerAction)1384 Server_SendPlayerAction (int gameTime, const PlayerAction * playerAction)
1385 {
1386 	unsigned client;
1387 	for (client = 1; client < MAX_HOSTS; client++) {
1388 		if (D2C_Connected (client)) {
1389 			D2C_SendPlayerAction (client, gameTime, playerAction);
1390 		}
1391 	}
1392 	/* Dbg_Server("queued actions for gt=%u to hosts\n", gameTime); */
1393 }								/* Server_SendPlayerAction */
1394 
1395 /*
1396  * queue finish player actions (= end of level) to clients
1397  */
1398 void
Server_FinishPlayerAction(int gameTime)1399 Server_FinishPlayerAction (int gameTime)
1400 {
1401 	unsigned client;
1402 	for (client = 1; client < MAX_HOSTS; client++) {
1403 		if (D2C_Connected (client)) {
1404 			D2C_SendFinish (client, gameTime);
1405 		}
1406 	}
1407 	Dbg_Server ("queued FINISH to hosts\n");
1408 }								/* Server_FinishPlayerAction */
1409 
1410 /*
1411  * flush last player actions
1412  */
1413 XBBool
Server_FlushPlayerAction(void)1414 Server_FlushPlayerAction (void)
1415 {
1416 	XBBool result;
1417 	unsigned client;
1418 
1419 	result = XBTrue;
1420 	for (client = 1; client < MAX_HOSTS; client++) {
1421 		if (D2C_Connected (client)) {
1422 			if (!D2C_Flush (client)) {
1423 				result = XBFalse;
1424 			}
1425 		}
1426 	}
1427 	Dbg_Server ("flushing actions: (%s needed)\n", result ? "not" : "");
1428 	return result;
1429 }								/* Server_FlushPlayerAction */
1430 
1431 /*
1432  * tell clients that all clients have reached sync point
1433  */
1434 void
Server_SendSync(XBNetworkEvent event)1435 Server_SendSync (XBNetworkEvent event)
1436 {
1437 	unsigned clientID;
1438 
1439 	/* to all connected client */
1440 	for (clientID = 1; clientID < MAX_HOSTS; clientID++) {
1441 		if (S2C_Connected (clientID)) {
1442 			S2C_Sync (clientID, event);
1443 		}
1444 	}
1445 	Dbg_Server ("queued sync %u to hosts\n", event);
1446 }								/* Server_SendSync */
1447 
1448 /*
1449  * queue a level sync result to all clients
1450  */
1451 void
Server_SendLevelSync(void)1452 Server_SendLevelSync (void)
1453 {
1454 	unsigned id;
1455 	/* to all connected client */
1456 	for (id = 1; id < MAX_HOSTS; id++) {
1457 		if (S2C_Connected (id)) {
1458 			S2C_SendLevelSync (id);
1459 		}
1460 	}
1461 	Dbg_Server ("queued LevelSync to clients\n");
1462 }								/* Server_SendLevelSync */
1463 
1464 /*
1465  * queue a level async signal to all clients
1466  */
1467 void
Server_SendLevelAsync(void)1468 Server_SendLevelAsync (void)
1469 {
1470 	unsigned id;
1471 	/* to all connected client */
1472 	for (id = 1; id < MAX_HOSTS; id++) {
1473 		if (S2C_Connected (id)) {
1474 			S2C_SendLevelAsync (id);
1475 		}
1476 	}
1477 	Server_SysChat (TempString ("level async ! (%s)", levelWinnerStr));
1478 	Dbg_Server ("queued LevelAsync to clients\n");
1479 }								/* Server_SendLevelAsync */
1480 
1481 /***********************
1482  * central connections *
1483  ***********************/
1484 
1485 /*
1486  * create connection to central for game info
1487  */
1488 void
Server_StartCentralNewGame(const CFGGameHost * cfg,const CFGGameSetup * setup)1489 Server_StartCentralNewGame (const CFGGameHost * cfg, const CFGGameSetup * setup)
1490 {
1491 #ifdef OLDIFCODE
1492 	size_t numInter;
1493 	const XBSocketInterface *inter;
1494 	size_t i, j;
1495 #endif
1496 	CFGCentralSetup centralSetup;
1497 
1498 	assert (NULL == query);
1499 	Dbg_Server ("attempting to register game\n");
1500 #ifndef OLDIFCODE
1501 	RetrieveCentralSetup (&centralSetup);
1502 	if (NULL == centralSetup.name) {
1503 		Dbg_Server ("no central defined!\n");
1504 		return;
1505 	}
1506 	/* alloc a single communication to central */
1507 	query = calloc (2, sizeof (XBComm *));
1508 	assert (NULL != query);
1509 	/* let system choose the interface to central */
1510 	Dbg_Server ("connecting to central %s:%u\n", centralSetup.name, centralSetup.port);
1511 	query[0] = NewGame_CreateComm (NULL, centralSetup.port, centralSetup.name, cfg, setup);
1512 	if (NULL == query[0]) {
1513 		Dbg_Server ("failed to establish socket to central, game not visible in central\n");
1514 		free (query);
1515 		return;
1516 	}
1517 	Dbg_Server ("socket to central established\n");
1518 #else
1519 	inter = Socket_GetInterfaces (&numInter);
1520 	if (NULL == inter) {
1521 		Dbg_Server ("no interfaces found!\n");
1522 		return;
1523 	}
1524 	Dbg_Server ("interfaces found = %u\n", numInter);
1525 	RetrieveCentralSetup (&centralSetup);
1526 	if (NULL == centralSetup.name) {
1527 		Dbg_Server ("no central defined!\n");
1528 		return;
1529 	}
1530 	/* alloc 1 pointer (to central) */
1531 	query = calloc (1 + numInter, sizeof (XBComm *));
1532 	assert (NULL != query);
1533 	Dbg_Server ("connecting to central %s:%u\n", centralSetup.name, centralSetup.port);
1534 	/* start query on at most one device */
1535 	/* FIXXX used to be i=1 worked on linux */
1536 #ifdef W32
1537 	Dbg_Server ("W32\n");
1538 #ifdef CYG
1539 	for (i = 0, j = 0; (j == 0) && (i < numInter); i++) {
1540 #else
1541 	for (i = 1, j = 0; (j == 0) && (i < numInter); i++) {
1542 #endif
1543 #else
1544 	Dbg_Server ("Linux\n");
1545 	for (i = 1, j = 0; (j == 0) && (i < numInter); i++) {
1546 #endif
1547 		if (NULL !=
1548 			(query[j] =
1549 			 NewGame_CreateComm (inter[i].addrDevice, centralSetup.port, centralSetup.name, cfg,
1550 								 setup))) {
1551 			Dbg_Server ("established query to %s on interface %u\n", centralSetup.name, i);
1552 			j++;
1553 		}
1554 	}
1555 	/* make sure not more than one device is used, would clash with close routines */
1556 	assert (j == 1);
1557 #endif
1558 	/* queue initial data to central */
1559 	Server_RestartNewGame (1, "");
1560 }								/* Server_StartCentralNewGame */
1561 
1562 /*
1563  * queue game info to central
1564  */
1565 /* GAMEONFIX */
1566 void
1567 Server_RestartNewGame (int num, const char *score)
1568 {
1569 	if (NULL != query) {
1570 		struct timeval tv;
1571 		int i;
1572 		gettimeofday (&tv, NULL);
1573 		for (i = 0; query[i] != NULL; i++) {
1574 			NewGame_Send (query[i], &tv, num, score);
1575 			Dbg_Server ("queued game data to central\n");
1576 		}
1577 	}
1578 }								/* Server_RestartQuery */
1579 
1580 /* GAMEONFIX */
1581 
1582 /*
1583  * queue request to central for removing game info
1584  */
1585 void
1586 Server_CloseNewGame (void)
1587 {
1588 	if (NULL != query) {
1589 		struct timeval tv;
1590 		int i;
1591 		gettimeofday (&tv, NULL);
1592 		for (i = 0; query[i] != NULL; i++) {
1593 			NewGame_Close (query[i], &tv);
1594 			Dbg_Server ("queued game close to central\n");
1595 		}
1596 	}
1597 }								/* Server_CloseNewGame */
1598 
1599 /*
1600  * delete open connection to central
1601  */
1602 void
1603 Server_StopNewGame (void)
1604 {
1605 	size_t i;
1606 	/* delete communications */
1607 	assert (NULL != query);
1608 	for (i = 0; query[i] != NULL; i++) {
1609 		assert (i == 0);
1610 		Dbg_Server ("forcing stop of previous newgame\n");
1611 		CommDelete (query[i]);
1612 	}
1613 }								/* Server_StopQuery */
1614 
1615 /*
1616  * receive close event from query
1617  */
1618 void
1619 Server_ReceiveNewGameClose (void)
1620 {
1621 	assert (NULL != query);
1622 	free (query);
1623 	query = NULL;
1624 	Dbg_Server ("removed newgame sockets to central\n");
1625 } /**/
1626 
1627 /*
1628  * end of file server.c
1629  */
1630