1 /*
2  * file network.c - shared functions for server and clients
3  *
4  * $Id: network.c,v 1.42 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 types
28  */
29 typedef struct _network_event
30 {
31 	struct _network_event *next;
32 	XBNetworkEvent msg;
33 	unsigned id;
34 } XBNetworkEventQueue;
35 
36 typedef struct
37 {
38 	unsigned char host;
39 	unsigned char player;
40 } XBHostPlayerId;
41 
42 /*
43  * local variables
44  */
45 
46 /* events */
47 static XBNetworkEventQueue *queueFirst = NULL;
48 static XBNetworkEventQueue *queueLast = NULL;
49 
50 /* network type */
51 static XBNetworkType nettype = XBNT_None;
52 static unsigned char localId = MAX_HOSTS;
53 
54 /* player atoms and player count */
55 static unsigned players = 0;
56 static XBAtom hostPlayer[MAX_HOSTS][NUM_LOCAL_PLAYER];
57 static XBAtom hostPlayer2[MAX_HOSTS][NUM_LOCAL_PLAYER];
58 
59 /* local player counts */
60 static unsigned localPlayers[MAX_HOSTS];
61 
62 /* global config and conversion to local */
63 static XBBool global = XBFalse;
64 static CFGGame globalcfg;
65 static XBHostPlayerId s2l[MAX_PLAYER];
66 static unsigned char l2s[MAX_HOSTS][NUM_LOCAL_PLAYER];
67 
68 /* ping times */
69 static unsigned hostPing[MAX_HOSTS];
70 
71 /* host/team states per host */
72 static XBHostState hostState[MAX_HOSTS];
73 static XBTeamState teamState[MAX_HOSTS][NUM_LOCAL_PLAYER];
74 
75 /* default team states */
76 static XBTeamState defTeam[MAX_HOSTS][NUM_LOCAL_PLAYER];
77 
78 /* requested raw host/team states received for each host */
79 static XBHostState hostStateReq[MAX_HOSTS][MAX_HOSTS];
80 static XBTeamState teamStateReq[MAX_HOSTS][NUM_LOCAL_PLAYER][MAX_HOSTS];
81 
82 /* constant team color assignment for team states */
83 const XBColor teamColors[NUM_XBTS] = {
84 	COLOR_INVALID, COLOR_INVALID, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_INVALID,
85 };
86 
87 /***************
88  * local stuff *
89  ***************/
90 
91 /*
92  * reset host data
93  */
94 static void
ClearHostLocalData(unsigned id)95 ClearHostLocalData (unsigned id)
96 {
97 	unsigned host;
98 	unsigned pl;
99 	/* update total player count first */
100 	players -= localPlayers[id];
101 	/* clear data for host id */
102 	hostPing[id] = -1;
103 	hostState[id] = XBHS_None;
104 	localPlayers[id] = 0;
105 	for (pl = 0; pl < NUM_LOCAL_PLAYER; pl++) {
106 		hostPlayer[id][pl] = ATOM_INVALID;
107 		hostPlayer2[id][pl] = ATOM_INVALID;
108 		teamState[id][pl] = XBTS_Invalid;
109 	}
110 	for (host = 0; host < MAX_HOSTS; host++) {
111 		hostStateReq[id][host] = XBHS_None;
112 		hostStateReq[host][id] = XBHS_None;
113 		for (pl = 0; pl < NUM_LOCAL_PLAYER; pl++) {
114 			defTeam[id][pl] = XBTS_Red;
115 			teamStateReq[id][pl][host] = XBTS_Invalid;
116 			teamStateReq[host][pl][id] = XBTS_Invalid;
117 		}
118 	}
119 }								/* ClearHostLocalData */
120 
121 /******************
122  * initialization *
123  ******************/
124 
125 /*
126  * clear all host data
127  */
128 void
Network_ClearHost(unsigned id)129 Network_ClearHost (unsigned id)
130 {
131 	ClearHostLocalData (id);
132 	Version_Remove (id);
133 	DeleteGameConfig (CT_Remote, LOCALGAMECONFIG (id));
134 	Dbg_Network ("cleared host data #%u\n", id);
135 }								/* Network_ClearHost */
136 
137 /*
138  * clear all host, event, chat and version data
139  */
140 void
Network_Clear(void)141 Network_Clear (void)
142 {
143 	unsigned id, pl;
144 	/* clear data */
145 	localId = MAX_HOSTS;
146 	players = 0;
147 	memset (hostPlayer, 0, sizeof (hostPlayer));
148 	memset (hostPlayer2, 0, sizeof (hostPlayer2));
149 	memset (localPlayers, 0, sizeof (localPlayers));
150 	memset (hostPing, -1, sizeof (hostPing));
151 	memset (hostState, 0, sizeof (hostState));
152 	memset (hostStateReq, 0, sizeof (hostStateReq));
153 	memset (teamState, 0, sizeof (teamState));
154 	memset (teamStateReq, 0, sizeof (teamStateReq));
155 	/* clear remote game configs and versions in database */
156 	for (id = 0; id < MAX_HOSTS; id++) {
157 		DeleteGameConfig (CT_Remote, LOCALGAMECONFIG (id));
158 		for (pl = 0; pl < NUM_LOCAL_PLAYER; pl++) {
159 			defTeam[id][pl] = XBTS_Red;
160 		}
161 	}
162 	/* clear global game config and links */
163 	global = XBFalse;
164 	DeleteGameConfig (CT_Remote, SERVERGAMECONFIG);
165 	memset (s2l, 0xFF, sizeof (s2l));
166 	memset (l2s, 0xFF, sizeof (l2s));
167 	/* reset version management */
168 	Version_Reset ();
169 	Network_SetType (XBNT_None);
170 #ifdef DEBUG_NETWORK
171 	Dbg_Network ("cleared all host data\n");
172 #endif
173 }								/* Network_Clear */
174 
175 /************************************
176  * network events for client/server *
177  ************************************/
178 
179 #ifdef DEBUG_NETWORK
180 /*
181  * network event to string
182  */
183 static const char *
NWEventName(XBNetworkEvent msg)184 NWEventName (XBNetworkEvent msg)
185 {
186 	switch (msg) {
187 	case XBNW_None:
188 		return "None";
189 	case XBNW_Accepted:
190 		return "Accepted";
191 	case XBNW_GameConfig:
192 		return "GameConfig";
193 	case XBNW_RightPlayerConfig:
194 		return "RightPlayerConfig";
195 	case XBNW_LeftPlayerConfig:
196 		return "LeftPlayerConfig";
197 	case XBNW_Joy1PlayerConfig:
198 		return "Joy1PlayerConfig";
199 	case XBNW_Joy2PlayerConfig:
200 		return "Joy2PlayerConfig";
201 	case XBNW_Disconnected:
202 		return "Disconnected";
203 	case XBNW_StartGame:
204 		return "StartGame";
205 	case XBNW_EndOfInit:
206 		return "EndOfInit";
207 	case XBNW_LevelConfig:
208 		return "LevelConfig";
209 	case XBNW_SyncEndOfInit:
210 		return "SyncEndOfInit";
211 	case XBNW_SyncLevelIntro:
212 		return "SyncLevelIntro";
213 	case XBNW_SyncLevelResult:
214 		return "SyncLevelResult";
215 	case XBNW_SyncLevelEnd:
216 		return "SyncLevelEnd";
217 	case XBNW_SyncScoreboard:
218 		return "SyncScoreboard";
219 	case XBNW_HostIsIn:
220 		return "HostIsIn";
221 	case XBNW_HostIsOut:
222 		return "HostIsOut";
223 	case XBNW_Error:
224 		return "Error";
225 	case XBNW_PingReceived:
226 		return "PingReceived";
227 	case XBNW_NetworkGame:
228 		return "NetworkGame";
229 	case XBNW_TeamChange:
230 		return "TeamChange";
231 	case XBNW_TeamChangeData:
232 		return "TeamChangeData";
233 	case XBNW_HostChange:
234 		return "HostChange";
235 	default:
236 		return "unknown";
237 	}
238 }								/* EventName */
239 #endif
240 
241 /*
242  * clear event queue
243  */
244 void
Network_ClearEvents(void)245 Network_ClearEvents (void)
246 {
247 	unsigned id;
248 	while (NULL != queueFirst) {
249 		(void)Network_GetEvent (&id);
250 	}
251 #ifdef DEBUG_NETWORK
252 	Dbg_Network ("clearing all network events\n");
253 #endif
254 }								/* Network_ClearEvents */
255 
256 /*
257  * add event to queue
258  */
259 void
Network_QueueEvent(XBNetworkEvent msg,unsigned id)260 Network_QueueEvent (XBNetworkEvent msg, unsigned id)
261 {
262 	/* alloc data */
263 	XBNetworkEventQueue *ptr = calloc (1, sizeof (XBNetworkEventQueue));
264 	assert (ptr != NULL);
265 	/* set values */
266 	ptr->msg = msg;
267 	ptr->id = id;
268 	/* put in queue */
269 	if (queueLast != NULL) {
270 		queueLast->next = ptr;
271 	}
272 	else {
273 		queueFirst = ptr;
274 	}
275 	queueLast = ptr;
276 #ifdef DEBUG_NETWORK
277 	Dbg_Network ("queue network event %s %u\n", NWEventName (msg), id);
278 #endif
279 }								/* QueueEvent */
280 
281 /*
282  * check for event in queue
283  */
284 XBNetworkEvent
Network_GetEvent(unsigned * pId)285 Network_GetEvent (unsigned *pId)
286 {
287 	XBNetworkEventQueue *ptr;
288 	XBNetworkEvent msg;
289 
290 	assert (NULL != pId);
291 	if (NULL == queueFirst) {
292 		return XBNW_None;
293 	}
294 	/* take element from list */
295 	ptr = queueFirst;
296 	queueFirst = queueFirst->next;
297 	if (NULL == queueFirst) {
298 		queueLast = NULL;
299 	}
300 	/* set results */
301 	msg = ptr->msg;
302 	*pId = ptr->id;
303 #ifdef DEBUG_NETWORK
304 	Dbg_Network ("get network event %s %u\n", NWEventName (msg), *pId);
305 #endif
306 	/* free element */
307 	free (ptr);
308 	/* that's all */
309 	return msg;
310 }								/* Network_GetEvent */
311 
312 /*******************
313  * type of network *
314  *******************/
315 
316 /*
317  * get network type
318  */
319 XBNetworkType
Network_GetType(void)320 Network_GetType (void)
321 {
322 	return nettype;
323 }								/* Network_GetType */
324 
325 /*
326  * set network type
327  */
328 void
Network_SetType(XBNetworkType type)329 Network_SetType (XBNetworkType type)
330 {
331 	nettype = type;
332 #ifdef DEBUG_NETWORK
333 	Dbg_Network ("setting type to %u\n", type);
334 #endif
335 }								/* Network_SetType */
336 
337 /************
338  * local id *
339  ************/
340 
341 /*
342  * receive local host id
343  */
344 void
Network_ReceiveLocalHostId(unsigned id)345 Network_ReceiveLocalHostId (unsigned id)
346 {
347 	Dbg_Network ("receiving local host id = %u\n", id);
348 	localId = id & 0xFF;
349 }								/* Network_ReceiveLocalHostId */
350 
351 /*
352  * get local host id
353  */
354 unsigned char
Network_LocalHostId(void)355 Network_LocalHostId (void)
356 {
357 	return localId;
358 }								/* Network_LocalHostId */
359 
360 /**************
361  * ping times *
362  **************/
363 
364 /*
365  * get ping time of host
366  */
367 int
Network_GetPingTime(unsigned id)368 Network_GetPingTime (unsigned id)
369 {
370 	assert (id < MAX_HOSTS);
371 	return hostPing[id];
372 }								/* Network_GetPingTime */
373 
374 /*
375  * ping received
376  */
377 void
Network_ReceivePing(unsigned id,int ping)378 Network_ReceivePing (unsigned id, int ping)
379 {
380 	assert (id < MAX_HOSTS);
381 	if (hostPing[id] != ping) {
382 		Network_QueueEvent (XBNW_PingReceived, id);
383 	}
384 	hostPing[id] = ping;
385 }								/* Network_ReceivePing */
386 
387 /****************
388  * player atoms *
389  ****************/
390 
391 /*
392  * get player atom
393  */
394 XBAtom
Network_GetPlayer(unsigned id,int player)395 Network_GetPlayer (unsigned id, int player)
396 {
397 	assert (id < MAX_HOSTS);
398 	assert (player < MAX_PLAYER);
399 	return hostPlayer[id][player];
400 }								/* Network_GetPlayer */
401 
402 /*
403  * get player atom
404  */
405 XBAtom
Network_GetPlayer2(unsigned id,int player)406 Network_GetPlayer2 (unsigned id, int player)
407 {
408 	assert (id < MAX_HOSTS);
409 	assert (player < MAX_PLAYER);
410 	return hostPlayer2[id][player];
411 }								/* Network_GetPlayer2 */
412 
413 /*
414  * set player atom
415  */
416 void
Network_SetPlayer(unsigned id,int player,XBAtom atom)417 Network_SetPlayer (unsigned id, int player, XBAtom atom)
418 {
419 	assert (id < MAX_HOSTS);
420 	assert (player < MAX_PLAYER);
421 	hostPlayer[id][player] = atom;
422 	if (atom != ATOM_INVALID) {
423 		players += 1;
424 		localPlayers[id] += 1;
425 	}
426 }								/* Network_SetPlayer */
427 
428 /*
429  * set player atom
430  */
431 void
Network_SetPlayer2(unsigned id,int player,XBAtom atom)432 Network_SetPlayer2 (unsigned id, int player, XBAtom atom)
433 {
434 	assert (id < MAX_HOSTS);
435 	assert (player < MAX_PLAYER);
436 	hostPlayer2[id][player] = atom;
437 }								/* Network_SetPlayer2 */
438 
439 /*
440  * get first player different from given one
441  */
442 XBBool
Network_GetFirstOtherPlayer(unsigned char id,unsigned char pl,unsigned char * h,unsigned char * p)443 Network_GetFirstOtherPlayer (unsigned char id, unsigned char pl, unsigned char *h, unsigned char *p)
444 {
445 	*h = 0;
446 	*p = 0;
447 	if (hostPlayer2[0][0] != ATOM_INVALID) {
448 		if (id != 0 || pl < NUM_LOCAL_PLAYER) {
449 			return XBTrue;
450 		}
451 	}
452 	return Network_GetNextOtherPlayer (id, pl, h, p);
453 }								/* Network_GetFirstOtherPlayer */
454 
455 /*
456  * get next player different from given one
457  */
458 XBBool
Network_GetNextOtherPlayer(unsigned char id,unsigned char pl,unsigned char * h,unsigned char * p)459 Network_GetNextOtherPlayer (unsigned char id, unsigned char pl, unsigned char *h, unsigned char *p)
460 {
461 	*p += 1;
462 	while (*h < MAX_HOSTS) {
463 		if (id != *h || pl < NUM_LOCAL_PLAYER) {
464 			while (*p < NUM_LOCAL_PLAYER) {
465 				if ((hostPlayer2[*h][*p] != ATOM_INVALID) && (*h != id || *p != pl)) {
466 					Dbg_Chat ("next = %s (%u,%u)\n", GUI_AtomToString (hostPlayer2[*h][*p]), *h,
467 							  *p);
468 					return XBTrue;
469 				}
470 				*p += 1;
471 			}
472 		}
473 		*h += 1;
474 		*p = 0;
475 	}
476 	return XBFalse;
477 }								/* Network_GetNextOtherPlayer */
478 
479 /*************
480  * host data *
481  *************/
482 
483 /*
484  * return player max for host, for check against global game config
485  */
486 unsigned
Network_HostPlayerMax(unsigned id)487 Network_HostPlayerMax (unsigned id)
488 {
489 	CFGGameConst con;
490 	unsigned max = -1;
491 	assert (id < MAX_HOSTS);
492 	(void)RetrieveGameConst (CT_Remote, LOCALGAMECONFIG (id), &con);
493 	max = 8 * con.maxbytes - 1;
494 	max = (max < con.maxplayers) ? max : con.maxplayers;
495 	return max;
496 }								/* Network_HostPlayerMax */
497 
498 /***************
499  * game config *
500  ***************/
501 
502 /*
503  * create global game config from current setup
504  */
505 XBGameConfigResult
Network_CreateGlobalGameConfig(CFGGame * cfg)506 Network_CreateGlobalGameConfig (CFGGame * cfg)
507 {
508 	CFGGamePlayers localcfg;
509 	unsigned char t[NUM_XBTS];
510 	unsigned char p, save;
511 	XBTeamState team = XBTS_Invalid;
512 	unsigned char id, pl, host;
513 	assert (cfg != NULL);
514 	Dbg_Network ("creating global game config\n");
515 	memset (t, 0, sizeof (t));
516 	p = 0;
517 	for (id = 0, host = XBPH_Server; id < MAX_HOSTS; id++, host++) {
518 		/* first check if host is allowed to take part */
519 		if (!Network_HostIsIn (id)) {
520 			Dbg_Network ("host #%u ignored, is out\n", id);
521 			continue;
522 		}
523 		/* now get player data for host */
524 		if (!RetrieveGamePlayers (CT_Remote, LOCALGAMECONFIG (id), &localcfg)) {
525 			Dbg_Network ("failed to get players for host #%u, failure\n", id);
526 			return XBGC_Error;
527 		}
528 		/* check number of players */
529 		if (localcfg.num == 0) {
530 			Dbg_Network ("no players for host #%u, failure\n", id);
531 			return XBGC_Error;
532 		}
533 		/* add each player */
534 		save = p;
535 		for (pl = 0; pl < localcfg.num; pl++) {
536 			team = Network_GetTeamState (id, pl);
537 			if (team == XBTS_Out) {
538 				Dbg_Network ("player %u(%u) is out, ignoring\n", id, pl);
539 				continue;
540 			}
541 			if (p >= MAX_PLAYER) {
542 				Dbg_Network ("more players than I can handle (=%u)!\n", MAX_PLAYER);
543 				return XBGC_TooManyPlayers;
544 			}
545 			cfg->players.player[p] = Network_GetPlayer (id, pl);
546 			if (ATOM_INVALID == cfg->players.player[p]) {
547 				Dbg_Network ("player #%u(%u) has no name, failure\n", id, pl);
548 				return XBGC_Error;
549 			}
550 			cfg->players.control[p] = localcfg.control[pl];
551 			if (XBPC_None == cfg->players.control[p]) {
552 				Dbg_Network ("warning, player #%u(%u) has no control\n", id, pl);
553 			}
554 			cfg->players.playerID[p] = localcfg.playerID[pl];
555 			cfg->players.host[p] = host;
556 			cfg->players.team[p] = team;
557 			Dbg_Network ("adding player #%u(%u)=%s, team %u\n", id, pl,
558 						 GUI_AtomToString (cfg->players.player[p]), team);
559 			p += 1;
560 			t[team] += 1;
561 		}
562 		if (save == p) {
563 			Dbg_Network ("host %u has no active players, will only watch/chat!\n", id);
564 		}
565 	}
566 	cfg->players.num = p;
567 	cfg->setup.maskBytes = (int)(1 + p / 8);
568 	Dbg_Network ("--- Results of game config creation ---\n");
569 	/* check for invalid teams */
570 	if (t[XBTS_Invalid] > 0) {
571 		Dbg_Network ("%u invalid teams were assigned, failure", t[XBTS_Invalid]);
572 		return XBGC_Error;
573 	}
574 	/* check player count */
575 	if (p <= 1) {
576 		Dbg_Network ("at least two players are required!\n");
577 		return XBGC_SingleTeam;
578 	}
579 	/* check for chaos mode */
580 	if (t[XBTS_None] == p) {
581 		Dbg_Network ("created game config with %u players, chaos mode\n", p);
582 		cfg->setup.teamMode = XBTM_None;
583 		return XBGC_Global;
584 	}
585 	else if (t[XBTS_None] > 0) {
586 		Dbg_Network ("invalid team mode, failure\n");
587 		return XBGC_Error;
588 	}
589 	/* check for multiple teams */
590 	for (team = XBTS_Red; team < NUM_XBTS; team++) {
591 		if (t[team] < p) {
592 			Dbg_Network ("%u players assigned to team %u\n", t[team], team);
593 		}
594 		else {
595 			Dbg_Network ("all players assigned to team %u\n", team);
596 			return XBGC_SingleTeam;
597 		}
598 	}
599 	Dbg_Network ("created game config with %u players, team mode\n", p);
600 	cfg->setup.teamMode = XBTM_Team;
601 	return XBGC_Global;
602 }								/* Network_CreateGameConfig */
603 
604 /*
605  * store game config from server or client in database
606  */
607 XBGameConfigResult
Network_ReceiveGameConfig(unsigned id,const char * data)608 Network_ReceiveGameConfig (unsigned id, const char *data)
609 {
610 	XBAtom atom;
611 	CFGGamePlayers cfg;
612 	CFGGameConst con;
613 	XBVersion ver;
614 	XBBool loc;
615 	unsigned char p;
616 	/* check if valid id was received */
617 	if (id >= MAX_HOSTS) {
618 		return XBGC_HostInvalid;
619 	}
620 	/* set section atom to write to */
621 	atom = (id == 0) ? SERVERGAMECONFIG : LOCALGAMECONFIG (id);
622 	if (NULL != data) {
623 		/* write data and return */
624 		AddToGameConfig (CT_Remote, atom, data);
625 		return XBGC_Unfinished;
626 	}
627 	/* game config complete */
628 	Dbg_Network ("received game config from host #%u\n", id);
629 	/* extract player data in struct, shouldn't fail */
630 	if (!RetrieveGamePlayers (CT_Remote, atom, &cfg)) {
631 		Dbg_Network ("error in game config\n");
632 		return XBGC_Error;
633 	}
634 	/* check if empty */
635 	if (cfg.num == 0) {
636 		Dbg_Network ("no players found\n");
637 		return XBGC_Empty;
638 	}
639 	Dbg_Network ("game config contains %u players\n", cfg.num);
640 	/* check for too many players */
641 	if (cfg.num > MAX_PLAYER) {
642 		Dbg_Network ("too many players found\n");
643 		return XBGC_TooManyPlayers;
644 	}
645 	/* extract constants, should not fail */
646 	if (!RetrieveGameConst (CT_Remote, atom, &con)) {
647 		Dbg_Network ("failed to get constants\n");
648 		return XBGC_Error;
649 	}
650 	/* check for existing version */
651 	if (!Version_isDefined (&con.version)) {
652 		Dbg_Network ("no version sent\n");
653 		return XBGC_NoVersion;
654 	}
655 	Dbg_Network ("remote constants: MH=%u, MP=%u, ML=%u, MB=%u\n", con.maxhosts, con.maxplayers,
656 				 con.maxlocals, con.maxbytes);
657 	/* register version */
658 	if (Version_Join (id & 0xFF, &con.version)) {
659 		Version_Get (VERSION_JOINT, &ver);
660 		Dbg_Network ("joint version updated to %s\n", Version_ToString (&ver));
661 	}
662 	/* check type, at least one player has been sent */
663 	loc = (cfg.host[0] == XBPH_Local);
664 	for (p = 0; p < cfg.num; p++) {
665 		if (ATOM_INVALID == cfg.player[p]) {
666 			Dbg_Network ("player #%u has no name, error\n", p);
667 			return XBGC_Error;
668 		}
669 		if (loc != (cfg.host[p] == XBPH_Local)) {
670 			Dbg_Network ("error in game config\n");
671 			return XBGC_Error;
672 		}
673 	}
674 	Dbg_Network ("game config type = %s\n", loc ? "local" : "global");
675 	return loc ? XBGC_Local : XBGC_Global;
676 }								/* Network_ReceiveGameConfig */
677 
678 /*
679  * create local players from game config id
680  */
681 unsigned
Network_CreateLocalPlayers(unsigned id)682 Network_CreateLocalPlayers (unsigned id)
683 {
684 	CFGGame cfg;
685 	XBVersion ver;
686 	XBAtom atom;
687 	unsigned p;
688 	char name[256];
689 	assert (id < MAX_HOSTS);
690 	/* get remote atom with received data */
691 	atom = (id == 0) ? SERVERGAMECONFIG : LOCALGAMECONFIG (id);
692 	/* extract data in struct, shouldn't fail */
693 	if (!RetrieveGame (CT_Remote, atom, &cfg)) {
694 		Dbg_Network ("error in game config!?\n");
695 		return NUM_LOCAL_PLAYER;
696 	}
697 	/* check local player max */
698 	if (cfg.players.num > NUM_LOCAL_PLAYER) {
699 		Dbg_Network ("create failed, too many local players\n");
700 		return NUM_LOCAL_PLAYER;
701 	}
702 	/* clear local data */
703 	ClearHostLocalData (id);
704 	/* for server data, store as local game config */
705 	if (id == 0) {
706 		StoreGame (CT_Remote, LOCALGAMECONFIG (0), &cfg);
707 		if (RetrieveGameVersion (CT_Remote, atom, &ver)) {
708 			StoreGameVersion (CT_Remote, LOCALGAMECONFIG (0), &ver);
709 		}
710 	}
711 	/* add players from game config */
712 	for (p = 0; p < cfg.players.num; p++) {
713 		/* first add name atom, must be valid */
714 		assert (ATOM_INVALID != cfg.players.player[p]);
715 		Network_SetPlayer2 (id, p, cfg.players.player[p]);
716 		/* use plain name as section name for server */
717 		atom = cfg.players.player[p];
718 		/* add @host:port for non-server, to make it unique */
719 		if (id != 0) {
720 			sprintf (name, "%s@%s:%d", GUI_AtomToString (atom), cfg.host.name, cfg.host.port);
721 			atom = GUI_StringToAtom (name);
722 		}
723 		/* now store player section atom */
724 		Network_SetPlayer (id, p, atom);
725 		Dbg_Network ("hostPlayer[%u][%d] = %s\n", id, p, GUI_AtomToString (atom));
726 		/* set chat controls */
727 		if (localId == id) {
728 			switch (cfg.players.control[p]) {
729 			case XBPC_RightKeyboard:
730 				Dbg_Network ("local player #%u on right keyboard, enabling chat\n", id);
731 				Chat_AddEventCode (p, XBE_KEYB_1);
732 				break;
733 			case XBPC_LeftKeyboard:
734 				Dbg_Network ("local player #%u on left keyboard, enabling chat\n", id);
735 				Chat_AddEventCode (p, XBE_KEYB_2);
736 				break;
737 			default:
738 				Dbg_Network ("local player #%u has no keyboard control, no chatting\n", id);
739 				break;
740 			}
741 		}
742 	}
743 	Dbg_Network ("created %u players, total %u\n", cfg.players.num, players);
744 	return cfg.players.num;
745 }								/* Network_CreateLocalPlayers */
746 
747 /*
748  * create global players from game config, only allowed from server
749  */
750 unsigned
Network_CreateGlobalPlayers(unsigned id)751 Network_CreateGlobalPlayers (unsigned id)
752 {
753 	unsigned h, p, pl;
754 	assert (id == 0);
755 	/* extract data in struct, shouldn't fail */
756 	if (!RetrieveGame (CT_Remote, SERVERGAMECONFIG, &globalcfg)) {
757 		Dbg_Network ("error in game config!?\n");
758 		return MAX_PLAYER;
759 	}
760 	/* check max player */
761 	if (globalcfg.players.num > MAX_PLAYER) {
762 		Dbg_Network ("create failed, too many global players\n");
763 		return MAX_PLAYER;
764 	}
765 	/* check mask bytes */
766 	if (globalcfg.setup.maskBytes > MAX_MASK_BYTES) {
767 		Dbg_Network ("create failed, too many mask bytes\n");
768 		return MAX_PLAYER;
769 	}
770 	/* now link with local */
771 	memset (s2l, 0xFF, sizeof (s2l));
772 	memset (l2s, 0xFF, sizeof (l2s));
773 	for (pl = 0; pl < globalcfg.players.num; pl++) {
774 		for (h = 0; h < MAX_HOSTS; h++) {
775 			for (p = 0; p < localPlayers[h]; p++) {
776 				if (globalcfg.players.player[pl] == hostPlayer[h][p]) {
777 					Dbg_Network ("linking global player %u with local player #%u(%u)\n", pl, h, p);
778 					s2l[pl].host = h;
779 					s2l[pl].player = p;
780 					l2s[h][p] = pl;
781 					break;
782 				}
783 			}
784 		}
785 		if (s2l[pl].host >= MAX_HOSTS) {
786 			Dbg_Network ("failed to match global player with local\n");
787 			return MAX_PLAYER;
788 		}
789 	}
790 	/* success */
791 	global = XBTrue;
792 	return globalcfg.players.num;
793 }								/* Network_CreateGlobalPlayers */
794 
795 /*
796  * getting current mask bytes
797  */
798 unsigned
Network_GetMaskBytes(void)799 Network_GetMaskBytes (void)
800 {
801 	/* no mask bytes if no global game config */
802 	if (!global) {
803 		return 0;
804 	}
805 	return globalcfg.setup.maskBytes;
806 }								/* Network_GetMaskBytes */
807 
808 /*****************
809  * player config *
810  *****************/
811 
812 /*
813  * player config received from client
814  */
815 XBAtom
Network_ReceivePlayerConfig(CFGType cfgType,unsigned id,int player,const char * line)816 Network_ReceivePlayerConfig (CFGType cfgType, unsigned id, int player, const char *line)
817 {
818 	XBAtom atom;
819 
820 	assert (id < MAX_HOSTS);
821 	assert (player < NUM_LOCAL_PLAYER);
822 	/* get player for config */
823 	atom = Network_GetPlayer (id, player);
824 	if (ATOM_INVALID == atom) {
825 		return ATOM_INVALID;
826 	}
827 	/* check if there is any data */
828 	if (NULL != line) {
829 		AddToPlayerConfig (cfgType, atom, line);
830 		/* ok that's all for now */
831 		return ATOM_INVALID;
832 	}
833 	/* all data received */
834 	Dbg_Network ("received player config for %u(%u)\n", id, player);
835 	switch (player) {
836 	case 0:
837 		Network_QueueEvent (XBNW_RightPlayerConfig, id);
838 		break;
839 	case 1:
840 		Network_QueueEvent (XBNW_LeftPlayerConfig, id);
841 		break;
842 	case 2:
843 		Network_QueueEvent (XBNW_Joy1PlayerConfig, id);
844 		break;
845 	case 3:
846 		Network_QueueEvent (XBNW_Joy2PlayerConfig, id);
847 		break;
848 	default:
849 		break;
850 	}
851 	return atom;
852 }								/* Network_ReceivePlayerConfig */
853 
854 /******************
855  * state requests *
856  ******************/
857 
858 /*
859  * receive host state for a host
860  */
861 XBBool
Network_ReceiveHostState(unsigned id,XBHostState state)862 Network_ReceiveHostState (unsigned id, XBHostState state)
863 {
864 	if (id < MAX_HOSTS) {
865 		hostState[id] = state;
866 		Network_QueueEvent (XBNW_HostChange, id);
867 		return XBTrue;
868 	}
869 	return XBFalse;
870 }								/* Network_ReceiveHostState */
871 
872 /*
873  * receive host state request for a host
874  */
875 XBBool
Network_ReceiveHostStateReq(unsigned who,unsigned id,XBHostState state)876 Network_ReceiveHostStateReq (unsigned who, unsigned id, XBHostState state)
877 {
878 	if (who < MAX_HOSTS && id < MAX_HOSTS) {
879 		hostStateReq[id][who] = state;
880 		return XBTrue;
881 	}
882 	return XBFalse;
883 }								/* Network_ReceiveHostStateReq */
884 
885 /*
886  * receive team state for a host/player
887  */
888 XBBool
Network_ReceiveTeamState(unsigned host,unsigned player,XBTeamState team)889 Network_ReceiveTeamState (unsigned host, unsigned player, XBTeamState team)
890 {
891 	if (host < MAX_HOSTS && player < MAX_PLAYER) {
892 		teamState[host][player] = team;
893 		return XBTrue;
894 	}
895 	return XBFalse;
896 }								/* Network_ReceiveTeamState */
897 
898 /*
899  * receive team state for a host/player
900  */
901 XBBool
Network_ReceiveTeamStateReq(unsigned who,unsigned host,unsigned player,XBTeamState team)902 Network_ReceiveTeamStateReq (unsigned who, unsigned host, unsigned player, XBTeamState team)
903 {
904 	if (who < MAX_HOSTS && host < MAX_HOSTS && player < MAX_PLAYER) {
905 		teamStateReq[host][player][who] = team;
906 		return XBTrue;
907 	}
908 	return XBFalse;
909 }								/* Network_ReceiveTeamStateReq */
910 
911 /*
912  * return host state
913  */
914 XBHostState
Network_GetHostState(unsigned id)915 Network_GetHostState (unsigned id)
916 {
917 	assert (id < MAX_HOSTS);
918 	return hostState[id];
919 }								/* Network_GetHostState */
920 
921 /*
922  * return if host is in
923  */
924 XBBool
Network_HostIsIn(unsigned id)925 Network_HostIsIn (unsigned id)
926 {
927 	switch (Network_GetHostState (id)) {
928 	case XBHS_Server:
929 	case XBHS_In:
930 	case XBHS_Ready:
931 		return XBTrue;
932 	default:
933 		return XBFalse;
934 	}
935 }								/* Network_HostIsIn */
936 
937 /*
938  * store current teams as default
939  */
940 void
Network_SetDefaultTeams(unsigned host,unsigned player)941 Network_SetDefaultTeams (unsigned host, unsigned player)
942 {
943 	unsigned id, pl;
944 	for (id = 0; id < MAX_HOSTS; id++) {
945 		for (pl = 0; pl < NUM_LOCAL_PLAYER; pl++) {
946 			if (teamState[id][pl] > XBTS_None) {
947 				defTeam[id][pl] = teamState[id][pl];
948 			}
949 		}
950 	}
951 	defTeam[host][player] = XBTS_Red;
952 }								/* Network_SetDefaultTeams */
953 
954 /*
955  * return default team state
956  */
957 XBTeamState
Network_GetDefaultTeam(unsigned id,unsigned player)958 Network_GetDefaultTeam (unsigned id, unsigned player)
959 {
960 	assert (id < MAX_HOSTS);
961 	assert (player < NUM_LOCAL_PLAYER);
962 	return defTeam[id][player];
963 }								/* Network_GetTeamState */
964 
965 /*
966  * return team state
967  */
968 XBTeamState
Network_GetTeamState(unsigned id,unsigned player)969 Network_GetTeamState (unsigned id, unsigned player)
970 {
971 	assert (id < MAX_HOSTS);
972 	assert (player < NUM_LOCAL_PLAYER);
973 	return teamState[id][player];
974 }								/* Network_GetTeamState */
975 
976 /*
977  * return host state requests
978  */
979 XBHostState *
Network_GetHostStateReq(unsigned id)980 Network_GetHostStateReq (unsigned id)
981 {
982 	assert (id < MAX_HOSTS);
983 	return &hostStateReq[id][0];
984 }								/* Network_GetHostStateReq */
985 
986 /*
987  * return team state requests
988  */
989 XBTeamState *
Network_GetTeamStateReq(unsigned id,unsigned player)990 Network_GetTeamStateReq (unsigned id, unsigned player)
991 {
992 	assert (id < MAX_HOSTS);
993 	assert (player < NUM_LOCAL_PLAYER);
994 	return &teamStateReq[id][player][0];
995 }								/* Network_GetTeamStateReq */
996 
997 /*
998  * check if all clients agree on a state for a host (at least two)
999  */
1000 XBBool
Network_HostReqClientsAgree(unsigned host,XBHostState state)1001 Network_HostReqClientsAgree (unsigned host, XBHostState state)
1002 {
1003 	unsigned id;
1004 	unsigned count = 0;
1005 	for (id = 1; id < MAX_HOSTS; id++) {
1006 		if (id != host) {
1007 			switch (hostState[id]) {
1008 			case XBHS_In:
1009 			case XBHS_Ready:
1010 				if (state != hostStateReq[host][id]) {
1011 					return XBFalse;
1012 				}
1013 				count++;
1014 				break;
1015 			default:
1016 				break;
1017 			}
1018 		}
1019 	}
1020 	return (count > 1);
1021 }								/* Network_HostReqClientsAgree */
1022 
1023 /*
1024  * check if all clients are ready (at least one)
1025  */
1026 XBBool
Network_ClientsReady(void)1027 Network_ClientsReady (void)
1028 {
1029 	unsigned id;
1030 	unsigned count = 0;
1031 	for (id = 1; id < MAX_HOSTS; id++) {
1032 		switch (hostState[id]) {
1033 		case XBHS_Ready:
1034 			count++;
1035 		case XBHS_None:
1036 			break;
1037 		default:
1038 			return XBFalse;
1039 		}
1040 	}
1041 	return (count > 0);
1042 }								/* Network_ClientsReady */
1043 
1044 /*
1045  * check if clients agree on team state (at least two)
1046  */
1047 XBBool
Network_TeamReqClientsAgree(unsigned host,unsigned player,unsigned state)1048 Network_TeamReqClientsAgree (unsigned host, unsigned player, unsigned state)
1049 {
1050 	unsigned id;
1051 	unsigned count = 0;
1052 	for (id = 1; id < MAX_HOSTS; id++) {
1053 		if (id != host) {
1054 			switch (hostState[id]) {
1055 			case XBHS_In:
1056 			case XBHS_Ready:
1057 				if (state != teamStateReq[host][player][id]) {
1058 					return XBFalse;
1059 				}
1060 				count++;
1061 				break;
1062 			default:
1063 				break;
1064 			}
1065 		}
1066 	}
1067 	return (count > 1);
1068 }								/* Network_TeamReqClientsAgree */
1069 
1070 /*
1071  * end of file network.c
1072  */
1073