1 /*
2  * file game_server.c - run game as server
3  *
4  * $Id: game_server.c,v 1.49 2006/02/18 21:40:02 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 #ifndef MAX_REJECTS
27 #define MAX_REJECTS 50
28 #endif
29 
30 /*
31  * local variables
32  */
33 typedef struct
34 {
35 	BMPlayer *ps;				/* player stat pointer */
36 	int cnt;					/* player stat index */
37 } BotData;
38 
39 static CFGGame serverGame;
40 static PlayerAction serverAction[MAX_PLAYER];
41 static XBBool playerLinked[MAX_PLAYER];
42 static int pa[MAX_PLAYER];
43 static int numActive;
44 static int teamActive;
45 static XBBool away;
46 
47 /*
48  * mark all external hosts
49  */
50 static void
InitPlayerLink(void)51 InitPlayerLink (void)
52 {
53 	int i;
54 	assert (serverGame.players.num <= MAX_PLAYER);
55 	for (i = 0; i < serverGame.players.num; i++) {
56 		playerLinked[i] = (serverGame.players.host[i] != XBPH_Server &&
57 						   serverGame.players.host[i] != XBPH_Local &&
58 						   serverGame.players.host[i] != XBPH_Demo);
59 	}
60 	for (; i < MAX_PLAYER; i++) {
61 		playerLinked[i] = XBFalse;
62 	}
63 }								/* InitPlayerLink */
64 
65 /*
66  * set unlinked active external players to inactive
67  */
68 static XBBool
UpdatePlayerLink(void)69 UpdatePlayerLink (void)
70 {
71 	int i;
72 	XBBool result = XBFalse;
73 	for (i = 0; i < serverGame.players.num; i++) {
74 		if (serverGame.players.host[i] != XBPH_Server &&
75 			serverGame.players.host[i] != XBPH_Local &&
76 			!playerLinked[i] && !player_stat[i].in_active) {
77 			player_stat[i].in_active = XBTrue;
78 			player_stat[i].lives = 0;
79 			result = XBTrue;
80 		}
81 	}
82 	return result;
83 }								/* UpdatePlayerLink */
84 
85 /*
86  * determine number of potentially available players/teams
87  */
88 static void
GetActivePlayers(int * pa,int * pl,int * tm)89 GetActivePlayers (int *pa, int *pl, int *tm)
90 {
91 	int i, reinco;
92 	*pl = 0;
93 	*tm = 0;
94 	reinco = 0;
95 	for (i = 0; i < serverGame.players.num; i++) {
96 		pa[i] = !player_stat[i].in_active;
97 		if (!player_stat[i].in_active) {
98 			*pl = *pl + 1;
99 			if (serverGame.setup.teamMode) {
100 				if (!(reinco & (1 << player_stat[i].team))) {
101 					reinco |= 1 << player_stat[i].team;
102 					*tm = *tm + 1;;
103 				}
104 			}
105 			else {
106 				*tm = *tm + 1;;
107 			}
108 		}
109 	}
110 	Dbg_Game ("%i active player, %i active teams (%s mode)\n", *pl, *tm,
111 			  serverGame.setup.teamMode ? "team" : "chaos");
112 }								/* GetActivePlayers */
113 
114 /*
115  * update the central game with current result
116  */
117 static void
UpdateCentralGame(void)118 UpdateCentralGame (void)
119 {
120 	int i;
121 	static char res[20];
122 	memset (res, 0, sizeof (res));
123 	for (i = 0; i < serverGame.players.num; i++) {
124 		sprintf (&res[i], "%i", player_stat[i].victories);
125 	}
126 	res[i] = '-';
127 	Dbg_Game ("updating central game entry, current result %s\n", res);
128 	Server_RestartNewGame (0, res);
129 }								/* UpdateCentralGame */
130 
131 /*
132  * check connections
133  */
134 static void
CheckConnections(void)135 CheckConnections (void)
136 {
137 	int pl, id;
138 	for (pl = 0; pl < serverGame.players.num; pl++) {
139 		/* get host id of player */
140 		id = serverGame.players.host[pl] + 1 - XBPH_Client1;
141 		/* only check remote host players */
142 		if (serverGame.players.host[pl] != XBPH_Server && serverGame.players.host[pl] != XBPH_Local) {
143 			/* check if host disconnect has to be registered */
144 			if (playerLinked[pl] && Network_GetHostState (id) == XBHS_None) {
145 				/* unlink disconnected players */
146 				playerLinked[pl] = XBFalse;
147 				Dbg_Game ("player %u at host %u disconnected\n", pl, id);
148 			}
149 			/* override player action as suicide, if unlinked */
150 			serverAction[pl].suicide = !playerLinked[pl];
151 		}
152 	}
153 }								/* CheckConnections */
154 
155 #ifdef DEBUG_GAME
156 /*
157  * output flag array (obsolete)
158  */
159 static char *
ShowPlayerFlags(XBBool * arr)160 ShowPlayerFlags (XBBool * arr)
161 {
162 	static char tmp[MAX_PLAYER + 1];
163 	int i;
164 	memset (tmp, 0, sizeof (tmp));
165 	for (i = 0; i < serverGame.players.num; i++) {
166 		tmp[i] = arr[i] ? 'x' : '-';
167 	}
168 	return tmp;
169 }								/* ShowPlayerFlags */
170 #endif
171 
172 /*
173  * server waits until all clients sent given event
174  */
175 static XBBool
WaitForClientEvent(XBNetworkEvent waitEvent,XBBool needFlush)176 WaitForClientEvent (XBNetworkEvent waitEvent, XBBool needFlush)
177 {
178 	int i;
179 	long num;
180 	unsigned id;
181 	XBEventCode xbEvent;
182 	XBEventData eData;
183 	XBNetworkEvent netEvent;
184 	XBBool playerWait[MAX_PLAYER];
185 
186 	/* determine for which players we have to wait */
187 	memcpy (playerWait, playerLinked, sizeof (playerWait));
188 	Dbg_Game ("linked players  |%s|\n", ShowPlayerFlags (playerLinked));
189 	/* set timer, disable keys/mouse */
190 	GUI_SetTimer (FRAME_TIME, XBTrue);
191 	GUI_SetKeyboardMode (KB_XBLAST);
192 	GUI_SetMouseMode (XBFalse);
193 	/* loop until all clients have reported back */
194 	do {
195 		/* determine how many player are not ready */
196 		num = 0;
197 		for (i = 0; i < serverGame.players.num; i++) {
198 			if (playerWait[i]) {
199 				num++;
200 			}
201 		}
202 		Dbg_Game ("waiting for players |%s|, remaining = %lu\n", ShowPlayerFlags (playerWait),
203 				  (unsigned long)num);
204 		/* update window */
205 		GameUpdateWindow ();
206 		/* get next event */
207 		xbEvent = GUI_WaitEvent (&eData);
208 		/* check for network events when timer triggers */
209 		switch (xbEvent) {
210 		case XBE_XBLAST:
211 			/* check for escape */
212 			if (eData.value == XBXK_EXIT) {
213 				return XBFalse;
214 			}
215 			break;
216 		case XBE_TIMER:
217 			/* try to flush udp connections, if requested */
218 			if (needFlush) {
219 				needFlush = Server_FlushPlayerAction ();
220 			}
221 			/* get single network event */
222 			netEvent = Network_GetEvent (&id);
223 			/* check for sync, error or disconnect */
224 			if (netEvent == waitEvent || netEvent == XBNW_Error || netEvent == XBNW_Disconnected) {
225 				if (id < MAX_HOSTS) {
226 					/* calculate host type */
227 					XBPlayerHost host = XBPH_Client1 + id - 1;
228 					/* loop through all players on that host */
229 					for (i = 0; i < serverGame.players.num; i++) {
230 						if (serverGame.players.host[i] == host) {
231 							/* mark as having responded */
232 							playerWait[i] = XBFalse;
233 							/* unlink if disconnected */
234 							switch (netEvent) {
235 							case XBNW_Error:
236 								Dbg_Game ("unlinked player %i (host %i), network error\n", i, id);
237 								playerLinked[i] = XBFalse;
238 								break;
239 							case XBNW_Disconnected:
240 								Dbg_Game ("unlinked player %i, (host %i), disconnected\n", i, id);
241 								playerLinked[i] = XBFalse;
242 								break;
243 							default:
244 								break;
245 							}
246 						}
247 					}
248 				}
249 				else {
250 					Dbg_Game ("network event %i on central connection \n", id);
251 				}
252 			}
253 			break;
254 		default:
255 			/* check for chat event */
256 			(void)Chat_Event (xbEvent, eData);
257 			break;
258 		}
259 		/* TODO: limit timer events and disconnect all non-responding hosts */
260 	} while (num > 0);
261 	return XBTrue;
262 }								/* WaitForClientEvent */
263 
264 /*
265  * server waits for specific event from all clients and acknowledges
266  */
267 static XBBool
SyncWithClients(XBNetworkEvent syncEvent,XBBool needFlush,XBBool showMsg)268 SyncWithClients (XBNetworkEvent syncEvent, XBBool needFlush, XBBool showMsg)
269 {
270 	if (showMsg) {
271 		SetMessage ("Waiting for others ...", XBTrue);
272 	}
273 	WaitForClientEvent (syncEvent, needFlush);
274 	/* acknowledge receiving all syncs and go on */
275 	Server_SendSync (syncEvent);
276 	return XBTrue;
277 }								/* SyncWithClients */
278 
279 /*
280  * insert keys from clients
281  */
282 static void
InsertClientAction(const CFGGamePlayers * cfgPlayers,PlayerAction * serverAction)283 InsertClientAction (const CFGGamePlayers * cfgPlayers, PlayerAction * serverAction)
284 {
285 	int i;
286 	assert (NULL != cfgPlayers);
287 	assert (NULL != serverAction);
288 	for (i = 0; i < cfgPlayers->num; i++) {
289 		switch (cfgPlayers->host[i]) {
290 		case XBPH_Client1:
291 			Server_GetPlayerAction (1, i, serverAction + i);
292 			break;
293 		case XBPH_Client2:
294 			Server_GetPlayerAction (2, i, serverAction + i);
295 			break;
296 		case XBPH_Client3:
297 			Server_GetPlayerAction (3, i, serverAction + i);
298 			break;
299 		case XBPH_Client4:
300 			Server_GetPlayerAction (4, i, serverAction + i);
301 			break;
302 		case XBPH_Client5:
303 			Server_GetPlayerAction (5, i, serverAction + i);
304 			break;
305 #ifdef SMPF
306 		case XBPH_Client6:
307 			Server_GetPlayerAction (6, i, serverAction + i);
308 			break;
309 		case XBPH_Client7:
310 			Server_GetPlayerAction (7, i, serverAction + i);
311 			break;
312 		case XBPH_Client8:
313 			Server_GetPlayerAction (8, i, serverAction + i);
314 			break;
315 		case XBPH_Client9:
316 			Server_GetPlayerAction (9, i, serverAction + i);
317 			break;
318 		case XBPH_Client10:
319 			Server_GetPlayerAction (10, i, serverAction + i);
320 			break;
321 		case XBPH_Client11:
322 			Server_GetPlayerAction (11, i, serverAction + i);
323 			break;
324 		case XBPH_Client12:
325 			Server_GetPlayerAction (12, i, serverAction + i);
326 			break;
327 		case XBPH_Client13:
328 			Server_GetPlayerAction (13, i, serverAction + i);
329 			break;
330 		case XBPH_Client14:
331 			Server_GetPlayerAction (14, i, serverAction + i);
332 			break;
333 		case XBPH_Client15:
334 			Server_GetPlayerAction (15, i, serverAction + i);
335 			break;
336 #endif
337 		default:
338 			break;
339 		}
340 	}
341 	Server_ClearPlayerAction ();
342 }								/* InsertClientAction */
343 
344 /*
345  * run a level
346  */
347 static int
ServerRunLevel(int numActive,const DBRoot * level)348 ServerRunLevel (int numActive, const DBRoot * level)
349 {
350 	int gameTime;
351 	int pauseStatus;
352 	int lastTeam, counter, winner;
353 	int remainingTeams;
354 	int frameTime;
355 	BMPlayer *ps;
356 	XBBool async;
357 	const char *msg;
358 	XBEventData eData;
359 
360 	/* sanity check */
361 	assert (level != NULL);
362 	/* necessary inits */
363 	winner = -1;
364 	gameTime = 0;
365 	pauseStatus = -1;
366 	lastTeam = -1;
367 	frameTime = serverGame.setup.frameRate ? 1000 / serverGame.setup.frameRate : 0;
368 	/* start demo recording if requested */
369 	if (serverGame.setup.recordDemo) {
370 		DemoInitLevel (DB_Atom (level));
371 	}
372 	/* post level name on chat */
373 	Server_SysChat (TempString ("playing level [%s]", GetLevelName (level)));
374 	/* Config level */
375 	if (!ConfigLevel (level)) {
376 		Dbg_Game ("level config failed!\n");
377 		goto Exit;
378 	}
379 	/* prepare async check at end of level */
380 	Server_ClearLevelWinners ();
381 	/* clean up player actions */
382 	Server_ClearPlayerAction ();
383 	Server_ResetPlayerAction ();
384 	/* level intro */
385 	if (!LevelIntro (serverGame.players.num, level, away ? -1 : serverGame.setup.infoTime)) {
386 		Dbg_Game ("abort in level intro\n");
387 		goto Exit;
388 	}
389 	/* wait for clients to show level info */
390 	Dbg_Game ("waiting for clients to show level intro\n");
391 	SyncWithClients (XBNW_SyncLevelIntro, XBFalse, XBTrue);
392 	Dbg_Game ("clients show level intro\n");
393 	/* determine active players/teams */
394 	UpdatePlayerLink ();
395 	GetActivePlayers (pa, &numActive, &teamActive);
396 	if (teamActive <= 1) {
397 		GUI_ErrorMessage ("Only one team left after level-intro sync!");
398 		goto Exit;
399 	}
400 	remainingTeams = teamActive;
401 	/* show level map */
402 	LevelBegin (GetLevelName (level));
403 	/* set timer for frames */
404 	GUI_SetTimer (frameTime, XBTrue);
405 	/* process key events */
406 	GUI_SetKeyboardMode (KB_XBLAST);
407 	GUI_SetMouseMode (XBFalse);
408 	/* play music, if requested */
409 	if (serverGame.setup.Music) {
410 		SND_Load (serverGame.setup.Music);
411 		SND_Play (serverGame.setup.Music, SOUND_MIDDLE_POSITION);
412 	}
413 	Dbg_Game ("starting level!\n");
414 	/* update central entry */
415 	UpdateCentralGame ();
416 	/* now start level */
417 	do {
418 		/* ready input */
419 		ClearPlayerAction (serverAction);
420 		/* handle all event until timer triggers */
421 		if (!GameEventLoop (XBE_TIMER, &eData)) {
422 			Dbg_Game ("game aborted during level\n");
423 			goto Exit;
424 		}
425 		/* increment game clock */
426 		gameTime++;
427 		/* update game entry occasionally */
428 		if (serverGame.host.central && (gameTime % 1024) == 0) {
429 			UpdateCentralGame ();
430 		}
431 		/* server bot */
432 		Player_BotAction (serverAction);
433 		/* handle game turn */
434 		GameTurn (gameTime, serverGame.players.num, &remainingTeams);
435 		/* insert any data received from clients */
436 		InsertClientAction (&serverGame.players, serverAction);
437 		/* trigger suicides for disconnected clients */
438 		CheckConnections ();
439 		/* send all data on player actions to clients */
440 		Server_SendPlayerAction (gameTime, serverAction);
441 		/* record demo data if requested */
442 		if (serverGame.setup.recordDemo) {
443 			DemoRecordFrame (gameTime, serverAction);
444 		}
445 		/* evaluate player action */
446 		(void)GameEvalAction (serverGame.players.num, serverAction);
447 		/* update window */
448 		GameUpdateWindow ();
449 	} while (gameTime < GAME_TIME &&
450 			 remainingTeams > 0 && (remainingTeams > 1 || NumberOfExplosions () != 0));
451 	/* tell client game is over */
452 	Server_FinishPlayerAction (gameTime + 1);
453 	/* check/reset away flags, make away bots */
454 	Player_CheckLocalAway ();
455 	/* now update away flag for local players */
456 	away = Player_CheckLocalBot ();
457 	/* calc last team for async check, do not store yet */
458 	LevelResult (gameTime, &lastTeam, serverGame.players.num, level, XBFalse);
459 	/* count number of players in winner team */
460 	if (lastTeam <= MAX_PLAYER) {
461 		for (ps = player_stat, counter = 1; ps < player_stat + serverGame.players.num;
462 			 ps++, counter++) {
463 			if (ps->team == lastTeam) {
464 				winner = counter;
465 			}
466 		}
467 	}
468 	/* finish demo file if requested */
469 	if (serverGame.setup.recordDemo) {
470 		DemoFinishLevel (gameTime, winner, "s");
471 	}
472 	Dbg_Game ("waiting for clients to send winner\n");
473 	Server_ReceiveWinnerTeam (0, lastTeam);
474 	WaitForClientEvent (XBNW_SyncLevelResult, XBTrue);
475 	/* determine active players/teams */
476 	UpdatePlayerLink ();
477 	GetActivePlayers (pa, &numActive, &teamActive);
478 	if (teamActive <= 1) {
479 		GUI_ErrorMessage ("Only one team left after async check!");
480 		goto Exit;
481 	}
482 	async = Server_LevelAsync ();
483 	if (async) {
484 		Dbg_Game ("async result determined, informing clients!\n");
485 		Server_SendLevelAsync ();
486 		GUI_ErrorMessage ("Async level, making it a draw\n");
487 		lastTeam = MAX_PLAYER;
488 	}
489 	else {
490 		Dbg_Game ("results sync, informing clients!\n");
491 		Server_SendLevelSync ();
492 		/* now store the level result */
493 		msg = LevelResult (gameTime, &lastTeam, serverGame.players.num, level, XBTrue);
494 		if (!LevelEnd (serverGame.players.num, lastTeam, msg, away ? -1 : 1)) {
495 			lastTeam = -1;
496 		}
497 	}
498   Exit:
499 	/* stop music if necessary */
500 	if (serverGame.setup.Music) {
501 		SND_Stop (serverGame.setup.Music);
502 	}
503 	FinishLevel ();
504 	DeleteAllExplosions ();
505 	/* fade out image */
506 	DoFade (XBFM_BLACK_OUT, PIXH + 1);
507 	/* that's all */
508 	return lastTeam;
509 }								/* ServerRunLevel */
510 
511 /*
512  * send level data to clients
513  */
514 static XBBool
SendLevelToClients(const DBRoot ** level)515 SendLevelToClients (const DBRoot ** level)
516 {
517 	int okay = MAX_REJECTS;
518 	/* send level data to clients */
519 	while (okay > 0) {
520 		*level = LoadLevelFile (GetNextLevel ());
521 		Dbg_Game ("Proposed level is: %s\n", GetLevelName (*level));
522 		Server_SendLevel (*level);
523 		Server_ClearLevelStatus ();
524 		Server_SetLevelStatus (0, XBTrue);
525 		WaitForClientEvent (XBNW_LevelConfig, XBTrue);
526 		if (Server_LevelApproved ()) {
527 			Dbg_Game ("Level accepted by all clients\n");
528 			/* now set and send fresh random seed so that it arrives before the activate */
529 			SeedRandom (time (NULL));
530 			Server_SendRandomSeed ();
531 			Server_SendLevelActivate ();
532 			Dbg_Game ("negotiations finished, proceeding\n");
533 			return (0);
534 		}
535 		else {
536 			okay--;
537 			Dbg_Game ("Level rejected (%i attempt(s) remaining)\n", okay);
538 			Server_SendLevelReset ();
539 		}
540 	}
541 	Dbg_Game ("negotiations failed!\n");
542 	return (-1);
543 }								/* SendLevelToClients */
544 
545 /*
546  * run the game as server
547  */
548 void
RunServerGame(void)549 RunServerGame (void)
550 {
551 	const DBRoot *level;
552 	int lastTeam, winner, maxNumWins;
553 	int i;
554 	CFGCentralSetup central;
555 
556 	/* get setup */
557 	if (!RetrieveGame (CT_Remote, SERVERGAMECONFIG, &serverGame)) {
558 		Dbg_Game ("failed to get game setup!\n");
559 		goto Disconnect;
560 	}
561 	/* select levels to play */
562 	if (!InitLevels (&serverGame)) {
563 		Dbg_Game ("failed to initialize levels!\n");
564 		goto Disconnect;
565 	}
566 	/* common inits */
567 	if (!InitGame (XBPH_Server, CT_Remote, &serverGame, serverAction)) {
568 		Dbg_Game ("failed to initialize game!\n");
569 		goto Disconnect;
570 	}
571 	/* local data */
572 	maxNumWins = 0;
573 	winner = -1;
574 	numActive = 0;
575 	teamActive = 0;
576 	away = Player_CheckLocalBot ();
577 	Dbg_Game ("server game initialized\n");
578 	/* mark external hosts */
579 	InitPlayerLink ();
580 	/* wait for clients to initialize game */
581 	Dbg_Game ("waiting for clients to init game\n");
582 	SyncWithClients (XBNW_SyncEndOfInit, XBFalse, XBFalse);
583 	Dbg_Game ("clients have initialized game\n");
584 	/* determine active players/teams */
585 	UpdatePlayerLink ();
586 	GetActivePlayers (pa, &numActive, &teamActive);
587 	if (teamActive <= 1) {
588 		GUI_ErrorMessage ("Only one team left after game init!");
589 		goto Disconnect;
590 	}
591 	/* Connect to central */
592 	if (serverGame.setup.rated) {
593 		SetMessage ("Connecting to central...", XBTrue);
594 		Dbg_Game ("rated game requested\n");
595 		RetrieveCentralSetup (&central);
596 		if (User_Connect (&central)) {
597 			Dbg_Game ("Connection to central established\n");
598 		}
599 		else {
600 			Dbg_Game ("failed to establish connection to central, unrated game\n");
601 		}
602 	}
603 	else {
604 		Dbg_Game ("unrated game requested\n");
605 	}
606 	/* play levels */
607 	do {
608 		/* negotiate next level */
609 		if (SendLevelToClients (&level) == -1) {
610 			GUI_ErrorMessage ("Level negotiations failed!");
611 			goto Exit;
612 		}
613 		/* update active players/teams */
614 		UpdatePlayerLink ();
615 		GetActivePlayers (pa, &numActive, &teamActive);
616 		if (teamActive <= 1) {
617 			GUI_ErrorMessage ("Only one team left after level negotiation!");
618 			goto Exit;
619 		}
620 		/* play level */
621 		lastTeam = ServerRunLevel (teamActive, level);
622 		/* check for quick exit */
623 		if (-1 == lastTeam) {
624 			Dbg_Game ("server aborted game\n");
625 			goto Exit;
626 		}
627 		/* update current winner */
628 		Dbg_Game ("team #%i won the level\n", lastTeam);
629 		for (i = 0; i < serverGame.players.num; i++) {
630 			if (player_stat[i].victories > maxNumWins) {
631 				maxNumWins = player_stat[i].victories;
632 				winner = i;
633 			}
634 		}
635 		/* wait for clients to reach level end */
636 		Dbg_Game ("waiting for clients to show level end\n");
637 		SyncWithClients (XBNW_SyncLevelEnd, XBFalse, XBTrue);
638 		Dbg_Game ("waiting for clients to show level end\n");
639 		/* update active players/teams */
640 		UpdatePlayerLink ();
641 		GetActivePlayers (pa, &numActive, &teamActive);
642 		if (teamActive <= 1) {
643 			GUI_ErrorMessage ("Only one team left after level-end sync!");
644 			goto Exit;
645 		}
646 		/* send level stats to central if rated game */
647 		if (User_Connected ()) {
648 			Dbg_Game ("sending level results to central\n");
649 			User_SendGameStat (serverGame.players.num, player_stat, pa);
650 		}
651 		else {
652 			Dbg_Game ("connection to central has broken down, no more ratings will be sent\n");
653 		}
654 		/* show scores */
655 		if (!ShowScoreBoard
656 			(lastTeam, maxNumWins, serverGame.players.num, player_stat, away ? -1 : 1)) {
657 			Dbg_Game ("game exit during Scoreboard\n");
658 			goto Exit;
659 		}
660 		/* wait for clients to show scoreboard */
661 		Dbg_Game ("waiting for clients to show score\n");
662 		SyncWithClients (XBNW_SyncScoreboard, XBFalse, XBTrue);
663 		Dbg_Game ("clients show score\n");
664 		/* determine number of active players */
665 		UpdatePlayerLink ();
666 		GetActivePlayers (pa, &numActive, &teamActive);
667 	} while (numActive > 1 && teamActive > 1 && maxNumWins < serverGame.setup.numWins);
668 	/* and the winner is ... */
669 	if (maxNumWins >= serverGame.setup.numWins) {
670 		Dbg_Game ("team #%i won the game!\n", winner);
671 		if (User_Connected ()) {
672 			Dbg_Game ("Sending final game result to central\n");
673 			for (i = 0; i < serverGame.players.num; i++) {
674 				pa[i] = 1;
675 				if (player_stat[i].victories == serverGame.setup.numWins) {
676 					player_stat[i].lives = -player_stat[i].victories;
677 				}
678 				else {
679 					player_stat[i].lives = player_stat[i].victories;
680 				}
681 			}
682 			User_SendGameStat (-serverGame.players.num, player_stat, pa);
683 		}
684 		/* determine and show winner */
685 		InitWinner (serverGame.players.num);
686 		ShowWinner (lastTeam, serverGame.players.num, player_stat);
687 	}
688 	else {
689 		Dbg_Game ("game finished, too few players - current winner %i\n", winner);
690 		GUI_ErrorMessage ("Not enough players/teams left in the game");
691 	}
692   Exit:
693 	FinishGame (&serverGame);
694   Disconnect:
695 	Dbg_Game ("disconnecting all clients\n");
696 	Server_SendDisconnectAll ();
697 	if (User_Connected ()) {
698 		Dbg_Game ("disconnecting result link to central\n");
699 		User_SendDisconnect ();
700 		User_Disconnect ();
701 	}
702 	Dbg_Game ("closing game entry in central\n");
703 	Server_CloseNewGame ();
704 	return;
705 }								/* StartServerGame */
706 
707 /*
708  * end of file game_server.c
709  */
710