home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NEXT Generation 27
/
NEXT27.iso
/
pc
/
demos
/
emperor
/
dx3.exe
/
SDK
/
SAMPLES
/
IKLOWNS
/
CGREMOTE.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1996-08-30
|
24KB
|
868 lines
/*===========================================================================*\
|
| File: cgremote.cpp
|
| Description:
| Routines to send and receive remote actions for controlling objects
| on remote machines. Uses DirectPlay for transferring data.
|
|-----------------------------------------------------------------------------
|
| Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
|
| Written by Moss Bay Engineering, Inc. under contract to Microsoft Corporation
|
\*===========================================================================*/
/**************************************************************************
(C) Copyright 1995-1996 Microsoft Corp. All rights reserved.
You have a royalty-free right to use, modify, reproduce and
distribute the Sample Files (and/or any modified version) in
any way you find useful, provided that you agree that
Microsoft has no warranty obligations or liability for any
Sample Application Files which are modified.
we do not recomend you base your game on IKlowns, start with one of
the other simpler sample apps in the GDK
**************************************************************************/
//** include files **
#include <stdlib.h>
#ifndef __WATCOMC__
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include "cgglobl.h" // For ghInst for dialog box
#include "cgrsrce.h"
#include "dplay.h"
#include "dplobby.h"
#include "cglevel.h"
#include "cgremote.h"
#include "strrec.h"
#include <stdarg.h>
#include <stdio.h>
static void InitReceivePoll( void );
// [johnhall]
// hack -- get GENERIC_CHAR_INFO from cgkrusty.cpp
typedef signed char SCHAR;
typedef struct
{
LONG posx;
LONG posy;
SCHAR state;
SCHAR velx;
SCHAR vely;
SCHAR curZ;
} GENERIC_CHAR_INFO;
//** local definitions **
// structure to be used to pass remote actions across the link
typedef struct _GAMEMESSAGE {
BYTE Action; // action code
BYTE NumBytes; // size of data
REMOTE_OBJECT RemObj; // unique object id
char Data[1]; // action specific data
} GAMEMESSAGE, *PGAMEMESSAGE;
//** external functions **
//** external data **
// KLUDGE:
CGameLevel *gCurLevel=NULL;
//** public data **
//** private data **
static IDirectPlay *lpIDC=NULL; // DirectPlay Object
static DPID dcoID=0; // our DirectPlay ID
static HANDLE dphEvent = NULL;
static BOOL fAbort = FALSE; // Abort flag for RemoteConnect()
//** public functions **
//** private functions **
static DWORD RequestThreadProc(void *Dummy);
static BOOL CALLBACK AbortDlgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
#define DP_BROADCAST_ID 0
// ----------------------------------------------------------
// CreateRemotePeer - create a new character across all peers
// ----------------------------------------------------------
REMOTE_OBJECT *CreateRemotePeers(
char *name, // name of object as per level->Add()
DWORD InstanceID // unique instance ID of object type
)
{
// Gotta have a DCO object to do this!
if (lpIDC == NULL)
{
return(NULL);
}
// Setup a message and an unique obect id
char *pBuffer = NULL;
DWORD lenBuff;
PGAMEMESSAGE pGameMsg;
REMOTE_OBJECT *pObj = new REMOTE_OBJECT;
memset(pObj, 0, sizeof(REMOTE_OBJECT));
// Make buffer big enough to hold game message plus
// leading non-system-char byte
lenBuff = sizeof(GAMEMESSAGE);
pBuffer = new char[lenBuff];
pGameMsg = (PGAMEMESSAGE)&pBuffer[0];
// Fill in object id info so that this object is unique across
// all machines
pObj->OwnerID = (BYTE) dcoID;
pObj->InstanceID = (BYTE) InstanceID;
lstrcpyn(pObj->ObjectID, name, MAX_OBJ_NAME);
memcpy(&pGameMsg->RemObj, pObj, sizeof(REMOTE_OBJECT));
pGameMsg->Action = (BYTE) CREATE_OBJECT;
// Broadcast it to everyone in the group.
lpIDC->Send( dcoID, // From
DP_BROADCAST_ID,
0,
(LPSTR)pBuffer,
lenBuff);
// Delete buffer, once sent
delete []pBuffer;
return(pObj);
}
// ----------------------------------------------------------
// SendRemoteAction - broadcast an action to remote peers
// ----------------------------------------------------------
BOOL SendRemoteAction(
REMOTE_OBJECT *pObj, // unique object id
ACTION Action, // action code
void *Data, // action data
DWORD nDataSize // size of action data
)
{
PGAMEMESSAGE gameMsg;
char *pBuffer;
DWORD lenBuff;
// char chBuffer[128];
// Gotta have a valid DirectPlay object
if (lpIDC == NULL)
{
return(NULL);
}
// Allocate the GAME buffer with room for data,
// plus room at the beginning for a non-sytem-message char
lenBuff = sizeof(GAMEMESSAGE) + nDataSize;
pBuffer = new char [lenBuff];
// Now point gameMsg at the rest of the buffer
gameMsg = (PGAMEMESSAGE)&pBuffer[0];
// Copy the action code and data to message buffer
gameMsg->Action = Action;
memcpy(&gameMsg->RemObj, pObj, sizeof(REMOTE_OBJECT));
gameMsg->NumBytes = (BYTE) nDataSize;
memcpy(gameMsg->Data, Data, nDataSize);
#if 0
wsprintf( chBuffer, "%d (10%s) %d %d %d",
gameMsg->Action,
gameMsg->RemObj.ObjectID,
gameMsg->RemObj.InstanceID,
gameMsg->RemObj.OwnerID,
gameMsg->NumBytes);
OutputDebugString(chBuffer);
if (nDataSize == sizeof(GENERIC_CHAR_INFO))
{
GENERIC_CHAR_INFO *pci;
pci = (GENERIC_CHAR_INFO *) Data;
wsprintf( chBuffer,"; %d %d %d %d %d %d",
pci->state,
pci->posx,
pci->posy,
pci->velx,
pci->vely,
pci->curZ);
OutputDebugString(chBuffer);
}
OutputDebugString("\r\n");
#endif
// Broadcast the action to all peers
lpIDC->Send(dcoID, // from
DP_BROADCAST_ID, // to
0,
pBuffer,
lenBuff);
delete []pBuffer;
return(TRUE);
}
// ----------------------------------------------------------
// DestroyRemotePeer - Tell remote peers to kill an object
// ----------------------------------------------------------
BOOL DestroyRemotePeer(
REMOTE_OBJECT *pObj // unique object id
)
{
PGAMEMESSAGE gameMsg;
char *pBuffer = NULL;
DWORD lenBuff;
if (pObj == NULL)
return(TRUE);
// Allocate a buffer for the GAME buffer with roon for data,
// plus an extra character at the beginning to be the non-system
// message char.
lenBuff = sizeof(GAMEMESSAGE)+1;
pBuffer = new char [lenBuff];
// Point the game message pointer at the rest of the buffer
gameMsg = (PGAMEMESSAGE)&pBuffer[0];
// Format a destroy message
memcpy(&gameMsg->RemObj, pObj, sizeof(REMOTE_OBJECT));
gameMsg->RemObj.OwnerID = (BYTE) dcoID;
gameMsg->NumBytes = 0;
gameMsg->Action = (BYTE) DESTROY_OBJECT;
// Broadcast the destroy message to all peers
lpIDC->Send( dcoID,
DP_BROADCAST_ID,
0,
pBuffer,
lenBuff);
// Delete buffer
delete []pBuffer;
// Don't need the object id anymore
delete pObj;
return(TRUE);
}
// -----------------------------------------------------------------
// RemoteConnect - establish an active connection with remotes
// -----------------------------------------------------------------
BOOL FAR PASCAL EnumSession(
LPDPSESSIONDESC lpDPGameDesc,
LPVOID pContext,
LPDWORD pTimeOut,
DWORD dwFlags )
{
LONG iIndex;
HWND hWnd = (HWND) pContext;
if( dwFlags == DPESC_TIMEDOUT )
{
return FALSE;
}
iIndex = SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM) lpDPGameDesc->szSessionName);
if (iIndex != LB_ERR)
SendMessage(hWnd, LB_SETITEMDATA, iIndex, (LPARAM) lpDPGameDesc->dwSession);
SetFocus(hWnd);
SendMessage(hWnd, LB_SETCURSEL, 0, 0);
return(TRUE);
}
BOOL FAR PASCAL EnumSP(LPGUID lpGuid, LPSTR lpDesc,
DWORD dwMajor, DWORD dwMinor, LPVOID lpv)
{
LONG iIndex;
HWND hWnd = (HWND) lpv;
iIndex = SendMessage(hWnd, LB_ADDSTRING, 0, (LPARAM) lpDesc);
if (iIndex != LB_ERR)
SendMessage(hWnd, LB_SETITEMDATA, iIndex, (LPARAM) lpGuid);
SetFocus(hWnd);
SendMessage(hWnd, LB_SETCURSEL, 0, 0);
return(TRUE);
}
BOOL CALLBACK DlgProcQCreate (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch(wParam)
{
case IDC_CREATE:
EndDialog(hDlg, 1);
return(TRUE);
case IDC_CONNECT:
EndDialog(hDlg, 2);
return(TRUE);
case IDCANCEL:
EndDialog(hDlg, -1);
return(TRUE);
}
break;
}
return(FALSE);
}
BOOL CALLBACK DlgProcChooseProvider (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPGUID lpGuid;
static LONG iIndex;
static HWND hWndCtl;
switch (msg)
{
case WM_INITDIALOG:
hWndCtl = GetDlgItem(hDlg, IDC_LIST1);
if (hWndCtl == NULL)
{
EndDialog(hDlg, TRUE);
return(TRUE);
}
DirectPlayEnumerate(EnumSP, (LPVOID) hWndCtl);
SetFocus(hWndCtl);
SendMessage(hWndCtl, LB_SETCURSEL, 0, 0);
return(FALSE);
case WM_COMMAND:
switch( HIWORD(wParam))
{
case LBN_SELCHANGE:
iIndex = SendMessage((HWND) lParam, LB_GETCURSEL, 0, 0);
hWndCtl = (HWND) lParam;
return(FALSE);
case LBN_DBLCLK:
iIndex = SendMessage((HWND) lParam, LB_GETCURSEL, 0, 0);
if (iIndex != LB_ERR)
{
lpGuid = (LPGUID) SendMessage((HWND) lParam, LB_GETITEMDATA, iIndex, 0);
DirectPlayCreate(lpGuid, &lpIDC, NULL);
EndDialog(hDlg, TRUE);
return(TRUE);
}
break;
case 0:
if (LOWORD(wParam) == IDOK)
{
if (iIndex != LB_ERR)
{
lpGuid = (LPGUID) SendMessage(hWndCtl, LB_GETITEMDATA, iIndex, 0);
if (lpGuid)
{
DirectPlayCreate(lpGuid, &lpIDC, NULL);
EndDialog(hDlg, TRUE);
}
else
EndDialog(hDlg, FALSE);
return(TRUE);
}
}
else if (LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, FALSE);
return(TRUE);
}
break;
}
}
return (FALSE);
}
LPGUID g_lpGuid;
BOOL CALLBACK DlgProcSelSession (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
static LONG iIndex;
static HWND hWndCtl;
DPSESSIONDESC dpDesc;
HRESULT hr = DP_OK + 10;
switch (msg)
{
case WM_INITDIALOG:
hWndCtl = GetDlgItem(hDlg, IDC_LB_SESSION);
if (hWndCtl == NULL)
{
EndDialog(hDlg, TRUE);
return(TRUE);
}
memset(&dpDesc, 0x00, sizeof(DPSESSIONDESC));
dpDesc.dwSize = sizeof(dpDesc);
dpDesc.guidSession = *g_lpGuid;
lpIDC->EnumSessions(&dpDesc, 5000, EnumSession, (LPVOID) hWndCtl, 0 );
SetFocus(hWndCtl);
return(FALSE);
case WM_COMMAND:
switch( HIWORD(wParam))
{
case LBN_SELCHANGE:
iIndex = SendMessage((HWND) lParam, LB_GETCURSEL, 0, 0);
hWndCtl = (HWND) lParam;
return(FALSE);
case 0:
if (LOWORD(wParam) == IDCANCEL)
{
lpIDC->Close();
lpIDC->Release();
lpIDC = NULL;
EndDialog(hDlg, FALSE);
return(TRUE);
}
//
// Fall Through.
//
case LBN_DBLCLK:
if (HIWORD(wParam) == LBN_DBLCLK)
{
hWndCtl = (HWND) lParam;
iIndex = SendMessage(hWndCtl, LB_GETCURSEL, 0, 0);
}
if (iIndex != LB_ERR)
{
memset(&dpDesc, 0x00, sizeof(DPSESSIONDESC));
dpDesc.dwSize = sizeof(dpDesc);
dpDesc.guidSession = *g_lpGuid;
dpDesc.dwFlags = DPOPEN_OPENSESSION;
dpDesc.dwSession = SendMessage((HWND) hWndCtl, LB_GETITEMDATA, iIndex, 0);
hr = lpIDC->Open(&dpDesc);
if (hr != DP_OK)
{
lpIDC->Close();
lpIDC->Release();
lpIDC = NULL;
EndDialog(hDlg, FALSE);
}
EndDialog(hDlg, TRUE);
return(TRUE);
}
}
}
return (FALSE);
}
INT GetProvider()
{
return(DialogBox (NULL, (LPCTSTR) IDD_CHOOSEPROVIDER, NULL, (DLGPROC) DlgProcChooseProvider));
}
INT CreateGame()
{
return(DialogBox (NULL, (LPCTSTR) IDD_Q_CREATE, NULL, (DLGPROC) DlgProcQCreate));
}
INT GetGame()
{
return(DialogBox (NULL, (LPCTSTR) IDD_SELSESSION, NULL, (DLGPROC) DlgProcSelSession));
}
BOOL RemoteCreateLobby(void)
{
LPDIRECTPLAY2A lpDPlay2 = NULL;
LPDIRECTPLAYLOBBYA lpDPLobby = NULL;
HRESULT hr;
// Be sure we aren't already initialized.
if (lpIDC != NULL)
{
return( FALSE );
}
// create a lobby object
hr = DirectPlayLobbyCreate(NULL, &lpDPLobby, NULL, NULL, 0);
if FAILED(hr)
return (FALSE);
// try to connect using the lobby
hr = lpDPLobby->Connect(0, &lpDPlay2, NULL) ;
// lobby launched us, so get a DirectPlay 1 interface
if SUCCEEDED(hr)
{
// query for a DirectPlay 1.0 interface
hr = lpDPlay2->QueryInterface(IID_IDirectPlay, (LPVOID *) &lpIDC);
// release the ANSI DirectPlay2 interface
lpDPlay2->Release();
}
// release lobby interface
lpDPLobby->Release();
return ((BOOL)(hr == DP_OK));
}
BOOL RemoteCreate(REFGUID pGuid, LPSTR FullName, LPSTR NickName)
{
HRESULT hr;
DPSESSIONDESC dpDesc;
// lobby did not launch us, so ask user for connection settings
if (lpIDC == NULL)
{
GetProvider();
if (lpIDC == NULL)
return(FALSE);
switch( CreateGame())
{
case 1: // Create
memset(&dpDesc, 0x00, sizeof(DPSESSIONDESC));
dpDesc.dwSize = sizeof(dpDesc);
dpDesc.dwMaxPlayers = 10;
dpDesc.dwFlags = DPOPEN_CREATESESSION;
dpDesc.guidSession = pGuid;
strcpy( dpDesc.szSessionName, FullName);
if ((hr = lpIDC->Open(&dpDesc)) != DP_OK)
{
lpIDC->Release();
lpIDC = NULL;
return(FALSE);
}
break;
case 2: // Connect
g_lpGuid = (LPGUID) &pGuid;
GetGame();
if (lpIDC == NULL)
return(FALSE);
break;
default:
return(FALSE);
}
}
if ((hr = lpIDC->CreatePlayer(&dcoID, NickName,
"IKlowns Player", &dphEvent)) != DP_OK)
{
lpIDC->Close();
lpIDC->Release();
lpIDC = NULL;
return(FALSE);
}
InitReceivePoll();
return(TRUE);
}
// -----------------------------------------------------------------
// SetCurrentLevel - set pointer to current level object
// -----------------------------------------------------------------
void SetCurrentLevel(
void *newLevel
)
{
gCurLevel = (CGameLevel *)newLevel;
}
// ----------------------------------------------------------
// CreateNewCharacter - create a new game object!
// ----------------------------------------------------------
void CreateNewCharacter(
REMOTE_OBJECT *pObj // unique object id
)
{
char dataBuf[256];
char graphicsBuf[256];
// Get specific data on object from profile
// NOTE: This means we hit the disk. For performance,
// it would be better if objects could be created based
// on an in memory object so that we don't ever have to
// hit the disk!
GetPrivateProfileString(
gCurLevel->GetLevelName(),
"Graphics",
"",
graphicsBuf,
sizeof( graphicsBuf ),
gCurLevel->GetProfileName()
);
GetPrivateProfileString(
graphicsBuf,
pObj->ObjectID,
"",
dataBuf,
sizeof( dataBuf ),
gCurLevel->GetProfileName()
);
// parse the data string into fields
CStringRecord fields( dataBuf, "," );
if (fields.GetNumFields() >= 5)
{
// Add the object to the game list!
gCurLevel->Add(pObj->ObjectID,
atoi(fields[1]),
atoi(fields[3]),
atoi(fields[4]),
(void *)pObj);
}
}
// ----------------------------------------------------------
// ProcessIncomingActions - Parse received messages & queue them
// ----------------------------------------------------------
BOOL ProcessIncomingActions(
REMOTE_OBJECT *pObj, // unique object id
ACTION action, // action code
void *Data, // action data
DWORD nDataSize // sizeof action data
)
{
CLinkedList *ActionList;
BOOL fQueuedUp = FALSE;
// char chBuffer[128];
// wsprintf( chBuffer, "IncomingAction %d size %d\r\n", action, nDataSize);
// OutputDebugString(chBuffer);
// Guard against activity occurring before we are ready!
if (gCurLevel == NULL)
return(FALSE);
// Don't do anything about our own requests
if (pObj->OwnerID == dcoID)
return(FALSE);
// Figure out what to do with this message
switch (action) {
// Need to create a brand new object locally
case CREATE_OBJECT: {
// OutputDebugString("CreateObject \r\n");
// Create a game character
if (!FindRemoteObjectEntry(pObj))
{
// OutputDebugString("CreateNewCharacter \r\n");
CreateNewCharacter(pObj);
}
// OutputDebugString("Leave CreateObject \r\n");
break;
}
// Time to destroy the object!
case DESTROY_OBJECT: {
REMOTE_DATA *pRemoteEntry = FindRemoteObjectEntry(pObj);
// OutputDebugString("DestroyObject \r\n");
// Need to tell level object to remove the character!
if (pRemoteEntry != NULL)
gCurLevel->Remove((CGameGraphic *)pRemoteEntry->Data);
break;
}
// Must be some character specific action
default:{
// Get action list for this object
ActionList = GetRemoteObjectQueue(pObj);
// No action list -> a new object, so try to create it.
if (ActionList == NULL)
{
CreateNewCharacter(pObj);
ActionList = GetRemoteObjectQueue(pObj);
if (ActionList == NULL)
return(FALSE);
}
// Place this action onto the queue
// OutputDebugString("QueueRemoteAction \r\n");
QueueRemoteAction(ActionList, action, Data, nDataSize);
fQueuedUp = TRUE;
break;
}
}
return(fQueuedUp);
}
// ----------------------------------------------------------
// ReleaseRemoteData - free remote buffer
// ----------------------------------------------------------
void ReleaseRemoteData(
LPVOID pData // ptr to app data portion
)
{
PGAMEMESSAGE pMsg; // desired pointer
int iDiff; // difference between the two
// Pretend we got passed in the proper ptr
pMsg = (PGAMEMESSAGE)pData;
// Calculate the difference between what we got
// and what we need.
iDiff = (LPBYTE)(&pMsg->Data) - (LPBYTE)(pMsg);
// Adjust accordingly and free the message!
pMsg = (PGAMEMESSAGE)((LPBYTE)pMsg - iDiff);
delete pMsg;
}
#define MAX_BUFFER_SIZE (sizeof(GAMEMESSAGE)+512)
static char *pBuffer = NULL;
static DWORD lenBuff = MAX_BUFFER_SIZE;
static void InitReceivePoll( void )
{
// We allocate a buffer to receive into first,
// which may have to cope with system messages,
// before copying the real message to the buffer to pass
// to the game
pBuffer = new char[ lenBuff ];
}
void PollForRemoteReceive( void )
{
DPID fromID, dcoReceiveID;
DWORD nBytes;
PGAMEMESSAGE pMsg;
int i;
const int MAX_MESSAGES = 16;
BOOL fCheckForMore = TRUE;
// Paranoia check
if ( lpIDC )
{
// We try to receive MAX_MESSAGES at a time so that
// a backlog doesn't build up. If we run out of messages,
// we stop looking for them.
for( i = 0; i < MAX_MESSAGES && fCheckForMore ; i++ )
{
HRESULT status;
nBytes = lenBuff;
status = lpIDC->Receive(
&fromID,
&dcoReceiveID,
DPRECEIVE_ALL,
pBuffer,
&nBytes);
switch( status )
{
case DP_OK:
if ( fromID == 0 )
{
// We do not in fact utilise the system
// messages during the game.
}
else
{
// User message - we copy the buffer minus the
// user message byte to the game buffer, then
// pass it on.
// Allocate buffer for game message
pMsg = (PGAMEMESSAGE)new char [MAX_BUFFER_SIZE];
memcpy( pMsg, &pBuffer[0], nBytes );
if (!ProcessIncomingActions(&pMsg->RemObj, pMsg->Action,
pMsg->Data, nBytes))
delete pMsg;
}
break;
default:
// Error condition of some kind - we just stop
// checking for now
fCheckForMore = FALSE;
break;
}
}
}
}
BOOL CALLBACK AbortDlgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
WORD wID, wNotifyCode;
BOOL retVal = 0;
switch( uMsg )
{
case WM_INITDIALOG:
// We didn't set focus
retVal = 1;
break;
case WM_COMMAND:
wID = LOWORD( wParam );
wNotifyCode = HIWORD( wParam );
switch ( wID )
{
case IDCANCEL:
if ( BN_CLICKED == wNotifyCode ) {
fAbort = TRUE;
retVal = 1;
}
break;
default:
break;
}
break;
}
return( retVal );
}