2 * Copyright (c) 2002 Lane Roathe. All rights reserved.
3 * Developed in cooperation with Freeverse, Inc.
5 * @APPLE_LICENSE_HEADER_START@
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
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
23 * @APPLE_LICENSE_HEADER_END@
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 $
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.
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 :)
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.
45 /* ----------------------------------------------------------- Includes */
57 /* ----------------------------------------------------------- Macro definitions */
61 kMessageType_Question = 256,
64 kMessageType_Information
67 #define kPlayerType 1234 /* just defines a way to allow/reject players of different types */
70 /* ----------------------------------------------------------- Type definitions */
72 /* Questions are sent out in message packets from server to players */
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) */
80 NSpMessageHeader header;
82 char str[1]; /* message (question, info, etc) string */
84 }MessagePacket_t, *MessagePacketPtr;
86 /* Answers are sent from the players to the server in this packet */
90 NSpMessageHeader header;
92 char answer; /* finally, the answer */
94 }AnswerPacket_t, *AnswerPacketPtr;
96 /* ----------------------------------------------------------- Local Variables */
98 /* name of this version of the app*/
99 char *GameNameStr = "OpenPlay NSp API Example 1";
101 /* Local vars used to handle our interaction with OpenPlay's NetSprockets API */
103 static NSpGameReference _gameReference = NULL; /* general game info */
104 static NSpProtocolListReference _protocolListRef = NULL; /* list of protocols we've started up */
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 */
109 /*--------------------------------------------------------------------------------*/
110 /*--------------------------------------------------------------------------------*/
113 #pragma mark È Local Routines
118 /*--------------------------------------------------------------------------------*/
119 /*--------------------------------------------------------------------------------*/
122 #pragma mark È Global Routines
126 unsigned char *GameC2PStr
128 char *s, /* source C string */
129 unsigned char *d /* destination buffer for Pascal string */
134 if( s && d ) /* make sure we got got inputs */
137 d[++c] = (unsigned char)*s++; /* no blockmove, copy & get length in one shot */
139 *d = (unsigned char)c;
146 unsigned char *s, /* source Pascal string */
147 char *d /* buffer to hold converted C string */
152 memcpy( d, s+1, *s ); /* pretty simple */
158 /* ================================================================================
159 Handle all of the network messages that are sent to this application
161 EXIT: 0 == no error, else error code
164 void NetworkHandleMessage( void )
166 NSpMessageHeader *mhp;
167 char str[256]; /* buffer for converted PStrings */
169 /* Ask NetSprocket to give us a message if any are waiting */
171 mhp = NSpMessage_Get( _gameReference );
174 /* Print out the type of message received */
179 /* --- Handle NetSprocket's generic messages */
181 case kNSpJoinApproved:
182 printf( "Approved!\n\n", str );
185 _approved = true; /* tell our waiting loop that we were approved */
190 err = NSpGame_GetInfo( _gameReference, &gameInfo );
192 SIOUXSetTitle( gameInfo.name );
198 GameP2CStr( ((NSpJoinDeniedMessage *)mhp)->reason, str );
199 printf( "Denied!\n Reason: %s\n\n", str );
204 _approved = false; /* tell our waiting loop we were denied */
208 printf( "*** ERROR *** (reported value: %d)\n\n", ((NSpErrorMessage *)mhp)->error );
215 case kNSpGameTerminated:
216 printf( "--- Host Terminated Game ---\n\n" );
224 case kNSpPlayerJoined:
226 NSpGameInfo gameInfo;
230 err = NSpGame_GetInfo( _gameReference, &gameInfo );
232 GameP2CStr( gameInfo.name, gamename );
234 strcpy( gamename, "ERRROR" );
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 );
241 /* Lets go ahead and re-send the current question, as the new guy deserves a shot at it too */
244 //QuestionPtr theQuestion = GameGetCurrentQuestion();
246 // NetworkSendQuestion(theQuestion->question);
252 GameP2CStr( ((NSpPlayerLeftMessage *)mhp)->playerName, str );
253 printf( "===> Player, %s, Left game, leaving %d players!\n\n",
254 str, ((NSpPlayerLeftMessage *)mhp)->playerCount );
258 case kNSpHostChanged:
259 printf( "===> ??? Host changed to player ID %d\n\n",
260 ((NSpHostChangedMessage *)mhp)->newHost );
264 case kNSpGroupCreated:
265 printf( "===> Player #%d created a new Group, ID %d\n\n",
266 ((NSpCreateGroupMessage *)mhp)->requestingPlayer, ((NSpCreateGroupMessage *)mhp)->groupID );
270 case kNSpGroupDeleted:
271 printf( "===> Player #%d deleted group #%d\n\n",
272 ((NSpDeleteGroupMessage *)mhp)->requestingPlayer, ((NSpDeleteGroupMessage *)mhp)->groupID );
276 case kNSpPlayerAddedToGroup:
277 printf( "===> Player %d was added to group %d\n\n",
278 ((NSpAddPlayerToGroupMessage *)mhp)->player, ((NSpAddPlayerToGroupMessage *)mhp)->group );
282 case kNSpPlayerRemovedFromGroup:
283 printf( "===> Player %d was removed from group %d\n\n",
284 ((NSpRemovePlayerFromGroupMessage *)mhp)->player, ((NSpRemovePlayerFromGroupMessage *)mhp)->group );
288 case kNSpPlayerTypeChanged:
289 printf( "===> Player %d changed to a new type, %d\n\n",
290 ((NSpPlayerTypeChangedMessage *)mhp)->player, ((NSpPlayerTypeChangedMessage *)mhp)->newType );
294 /* --- Handle our game specific messages */
297 /* Got a message, see if it is correct or not and let everyone know the results */
299 case kMessageType_Answer:
301 NSpPlayerInfoPtr pip;
302 char cname[kNSpStr32Len];
305 err = NSpPlayer_GetInfo( _gameReference, mhp->from, &pip );
308 GameP2CStr( pip->name, cname );
310 NSpPlayer_ReleaseInfo( _gameReference, pip );
313 strcpy( cname, "UNKOWN -- error!" );
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!" );
318 NetworkSendInformation( str );
322 /* allow game to do any special processing needed when a question arrives */
324 case kMessageType_Question:
325 //GameDisplayQuestion( ((MessagePacketPtr)mhp)->str );
328 /* pretty simple, just display the info message */
330 case kMessageType_Information:
331 printf( "%s\n\n", ((MessagePacketPtr)mhp)->str );
339 /* Once done with it, release the message */
341 NSpMessage_Release( _gameReference, mhp );
345 /* ================================================================================
346 Return the # of players in the current game
351 NMUInt32 NetworkGetPlayerCount( void )
353 NSpGameInfo gameInfo;
356 err = NSpGame_GetInfo( _gameReference, &gameInfo ); /* player count is in the game info record */
359 return( gameInfo.currentPlayers );
365 /* ================================================================================
366 Send an answer to the server
371 NMErr NetworkSendAnswer
373 char answer /* the answer to send (just a char!) */
376 AnswerPacket_t answerPacket;
379 /* init the NetSprocket portion of the packet */
381 NSpClearMessageHeader( &answerPacket.header );
383 answerPacket.header.what = kMessageType_Answer;
384 answerPacket.header.to = kNSpHostOnly;
385 answerPacket.header.messageLen = sizeof(answerPacket);
387 /* fill in the data section */
389 answerPacket.answer = answer;
391 /* tell NetSprocket to send the message */
393 err = NSpMessage_Send( _gameReference, &answerPacket.header, kNSpSendFlag_Registered );
396 printf( "*** ERROR *** Unable to send answer packet, error # %d\n\n", err );
403 /* ================================================================================
404 Send a message to all players
409 NMErr NetworkSendPlayerMessage
411 const char *message, /* ptr to message string to send */
412 NMUInt32 messageType /* type of message (question, info, etc. */
415 MessagePacketPtr qpp;
416 unsigned long messageLen, size;
422 return( kNSpInvalidParameterErr );
424 /* get size of message string & total size of network packet */
426 messageLen = strlen( message );
427 size = sizeof(MessagePacket_t) + messageLen + 1; /* header + num_chars + terminator */
429 /* allocate the memory for the packet */
431 qpp = (MessagePacketPtr)malloc( size );
434 printf( " *** ERROR *** Unable to allocate message buffer!\n\n" );
436 return( kNSpMemAllocationErr );
439 /* init the NetSprocket portion of the packet */
441 NSpClearMessageHeader( &qpp->header );
443 qpp->header.what = (NMSInt32)messageType;
444 qpp->header.to = kNSpAllPlayers;
445 qpp->header.messageLen = size;
447 /* fill in the data section */
449 strcpy( qpp->str, message );
451 /* tell NetSprocket to send the message */
453 err = NSpMessage_Send( _gameReference, &qpp->header, kNSpSendFlag_Registered );
456 qpp->header.to = kNSpHostOnly; /* allow host to play as well! */
458 err = NSpMessage_Send( _gameReference, &qpp->header, kNSpSendFlag_Registered );
463 printf( "*** ERROR *** Unable to send message packet, error # %d\n\n", err );
467 /* clean up after ourselves! */
474 /* ================================================================================
475 Send a question to all players
480 NMErr NetworkSendQuestion
482 const char *question /* ptr to question string to send */
485 return( NetworkSendPlayerMessage( question, kMessageType_Question ) );
488 /* ================================================================================
489 Send information to all players
494 NMErr NetworkSendInformation
496 const char *message /* ptr to information string to send */
499 return( NetworkSendPlayerMessage( message, kMessageType_Information ) );
506 /* ================================================================================
507 Initialize server networking
509 EXIT: 0 == no error, else error code
512 NMErr NetworkStartServer
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 */
520 NSpProtocolReference protocolRef;
523 /* Create a new protocol list to store our IP protocol reference in */
525 err = NSpProtocolList_New( NULL, &_protocolListRef );
528 /* Create a protocol reference for our IP connection, on our specified port w/default maxRTT and throughput */
530 protocolRef = NSpProtocol_CreateIP( port, 0, 0 );
533 /* We got a good reference, append it to the list we created earlier */
535 err = NSpProtocolList_Append( _protocolListRef, protocolRef );
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 */
541 err = NSpGame_Host( &_gameReference, _protocolListRef, maxPlayers, gameName,
542 "\pPassword", playerName, kPlayerType, kNSpClientServer, 0 );
546 err = kNSpInvalidProtocolRefErr; /* assign somewhat meaningful error on failure to create protocol reference */
553 /* ================================================================================
554 Shutdown the networking, release resources, etc.
556 EXIT: 0 == no error, else error code
559 NMErr NetworkStartClient
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 */
566 NSpAddressReference addRef;
569 /* Given our input strings, create an OpenPlay address reference for talking to server */
571 addRef = NSpCreateIPAddressReference( ipAddr, port );
574 printf( "\nAttempting to join game..." );
577 /* Now, look for a server on the IP/Port given and see if we can connect */
579 err = NSpGame_Join( &_gameReference, addRef, playerName, "\pPassword", kPlayerType, NULL, 0, 0 );
582 NMUInt32 startTime, currentTime;
585 printf( "connected!\n\nWaiting for approval to join game (press 'q' to quit)..." );
591 _response = _approved = false;
593 /* We connected, now we have to wait for the server to approve our join request */
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...) */
601 currentTime = seconds;
603 if( (currentTime - startTime > 60) && (!_response) )
605 printf( "ERROR: Connection timed out!\n\n" );
612 /* Handle system messages and allow user to quit via 'q' */
613 /* This also gets and handles network messages, like accept/deny */
615 //GameHandleEvents();
618 /* if we were not approved, we must dispose of the game object here */
622 err = NSpGame_Dispose( _gameReference, kNSpGameFlag_ForceTerminateGame );
623 _gameReference = NULL;
625 else /* let the user know that they were accepted to the game */
627 NSpGameInfo gameInfo;
630 err = NSpGame_GetInfo( _gameReference, &gameInfo );
633 GameP2CStr( gameInfo.name, str );
635 printf( " Welcome to the game '%s', with %d players\n\n", str, (int)gameInfo.currentPlayers );
642 err = kNMParameterErr;
652 /* ================================================================================
653 Shutdown the networking, release resources, etc.
655 EXIT: 0 == no error, else error code
658 NMErr NetworkShutdown( void )
661 NMErr err = kNMNoError;
663 /* if we have a game object (we should!) dispose if it now */
666 err = NSpGame_Dispose( _gameReference, kNSpGameFlag_ForceTerminateGame );
668 /* dispose of our protocol references & the list containing them */
670 if( _protocolListRef )
672 NSpProtocolReference pRef;
674 refCount = NSpProtocolList_GetCount( _protocolListRef ); /* get number of protocols */
676 while( refCount-- && !err )
678 pRef = NSpProtocolList_GetIndexedRef( _protocolListRef, refCount ); /* get currect reference */
680 err = NSpProtocolList_RemoveIndexed( _protocolListRef, refCount ); /* then remove it from the list */
682 /* now, we can dispose of the reference safely */
683 NSpProtocol_Dispose( pRef ); /* this should have an NMErr return, but has a void return... */
686 /* once all of the protocols are disposed, we can dispose of the containing reference list */
687 NSpProtocolList_Dispose( _protocolListRef );
691 /* Make sure we can't use old values */
693 _protocolListRef = NULL;
694 _gameReference = NULL;
699 /* ================================================================================
700 Startup the networking system (NetSprockets in this case)
702 EXIT: 0 == no error, else error code
705 NMErr NetworkStartup( void )
709 /* First, make sure that NetSprockets is available (we weak linked to OpenPlayStubLib) */
711 if( NULL == NSpInitialize ) /*|| NULL == ProtocolAcceptConnection )*/
712 return( errModuleNotFound );
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 */
717 err = NSpInitialize( 0, 0, 0, 'NSe1', 0 );