]> git.jsancho.org Git - lugaru.git/blob - Source/nsp_network.c
28a8c435f4aecd1085e924bed4284b6c8c95dd9c
[lugaru.git] / Source / nsp_network.c
1 /*
2  * Copyright (c) 2002 Lane Roathe. All rights reserved.
3  *      Developed in cooperation with Freeverse, Inc.
4  *
5  * @APPLE_LICENSE_HEADER_START@
6  * 
7  * Portions Copyright (c) 1999-2002 Apple Computer, Inc.  All Rights
8  * Reserved.  This file contains Original Code and/or Modifications of
9  * Original Code as defined in and that are subject to the Apple Public
10  * Source License Version 1.1 (the "License").  You may not use this file
11  * except in compliance with the License.  Please obtain a copy of the
12  * License at http://www.apple.com/publicsource and read it before using
13  * this file.
14  * 
15  * The Original Code and all software distributed under the License are
16  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT.  Please see the
20  * License for the specific language governing rights and limitations
21  * under the License.
22  * 
23  * @APPLE_LICENSE_HEADER_END@
24  *
25  * Modified: $Date: 2002/04/27 21:42:01 $
26  * Revision: $Id: nsp_network.c,v 1.1 2002/04/27 21:42:01 lane Exp $
27  */
28
29 /* NOTES:
30
31         - This example is designed so that all networking code is in this one file. The purpose is to make
32                 it easier for a programmer to study the basic use of OpenPlay's NetSprocket API and not to
33                 demonstrate good programming style or OOP practices.
34
35         - This file is not game independent (hey, that's what OpenPlay is about!), it simply helped me keep
36                 the example use of NetSprocket's more managable ... I hope :)
37
38         - I wanted the host (ie, server) to also be a player, and found it was pretty easy to do; I simply
39                 made sure that I tracked whether I was a server or client (see the _server variable in main.h) and
40                 then when sending all players a message I had to modify the "to" portion of the header and
41                 resend it to the host. This means the host is sending itself messages, which works great.
42
43 */
44
45 /* ----------------------------------------------------------- Includes */
46
47 #include <ctype.h>
48 #include <stdio.h>
49 #include <time.h>
50 #include <string.h>
51 #include <stdlib.h>
52
53 #include "main.h"
54
55 #include "network.h"
56
57 /* ----------------------------------------------------------- Macro definitions */
58
59 enum
60 {
61         kMessageType_Question = 256,
62         kMessageType_Answer,
63
64         kMessageType_Information
65 };
66
67 #define kPlayerType 1234        /* just defines a way to allow/reject players of different types */
68
69
70 /* ----------------------------------------------------------- Type definitions */
71
72 /* Questions are sent out in message packets from server to players */
73
74 /* NOTE Message string is ZERO terminated!      */
75 /*              This allows us to strcpy, strcmp, etc. on raw packet data       */
76 /*              Meaning packet size = sizeof(packet_t) + strlen(string) */
77
78 typedef struct
79 {
80         NSpMessageHeader  header;
81
82         char            str[1];                 /* message (question, info, etc) string */
83
84 }MessagePacket_t, *MessagePacketPtr;
85
86 /* Answers are sent from the players to the server in this packet */
87
88 typedef struct
89 {
90         NSpMessageHeader  header;
91
92         char            answer;                 /* finally, the answer */
93
94 }AnswerPacket_t, *AnswerPacketPtr;
95
96 /* ----------------------------------------------------------- Local Variables */
97
98 /* name of this version of the app*/
99 char *GameNameStr = "OpenPlay NSp API Example 1";
100
101 /* Local vars used to handle our interaction with OpenPlay's NetSprockets API */
102
103 static NSpGameReference                 _gameReference = NULL;          /* general game info */
104 static NSpProtocolListReference _protocolListRef = NULL;        /* list of protocols we've started up */
105
106 static NMBoolean _approved;             /* after requesting to join a new game, this tracks whether or not we were approved */
107 static NMBoolean _response;             /* true if we get a response following a request to join a new game (else we time out */
108
109 /*--------------------------------------------------------------------------------*/
110 /*--------------------------------------------------------------------------------*/
111 #if __MWERKS__
112         #pragma mark -
113         #pragma mark È Local Routines
114         #pragma mark -
115 #endif
116
117
118 /*--------------------------------------------------------------------------------*/
119 /*--------------------------------------------------------------------------------*/
120 #if __MWERKS__
121         #pragma mark -
122         #pragma mark È Global Routines
123         #pragma mark -
124 #endif
125
126 unsigned char *GameC2PStr
127 (
128         char *s,                        /* source C string */
129         unsigned char *d        /* destination buffer for Pascal string */
130 )
131 {
132         int c = 0;
133
134         if( s && d )    /* make sure we got got inputs */
135         {
136                 while( *s )
137                         d[++c] = (unsigned char)*s++;   /* no blockmove, copy & get length in one shot */
138         }
139         *d = (unsigned char)c;
140
141         return( d );
142 }
143
144 char *GameP2CStr
145 (
146         unsigned char *s,       /* source Pascal string */
147         char *d                         /* buffer to hold converted C string */
148 )
149 {
150         if( s && d )
151         {
152                 memcpy( d, s+1, *s );   /* pretty simple */
153                 d[*s] = 0;
154         }
155         return( d );
156 }
157
158 /* ================================================================================
159         Handle all of the network messages that are sent to this application
160
161          EXIT:  0 == no error, else error code
162 */
163
164 void NetworkHandleMessage( void )
165 {
166         NSpMessageHeader *mhp;
167         char str[256];                          /* buffer for converted PStrings */
168
169         /* Ask NetSprocket to give us a message if any are waiting */
170
171         mhp = NSpMessage_Get( _gameReference );
172         if( mhp )
173         {
174                 /* Print out the type of message received */
175
176                 switch(mhp -> what)
177                 {
178
179 /* --- Handle NetSprocket's generic messages */
180
181                         case kNSpJoinApproved:
182                                 printf( "Approved!\n\n", str );
183                                 fflush(stdout);
184                                 _response = true;
185                                 _approved = true;       /* tell our waiting loop that we were approved */
186 #if __USE_SIOUX__
187                                 {
188                                         NMErr err;
189
190                                         err = NSpGame_GetInfo( _gameReference, &gameInfo );
191                                         if( !err )
192                                                 SIOUXSetTitle( gameInfo.name );
193                                 }
194 #endif
195                                 break;
196                         
197                         case kNSpJoinDenied:
198                                 GameP2CStr( ((NSpJoinDeniedMessage *)mhp)->reason, str );
199                                 printf( "Denied!\n   Reason: %s\n\n", str );
200                                 fflush(stdout);
201                                 //GameOver();
202
203                                 _response = true;
204                                 _approved = false;      /* tell our waiting loop we were denied */
205                                 break;
206
207                         case kNSpError:
208                                 printf( "*** ERROR *** (reported value: %d)\n\n", ((NSpErrorMessage *)mhp)->error );
209                                 fflush(stdout);
210
211                                 _response = true;
212                                 _approved = false;
213                                 break;
214                         
215                         case kNSpGameTerminated:
216                                 printf( "--- Host Terminated Game ---\n\n" );
217                                 fflush(stdout);
218                                 //GameOver();
219
220                                 _response = true;
221                                 _approved = false;
222                                 break;
223
224                         case kNSpPlayerJoined:
225                         {
226                                 NSpGameInfo gameInfo;
227                                 char gamename[256];
228                                 NMErr err;
229
230                                 err = NSpGame_GetInfo( _gameReference, &gameInfo );
231                                 if( !err )
232                                         GameP2CStr( gameInfo.name, gamename );
233                                 else
234                                         strcpy( gamename, "ERRROR" );
235
236                                 GameP2CStr( ((NSpPlayerJoinedMessage *)mhp)->playerInfo.name, str );
237                                 printf( "===> Player %s joined game '%s', %d players now!\n\n",
238                                                 str, gamename, ((NSpPlayerJoinedMessage *)mhp)->playerCount );
239                                 fflush(stdout);
240
241                                 /* Lets go ahead and re-send the current question, as the new guy deserves a shot at it too */
242                                 /*if( GameIsHost() )
243                                 {
244                                         //QuestionPtr theQuestion = GameGetCurrentQuestion();
245                                         //if (theQuestion)
246                                         //      NetworkSendQuestion(theQuestion->question);
247                                 }*/
248                         }
249                         break;
250
251                         case kNSpPlayerLeft:
252                                 GameP2CStr( ((NSpPlayerLeftMessage *)mhp)->playerName, str );
253                                 printf( "===> Player, %s, Left game, leaving %d players!\n\n",
254                                                 str, ((NSpPlayerLeftMessage *)mhp)->playerCount );
255                                 fflush(stdout);
256                                 break;
257
258                         case kNSpHostChanged:
259                                 printf( "===> ??? Host changed to player ID %d\n\n",
260                                                 ((NSpHostChangedMessage *)mhp)->newHost );
261                                 fflush(stdout);
262                                 break;
263
264                         case kNSpGroupCreated:
265                                 printf( "===> Player #%d created a new Group, ID %d\n\n",
266                                                 ((NSpCreateGroupMessage *)mhp)->requestingPlayer, ((NSpCreateGroupMessage *)mhp)->groupID );
267                                 fflush(stdout);
268                                 break;
269
270                         case kNSpGroupDeleted:
271                                 printf( "===> Player #%d deleted group #%d\n\n",
272                                                 ((NSpDeleteGroupMessage *)mhp)->requestingPlayer, ((NSpDeleteGroupMessage *)mhp)->groupID );
273                                 fflush(stdout);
274                                 break;
275                         
276                         case kNSpPlayerAddedToGroup:
277                                 printf( "===> Player %d was added to group %d\n\n",
278                                                 ((NSpAddPlayerToGroupMessage *)mhp)->player, ((NSpAddPlayerToGroupMessage *)mhp)->group );
279                                 fflush(stdout);
280                                 break;
281
282                         case kNSpPlayerRemovedFromGroup:
283                                 printf( "===> Player %d was removed from group %d\n\n",
284                                                 ((NSpRemovePlayerFromGroupMessage *)mhp)->player, ((NSpRemovePlayerFromGroupMessage *)mhp)->group );
285                                 fflush(stdout);
286                                 break;
287
288                         case kNSpPlayerTypeChanged:
289                                 printf( "===> Player %d changed to a new type, %d\n\n",
290                                                 ((NSpPlayerTypeChangedMessage *)mhp)->player, ((NSpPlayerTypeChangedMessage *)mhp)->newType );
291                                 break;
292
293
294 /* --- Handle our game specific messages */
295
296
297                         /* Got a message, see if it is correct or not and let everyone know the results */
298
299                         case kMessageType_Answer:
300                         {
301                                 NSpPlayerInfoPtr pip;
302                                 char cname[kNSpStr32Len];
303                                 NMErr err;
304
305                                 err = NSpPlayer_GetInfo( _gameReference, mhp->from, &pip );
306                                 if( !err )
307                                 {               
308                                         GameP2CStr( pip->name, cname );
309
310                                         NSpPlayer_ReleaseInfo( _gameReference, pip );
311                                 }
312                                 else
313                                         strcpy( cname, "UNKOWN -- error!" );
314
315                                 //sprintf( str, "Player #%d, %s, answered with '%c', which is %s", mhp->from, cname, ((AnswerPacketPtr)mhp)->answer,
316                                         //GameCheckAnswer( ((AnswerPacketPtr)mhp)->answer ) ? "Correct!" : "WRONG!" );
317
318                                 NetworkSendInformation( str );
319                         }
320                         break;
321
322                         /* allow game to do any special processing needed when a question arrives */
323
324                         case kMessageType_Question:
325                                 //GameDisplayQuestion( ((MessagePacketPtr)mhp)->str );
326                                 break;
327
328                         /* pretty simple, just display the info message */
329
330                         case kMessageType_Information:
331                                 printf( "%s\n\n", ((MessagePacketPtr)mhp)->str );
332                                 fflush(stdout);
333                                 break;
334
335                         default:
336                                 break;
337                 }
338
339                 /* Once done with it, release the message */
340
341                 NSpMessage_Release( _gameReference, mhp );
342         }
343 }
344
345 /* ================================================================================
346         Return the # of players in the current game
347
348          EXIT:  none
349 */
350
351 NMUInt32 NetworkGetPlayerCount( void )
352 {
353         NSpGameInfo gameInfo;
354         NMErr err;
355
356         err = NSpGame_GetInfo( _gameReference, &gameInfo );     /* player count is in the game info record */
357         if( !err )
358         {
359                 return( gameInfo.currentPlayers );
360         }
361
362         return( 0 );
363 }
364
365 /* ================================================================================
366         Send an answer to the server
367
368          EXIT:  none
369 */
370
371 NMErr NetworkSendAnswer
372 (
373         char answer                     /* the answer to send (just a char!) */
374 )
375 {
376         AnswerPacket_t answerPacket;
377         NMErr err;
378
379         /* init the NetSprocket portion of the packet */
380
381         NSpClearMessageHeader( &answerPacket.header );
382
383         answerPacket.header.what = kMessageType_Answer;
384         answerPacket.header.to = kNSpHostOnly;
385         answerPacket.header.messageLen = sizeof(answerPacket);
386
387         /* fill in the data section */
388
389         answerPacket.answer = answer;
390
391         /* tell NetSprocket to send the message */
392
393         err = NSpMessage_Send( _gameReference, &answerPacket.header, kNSpSendFlag_Registered );
394         if( err )
395         {
396                 printf( "*** ERROR *** Unable to send answer packet, error # %d\n\n", err );
397                 fflush(stdout);
398         }
399
400         return( err );
401 }
402
403 /* ================================================================================
404         Send a message to all players
405
406          EXIT:  none
407 */
408
409 NMErr NetworkSendPlayerMessage
410 (
411         const char *message,            /* ptr to message string to send */
412         NMUInt32 messageType            /* type of message (question, info, etc. */
413 )
414 {
415         MessagePacketPtr qpp;
416         unsigned long messageLen, size;
417         NMErr err;
418
419         /* sanity checks */
420
421         if( !message )
422                 return( kNSpInvalidParameterErr );
423
424         /* get size of message string & total size of network packet */
425
426         messageLen = strlen( message );
427         size = sizeof(MessagePacket_t) + messageLen + 1;        /* header + num_chars + terminator */
428
429         /* allocate the memory for the packet */
430
431         qpp = (MessagePacketPtr)malloc( size );
432         if( !qpp )
433         {
434                 printf( " *** ERROR *** Unable to allocate message buffer!\n\n" );
435                 fflush(stdout);
436                 return( kNSpMemAllocationErr );
437         }
438
439         /* init the NetSprocket portion of the packet */
440
441         NSpClearMessageHeader( &qpp->header );
442
443         qpp->header.what = (NMSInt32)messageType;
444         qpp->header.to = kNSpAllPlayers;
445         qpp->header.messageLen = size;
446
447         /* fill in the data section */
448
449         strcpy( qpp->str, message );
450
451         /* tell NetSprocket to send the message */
452
453         err = NSpMessage_Send( _gameReference, &qpp->header, kNSpSendFlag_Registered );
454         if( !err )
455         {
456                 qpp->header.to = kNSpHostOnly;          /* allow host to play as well! */
457
458                 err = NSpMessage_Send( _gameReference, &qpp->header, kNSpSendFlag_Registered );
459         }
460
461         if( err )
462         {
463                 printf( "*** ERROR *** Unable to send message packet, error # %d\n\n", err );
464                 fflush(stdout);
465         }
466
467         /* clean up after ourselves! */
468
469         free( qpp );
470
471         return( err );
472 }
473
474 /* ================================================================================
475         Send a question to all players
476
477          EXIT:  none
478 */
479
480 NMErr NetworkSendQuestion
481 (
482         const char *question    /* ptr to question string to send */
483 )
484 {
485         return( NetworkSendPlayerMessage( question, kMessageType_Question ) );
486 }
487
488 /* ================================================================================
489         Send information to all players
490
491          EXIT:  none
492 */
493
494 NMErr NetworkSendInformation
495 (
496         const char *message             /* ptr to information string to send */
497 )
498 {
499         return( NetworkSendPlayerMessage( message, kMessageType_Information ) );
500 }
501
502 #if __MWERKS__
503         #pragma mark -
504 #endif
505
506 /* ================================================================================
507         Initialize server networking
508
509          EXIT:  0 == no error, else error code
510 */
511
512 NMErr NetworkStartServer
513 (
514         NMUInt16 port,                                  /* port clients will connect on */
515         NMUInt32 maxPlayers,                    /* max # of players to allow into game */
516         const unsigned char *gameName,  /* name of game (displayed to clients on connect) */
517         const unsigned char *playerName /* name of player on server computer */
518 )
519 {
520         NSpProtocolReference protocolRef;
521         NMErr err;
522
523         /* Create a new protocol list to store our IP protocol reference in */
524
525         err = NSpProtocolList_New( NULL, &_protocolListRef );
526         if( !err )
527         {
528                 /* Create a protocol reference for our IP connection, on our specified port w/default maxRTT and throughput */
529
530                 protocolRef = NSpProtocol_CreateIP( port, 0, 0 );
531                 if( protocolRef )
532                 {
533                         /* We got a good reference, append it to the list we created earlier */
534
535                         err = NSpProtocolList_Append( _protocolListRef, protocolRef );
536                         if( !err )
537                         {
538                                 /* We got a protocol and it's in our reference list, now we can create the game
539                                         to host, using the parms sent in and defaulting rest of the "unused" hosting parms */
540
541                                 err = NSpGame_Host( &_gameReference, _protocolListRef, maxPlayers, gameName,
542                                                                         "\pPassword", playerName, kPlayerType, kNSpClientServer, 0 );
543                         }
544                 }
545                 else
546                         err = kNSpInvalidProtocolRefErr;        /* assign somewhat meaningful error on failure to create protocol reference */
547         }
548
549         return( err );
550 }
551
552
553 /* ================================================================================
554         Shutdown the networking, release resources, etc.
555
556          EXIT:  0 == no error, else error code
557 */
558
559 NMErr NetworkStartClient
560 (
561         char *ipAddr,                                   /* IP address (or domain name) to look for server (host) on */
562         char *port,                                             /* Port to talk to server via */
563         const unsigned char *playerName /* name of player wanting to join */
564 )
565 {
566         NSpAddressReference addRef;
567         NMErr err;
568
569         /* Given our input strings, create an OpenPlay address reference for talking to server */
570
571         addRef = NSpCreateIPAddressReference( ipAddr, port );
572         if( addRef )
573         {
574                 printf( "\nAttempting to join game..." );
575                 fflush(stdout);
576
577                 /* Now, look for a server on the IP/Port given and see if we can connect */
578
579                 err = NSpGame_Join( &_gameReference, addRef, playerName, "\pPassword", kPlayerType, NULL, 0, 0 );
580                 if( !err )
581                 {
582                         NMUInt32 startTime, currentTime;
583                         time_t seconds;
584
585                         printf( "connected!\n\nWaiting for approval to join game (press 'q' to quit)..." );
586                         fflush(stdout);
587
588                         time(&seconds);
589                         startTime = seconds;
590
591                         _response = _approved = false;
592
593                         /* We connected, now we have to wait for the server to approve our join request */
594
595                         while( !_response )
596                         {
597                                 /* Check for a time out in connecting to server */
598                                 /* this is before the event handler so that we are not approved, but time out anyway (small chance, but...) */
599
600                                 time(&seconds);
601                                 currentTime = seconds;
602
603                                 if( (currentTime - startTime > 60) && (!_response) )
604                                 {
605                                         printf( "ERROR: Connection timed out!\n\n" );
606                                         fflush(stdout);
607
608                                         _response = true;
609                                         _approved = false;
610                                 }
611
612                                 /* Handle system messages and allow user to quit via 'q' */
613                                 /* This also gets and handles network messages, like accept/deny */
614
615                                 //GameHandleEvents();
616                         }
617
618                         /* if we were not approved, we must dispose of the game object here */
619
620                         if( !_approved )
621                         {
622                                 err = NSpGame_Dispose( _gameReference, kNSpGameFlag_ForceTerminateGame );
623                                 _gameReference = NULL;
624                         }
625                         else    /* let the user know that they were accepted to the game */
626                         {
627                                 NSpGameInfo gameInfo;
628                                 char str[256];
629
630                                 err = NSpGame_GetInfo( _gameReference, &gameInfo );
631                                 if( !err )
632                                 {
633                                         GameP2CStr( gameInfo.name, str );
634
635                                         printf( "   Welcome to the game '%s', with %d players\n\n", str, (int)gameInfo.currentPlayers );
636                                         fflush(stdout);
637                                 }
638                         }
639                 }
640         }
641         else
642                 err = kNMParameterErr;
643
644         return( err );
645 }
646
647
648 #if __MWERKS__
649         #pragma mark -
650 #endif
651
652 /* ================================================================================
653         Shutdown the networking, release resources, etc.
654
655          EXIT:  0 == no error, else error code
656 */
657
658 NMErr NetworkShutdown( void )
659 {
660         NMUInt32 refCount;
661         NMErr err = kNMNoError;
662
663         /* if we have a game object (we should!) dispose if it now */
664
665         if( _gameReference )
666                 err = NSpGame_Dispose( _gameReference, kNSpGameFlag_ForceTerminateGame );
667
668         /* dispose of our protocol references & the list containing them */
669
670         if( _protocolListRef )
671         {
672                 NSpProtocolReference pRef;
673
674                 refCount = NSpProtocolList_GetCount( _protocolListRef );        /* get number of protocols */
675
676                 while( refCount-- && !err )
677                 {
678                         pRef = NSpProtocolList_GetIndexedRef( _protocolListRef, refCount );     /* get currect reference */
679
680                         err = NSpProtocolList_RemoveIndexed( _protocolListRef, refCount );      /* then remove it from the list */
681
682                         /* now, we can dispose of the reference safely */
683                         NSpProtocol_Dispose( pRef );    /* this should have an NMErr return, but has a void return... */
684                 }
685
686                 /* once all of the protocols are disposed, we can dispose of the containing reference list */
687                 NSpProtocolList_Dispose( _protocolListRef );
688         }
689
690
691         /* Make sure we can't use old values */
692
693         _protocolListRef = NULL;
694         _gameReference = NULL;
695         
696         return( err );
697 }
698
699 /* ================================================================================
700         Startup the networking system (NetSprockets in this case)
701
702          EXIT:  0 == no error, else error code
703 */
704
705 NMErr NetworkStartup( void )
706 {
707         NMErr err;
708
709         /* First, make sure that NetSprockets is available (we weak linked to OpenPlayStubLib) */
710
711         if( NULL == NSpInitialize )     /*|| NULL == ProtocolAcceptConnection )*/
712                 return( errModuleNotFound );
713
714         /* Initialize NetSprockets, 0 == use defaults & NSe1 stands for "NetSprocket Example 1" 
715                         It is an identifier for games of our type on the network */
716
717         err = NSpInitialize( 0, 0, 0, 'NSe1', 0 );
718
719         return( err );
720 }