-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.c
1767 lines (1502 loc) · 54.5 KB
/
server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* XFrisk - The classic board game for X
* Copyright (C) 1993-1999 Elan Feingold ([email protected])
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: server.c,v 1.24 2000/01/02 22:52:17 tony Exp $
*
* $Log: server.c,v $
* Revision 1.24 2000/01/02 22:52:17 tony
* and a typo
*
* Revision 1.23 2000/01/02 22:51:23 tony
* oops :-) still told hostname to clients
*
* Revision 1.22 1999/12/25 23:19:01 morphy
* Yet more comments.
*
* Revision 1.21 1999/12/25 23:12:20 morphy
* More comments on functions
*
* Revision 1.20 1999/12/25 22:03:36 morphy
* Doxygen comments, removed 3 commented out functions
*
*
*/
/** \file
* Server main loop and associated functionality.
*/
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#ifdef _AIX
#include <sys/select.h>
#endif
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include "language.h"
#include "types.h"
#include "riskgame.h"
#include "network.h"
#include "server.h"
#include "deck.h"
#include "debug.h"
#include "version.h"
#include "clients.h"
/* Move this to the Imakefile!! */
#ifdef __hpux
#define FDSET int
#else
#define FDSET fd_set
#endif
/* This may be a static value (i.e. OPEN_MAX), or a dynamic value,
* as in OSF/1, accessed through sysconf(). I'm not sure that all
* systems have these, so we'll just guess. I don't think this is
* exceptionally evil, since if we run out of descriptors, the socket
* or accept calls will fail.
*/
#if 0
#define MAX_DESCRIPTORS 128
#endif
static Int32 iServerCommLink; /**< Server socket */
static Int32 iState; /**< */
static fd_set fdSet; /**< Client socket states */
static fd_set fdBackup; /**< Client socket states backup */
static Deck *pPlayerDeck = NULL; /**< Deck of free player ids */
static Deck *pCardDeck; /**< Card deck */
static Int32 iReply; /**< ??? */
static Int32 iMaxFileDescUsed = -1; /**< ??? */
static Int32 iServerMode = SERVER_REGISTERING; /**< ??? */
static Flag fGameReset = TRUE; /**< ??? */
static Flag fRememberKilled = FALSE; /**< ??? */
static Int32 iTurn; /**< ??? */
static Int32 iFirstPlayer; /**< ??? */
/* Private functions */
void SRV_ResetGame(void);
void SRV_ReplicateRegistrationData(Int32 iCommLinkDest);
void SRV_ReplicateAllData(Int32 iCommLinkDest);
void SRV_AttemptNewGame(void);
void SRV_DistributeCountries(void);
Flag SRV_DistributeMissions(void);
void SRV_SetInitialArmiesOfPlayers(void);
void SRV_SetInitialMissionOfPlayers(void);
void SRV_NotifyClientsOfTurn(Int32 iTurn);
void SRV_HandleRegistration(Int32 iCommLink);
void SRV_HandleSignals(Int32 iParam);
void UTIL_ExitProgram(Int32 iExitValue);
Int32 SRV_IterateTurn(void);
/* Server message handlers */
void SRV_HandleEXIT(Int32 iExitValue);
void SRV_HandleALLOCPLAYER(Int32 iClient);
void SRV_HandleFREEPLAYER(void *pvMessage);
void SRV_HandleREPLYPACKET(void *pvMessage);
void SRV_HandleMESSAGEPACKET(Int32 iClient, void *pvMessage);
void SRV_HandleENTERSTATE(void *pvMessage);
void SRV_HandleDEREGISTERCLIENT(Int32 iClient);
/**
* Server initialization (signal handling, creating server socket, ...)
*
* \b History:
* \arg 01.23.94 ESF Created.
* \arg 02.22.94 ESF Cleaned up a bit, removing warnings.
* \arg 05.08.94 ESF Fixed MSG_MESSAGEPACKET handling.
* \arg 05.10.94 ESF Fixed, not registering needed callback with DistObj.
* \arg 05.12.94 ESF Added fd usage map.
* \arg 05.19.94 ESF Fixed bug, passing &pvMessage instead of pvMessage.
* \arg 08.16.94 ESF Cleaned up.
* \arg 10.01.94 ESF Added initialization of failures.
* \arg 11.11.99 TdH Added call to CLIENTS_Init
*/
void SRV_Init(void)
{
struct sockaddr_in server;
/* Print an informative message */
printf("%s: %s %s\n",SERVERNAME,SERVER_STARTING ,VERSION);
/* Catch signals so that we can clean up upon termination */
signal(SIGHUP, SRV_HandleSignals);
signal(SIGINT, SRV_HandleSignals);
signal(SIGQUIT, SRV_HandleSignals);
signal(SIGILL, SRV_HandleSignals);
signal(SIGTRAP, SRV_HandleSignals);
signal(SIGFPE, SRV_HandleSignals);
signal(SIGKILL, SRV_HandleSignals);
signal(SIGBUS, SRV_HandleSignals);
signal(SIGTERM, SRV_HandleSignals);
/* We want to ignore this signal, because we don't want a I/O
* failure to terminate the program.
*/
signal(SIGPIPE, SIG_IGN);
/* Init. clients and players */
CLIENTS_Init();
/* Initialize the pool of free players */
pPlayerDeck = DECK_Create(MAX_PLAYERS);
/* Create server socket */
if ((iServerCommLink = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Creating CommLink");
UTIL_ExitProgram(1);
}
/* Update the max */
iMaxFileDescUsed = MAX(iMaxFileDescUsed, iServerCommLink);
/* Name sockets using wildcards */
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(RISK_PORT);
/* Bind the socket to the port */
if (bind(iServerCommLink, (struct sockaddr *)&server, sizeof(server))) {
printf("%s: Frisk port %d %s\n",SERVERNAME,RISK_PORT,ERR_PORT_IN_USE);
UTIL_ExitProgram(1);
}
/* Add the socket options to the socket */
NET_SetCommLinkOptions(iServerCommLink);
}
/**
* Broadcasts the given textual message to all clients
*
* \b History:
* \arg 01.26.94 ESF Created.
* \arg 01.15.95 ESF Initialized .iTo field to avoid uninitialized memory.
*/
void SRV_BroadcastTextMessage(CString strMessage)
{
MsgMessagePacket msgMess;
msgMess.strMessage = strMessage;
msgMess.iFrom = FROM_SERVER;
msgMess.iTo = DST_ALLPLAYERS;
/* Send the message out to all clients */
SRV_BroadcastMessage(MSG_MESSAGEPACKET, &msgMess);
}
/**
* Main server message handler. Branches to message specific handlers.
*
* \b History:
* \arg x.10.99 TdH took this bit out of SRV_PlayGame
*
* \bug morphy got -1 cards :-) has to do with greenland being #0
* \bug (MSH 25.12.99) This function is humongous - perhaps a better way exists?
*/
int SRV_HandleMessage(Int32 iClient, Int32 iMessageType, void * pvMessage)
{
Int32 n;
char buf[256];
/* Depending on the message type, dispatch it to one
* of the handlers. They usually take just a message,
* but depending on what they do they could take the
* and/or the client index.
*/
D_Assert(iClient <= MAX_CLIENTS, "client > MAX_CLIENTS");
switch (iMessageType) {
case MSG_NETMESSAGE:
case MSG_DICEROLL:
case MSG_PLACENOTIFY:
case MSG_ATTACKNOTIFY:
case MSG_MOVENOTIFY:
SRV_CompleteBroadcast(iClient, iMessageType, pvMessage);
break;
case MSG_ENDOFMISSION:
case MSG_VICTORY:
SRV_BroadcastMessage(iMessageType, pvMessage);
break;
case MSG_MISSION:
if (SRV_DistributeMissions())
SRV_BroadcastMessage(iMessageType, pvMessage);
break;
case MSG_ENDOFGAME:
iServerMode = SERVER_REGISTERING;
/* Let everyone else know */
SRV_BroadcastMessage(MSG_ENDOFGAME, NULL);
SRV_ResetGame();
break;
case MSG_ALLOCPLAYER:
SRV_HandleALLOCPLAYER(iClient);
break;
case MSG_FREEPLAYER:
SRV_HandleFREEPLAYER(pvMessage);
break;
case MSG_REPLYPACKET:
SRV_HandleREPLYPACKET(pvMessage);
break;
case MSG_MESSAGEPACKET:
SRV_HandleMESSAGEPACKET(iClient, pvMessage);
break;
/* Mark the client as started */
case MSG_STARTGAME:
CLIENTS_SetStartState(iClient, TRUE);
/* Informative message */
/* should tell which player, not easy*/
printf("%s: %s %s\n",SERVERNAME,CLIENTS_GetAddress(iClient),FINISHED_REGISTRY);
snprintf(buf,sizeof(buf),"%s: %s %s", SERVERNAME,NEW_CLIENT,FINISHED_REGISTRY);
SRV_BroadcastTextMessage(buf);
if (iServerMode == SERVER_REGISTERING) {
/* Reset the game, and possible start it */
SRV_ResetGame();/*will only reset if not already done...*/
/* The first AI-Client can't start the game */
if ( (CLIENTS_GetType(iClient) != CLIENT_AI)
|| (CLIENTS_GetNumClientsStarted() > 1))
SRV_AttemptNewGame();
}
else {
/* Some random client tried to start in the middle
* of a game, probably an overeager AI client.
*/
SRV_BroadcastTextMessage("somebody tried to join after game was started");
printf("client %s tried to join after game started\n",CLIENTS_GetAddress(iClient));
}
break;
case MSG_DEREGISTERCLIENT:
/* Do the actual deregistration */
SRV_HandleDEREGISTERCLIENT(iClient);
/* Informative message */
sprintf(buf,"%s: %s %s\n",SERVERNAME,CLIENTS_GetAddress(iClient),HAS_DEREGISTERED);
printf(buf);
SRV_BroadcastTextMessage("A client has deregistered");
break;
case MSG_ENDTURN: {
Int32 iPlayer;
/* Sanity check */
D_Assert(RISK_GetNumLivePlayers(), "Modulo by zero!");
if (iServerMode == SERVER_FORTIFYING) {
Int32 i;
Flag fFoundPlayer;
/* If noone has any more armies, then move to
* SERVER_PLAYING mode. Otherwise, go along
* the list of players, looking for a player who
* still has armies to place.
*/
for (i=0, fFoundPlayer=FALSE;
i!=RISK_GetNumLivePlayers() && !fFoundPlayer;
i++) {
/* The next player who's turn it is */
iPlayer = SRV_IterateTurn();
/* Is there a player with armies left? */
if (RISK_GetNumArmiesOfPlayer(iPlayer) != 0) {
SRV_NotifyClientsOfTurn(iPlayer);
fFoundPlayer = TRUE;
}
}
/* If we have not found a player with armies,
* then there are no longer any players with any
* armies to fortify with. Let the game begin!!
* Theorem (proof left to reader): When some
* players have more armies to to fortify with
* than others (because they got less countries),
* then the last player to fortify here will be
* the last player in relation to the first player
* who fortified. So if we move to the next
* player, it will be the player who fortified
* first, which is what we want.
*
* This theorem is false when you use bpk's
* multiple-fortify hack. See my comment in
* SRV_AttemptNewGame. --Pac.
*/
if (!fFoundPlayer) {
iServerMode = SERVER_PLAYING;
iPlayer = iTurn = iFirstPlayer;
SRV_NotifyClientsOfTurn(iPlayer);
}
}
else { /* iServerMode == SERVER_PLAYING */
/* Get the next player */
iPlayer = SRV_IterateTurn();
SRV_NotifyClientsOfTurn(iPlayer);
}
}
break;
case MSG_ENTERSTATE:
SRV_HandleENTERSTATE(pvMessage);
break;
case MSG_EXCHANGECARDS: {/*went wrong for morphy, he got -1 cards */
MsgExchangeCards *pMess = (MsgExchangeCards *)pvMessage;
MsgReplyPacket msgMess;
Int32 iNumJokers;
/* Put cards back on the deck and change them to type */
for (n=iNumJokers=0; n!=3; n++) {
DECK_PutCard(pCardDeck, pMess->piCards[n]);
if (pMess->piCards[n] < NUM_COUNTRIES)
pMess->piCards[n] %= 3;
else
pMess->piCards[n] = -1, iNumJokers++;
}
/* Find out how many armies the player gets in
* exchange for the cards and send them to him or
* her, in an _UPDATEARMIES message. Right now
* the only option is fixed return values for card
* exchanges.
*/
/* Do we have one of each (possibly with jokers)? */
if ((pMess->piCards[0] != pMess->piCards[1] &&
pMess->piCards[1] != pMess->piCards[2] &&
pMess->piCards[0] != pMess->piCards[2]) ||
iNumJokers >= 2) {
msgMess.iReply = 10;
}
else if (pMess->piCards[0]==0 ||
pMess->piCards[1]==0 ||
pMess->piCards[2]==0) {
msgMess.iReply = 8;
}
else if (pMess->piCards[0]==1 ||
pMess->piCards[1]==1 ||
pMess->piCards[2]==1) {
msgMess.iReply = 6;
}
else {
msgMess.iReply = 4;
}
(void)RISK_SendMessage(CLIENTS_GetCommLinkOfClient(iClient),
MSG_REPLYPACKET, &msgMess);
}
break;
case MSG_REQUESTCARD: {
MsgRequestCard *pMess = (MsgRequestCard *)pvMessage;
RISK_SetCardOfPlayer(pMess->iPlayer,
RISK_GetNumCardsOfPlayer
(pMess->iPlayer),
DECK_GetCard(pCardDeck));
RISK_SetNumCardsOfPlayer(pMess->iPlayer,
RISK_GetNumCardsOfPlayer
(pMess->iPlayer)+1);
}
break;
case MSG_FORCEEXCHANGECARDS: {
Int32 iPlayer ;
iPlayer = ((MsgForceExchangeCards *)pvMessage)->iPlayer;
if (RISK_GetNumCardsOfPlayer(iPlayer) < 5)
((MsgForceExchangeCards *)pvMessage)->iPlayer = -1;
(void)RISK_SendMessage(CLIENTS_GetCommLinkOfClient(iClient),
MSG_FORCEEXCHANGECARDS,
pvMessage);
}
break;
case MSG_EXIT:
SRV_HandleEXIT(0);
break;
case MSG_NOMESSAGE:
break;
default: {
MsgNetPopup msg;
/* Assume that client is messed up. Consider it
* a failure and kill the client.
*/
printf("%s: %s %s: %s",SERVERNAME,CLIENT,CLIENT_DEAD,INVALID_MESSAGE);
msg.strMessage = buf;
(void)RISK_SendMessage(CLIENTS_GetCommLinkOfClient(iClient),
MSG_NETPOPUP, &msg);
/* Log the failure */
SRV_LogFailure("Sent bogus message",
CLIENTS_GetCommLinkOfClient(iClient));
}
/* Free up the memory the message was taking */
NET_DeleteMessage(iMessageType, pvMessage);
/* broken
D_Assert(iClient != MAX_CLIENTS && fHandledMessage == TRUE,
"Message received from unknown source!!");
*/
}
return(0);
}
/**
* Server main loop - 'Play the game'
*
* \b History:
* \arg 02.04.94 ESF Created.
* \arg 02.05.94 ESF Fixed broadcast loop bug.
* \arg 02.05.94 ESF Fixed message receive bug.
* \arg 03.03.94 ESF Changed to send _UPDATE to all clients but sender.
* \arg 03.28.94 ESF Added _DEADPLAYER & _ENDOFGAME.
* \arg 03.29.94 ESF Added _REQUESTCARD.
* \arg 04.01.94 ESF Fixed card exchange to work right with jokers.
* \arg 04.11.94 ESF Fixed CARDPACKET to broadcast the card.
* \arg 05.05.94 ESF Added MSG_OBJ* msgs.
* \arg 05.06.94 ESF Factored out dealing cards code.
* \arg 05.15.94 ESF Added MSG_[ALLOC|FREE]PLAYER.
* \arg 05.15.94 ESF Added MSG_REPLYPACKET.
* \arg 05.17.94 ESF Added MSG_NETMESSAGE.
* \arg 06.24.94 ESF Fixed memory leak bug.
* \arg 07.27.94 ESF Completely revamped, combined with CollectPlayers().
* \arg 09.31.94 ESF Fixed so that a new deck is created upon reset.
* \arg 10.01.94 ESF Fixed MSG_ENDOFGAME to pass message and not NULL.
* \arg 10.02.94 ESF Fixed so in case of bogus message, client is killed.
* \arg 10.03.94 ESF Fixed bug, excessive processing of MSG_DEREGISTERCLIENT.
* \arg 10.08.94 ESF Added SERVER_FORTIFYING mode to fix a bug.
* \arg 10.29.94 ESF Added handling for MSG_DICEROLL.
* \arg 10.30.94 ESF Fixed serious bug: SERVER[_REGISTERING -> _FORTIFYING].
* \arg 25.08.95 JC Don't call perror if errno is equal to 4.
* \arg 28.08.95 JC Added handling for MSG_ENDOFMISSION and MSG_VICTORY.
* \arg 30.08.95 JC Added handling for MSG_FORCEEXCHANGECARDS.
* \arg 30.08.95 JC The first AI-Client can't start the game, but other
* AI-Client can do this for computer's battles.
* \arg 14.06.97 DAH Don't use errno "4", use EINTR
*/
void SRV_PlayGame(void)
{
Int32 iClient, iMessageType, iError;
void *pvMessage;
/* Create the card deck */
pCardDeck = DECK_Create(NUM_COUNTRIES + 2);
/* Add the initial fd to keep an eye on -- the connect socket */
FD_ZERO(&fdBackup);
FD_SET(iServerCommLink, &fdBackup);
/* Start accepting connections */
listen(iServerCommLink, 5);
/* Loop for the entirety of the game */
for(;;)
{
fdSet = fdBackup;
/* If there have been any failures, then deal with them */
SRV_RecoverFailures();
/* Wait for a message to come in */
if (select(iMaxFileDescUsed+1, (FDSET *)&fdSet, (FDSET *)0, (FDSET *)0,
NULL) < 0)
/* errno = EINTR is caused by SRV_HandleSignals */
if (errno != EINTR)
perror("Select");
/* Two things might have happened here. Either an existing client
* sent a message to the server, or a new client sent a message to
* the connect port, trying to join the game. If the former occurred
* process it normally. If the latter occurred, let the client
* connect, and add its fd to the fd map. If we are in the middle
* of a game, send it a message saying this, and then perform a
* RISK_SelectiveReplicate() so as to get the new client in the
* same state as the others. Also send a message to the other
* clients telling them what is happening.
*/
if (FD_ISSET(iServerCommLink, &fdSet))
{ /* new client trying to connect */
Int32 iNewCommLink;
/* Try to accept the new connection */
if ((iNewCommLink = accept(iServerCommLink, 0, 0)) < 0) {
/* Couldn't do it, go back to top */
printf("%s %s\n",SERVERNAME,SERVER_CONNECT_FAILED);
continue;
} else {/* connection went fine */
/* Does Frisk have enough resources to hold this client? */
if (CLIENTS_GetNumClients() >= MAX_CLIENTS
|| iNewCommLink >= MAX_DESCRIPTORS) {
(void)RISK_SendMessage(iNewCommLink, MSG_EXIT, NULL);
close(iNewCommLink);
continue;
}
}
/* Assert: At this point, connection is complete and we have
* enough resources to keep it around. Begin connection protocol.
*/
/* Set options on new CommLink */
NET_SetCommLinkOptions(iNewCommLink);
SRV_HandleRegistration(iNewCommLink);
}
else {/* no FD_ISSET, there is a client trying to send a message */
for (iClient=0; iClient < MAX_CLIENTS ; iClient++) {
if (CLIENTS_GetAllocationState(iClient) == ALLOC_COMPLETE &&
FD_ISSET(CLIENTS_GetCommLinkOfClient(iClient), &fdSet)) {
if (!RISK_ReceiveMessage(CLIENTS_GetCommLinkOfClient(iClient),
&iMessageType, &pvMessage))
continue;
if ( (iError = SRV_HandleMessage(iClient,iMessageType,pvMessage)) != 0) {
printf("error %d when handling messagetype %d from %s\n",iError,iMessageType,CLIENTS_GetAddress(iClient));
}
break;
}
}
}
}
}
/**
* Notify all clients of turn change
*
* \b History:
* \arg 08.27.94 ESF Created.
* \arg 30.08.95 JC Send a message if the player have too many cards.
* \arg 25.12.99 MSH Functionality of previous entry seems to have vanished
*/
void SRV_NotifyClientsOfTurn(Int32 iPlayer)
{
MsgTurnNotify msgTurnNotify;
/* Let all clients know whos turn it is */
msgTurnNotify.iPlayer = iPlayer;
msgTurnNotify.iClient = RISK_GetClientOfPlayer(iPlayer);
SRV_BroadcastMessage(MSG_TURNNOTIFY, &msgTurnNotify);
}
/**
* Distribute countries among live players.
*
* \b History:
* \arg 05.12.94 ESF Created.
* \arg 07.25.94 ESF Changed to support TEST_GAMEs.
* \arg 10.30.94 ESF Changed to support TEST_GAMEs better.
* \arg 10.30.94 ESF Changed to refer to LivePlayer instead of Player.
*/
void SRV_DistributeCountries(void)
{
Deck *pCountryDeck = DECK_Create(NUM_COUNTRIES);
Int32 iCountry, iPlayer, i;
/* Dole out the countries */
for(i=0; i!=NUM_COUNTRIES; i++)
{
/* Pick a country, any country */
iCountry = DECK_GetCard(pCountryDeck);
#ifdef TEST_GAME
/* Give countries to the first player, leave one for the rest */
iPlayer = (i <= NUM_COUNTRIES-RISK_GetNumLivePlayers())
? RISK_GetNthLivePlayer(0)
: RISK_GetNthLivePlayer(i-(NUM_COUNTRIES-RISK_GetNumLivePlayers()));
#else
iPlayer = iTurn;
#endif
/* Update the game object */
RISK_SetOwnerOfCountry(iCountry, iPlayer);
RISK_SetNumCountriesOfPlayer(iPlayer,
RISK_GetNumCountriesOfPlayer(iPlayer)+1);
RISK_SetNumArmiesOfCountry(iCountry, 1);
RISK_SetNumArmiesOfPlayer(iPlayer, RISK_GetNumArmiesOfPlayer(iPlayer)-1);
/* Iterate to next player */
(void)SRV_IterateTurn();
}
#ifdef TEST_GAME
/* Set the number of armies to be small, to let the game start soon. */
for (i=0; i!=RISK_GetNumLivePlayers(); i++)
RISK_SetNumArmiesOfPlayer(RISK_GetNthLivePlayer(i), 2);
#endif
DECK_Destroy(pCountryDeck);
}
/**
* Calculate number of armies given to each player at game start
*
* \b History:
* \arg 08.27.94 ESF Created.
* \arg 12.07.95 ESF Fixed initial number of armies to be by the rules.
*
* \bug Part of rules is hardcoded here
*/
void SRV_SetInitialArmiesOfPlayers(void)
{
Int32 i, iPlayer, iNumArmies;
const Int32 iNumPlayers = RISK_GetNumPlayers();
D_Assert(iNumPlayers >= 2, "Not enough players!");
/* Calculate the number of armies. */
iNumArmies = MAX( 50 - iNumPlayers*5, /* According to the rules */
NUM_COUNTRIES/iNumPlayers+1 ); /* At least one per country */
/* Make sure it's enough */
D_Assert(iNumPlayers*iNumArmies >= NUM_COUNTRIES, "Not enough armies!");
/* Set the initial number of armies for all the players */
for (i=0; i<RISK_GetNumPlayers(); i++)
{
iPlayer = RISK_GetNthPlayer(i);
RISK_SetNumArmiesOfPlayer(iPlayer, iNumArmies);
/* Sanity check */
D_Assert(RISK_GetNumArmiesOfPlayer(iPlayer) > 0,
"Number of armies is negative!");
}
}
/**
* Set initial mission of all players to "no mission"
*
* \b History:
* \arg 24.08.95 JC Created.
*/
void SRV_SetInitialMissionOfPlayers(void)
{
Int32 i, iPlayer, nb;
nb = RISK_GetNumPlayers();
/* Set the initial mission's type for all the players */
for (i=0; i<nb; i++)
{
iPlayer = RISK_GetNthPlayer(i);
RISK_SetMissionTypeOfPlayer(iPlayer, NO_MISSION);
}
}
/**
* ????????
*
* \b History:
* \arg 24.08.94 JC Created.
*
* \bug (MSH 25.12.99) Obscure code with no comments!
*/
Flag SRV_DistributeMissions(void)
{
Int32 iMission[MAX_PLAYERS];
Int32 iPlayer, i, j, cont1, cont2, n, nb, m;
nb = RISK_GetNumLivePlayers();
for (n = 0; n<nb; n++)
{
iPlayer = RISK_GetNthPlayer(n);
if (RISK_GetMissionTypeOfPlayer(iPlayer) != NO_MISSION)
return FALSE;
}
for (n = 0; n<MAX_PLAYERS; n++)
iMission[n] = -1;
for (n = 0; n<nb; n++)
{
iPlayer = RISK_GetNthLivePlayer(n);
i = rand() % (2 + (NUM_CONTINENTS * (NUM_CONTINENTS - 1))/2 + nb - 1);
j = 0;
while (j<MAX_PLAYERS)
{
if (i == iMission[j])
{
j = 0;
i = (i + 1) %(2 + NUM_CONTINENTS * (NUM_CONTINENTS - 1)
+ nb - 1);
}
else
j++;
}
iMission[iPlayer]=i;
if (i == 0)
RISK_SetMissionTypeOfPlayer(iPlayer, CONQUIER_WORLD);
else if (i == 1)
{
RISK_SetMissionTypeOfPlayer(iPlayer, CONQUIER_Nb_COUNTRY);
RISK_SetMissionNumberOfPlayer(iPlayer, NUM_COUNTRIES / 2);
}
else if (i <= ((NUM_CONTINENTS * (NUM_CONTINENTS - 1))/2 + 1))
{
i = i - 2;
cont1 = -1;
m = 0;
while (cont1 == -1)
{
cont2 = m + i + 1;
if (cont2 < NUM_CONTINENTS)
cont1 = m;
i = i - (NUM_CONTINENTS - m - 1);
m++;
}
RISK_SetMissionTypeOfPlayer(iPlayer, CONQUIER_TWO_CONTINENTS);
RISK_SetMissionContinent1OfPlayer(iPlayer, cont1);
RISK_SetMissionContinent2OfPlayer(iPlayer, cont2);
}
else
{
i = i - (NUM_CONTINENTS * (NUM_CONTINENTS - 1))/2 - 2;
j = RISK_GetNthLivePlayer(i);
if (j == iPlayer)
{
i++;
i = i % nb;
j = RISK_GetNthLivePlayer(i);
}
RISK_SetMissionTypeOfPlayer(iPlayer, KILL_A_PLAYER);
RISK_SetMissionMissionPlayerToKillOfPlayer(iPlayer, j);
RISK_SetMissionPlayerIsKilledOfPlayer(iPlayer, FALSE);
}
}
return TRUE;
}
/**
* Broadcast given message to all active clients.
*
* \b History:
* \arg 02.05.94 ESF Created.
* \arg 08.16.94 ESF Rewrote a bit.
*/
void SRV_BroadcastMessage(Int32 iMessType, void *pvMessage)
{
Int32 i;
/* If there are no clients, then do nothing */
if (CLIENTS_GetNumClients() == 0)
return;
/* Loop through and send the message to each active client */
for (i=0; i!=MAX_CLIENTS; i++)
if (CLIENTS_GetAllocationState(i) == ALLOC_COMPLETE)
(void)RISK_SendMessage(CLIENTS_GetCommLinkOfClient(i), iMessType, pvMessage);
}
/**
* Broadcast given message to all active clients except originator.
*
* \b History:
* \arg 03.28.94 ESF Created.
* \arg 08.16.94 ESF Rewrote a bit.
* \arg 10.02.94 ESF Fixed a heinous bug, was going 'till NumClients.
*/
void SRV_CompleteBroadcast(Int32 iClientExclude, Int32 iMessageType,
void *pvMess)
{
Int32 i;
for (i=0; i!=MAX_CLIENTS; i++)
if (i!=iClientExclude &&
CLIENTS_GetAllocationState(i) == ALLOC_COMPLETE)
(void)RISK_SendMessage(CLIENTS_GetCommLinkOfClient(i), iMessageType, pvMess);
}
/**
* Send message to all clients. Handles server-originated messages and
* relaying of client-originated messages.
*
* \b History:
* \arg 08.18.94 ESF Created.
*/
void SRV_Replicate(Int32 iMessType, void *pvMess, Int32 iType, Int32 iSrc)
{
if (iType == MESS_OUTGOING)
SRV_BroadcastMessage(iMessType, pvMess);
else /* (iType == MESS_INCOMING) */
{
/* Convert the CommLink to a client */
SRV_CompleteBroadcast(CLIENTS_GetCommLinkToClient(iSrc), iMessType, pvMess);
}
}
/**
* Server exit cleanup.
*
* \b History:
* \arg 06.10.94 ESF Created.
*/
void SRV_HandleEXIT(Int32 iExitValue)
{
Int32 n;
/* close the listening socket */
close(iServerCommLink);
/* close all of the player sockets */
for(n=0; n!=MAX_CLIENTS; n++)
if (CLIENTS_GetAllocationState(n) == ALLOC_COMPLETE)
{
RISK_SendMessage(CLIENTS_GetCommLinkOfClient(n), MSG_EXIT, NULL);
close(CLIENTS_GetCommLinkOfClient(n));
}
UTIL_ExitProgram(iExitValue);
}
/**
* Handles allocation of new player.
*
* \b History:
* \arg 06.10.94 ESF Created.
* \arg 10.04.94 ESF Fixed a bug, creating player not a transaction.
* \arg 04.01.95 ESF Changed to use a more robust method. Simplified.
*/
void SRV_HandleALLOCPLAYER(Int32 iClient)
{
MsgReplyPacket mess;
Int32 iPlayer;
/* Get a player and return it, or -1 if none available */
iPlayer = mess.iReply = DECK_GetCard(pPlayerDeck);
/* If there was a valid player, make a note */
if (iPlayer != -1)
{
/* Reset all of the player fields */
RISK_SetAttackModeOfPlayer(iPlayer, 1);
RISK_SetStateOfPlayer(iPlayer, PLAYER_ALIVE);
RISK_SetClientOfPlayer(iPlayer, -1);
RISK_SetNumCountriesOfPlayer(iPlayer, 0);
RISK_SetNumArmiesOfPlayer(iPlayer, 0);
RISK_SetNumCardsOfPlayer(iPlayer, 0);
RISK_SetMissionTypeOfPlayer(iPlayer, NO_MISSION);
/* Note that the player is in the process of being allocated.
* The client will complete the procedure. The client that
* sets the allocation state to ALLOC_COMPLETE is the one
* responsible for increasing the number of players.
*/
RISK_SetAllocationStateOfPlayer(iPlayer, ALLOC_INPROGRESS);
}
RISK_SendMessage(CLIENTS_GetCommLinkOfClient(iClient), MSG_REPLYPACKET, &mess);
}
/**
* (MSH 25.12.99) What is this???
*
* \b History:
* \arg 06.10.94 ESF Created.
*/
void SRV_HandleREPLYPACKET(void *pvMessage)
{
iReply = ((MsgReplyPacket *)pvMessage)->iReply;
}
/**
* Handles player resignation during game.
* \bug Terminates the game - recovery procedures needed!
*
* \b History:
* \arg 06.10.94 ESF Created.
* \arg 10.15.94 ESF Fixed a bug, only Set LivePlayers if player is alive.
* \arg 11.06.94 ESF Fixed a bug, return player's cards to server.
*/
void SRV_HandleFREEPLAYER(void *pvMessage)
{
Int32 iPlayer, i;
char buf[256];
/* Put the player ID back onto pool of free players */
iPlayer = ((MsgFreePlayer *)(pvMessage))->iPlayer;
DECK_PutCard(pPlayerDeck, iPlayer);
/* If the player has cards at this point, take them away and put
* them back onto the card deck.
*/
for (i=0; i!=RISK_GetNumCardsOfPlayer(iPlayer); i++)
DECK_PutCard(pCardDeck, RISK_GetCardOfPlayer(iPlayer, i));
/* Details, details... */
RISK_SetAllocationStateOfPlayer(iPlayer, ALLOC_NONE);
/* If there were no live players at the client then we can let it go
* without ending the game. Otherwise, end the game if we're in the
* PLAY or FORTIFICATION stages of the game. If the number of live
* players went down to 0, then we must go back to registering mode.
* Kind of moot, but safe...
*/
/* Was the player alive? */
if (RISK_GetStateOfPlayer(iPlayer) == PLAYER_ALIVE &&
(iServerMode == SERVER_PLAYING ||
iServerMode == SERVER_FORTIFYING))
{
MsgNetPopup msgNetPopup;
/* Informative message */
snprintf(buf, sizeof(buf),GAME_OVER);
msgNetPopup.strMessage = buf;
SRV_BroadcastMessage(MSG_NETPOPUP, &msgNetPopup);
/* There was no winner, reset the game */