2 Copyright (C) 2003, 2010 - Wolfire Games
4 This file is part of Lugaru.
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.
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.
15 See the GNU General Public License for more details.
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.
23 * Copyright (c) 2002 Lane Roathe. All rights reserved.
24 * Developed in cooperation with Freeverse, Inc.
26 * @APPLE_LICENSE_HEADER_START@
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
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
44 * @APPLE_LICENSE_HEADER_END@
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 $
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.
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 :)
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.
66 /* ----------------------------------------------------------- Includes */
78 /* ----------------------------------------------------------- Macro definitions */
82 kMessageType_Question = 256,
85 kMessageType_Information
88 #define kPlayerType 1234 /* just defines a way to allow/reject players of different types */
91 /* ----------------------------------------------------------- Type definitions */
93 /* Questions are sent out in message packets from server to players */
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) */
101 NSpMessageHeader header;
103 char str[1]; /* message (question, info, etc) string */
105 }MessagePacket_t, *MessagePacketPtr;
107 /* Answers are sent from the players to the server in this packet */
111 NSpMessageHeader header;
113 char answer; /* finally, the answer */
115 }AnswerPacket_t, *AnswerPacketPtr;
117 /* ----------------------------------------------------------- Local Variables */
119 /* name of this version of the app*/
120 char *GameNameStr = "OpenPlay NSp API Example 1";
122 /* Local vars used to handle our interaction with OpenPlay's NetSprockets API */
124 static NSpGameReference _gameReference = NULL; /* general game info */
125 static NSpProtocolListReference _protocolListRef = NULL; /* list of protocols we've started up */
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 */
130 /*--------------------------------------------------------------------------------*/
131 /*--------------------------------------------------------------------------------*/
134 #pragma mark È Local Routines
139 /*--------------------------------------------------------------------------------*/
140 /*--------------------------------------------------------------------------------*/
143 #pragma mark È Global Routines
147 unsigned char *GameC2PStr
149 char *s, /* source C string */
150 unsigned char *d /* destination buffer for Pascal string */
155 if( s && d ) /* make sure we got got inputs */
158 d[++c] = (unsigned char)*s++; /* no blockmove, copy & get length in one shot */
160 *d = (unsigned char)c;
167 unsigned char *s, /* source Pascal string */
168 char *d /* buffer to hold converted C string */
173 memcpy( d, s+1, *s ); /* pretty simple */
179 /* ================================================================================
180 Handle all of the network messages that are sent to this application
182 EXIT: 0 == no error, else error code
185 void NetworkHandleMessage( void )
187 NSpMessageHeader *mhp;
188 char str[256]; /* buffer for converted PStrings */
190 /* Ask NetSprocket to give us a message if any are waiting */
192 mhp = NSpMessage_Get( _gameReference );
195 /* Print out the type of message received */
200 /* --- Handle NetSprocket's generic messages */
202 case kNSpJoinApproved:
203 printf( "Approved!\n\n", str );
206 _approved = true; /* tell our waiting loop that we were approved */
211 err = NSpGame_GetInfo( _gameReference, &gameInfo );
213 SIOUXSetTitle( gameInfo.name );
219 GameP2CStr( ((NSpJoinDeniedMessage *)mhp)->reason, str );
220 printf( "Denied!\n Reason: %s\n\n", str );
225 _approved = false; /* tell our waiting loop we were denied */
229 printf( "*** ERROR *** (reported value: %d)\n\n", ((NSpErrorMessage *)mhp)->error );
236 case kNSpGameTerminated:
237 printf( "--- Host Terminated Game ---\n\n" );
245 case kNSpPlayerJoined:
247 NSpGameInfo gameInfo;
251 err = NSpGame_GetInfo( _gameReference, &gameInfo );
253 GameP2CStr( gameInfo.name, gamename );
255 strcpy( gamename, "ERRROR" );
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 );
262 /* Lets go ahead and re-send the current question, as the new guy deserves a shot at it too */
265 //QuestionPtr theQuestion = GameGetCurrentQuestion();
267 // NetworkSendQuestion(theQuestion->question);
273 GameP2CStr( ((NSpPlayerLeftMessage *)mhp)->playerName, str );
274 printf( "===> Player, %s, Left game, leaving %d players!\n\n",
275 str, ((NSpPlayerLeftMessage *)mhp)->playerCount );
279 case kNSpHostChanged:
280 printf( "===> ??? Host changed to player ID %d\n\n",
281 ((NSpHostChangedMessage *)mhp)->newHost );
285 case kNSpGroupCreated:
286 printf( "===> Player #%d created a new Group, ID %d\n\n",
287 ((NSpCreateGroupMessage *)mhp)->requestingPlayer, ((NSpCreateGroupMessage *)mhp)->groupID );
291 case kNSpGroupDeleted:
292 printf( "===> Player #%d deleted group #%d\n\n",
293 ((NSpDeleteGroupMessage *)mhp)->requestingPlayer, ((NSpDeleteGroupMessage *)mhp)->groupID );
297 case kNSpPlayerAddedToGroup:
298 printf( "===> Player %d was added to group %d\n\n",
299 ((NSpAddPlayerToGroupMessage *)mhp)->player, ((NSpAddPlayerToGroupMessage *)mhp)->group );
303 case kNSpPlayerRemovedFromGroup:
304 printf( "===> Player %d was removed from group %d\n\n",
305 ((NSpRemovePlayerFromGroupMessage *)mhp)->player, ((NSpRemovePlayerFromGroupMessage *)mhp)->group );
309 case kNSpPlayerTypeChanged:
310 printf( "===> Player %d changed to a new type, %d\n\n",
311 ((NSpPlayerTypeChangedMessage *)mhp)->player, ((NSpPlayerTypeChangedMessage *)mhp)->newType );
315 /* --- Handle our game specific messages */
318 /* Got a message, see if it is correct or not and let everyone know the results */
320 case kMessageType_Answer:
322 NSpPlayerInfoPtr pip;
323 char cname[kNSpStr32Len];
326 err = NSpPlayer_GetInfo( _gameReference, mhp->from, &pip );
329 GameP2CStr( pip->name, cname );
331 NSpPlayer_ReleaseInfo( _gameReference, pip );
334 strcpy( cname, "UNKOWN -- error!" );
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!" );
339 NetworkSendInformation( str );
343 /* allow game to do any special processing needed when a question arrives */
345 case kMessageType_Question:
346 //GameDisplayQuestion( ((MessagePacketPtr)mhp)->str );
349 /* pretty simple, just display the info message */
351 case kMessageType_Information:
352 printf( "%s\n\n", ((MessagePacketPtr)mhp)->str );
360 /* Once done with it, release the message */
362 NSpMessage_Release( _gameReference, mhp );
366 /* ================================================================================
367 Return the # of players in the current game
372 NMUInt32 NetworkGetPlayerCount( void )
374 NSpGameInfo gameInfo;
377 err = NSpGame_GetInfo( _gameReference, &gameInfo ); /* player count is in the game info record */
380 return( gameInfo.currentPlayers );
386 /* ================================================================================
387 Send an answer to the server
392 NMErr NetworkSendAnswer
394 char answer /* the answer to send (just a char!) */
397 AnswerPacket_t answerPacket;
400 /* init the NetSprocket portion of the packet */
402 NSpClearMessageHeader( &answerPacket.header );
404 answerPacket.header.what = kMessageType_Answer;
405 answerPacket.header.to = kNSpHostOnly;
406 answerPacket.header.messageLen = sizeof(answerPacket);
408 /* fill in the data section */
410 answerPacket.answer = answer;
412 /* tell NetSprocket to send the message */
414 err = NSpMessage_Send( _gameReference, &answerPacket.header, kNSpSendFlag_Registered );
417 printf( "*** ERROR *** Unable to send answer packet, error # %d\n\n", err );
424 /* ================================================================================
425 Send a message to all players
430 NMErr NetworkSendPlayerMessage
432 const char *message, /* ptr to message string to send */
433 NMUInt32 messageType /* type of message (question, info, etc. */
436 MessagePacketPtr qpp;
437 unsigned long messageLen, size;
443 return( kNSpInvalidParameterErr );
445 /* get size of message string & total size of network packet */
447 messageLen = strlen( message );
448 size = sizeof(MessagePacket_t) + messageLen + 1; /* header + num_chars + terminator */
450 /* allocate the memory for the packet */
452 qpp = (MessagePacketPtr)malloc( size );
455 printf( " *** ERROR *** Unable to allocate message buffer!\n\n" );
457 return( kNSpMemAllocationErr );
460 /* init the NetSprocket portion of the packet */
462 NSpClearMessageHeader( &qpp->header );
464 qpp->header.what = (NMSInt32)messageType;
465 qpp->header.to = kNSpAllPlayers;
466 qpp->header.messageLen = size;
468 /* fill in the data section */
470 strcpy( qpp->str, message );
472 /* tell NetSprocket to send the message */
474 err = NSpMessage_Send( _gameReference, &qpp->header, kNSpSendFlag_Registered );
477 qpp->header.to = kNSpHostOnly; /* allow host to play as well! */
479 err = NSpMessage_Send( _gameReference, &qpp->header, kNSpSendFlag_Registered );
484 printf( "*** ERROR *** Unable to send message packet, error # %d\n\n", err );
488 /* clean up after ourselves! */
495 /* ================================================================================
496 Send a question to all players
501 NMErr NetworkSendQuestion
503 const char *question /* ptr to question string to send */
506 return( NetworkSendPlayerMessage( question, kMessageType_Question ) );
509 /* ================================================================================
510 Send information to all players
515 NMErr NetworkSendInformation
517 const char *message /* ptr to information string to send */
520 return( NetworkSendPlayerMessage( message, kMessageType_Information ) );
527 /* ================================================================================
528 Initialize server networking
530 EXIT: 0 == no error, else error code
533 NMErr NetworkStartServer
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 */
541 NSpProtocolReference protocolRef;
544 /* Create a new protocol list to store our IP protocol reference in */
546 err = NSpProtocolList_New( NULL, &_protocolListRef );
549 /* Create a protocol reference for our IP connection, on our specified port w/default maxRTT and throughput */
551 protocolRef = NSpProtocol_CreateIP( port, 0, 0 );
554 /* We got a good reference, append it to the list we created earlier */
556 err = NSpProtocolList_Append( _protocolListRef, protocolRef );
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 */
562 err = NSpGame_Host( &_gameReference, _protocolListRef, maxPlayers, gameName,
563 "\pPassword", playerName, kPlayerType, kNSpClientServer, 0 );
567 err = kNSpInvalidProtocolRefErr; /* assign somewhat meaningful error on failure to create protocol reference */
574 /* ================================================================================
575 Shutdown the networking, release resources, etc.
577 EXIT: 0 == no error, else error code
580 NMErr NetworkStartClient
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 */
587 NSpAddressReference addRef;
590 /* Given our input strings, create an OpenPlay address reference for talking to server */
592 addRef = NSpCreateIPAddressReference( ipAddr, port );
595 printf( "\nAttempting to join game..." );
598 /* Now, look for a server on the IP/Port given and see if we can connect */
600 err = NSpGame_Join( &_gameReference, addRef, playerName, "\pPassword", kPlayerType, NULL, 0, 0 );
603 NMUInt32 startTime, currentTime;
606 printf( "connected!\n\nWaiting for approval to join game (press 'q' to quit)..." );
612 _response = _approved = false;
614 /* We connected, now we have to wait for the server to approve our join request */
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...) */
622 currentTime = seconds;
624 if( (currentTime - startTime > 60) && (!_response) )
626 printf( "ERROR: Connection timed out!\n\n" );
633 /* Handle system messages and allow user to quit via 'q' */
634 /* This also gets and handles network messages, like accept/deny */
636 //GameHandleEvents();
639 /* if we were not approved, we must dispose of the game object here */
643 err = NSpGame_Dispose( _gameReference, kNSpGameFlag_ForceTerminateGame );
644 _gameReference = NULL;
646 else /* let the user know that they were accepted to the game */
648 NSpGameInfo gameInfo;
651 err = NSpGame_GetInfo( _gameReference, &gameInfo );
654 GameP2CStr( gameInfo.name, str );
656 printf( " Welcome to the game '%s', with %d players\n\n", str, (int)gameInfo.currentPlayers );
663 err = kNMParameterErr;
673 /* ================================================================================
674 Shutdown the networking, release resources, etc.
676 EXIT: 0 == no error, else error code
679 NMErr NetworkShutdown( void )
682 NMErr err = kNMNoError;
684 /* if we have a game object (we should!) dispose if it now */
687 err = NSpGame_Dispose( _gameReference, kNSpGameFlag_ForceTerminateGame );
689 /* dispose of our protocol references & the list containing them */
691 if( _protocolListRef )
693 NSpProtocolReference pRef;
695 refCount = NSpProtocolList_GetCount( _protocolListRef ); /* get number of protocols */
697 while( refCount-- && !err )
699 pRef = NSpProtocolList_GetIndexedRef( _protocolListRef, refCount ); /* get currect reference */
701 err = NSpProtocolList_RemoveIndexed( _protocolListRef, refCount ); /* then remove it from the list */
703 /* now, we can dispose of the reference safely */
704 NSpProtocol_Dispose( pRef ); /* this should have an NMErr return, but has a void return... */
707 /* once all of the protocols are disposed, we can dispose of the containing reference list */
708 NSpProtocolList_Dispose( _protocolListRef );
712 /* Make sure we can't use old values */
714 _protocolListRef = NULL;
715 _gameReference = NULL;
720 /* ================================================================================
721 Startup the networking system (NetSprockets in this case)
723 EXIT: 0 == no error, else error code
726 NMErr NetworkStartup( void )
730 /* First, make sure that NetSprockets is available (we weak linked to OpenPlayStubLib) */
732 if( NULL == NSpInitialize ) /*|| NULL == ProtocolAcceptConnection )*/
733 return( errModuleNotFound );
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 */
738 err = NSpInitialize( 0, 0, 0, 'NSe1', 0 );