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