1 /*
2  *   XFrisk - The classic board game for X
3  *   Copyright (C) 1993-1999 Elan Feingold (elan@aetherworks.com)
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *   $Id: aiClientMain.c,v 1.8 1999/11/27 18:19:33 tony Exp $
20  */
21 
22 #include <sys/param.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #ifdef _AIX
27 #include <sys/select.h>
28 #endif
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <time.h>
36 #include <signal.h>
37 
38 #include "types.h"
39 #include "utils.h"
40 #include "client.h"
41 #include "version.h"
42 #include "network.h"
43 #include "riskgame.h"
44 #include "game.h"
45 #include "debug.h"
46 #include "aiClient.h"
47 #include "aiController.h"
48 
49 /* Private functions */
50 void  AI_Start(Int32 argc, CString *argv);
51 void  AI_RecoverFailure(CString strReason, Int32 iNumFailures);
52 void  AI_MainLoop(void);
53 void  AI_Replicate(Int32 iMessType, void *pvMess, Int32 iType, Int32 iSource);
54 
55 /* Dummies or stubs or fill-ins */
56 Int32 CLNT_GetCommLinkOfClient(Int32 iThisClient);
57 /* EEEK!!! this same function(name) has been defined in callbacks.c
58  * callbacks is included, what is going on? which one called?
59  */
60 void  CBK_IncomingMessage(Int32 iMessType, void *pvMess);
61 void  FatalError(CString strError, Int32 iExitValue);
62 CString  strClientName;
63 
64 Int32    iCommLink, iReply;
65 Int32    iSpeciesID, iCurrentClient, iCurrentPlayer, iState;
66 Flag     fGameInitialized = FALSE;
67 Flag     fGameStarted = FALSE;
68 
69 
70 extern CString  __strName;
71 extern CString  __strAuthor;
72 extern CString  __strVersion;
73 extern CString  __strDesc;
74 extern void  *(*__AI_Callback)(void *, Int32, void *);
75 
76 void    *pvContext[MAX_PLAYERS];
77 
78 /* Move this to the Imakefile!! */
79 #ifdef __hpux
80 #define FDSET int
81 #else
82 #define FDSET fd_set
83 #endif
84 
85 
86 /************************************************************************
87  *  FUNCTION: main
88  *  HISTORY:
89  *     02.20.95  ESF  Created from clientMain.c`main
90  *  PURPOSE:
91  *  NOTES:
92  ************************************************************************/
main(int argc,char ** argv)93 int main(int argc, char **argv)
94 {
95   Int32 i;
96 
97   /* Check args */
98   if (argc != 2)
99     {
100       printf("Usage: %s <server_host>\n", argv[0]);
101       exit(-1);
102     }
103 
104   for (i=0; i!=MAX_PLAYERS; i++)
105     pvContext[i] = (void *)NULL;
106 
107   /* Setup memory debugging library */
108   MEM_BootStrap("aiClient-memory.log");
109 
110   /* Initialize everything */
111   RISK_InitObject(AI_Replicate, NULL, NULL, AI_RecoverFailure, NULL);
112   AI_Start(argc, argv);
113 
114   /* Let the server know that we're ready to go as soon as it is... */
115   (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
116 			 MSG_STARTGAME, NULL);
117   iState = STATE_FORTIFY;
118 
119   /* And we're off */
120   AI_MainLoop();
121   return(0);
122 }
123 
124 
125 /************************************************************************
126  *  FUNCTION: AI_Start
127  *  HISTORY:
128  *     02.20.95  ESF  Created from CLNT_Init.
129  *  PURPOSE:
130  *  NOTES:
131  ************************************************************************/
AI_Start(Int32 argc,CString * argv)132 void AI_Start(Int32 argc, CString *argv)
133 {
134   struct sockaddr_in   server;
135   struct hostent      *hp;
136   char                 strHostName[MAXHOSTNAMELEN + 1];
137   Int32                iMessageType, i;
138   void                *pvMessage;
139   MsgRegisterClient    msgRegisterClient;
140 
141   UNUSED(argc);
142 
143   /*
144    * We want to ignore this signal, because we don't want a I/O
145    * failure to terminate the program.
146    */
147 
148   signal(SIGPIPE, SIG_IGN);
149 
150   /* Create sockets */
151   if ((iCommLink = socket(AF_INET, SOCK_STREAM, 0)) < 0)
152 #ifdef ENGLISH
153     FatalError("Cannot create CommLink.", 1);
154 #endif
155 #ifdef FRENCH
156     FatalError("CLIENT: Ne peut cr�er la liaison.", 1);
157 #endif
158 
159   /* Connect socket using name specified by command line */
160   server.sin_family = AF_INET;
161 
162   if ((hp = gethostbyname(argv[1])) == 0)
163     {
164       char buf[256];
165 #ifdef ENGLISH
166       snprintf(buf, sizeof(buf),
167 	       "AI-CLIENT: The host `%s' is unknown.", argv[1]);
168 #endif
169 #ifdef FRENCH
170       snprintf(buf, sizeof(buf),
171 	       "IA-CLIENT: La machine `%s' est inconnue.", argv[1]);
172 #endif
173       FatalError(buf, 1);
174     }
175 
176   memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
177   server.sin_port = htons(RISK_PORT);
178 
179   /* Send the server name information through the sockets so it can ID
180    * the sending clients, and discover the pair of sockets that belong
181    * to each client.  The server sends back the client ID.
182    */
183 
184   gethostname(strHostName, sizeof(strHostName));
185   msgRegisterClient.strClientAddress =
186     (CString)MEM_Alloc(strlen(strHostName)+32);
187   snprintf(msgRegisterClient.strClientAddress, strlen(strHostName)+32,
188 	   "%s[%lu]", strHostName, (unsigned long)getpid());
189   strClientName = msgRegisterClient.strClientAddress;
190   msgRegisterClient.iClientType = CLIENT_AI;
191 
192   /* The Protocol:
193    *
194    * Connect to the server.  Then do the following:
195    *
196    *  Send MSG_HELLO.
197    *                      Receive MSG_VERSION     --> OK!
198    *                              Other           --> Protocol bogosity.
199    *  Send MSG_REGISTERCLIENT if version is correct.
200    *                      Receive MSG_CLIENTIDENT --> OK!
201    *                              MSG_CLIENTEXIT  --> Server is full.
202    *                              Other           --> Protocol bogosity.
203    *  Send MSG_VERSION if version is incorrect.
204    */
205 
206   if (connect(iCommLink, (struct sockaddr *)&server, sizeof(server)) < 0)
207     {
208 #ifdef ENGLISH
209       printf("AI-CLIENT: Cannot connect to a Frisk server on `%s'.\n"
210 	     "        One probably needs to be started by running\n"
211 	     "        `friskserver' on the machine.\n", argv[1]);
212 #endif
213 #ifdef FRENCH
214       printf("IA-CLIENT: Impossible de se connecter au serveur Frisk sur `%s'.\n"
215 	     "           Quelqu'un doit commencer par lancer `friskserver' \n"
216 	     "           sur cette machine.\n", argv[1]);
217 #endif
218       FatalError(NULL, 0);
219     }
220 
221   /* Send the greeting */
222 #ifdef ENGLISH
223   printf("AI-CLIENT: Establishing CommLink to server.\n");
224 #endif
225 #ifdef FRENCH
226   fflush(stdout);
227   printf("IA-CLIENT: �tablissement d'une communication avec le serveur.\n");
228 #endif
229   (void)RISK_SendMessage(iCommLink, MSG_HELLO, NULL);
230 
231   /* Get the version and check it. */
232   (void)RISK_ReceiveMessage(iCommLink, &iMessageType, &pvMessage);
233 
234   if (iMessageType != MSG_VERSION)
235     {
236 #ifdef ENGLISH
237       printf("AI-CLIENT: Server is not following protocol (%d)!  Exiting.\n",
238 #endif
239 #ifdef FRENCH
240       printf("IA-CLIENT: Le serveur n'utilise pas le m�me protocol (%d)!  Quit.\n",
241 #endif
242 	     iMessageType);
243       FatalError(NULL, 0);
244     }
245 
246   if (strcmp(((MsgVersion *)pvMessage)->strVersion, VERSION))
247     {
248 #ifdef ENGLISH
249       printf("AI-CLIENT: Version mismatch (server running %s)!  Exiting.\n",
250 #endif
251 #ifdef FRENCH
252       printf("IA-CLIENT: Mauvaise version (le server utilise %s)!  Quit.\n",
253 #endif
254 	     ((MsgVersion *)pvMessage)->strVersion);
255       FatalError(NULL, 0);
256     }
257 
258   NET_DeleteMessage(iMessageType, pvMessage);
259 
260   /* Send back the registration message */
261   (void)RISK_SendMessage(iCommLink, MSG_REGISTERCLIENT, &msgRegisterClient);
262 
263   /* Get the species ID */
264 #ifdef ENGLISH
265   printf("AI-CLIENT: Waiting for server to send species ID...");
266 #endif
267 #ifdef FRENCH
268   printf("IA-CLIENT: Attente du server pour obtenir l'ID de l'esp�ce...");
269 #endif
270   (void)RISK_ReceiveMessage(iCommLink, &iMessageType, &pvMessage);
271 
272   if (iMessageType != MSG_SPECIESIDENT)
273     {
274 #ifdef ENGLISH
275       printf("Server is not following protocol (%d)!", iMessageType);
276       FatalError("Protocol error.", -1);
277 #endif
278 #ifdef FRENCH
279       printf("IA-CLIENT: Le serveur n'utilise pas le m�me protocol (%d)!\n",
280              iMessageType);
281       FatalError("Erreur de protocol.", -1);
282 #endif
283     }
284   else
285 #ifdef ENGLISH
286     printf("Done.\n");
287 #endif
288 #ifdef FRENCH
289     printf("Re�u.\n");
290 #endif
291 
292   iSpeciesID = ((MsgSpeciesIdent *)pvMessage)->iSpeciesID;
293   NET_DeleteMessage(iMessageType, pvMessage);
294 
295   /* Wait for the response */
296 #ifdef ENGLISH
297   printf("AI-CLIENT: Waiting for server to send client ID...");
298 #endif
299 #ifdef FRENCH
300   printf("IA-CLIENT: Attend que le serveur �mette l'ID client...");
301 #endif
302   (void)RISK_ReceiveMessage(iCommLink, &iMessageType, &pvMessage);
303 
304   if (iMessageType == MSG_EXIT)
305     FatalError("Can't connect, server is full -- I'm impressed!", -1);
306   else if (iMessageType == MSG_CLIENTIDENT)
307     {
308 #ifdef ENGLISH
309       printf("Done.\n");
310 #endif
311 #ifdef FRENCH
312       printf("Re�u.\n");
313 #endif
314 
315       /* Set the ID of the client, and then set the sockets of the client */
316       iCurrentClient = ((MsgClientIdent *)pvMessage)->iClientID;
317     }
318   else
319     {
320 #ifdef ENGLISH
321       printf("Server is not following protocol (%d)!", iMessageType);
322       FatalError("Protocol error!", -1);
323 #endif
324 #ifdef FRENCH
325       printf("IA-CLIENT: Le serveur n'utilise pas le m�me protocol (%d)!\n",
326              iMessageType);
327       FatalError("Erreur de protocol.", -1);
328 #endif
329     }
330 
331   /* Free up memory */
332   NET_DeleteMessage(MSG_CLIENTIDENT, pvMessage);
333 
334   /* Get the species ID */
335   i = iSpeciesID;
336 #ifdef ENGLISH
337   printf("AI-CLIENT: Me species #%d.  You Jane.\n", i);
338 #endif
339 #ifdef FRENCH
340   printf("IA-CLIENT: Mon esp�ce est %d.\n", i);
341 #endif
342 
343   /* Init the species; afterwards, indicate the species is finished. */
344   RISK_SetNameOfSpecies(i, __strName);
345   RISK_SetClientOfSpecies(i, CLNT_GetThisClientID());
346   RISK_SetAuthorOfSpecies(i, __strAuthor);
347   RISK_SetVersionOfSpecies(i, __strVersion);
348   RISK_SetDescriptionOfSpecies(i, __strDesc);
349   RISK_SetAllocationStateOfSpecies(i, ALLOC_COMPLETE);
350 
351   /* Let the player do one-time initializations */
352   __AI_Callback(NULL, AI_INIT_ONCE, (void *)iSpeciesID);
353 }
354 
355 
356 
357 /************************************************************************
358  *  FUNCTION: CLNT_GetThisClientID
359  *  HISTORY:
360  *     30.10.99  MSH  Created.
361  *  PURPOSE:
362  *     Returns client ID of 'this' client
363  *  NOTES: See below
364  ************************************************************************/
365 Int32 CLNT_GetThisClientID(void)
366 {
367 	return iCurrentClient;
368 }
369 
370 
371 /************************************************************************
372  *  FUNCTION: CLNT_GetCommLinkOfClient
373  *  HISTORY:
374  *     02.20.95  ESF  Created.
375  *  PURPOSE:
376  *  NOTES: DUMMY!!
377  ************************************************************************/
378 Int32 CLNT_GetCommLinkOfClient(Int32 iThisClient)
379 {
380   UNUSED(iThisClient);
381   return iCommLink;
382 }
383 
384 
385 /************************************************************************
386  *  FUNCTION: AI_RecoverFailure
387  *  HISTORY:
388  *     02.21.95  ESF  Created.
389  *  PURPOSE:
390  *  NOTES:
391  ************************************************************************/
392 void AI_RecoverFailure(CString strReason, Int32 iFailureCommlink)
393 {
394 #ifdef ENGLISH
395   printf("AI-CLIENT: Fatal Error (%s) on CommLink %d.  Not equipped to\n"
396 	 "recover from failure yet.  I'm exiting.\n",
397 #endif
398 #ifdef FRENCH
399   printf("IA-CLIENT: Erreur fatale (%s) sur la ligne %d.\n"
400          "           Pas equipp� pour r�cup�rer cette erreur.\n"
401          "           Je quitte.\n",
402 #endif
403 	 strReason, iFailureCommlink);
404   exit(-1);
405 }
406 
407 
408 /************************************************************************
409  *  FUNCTION: FatalError
410  *  HISTORY:
411  *     02.21.95  ESF  Created.
412  *  PURPOSE:
413  *  NOTES:
414  ************************************************************************/
415 void FatalError(CString strError, Int32 iExitValue)
416 {
417   if (strError)
418 #ifdef ENGLISH
419     printf("AI-CLIENT: Fatal error (%s)\n", strError);
420 #endif
421 #ifdef ENGLISH
422     printf("IA-CLIENT: Erreur fatale (%s)\n", strError);
423 #endif
424   exit(iExitValue);
425 }
426 
427 
428 /************************************************************************
429  *  FUNCTION: AI_MainLoop
430  *  HISTORY:
431  *     02.21.95  ESF  Created from CLNT_Init.
432  *  PURPOSE:
433  *  NOTES:
434  ************************************************************************/
435 void AI_MainLoop(void)
436 {
437   void   *pvMessage;
438   Int32   iMessageType;
439   fd_set  fdSet, fdBackup;
440 
441   FD_ZERO(&fdBackup);
442   FD_SET(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()), &fdBackup);
443 
444   /* Initialize everything */
445   AI_Init();
446 
447   /* Loop forever */
448   for(;;)
449     {
450       fdSet = fdBackup;
451 
452       /* Wait for a message */
453       if (select(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID())+1,
454 		 (FDSET *)&fdSet, (FDSET *)0, (FDSET *)0,
455 		 NULL) < 0)
456 	perror("Select");
457 
458       /* Receive the message */
459       if (!RISK_ReceiveMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
460 			       &iMessageType, &pvMessage))
461 	AI_RecoverFailure("Server failed.", -1);
462 
463       /* Process it */
464       CBK_IncomingMessage(iMessageType, pvMessage);
465     }
466 }
467 
468 
469 /************************************************************************
470  *  FUNCTION: CBK_IncomingMessage
471  *  HISTORY:
472  *     04.30.95  ESF  Created.
473  *     25.08.95  JC   Added MSG_EXIT.
474  *     25.08.95  JC   Modified MSG_ENDOFGAME, if computer is the winner and
475  *                    if more than one player, then the computer decide to
476  *                    continue.
477  *     25.08.95  JC   Start the game only after a fortification.
478  *     28.08.95  JC   Added MSG_ENDOFMISSION and MSG_VICTORY.
479  *     30.08.95  JC   Added MSG_FORCEEXCHANGECARDS.
480  *     04.09.95  JC   Added AI_SERVER_MESSAGE.
481  *  PURPOSE:
482  *  NOTES:
483  ************************************************************************/
484 void CBK_IncomingMessage(Int32 iMessType, void *pvMessage)
485 {
486   switch(iMessType)
487     {
488     case MSG_TURNNOTIFY:
489       {
490 	MsgTurnNotify *pvMess = (MsgTurnNotify *)pvMessage;
491 
492 	/* Set the current player */
493 	iCurrentPlayer = pvMess->iPlayer;
494 
495 	if (!fGameInitialized)
496 	  {
497             fGameInitialized = TRUE;
498             /* Let the player do initializations of game */
499             __AI_Callback(NULL, AI_INIT_GAME, (void *)iSpeciesID);
500           }
501 
502 	/* Check to see if it's this client. */
503 	if (pvMess->iClient == CLNT_GetThisClientID())
504 	  {
505 	    pvContext[iCurrentPlayer] =
506 	      __AI_Callback(pvContext[iCurrentPlayer], AI_INIT_TURN, NULL);
507 
508 	    /* See if the fortification is done */
509 	    if (!fGameStarted && (iState == STATE_FORTIFY) &&
510 		(RISK_GetNumArmiesOfPlayer(iCurrentPlayer) == 0))
511 	      fGameStarted = TRUE;
512 
513 	    if (fGameStarted)
514 	      {
515 		if (RISK_GetNumCardsOfPlayer(iCurrentPlayer) >= 3)
516 		  {
517 		    pvContext[iCurrentPlayer] =
518 		      __AI_Callback(pvContext[iCurrentPlayer],
519 				    AI_EXCHANGE_CARDS,
520 				    NULL);
521 		    AI_CheckCards();
522 		  }
523 
524 		/* Give the players the initial armies */
525 		GAME_SetTurnArmiesOfPlayer(iCurrentPlayer);
526                 if (iState == STATE_REGISTER)
527                   return;
528 
529 		iState = STATE_PLACE;
530                 do
531                   {
532 		    /* Send the commands */
533 		    pvContext[iCurrentPlayer] =
534 		      __AI_Callback(pvContext[iCurrentPlayer],
535 		    		    AI_PLACE,
536 		    		    NULL);
537                     if (iState == STATE_ATTACK)
538                       {
539                         AI_CheckPlacement();
540 		        pvContext[iCurrentPlayer] =
541 		          __AI_Callback(pvContext[iCurrentPlayer],
542 				        AI_ATTACK,
543 				        NULL);
544                       }
545                   }
546                 while (iState == STATE_PLACE);
547 
548                 if (iState == STATE_ATTACK)
549                   {
550 		    iState = STATE_MOVE;
551 		    pvContext[iCurrentPlayer] =
552 		      __AI_Callback(pvContext[iCurrentPlayer],
553 				    AI_MOVE,
554 				    NULL);
555 	          }
556 	      }
557 	    else if (iState == STATE_FORTIFY)
558 	      {
559 		pvContext[iCurrentPlayer] =
560 		  __AI_Callback(pvContext[iCurrentPlayer],
561 				AI_FORTIFY,
562 				(void *)1);
563                 AI_CheckFortification();
564 	      }
565 
566             if (iState != STATE_REGISTER)
567               {
568 	        /* End the turn */
569 	        AI_EndTurn();
570 	      }
571 	  }
572       }
573       break;
574 
575     case MSG_MESSAGEPACKET:
576       {
577         MsgMessagePacket *msgMessagePacket = (MsgMessagePacket *)pvMessage;
578 
579         if (msgMessagePacket->iFrom >= FROM_SERVER)
580           {
581             Int32 iOldPlayer = iCurrentPlayer;
582 
583             if (msgMessagePacket->iTo >= 0)
584                 iCurrentPlayer = msgMessagePacket->iTo;
585             else
586                 iCurrentPlayer = RISK_GetNthPlayerAtClient(CLNT_GetThisClientID(), 0);
587 	    (void)__AI_Callback(NULL, AI_SERVER_MESSAGE,
588 			        (void *)(msgMessagePacket->strMessage));
589             iCurrentPlayer = iOldPlayer;
590           }
591         else if (   (msgMessagePacket->iFrom >= 0)
592             && (msgMessagePacket->iTo   >= 0))
593           {
594             Int32 iOldPlayer = iCurrentPlayer;
595 
596             iCurrentPlayer = msgMessagePacket->iTo;
597 	    pvContext[iCurrentPlayer] =
598 	      __AI_Callback(pvContext[iCurrentPlayer],
599 			    AI_MESSAGE,
600 			    (void *)msgMessagePacket);
601             iCurrentPlayer = iOldPlayer;
602           }
603       }
604       break;
605 
606     case MSG_FORCEEXCHANGECARDS:
607       {
608         MsgForceExchangeCards *msg = (MsgForceExchangeCards *)pvMessage;
609 
610         if (msg->iPlayer >= 0)
611           {
612             if (iState != STATE_ATTACK)
613               return;
614             iCurrentPlayer = msg->iPlayer;
615             pvContext[iCurrentPlayer] =
616 	    __AI_Callback(pvContext[iCurrentPlayer],
617 		          AI_EXCHANGE_CARDS, NULL);
618 	    AI_CheckCards();
619             if (iState != STATE_ATTACK)
620               return;
621 	    iState = STATE_PLACE;
622 	  }
623       }
624       break;
625 
626     case MSG_ENDOFMISSION:
627       {
628 	Int32 iWinner = ((MsgEndOfMission *)pvMessage)->iWinner;
629 
630 	D_Assert(iWinner >= -1 && iWinner < MAX_PLAYERS, "Bogus Winner!");
631 
632 	if (RISK_GetClientOfPlayer(iWinner) == CLNT_GetThisClientID())
633           {
634 	    if (RISK_GetNumLivePlayers() <= 1)
635 	      {
636 	        fGameStarted = FALSE;
637 	        iState = STATE_REGISTER;
638                 (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
639 			               MSG_ENDOFGAME, NULL);
640               }
641           }
642       }
643       break;
644 
645     case MSG_VICTORY:
646       {
647 	Int32 iWinner = ((MsgVictory *)pvMessage)->iWinner;
648 
649 	D_Assert(iWinner >= -1 && iWinner < MAX_PLAYERS, "Bogus Winner!");
650 
651 	if (RISK_GetClientOfPlayer(iWinner) == CLNT_GetThisClientID())
652           {
653 	    fGameStarted = FALSE;
654 	    iState = STATE_REGISTER;
655             (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
656 			           MSG_ENDOFGAME, NULL);
657           }
658       }
659       break;
660 
661     case MSG_ENDOFGAME:
662       {
663 	fGameStarted = FALSE;
664 	iState = STATE_FORTIFY;
665 
666         /* Let the server know that this client is ready to play */
667         (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
668 			       MSG_STARTGAME, NULL);
669 
670         fGameInitialized = FALSE;
671         AI_Init();
672       }
673 
674     case MSG_NOMESSAGE:
675       break;
676 
677     case MSG_REPLYPACKET:
678       iReply = ((MsgReplyPacket *)pvMessage)->iReply;
679       break;
680 
681     case MSG_EXIT:
682       UTIL_ExitProgram(0);
683       break;
684     default:
685       ; /* Nothing... */
686     }
687 }
688 
689 
690 /************************************************************************
691  *  FUNCTION: UTIL_GetArmyNumber
692  *  HISTORY:
693  *     02.25.95  ESF  Created.
694  *     30.08.95  JC   Computer can play like a human.
695  *     31.08.95  JC   if (ARMIES_MOVE_MIN == ARMIES_MOVE_MAX) ->
696  *                    if (iMoveMode == ARMIES_MOVE_MIN).
697  *  PURPOSE:
698  *  NOTES:
699  ************************************************************************/
700 Int32 UTIL_GetArmyNumber(Int32 iMin, Int32 iMax, Flag fLetCancel)
701 {
702   extern Int32 iMoveMode;
703   UNUSED(fLetCancel);
704 
705   if (iMoveMode == ARMIES_MOVE_MAX)
706       return iMax;
707   if (iMoveMode == ARMIES_MOVE_MIN)
708       return iMin;
709   if (iMoveMode == ARMIES_MOVE_MANUAL)
710     {
711       Int32 iNumArmies = iMax-iMin;
712 
713       pvContext[iCurrentPlayer] =
714         __AI_Callback(pvContext[iCurrentPlayer], AI_MOVE_MANUAL,
715                       (void *)&iNumArmies);
716       return iNumArmies+iMin;
717     }
718   return (iMax-iMin)/2;
719 }
720 
721 
722 /************************************************************************
723  *  FUNCTION:
724  *  HISTORY:
725  *     02.25.95  ESF  Created.
726  *  PURPOSE:
727  *  NOTES:
728  ************************************************************************/
729 void AI_RegisterPreObserver(void (*PreCallback)(Int32, void *))
730 {
731   UNUSED(PreCallback);
732   /* A bit of a hack, for now... */
733 
734 }
735 
736 
737 /************************************************************************
738  *  FUNCTION:
739  *  HISTORY:
740  *     02.25.95  ESF  Created.
741  *  PURPOSE:
742  *  NOTES:
743  ************************************************************************/
744 void AI_RegisterPostObserver(void (*PostCallback)(Int32, void *))
745 {
746   UNUSED(PostCallback);
747   /* A bit of a hack, for now... */
748 
749 }
750 
751 
752 /************************************************************************
753  *  FUNCTION: AI_Replicate
754  *  HISTORY:
755  *     02.25.95  ESF  Created.
756  *  PURPOSE:
757  *  NOTES:
758  ************************************************************************/
759 void AI_Replicate(Int32 iMessType, void *pvMess, Int32 iType, Int32 iSource)
760 {
761   UNUSED(iSource);
762   /*
763    * Do the physical replication (the server will broadcast it),
764    * but only if this callback is being called for an outgoing
765    * message (i.e. a replication).  Otherwise, a message just
766    * came in.
767    */
768 
769   if (iType == MESS_OUTGOING)
770     {
771       (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(CLNT_GetThisClientID()),
772 			     iMessType, pvMess);
773     }
774 }
775