Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Source/NetSprocketSupport.c
/* |
File: NetSprocketSupport.c |
Contains: xxx put contents here xxx |
Version: xxx put version here xxx |
Copyright: © 1999 by Apple Computer, Inc., all rights reserved. |
File Ownership: |
DRI: xxx put dri here xxx |
Other Contact: xxx put other contact here xxx |
Technology: xxx put technology here xxx |
Writers: |
(cjd) Chris De Salvo |
Change History (most recent first): |
<SP7> 3/3/99 cjd Updated chat window UI code |
*/ |
//¥ ------------------------------------------------------------------------------------------ ¥ |
//¥ |
//¥ Copyright © 1996 Apple Computer, Inc., All Rights Reserved |
//¥ |
//¥ |
//¥ You may incorporate this sample code into your applications without |
//¥ restriction, though the sample code has been provided "AS IS" and the |
//¥ responsibility for its operation is 100% yours. However, what you are |
//¥ not permitted to do is to redistribute the source as "DSC Sample Code" |
//¥ after having made changes. If you're going to re-distribute the source, |
//¥ we require that you make it clear in the source that the code was |
//¥ descended from Apple Sample Code, but that you've made changes. |
//¥ |
//¥ Authors: |
//¥ Jamie Osborne |
//¥ |
//¥ ------------------------------------------------------------------------------------------ ¥ |
//¥ ------------------------------ Includes |
#include <ControlDefinitions.h> |
#include <Dialogs.h> |
#include <Fonts.h> |
#include <Lists.h> |
#include <QDOffScreen.h> |
#include <Resources.h> |
#include <Sound.h> |
#include <TextEdit.h> |
#include <TextUtils.h> |
#include "CommonStuff.h" |
#include "EventHandler.h" |
#include "NetSprocketSupport.h" |
//¥ ------------------------------ Private Definitions |
//¥ ------------------------------ Private Types |
enum |
{ |
kWaitForPlayersDialogID = 1000 |
}; |
enum |
{ |
kOKButton = 1, |
kCancelButton, |
kPlayerListLabel, |
kPlayerList, |
kChatWindow, |
kChatTextEntry, |
kStatus |
}; |
typedef struct DialogInfo |
{ |
ListHandle playerList; |
TEHandle teHandle; |
Str32 nameListBuffer[kMaxPlayers]; |
} DialogInfo; |
//¥ ------------------------------ Private Variables |
//¥ ------------------------------ Private Functions |
static void GetChooserName(Str255 name); |
static void SendChatText(StringPtr inText, NSpPlayerID inTo); |
static void DoHandleMessage(NSpMessageHeader *inMessage); |
static pascal void PlayerListProc(WindowPtr dialog, SInt16 itemNo); |
static pascal void ChatViewProc(WindowPtr dialog, SInt16 itemNo); |
static void SetDialogtemUserProc(DialogPtr dialog, SInt16 item, UserItemUPP theProc); |
static void WatchForPlayers(DialogPtr dialog); |
static OSStatus SendWelcomeMessage(NSpPlayerID inID); |
static void MakePlayerList(DialogPtr dialog); |
static void SendText(DialogPtr dialog); |
static pascal Boolean DialogEventFilter(DialogPtr dialog, EventRecord *event, SInt16 *itemHit); |
static void DoHandleChatTextMessage(ChatTextMessage *inMessage, DialogPtr dialog); |
static void PrintPlayerText(StringPtr inText, NSpPlayerID inPlayer, TEHandle inTEHandle); |
static void DoHandlePlayerInput(PlayerInputMessage *inMessage); |
static void DoHandleEndGameMessage(void); |
static void DoHandleStartGame(StartGameMessage *inMessage); |
static void DisableDialogItem(DialogPtr theDialog, SInt16 theItem); |
//¥ ------------------------------ Public Variables |
NSpGameReference gNetGame = NULL; |
Boolean gIAmHost = false; |
Str31 kNetworkGameName = "\pNetSprocket Game"; |
Str31 kJoinDialogLabel = "\pChoose a Game:"; |
Str31 kNBPType = "\pInvd"; // This should be the same as kGameID |
NetworkState gNetState; |
Str31 kSeparatorString = "\p: "; |
SInt8 kReturn = '\r'; |
Boolean gGotEndGameMessage = false; |
Boolean gReceivedInput; |
Boolean gNetSprocketPresent; |
#pragma mark === Utility functions === |
//¥ -------------------- NetSprocketPresent |
//¥ |
//¥ returns TRUE if networking is present, FALSE otherwise |
Boolean |
NetSprocketPresent(void) |
{ |
return (NSpInitialize != nil); |
} |
//¥ -------------------- GetChooserName |
static void |
GetChooserName(Str255 name) |
{ |
StringHandle userName; |
userName = GetString(-16096); |
FAIL_NIL(userName, "GetString for Chooser Name failed"); |
CopyPStr(*userName, name); |
ReleaseResource ((Handle) userName); |
error: |
return; |
} |
#pragma mark === Networking stuff === |
//¥ -------------------- InitNetworking |
Boolean |
InitNetworking(void) |
{ |
OSStatus status; |
if (! NetSprocketPresent()) |
return (false); |
//¥ Initialize NetSprocket |
status = NSpInitialize(kStandardMessageSize, kBufferSize, kQElements, kGameID, kTimeout); |
FAIL_OSSTATUS(status, "NSpInitialize failed"); |
if (status != noErr) |
return (false); |
gNetState = kSlacking; |
return (true); |
error: |
return (false); |
} |
//¥ -------------------- ShutdownNetworking |
void |
ShutdownNetworking(void) |
{ |
OSStatus status; |
if (gNetGame) |
status = NSpGame_Dispose( gNetGame, kNSpGameFlag_ForceTerminateGame ); |
gNetState = kSlacking; |
gNetGame = NULL; |
} |
//¥ -------------------- DoHostGame |
//¥ |
//¥ Returns TRUE if the user says OK (and NetSprocket doesn't return an error), false otherwise |
Boolean |
DoHostGame(void) |
{ |
OSStatus status = noErr; |
NSpProtocolListReference theList = NULL; |
Str31 playerName, gameName, password; |
Boolean OKHit; |
status = NSpProtocolList_New(NULL, &theList); |
FAIL_OSSTATUS(status, "Couldn't create a protocol list"); |
// Do the host dialog |
GetChooserName(playerName); |
CopyPStr(kNetworkGameName, gameName); |
password[0] = 0; |
OKHit = NSpDoModalHostDialog(theList, gameName, playerName, password, NULL); |
if (!OKHit) |
return (false); |
// Now host the game |
status = NSpGame_Host(&gNetGame, theList, kMaxPlayers, gameName, |
password, playerName, 0, kNSpClientServer, 0); |
FAIL_OSSTATUS(status, "NSpGame_Host returned an error"); |
NSpProtocolList_Dispose(theList); |
gIAmHost = true; |
gNetState = kHosting; |
// let all the players join |
OKHit = WaitForAllPlayers(); |
if (OKHit == false) |
{ |
ShutdownNetworking(); |
return (false); |
} |
else |
{ |
gNetState = kStarting; |
SendStartGame(); |
} |
return (true); |
error: |
if (theList) |
NSpProtocolList_Dispose(theList); |
return (false); |
} |
//¥ -------------------- DoJoinGame |
//¥ |
//¥ Returns TRUE if the user says OK (and NetSprocket doesn't return an error), false otherwise |
Boolean |
DoJoinGame(void) |
{ |
NSpAddressReference theAddress; |
Str31 name; |
Str31 password; |
OSStatus status; |
Boolean OKHit = true; |
GetChooserName(name); |
password[0] = 0; |
gIAmHost = false; |
theAddress = NSpDoModalJoinDialog(kNBPType, kJoinDialogLabel, name, password, NULL); |
if (theAddress == NULL) // The user cancelled |
return (false); |
status = NSpGame_Join(&gNetGame, theAddress, name, password, 0, NULL, 0, 0); |
FAIL_OSSTATUS(status, "NSpGame_Join returned an error"); |
gNetState = kJoining; |
OKHit = WaitForAllPlayers(); |
if (OKHit == false) |
{ |
gNetState = kSlacking; |
ShutdownNetworking(); |
} |
return OKHit; |
error: |
return (false); |
} |
//¥ -------------------- HandleNetworking |
void |
HandleNetworking(void) |
{ |
NSpMessageHeader *theMessage; |
if (gNetGame == NULL) |
return; |
theMessage = NSpMessage_Get(gNetGame); |
if (theMessage != NULL) |
{ |
DoHandleMessage(theMessage); |
NSpMessage_Release(gNetGame, theMessage); |
} |
} |
//¥ -------------------- DoHandleMessage |
static void |
DoHandleMessage(NSpMessageHeader *inMessage) |
{ |
switch (inMessage->what) |
{ |
case kNSpPlayerJoined: |
break; |
case kNSpPlayerLeft: |
break; |
case kChatTextMessage: |
DoHandleChatTextMessage((ChatTextMessage *)inMessage, nil); |
break; |
case kStartGameMessage: |
DoHandleStartGame((StartGameMessage *) inMessage); |
break; |
case kInputMessage: |
DoHandlePlayerInput((PlayerInputMessage *) inMessage); |
break; |
case kEndGameMessage: |
DoHandleEndGameMessage(); |
break; |
default: |
break; |
} |
} |
//¥ -------------------- WatchForPlayers |
static void |
WatchForPlayers(DialogPtr dialog) |
{ |
NSpMessageHeader *theMessage; |
if (gNetGame == NULL) |
return; |
while ((theMessage = NSpMessage_Get(gNetGame)) != NULL) |
{ |
if (theMessage->what == kNSpPlayerJoined) |
{ |
if (gNetState == kHosting) |
SendWelcomeMessage(((NSpPlayerJoinedMessage *) theMessage)->playerInfo.id); |
MakePlayerList(dialog); |
} |
else if (theMessage->what == kNSpPlayerLeft) |
{ |
MakePlayerList(dialog); |
} |
else if (theMessage->what == kChatTextMessage) |
{ |
DoHandleChatTextMessage((ChatTextMessage *)theMessage, dialog); |
} |
else |
{ |
DoHandleMessage(theMessage); |
} |
NSpMessage_Release(gNetGame, theMessage); |
} |
} |
//¥ -------------------- SendWelcomeMessage |
static OSStatus |
SendWelcomeMessage(NSpPlayerID inID) |
{ |
#pragma unused (inID) |
OSStatus status = noErr; |
return (status); |
} |
#pragma mark === Dialog Manager Cruft |
//¥ -------------------- SetDialogtemUserProc |
static void |
SetDialogtemUserProc(DialogPtr dialog, SInt16 item, UserItemUPP theProc) |
{ |
Rect r; |
SInt16 itemType; |
Handle itemH; |
GetDialogItem(dialog, item, &itemType, &itemH, &r); |
itemH = (Handle) theProc; |
SetDialogItem(dialog, item, itemType, itemH, &r); |
} |
//¥ -------------------- MakePlayerList |
static void |
MakePlayerList(DialogPtr dialog) |
{ |
SInt16 i; |
Cell theCell; |
NSpPlayerEnumerationPtr players; |
OSStatus status; |
DialogInfo *theInfo = (DialogInfo *) GetWRefCon(dialog); |
ListHandle theList = theInfo->playerList; |
LSetDrawingMode(true, theList); // turn on updating |
status = NSpPlayer_GetEnumeration(gNetGame, &players); |
FAIL_OSSTATUS(status, "NSpPlayer_GetEnumeration returned an error"); |
for (i = 0; i < players->count; i++) |
CopyPStr(players->playerInfo[i]->name, theInfo->nameListBuffer[i]); |
/* DELETE ALL EXISTING ROWS IN LIST */ |
LDelRow(0, 0, theList); |
/* ADD NAMES TO LIST */ |
if (players->count > 0) |
LAddRow(players->count, 0, theList); // create rows |
for (i = 0; i < players->count; i++) |
{ |
theCell.h = 0; |
theCell.v = i; |
LSetCell(&theInfo->nameListBuffer[i][1], theInfo->nameListBuffer[i][0], theCell, theList); |
} |
NSpPlayer_ReleaseEnumeration(gNetGame, players); |
error: |
return; |
} |
//¥ -------------------- WaitForAllPlayers |
Boolean |
WaitForAllPlayers(void) |
{ |
DialogPtr theDialog = GetNewDialog(kWaitForPlayersDialogID, NULL, (WindowPtr) -1); |
ListHandle playerList; |
SInt16 itemHit; |
Boolean okHit = false; |
Rect r, dataBounds = {0,0,0,1}; |
Point cellSize = {0,0}; |
SInt16 itemType; |
Handle itemH; |
DialogInfo info; |
TextStyle theStyle; |
ControlHandle theControl; |
ModalFilterUPP theFilter = NewModalFilterProc(DialogEventFilter); |
UserItemUPP playerListProc = NewUserItemProc(PlayerListProc); |
UserItemUPP chatViewProc = NewUserItemProc(ChatViewProc); |
// NOTE: check okUserItemProcUPP != NULL |
if (theDialog == NULL) |
return (false); |
SetPort(theDialog); |
SetDialogDefaultItem(theDialog, kOKButton); |
SetDialogCancelItem(theDialog, kCancelButton); |
SetDialogtemUserProc(theDialog, kPlayerList, playerListProc); |
SetDialogtemUserProc(theDialog, kChatWindow, chatViewProc); |
(void) GetDialogItemAsControl(theDialog, kChatTextEntry, &theControl); |
(void) SetKeyboardFocus(theDialog, theControl, kControlLabelPart); |
// Now create the list of players |
GetDialogItem(theDialog, kPlayerList, &itemType, &itemH, &r); |
r.right -= 15; |
playerList = LNew(&r, &dataBounds, cellSize, 0, theDialog, true, |
false, false, true); |
info.playerList = playerList; |
// Set up the text edut stuff |
GetDialogItem(theDialog, kChatWindow, &itemType, &itemH, &r); |
InsetRect(&r, 2, 2); |
info.teHandle = TEStyleNew(&r, &r); |
TEAutoView(true, info.teHandle); |
// Set up the default drawing info |
theStyle.tsFont = kFontIDMonaco; |
theStyle.tsSize = 9; |
theStyle.tsColor.red = 0; |
theStyle.tsColor.green = 0; |
theStyle.tsColor.blue = 0; |
theStyle.tsFace = normal; |
TESetStyle(doAll, &theStyle, true, info.teHandle); |
// If we're the joiner, disable the OK button |
if (gNetState == kJoining) |
DisableDialogItem(theDialog, kOKButton); |
// Save our custom stuff in the refcon |
SetWRefCon(theDialog, (SInt32) &info); |
ShowWindow(theDialog); |
DrawControls(theDialog); |
do |
{ |
ModalDialog(theFilter, &itemHit); |
} while (itemHit != kOKButton && itemHit != kCancelButton); |
LDispose(playerList); |
DisposeDialog(theDialog); |
DisposeRoutineDescriptor(theFilter); |
DisposeRoutineDescriptor(playerListProc); |
DisposeRoutineDescriptor(chatViewProc); |
okHit = (itemHit == kOKButton || gNetState == kRunning); |
return (okHit); |
} |
//¥ -------------------- DisableDialogItem |
static void |
DisableDialogItem(DialogPtr theDialog, SInt16 theItem) |
{ |
SInt16 theType; |
Rect theRect; |
Handle theHandle; |
if (! theDialog) |
return; |
GetDialogItem(theDialog, theItem, &theType, &theHandle, &theRect); |
if (! theHandle) |
return; |
HiliteControl((ControlHandle) theHandle, 255); |
theType |= kItemDisableBit; |
SetDialogItem(theDialog, theItem, theType, theHandle, &theRect); |
} |
#pragma mark === Callback Procs === |
//¥ -------------------- PlayerListProc |
static pascal void |
PlayerListProc(WindowPtr dialog, SInt16 itemNo) |
{ |
Rect r; |
SInt16 itemType; |
Handle item; |
GrafPtr theWindow = (GrafPtr) dialog; |
DialogInfo *theInfo = (DialogInfo *)GetWRefCon(dialog); |
ListHandle list = theInfo->playerList; |
if (itemNo == kPlayerList) |
{ |
GetDialogItem(dialog, itemNo, &itemType, &item, &r); |
InsetRect(&r, -1, -1); |
FrameRect(&r); |
LUpdate(theWindow->visRgn, list); |
} |
} |
//¥ -------------------- ChatViewProc |
static pascal void |
ChatViewProc(WindowPtr dialog, SInt16 itemNo) |
{ |
Rect r; |
SInt16 itemType; |
Handle item; |
DialogInfo *theInfo; |
GetDialogItem(dialog, itemNo, &itemType, &item, &r); |
theInfo = (DialogInfo *)GetWRefCon(dialog); |
FrameRect(&r); |
InsetRect(&r, 2, 2); |
TEUpdate(&r, theInfo->teHandle); |
} |
//¥ -------------------- DialogEventFilter |
static pascal Boolean |
DialogEventFilter(DialogPtr dialog, EventRecord *event, SInt16 *itemHit) |
{ |
OSErr err; |
CGrafPtr oldPort; |
GDHandle oldGD; |
WindowRef window; |
ModalFilterUPP modalProc; |
Rect itemRect; |
SInt16 key; |
SInt16 thePart; |
Boolean accepted = false; |
Point theP; |
Boolean hit; |
DialogInfo *theInfo; |
GetGWorld(&oldPort, &oldGD); |
SetGWorld((CGrafPtr)dialog, nil); |
IdleControls(dialog); |
WatchForPlayers(dialog); |
if (gNetState == kRunning) |
{ |
*itemHit = kOKButton; |
return (true); |
} |
switch(event->what) |
{ |
case keyDown: |
case autoKey: |
key = event->message & charCodeMask; |
if (key == 0x0D) |
{ |
SendText(dialog); |
accepted = true; |
} |
else if (key == '\t') |
{ |
if (event->modifiers & shiftKey) |
ReverseKeyboardFocus(dialog); |
else |
AdvanceKeyboardFocus(dialog); |
} |
else |
{ |
accepted = false; |
} |
break; |
case updateEvt: |
if (event->message == (SInt32)dialog) |
{ |
// in case a screen saver activates. |
UpdateDialog(dialog, dialog->visRgn); |
SetCursor(&qd.arrow); |
} |
break; |
case activateEvt: |
accepted = true; |
break; |
case mouseDown: |
thePart = FindWindow(event->where,&window); |
switch (thePart) |
{ |
case inContent: |
theP = event->where; |
GlobalToLocal(&theP); |
theInfo = (DialogInfo *)GetWRefCon(dialog); |
itemRect = (*theInfo->playerList)->rView; |
// add the scroll bar back in for hit testing (remember we took it out earlier) |
itemRect.right += 16; |
// See if they clicked in our list! |
if (PtInRect(theP, &itemRect)) |
{ |
hit = LClick(theP, nil, theInfo->playerList); |
// if they double-clicked the list, return 1, as if the OK button had been pressed |
if (hit) |
*itemHit = kOKButton; |
else |
*itemHit = kPlayerList; |
// tell the Dialog Manager that we handled this click, it can stop searching for a click-owner |
accepted = true; |
} |
break; |
case inSysWindow: |
SysBeep(30); |
break; |
case inDrag: |
if (window == dialog) |
{ |
DragWindow(dialog, event->where,&qd.screenBits.bounds); |
accepted = true; |
} |
break; |
} |
break; |
} |
if (false == accepted) |
{ |
err = GetStdFilterProc(&modalProc); |
if (err == noErr) |
accepted = CallModalFilterProc(modalProc, dialog, event, itemHit); |
} |
SetGWorld(oldPort, oldGD); |
return (accepted); |
} |
//¥ -------------------- SendText |
static void |
SendText(DialogPtr dialog) |
{ |
ControlHandle item; |
Str255 theText; |
(void) GetDialogItemAsControl(dialog, kChatTextEntry, &item); |
GetDialogItemText((Handle) item, theText); |
SetDialogItemText((Handle) item, "\p"); |
DrawOneControl(item); |
if (theText[0] > 0) |
SendChatText(theText, kNSpAllPlayers); |
} |
#pragma mark ===== |
//¥ -------------------- SendChatText |
static void |
SendChatText(StringPtr inText, NSpPlayerID inTo) |
{ |
ChatTextMessage theMessage; |
OSStatus status; |
theMessage.h.to = inTo; |
theMessage.h.what = kChatTextMessage; |
theMessage.h.messageLen = sizeof(ChatTextMessage); |
CopyPStr(inText, theMessage.text); |
status = NSpMessage_Send(gNetGame, &theMessage.h, kNSpSendFlag_Junk | kNSpSendFlag_SelfSend); |
} |
//¥ -------------------- SendStartGame |
void |
SendStartGame(void) |
{ |
StartGameMessage theMessage; |
OSStatus status; |
NSpClearMessageHeader(&theMessage.h); |
theMessage.h.to = kNSpAllPlayers; |
theMessage.h.what = kStartGameMessage; |
theMessage.h.messageLen = sizeof(StartGameMessage); |
theMessage.seed = qd.randSeed; |
status = NSpMessage_Send(gNetGame, &theMessage.h, kNSpSendFlag_Registered | kNSpSendFlag_SelfSend); |
} |
//¥ -------------------- SendInputState |
void |
SendInputState(Boolean left, Boolean right, Boolean fire) |
{ |
PlayerInputMessage theMessage; |
OSStatus status; |
NSpClearMessageHeader(&theMessage.h); |
theMessage.h.to = kNSpAllPlayers; |
theMessage.h.what = kInputMessage; |
theMessage.h.messageLen = sizeof(PlayerInputMessage); |
theMessage.left = left; |
theMessage.right = right; |
theMessage.fire = fire; |
status = NSpMessage_Send(gNetGame, &theMessage.h, kNSpSendFlag_Registered); |
} |
//¥ -------------------- SendEndGame |
void |
SendEndGame(void) |
{ |
OSStatus status; |
NSpMessageHeader theMessage; |
NSpClearMessageHeader(&theMessage); |
theMessage.to = kNSpAllPlayers; |
theMessage.what = kEndGameMessage; |
theMessage.messageLen = sizeof(NSpMessageHeader); |
status = NSpMessage_Send(gNetGame, &theMessage, kNSpSendFlag_Registered); |
} |
//¥ -------------------- DoHandleChatTextMessage |
static void |
DoHandleChatTextMessage(ChatTextMessage *inMessage, DialogPtr dialog) |
{ |
DialogInfo *theInfo; |
if (! dialog) |
return; |
theInfo = (DialogInfo *)GetWRefCon(dialog); |
PrintPlayerText(inMessage->text, inMessage->h.from, theInfo->teHandle); |
} |
//¥ -------------------- DoHandleStartGame |
static void |
DoHandleStartGame(StartGameMessage *inMessage) |
{ |
gNetState = kRunning; |
qd.randSeed = inMessage->seed; |
} |
//¥ -------------------- DoHandlePlayerInput |
static void |
DoHandlePlayerInput(PlayerInputMessage *inMessage) |
{ |
if (gIAmHost) |
{ |
gGameKeys.redLeft = inMessage->left; |
gGameKeys.redRight = inMessage->right; |
gGameKeys.redFire = inMessage->fire; |
} |
else |
{ |
gGameKeys.greenLeft = inMessage->left; |
gGameKeys.greenRight = inMessage->right; |
gGameKeys.greenFire = inMessage->fire; |
} |
gReceivedInput = true; |
} |
//¥ -------------------- DoHandleEndGameMessage |
static void |
DoHandleEndGameMessage(void) |
{ |
gGotEndGameMessage = true; |
} |
//¥ -------------------- PrintPlayerText |
static void |
PrintPlayerText(StringPtr inText, NSpPlayerID inPlayer, TEHandle inTEHandle) |
{ |
NSpPlayerInfoPtr info; |
NSpPlayer_GetInfo(gNetGame, inPlayer, &info); |
TEInsert(&info->name[1], info->name[0], inTEHandle); |
TEInsert(&kSeparatorString[1], kSeparatorString[0], inTEHandle); |
TEInsert(&inText[1], inText[0], inTEHandle); |
TEInsert(&kReturn, 1, inTEHandle); |
//¥ Select end of the TE text |
TESetSelect(32767L, 32767L, inTEHandle); |
//¥ Scroll it into view if necessary |
TESelView(inTEHandle); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-14