/****************************************************************************
* Copyright (C) 1995-1996 Microsoft Corporation. All Rights Reserved.
*
* File: NetworkGame.cpp
*
* Author: Curt Carpenter
*
* Description: DPlay interface for networking stuff
*
******************************************************************************/
#include "pch.h"
//#include <dplobby.h>
#define CURRENT_DPLAY_VER 318
//
// Variables global to this module.
//
DPlayWrapper mDPlayWrap;
BOOL mfMultiplex = FALSE;
//
// NetworkGame
//
// Basic constructor for a network game.
NetworkGame::NetworkGame()
{
mpDirectPlay = NULL;
mhPlayerEvent = NULL;
mdpId = 0;
mcMessagesSent = 0;
mcMessagesReceived = 0;
mcPlayers = 0;
#ifdef RECEIVETHREAD
mhReceiveThread = NULL;
mhKillReceiveEvent = NULL;
#endif
}
// ~NetworkGame
//
// Destructor. Releases all DirectPlay state.
//
NetworkGame::~NetworkGame()
{
debugf("NWG:~NetworkGame()\n");
this->ShutdownConnection();
}
/****************************************************************************\
*
* FUNCTION: EnumSessionsCallback2
*
* PURPOSE: Called by IDirectPlay#::EnumSessions
*
*\***************************************************************************/
BOOL FAR PASCAL EnumSessionsCallback2(LPCDPSESSIONDESC2 lpThisSD,
LPDWORD lpdwTimeOut,
DWORD dwFlags,
LPVOID lpContext)
{
// lpContext is a pointer for where to stick the instance guid, which is all we want
if (lpThisSD) // will be null if we timed out
*(GUID*)lpContext = lpThisSD->guidInstance;
return FALSE;
}
/****************************************************************************\
*
* FUNCTION: SetupConnection
*
* PURPOSE: sets up all the dplay stuff and puts us in a listen state
*
*\***************************************************************************/
HRESULT NetworkGame::SetupConnection(const char * szFedServer,
const char * szCharName,
const char * szCharPW,
bool fCreateNew,
FILETIME * pftLastArtUpdate)
{
HRESULT hr = S_OK;
char szPlayerName[30] = "/"; // we're going to start copying past the slash
DWORD dwcbPlayerName = sizeof(szPlayerName) - 1;
DWORD dwAddressSize = 0;
#ifdef RECEIVETHREAD
DWORD idReceiveThread = 0; // id of receive thread
#endif
//
// Create an IDirectPlay3 session to the server.
//
hr = mDPlayWrap.AllocDirectPlaySession(szFedServer, &mpDirectPlay);
if (SUCCEEDED(hr))
{
// Use character name / user name for player name
GetUserName(szPlayerName + 1, &dwcbPlayerName);
//
// Create a player
//
DPNAME dpName;
dpName.dwSize = sizeof(dpName);
dpName.dwFlags = 0;
dpName.lpszShortNameA = *(char**)&szCharName; // make compiler happy about const-ness
dpName.lpszLongNameA = szPlayerName;
hr = mpDirectPlay->CreatePlayer(&mdpId,
&dpName,
mhPlayerEvent,
NULL,
0,
0);
if (SUCCEEDED(hr))
{
//
// Player on this machine always in first slot
//
mPlayers[0].dpid = mdpId;
mPlayers[0].FormalName[0] = 0;
strcpy(mPlayers[0].FriendlyName, szPlayerName);
mcPlayers++;
DPCAPS dpcaps;
dpcaps.dwSize = sizeof(dpcaps);
hr = mpDirectPlay->GetCaps(&dpcaps, 0);
FM.Init(mpDirectPlay, mdpId);
{
}
} else
debugf("NWG: Failed to create user\n");
} else
debugf("NWG: Failed to obtain dplay session\n");
if (FAILED(hr))
this->ShutdownConnection();
return(hr);
}
LPSTR NetworkGame::GetPlayerName(int i)
{
if ((i < 0) || (i >= mcPlayers))
return NULL;
return(&mPlayers[i].FriendlyName[0]);
}
HRESULT NetworkGame::SendMessages(DPID dpidTo, bool fGuaranteed)
{
FM.SendMessages(dpidTo, fGuaranteed, NULL);
return S_OK;
}
DPID NetworkGame::DpId()
{
return(mdpId);
}
#ifdef RECEIVETHREAD
/****************************************************************************\
*
* FUNCTION: ReceiveThread
*
* PURPOSE: Waits for messages in its own thread
*
*\***************************************************************************/
DWORD WINAPI ReceiveThread(LPVOID pNWG_This)
{
//
// BUGBUG: This won't work any more. If we want to implement this, we
// need access to the BasePlayerData so that we can call the callback.
// If we decide to implement this, it should probably be in the
// BasePlayerData class instead of here.
//
NetworkGame * pNetworkGame = (NetworkGame * ) pNWG_This;
HANDLE eventHandles[2];
BOOL fSysMsg;
eventHandles[0] = pNetworkGame->mhPlayerEvent;
eventHandles[1] = pNetworkGame->mhKillReceiveEvent;
// loop waiting for player events. If the kill event is signaled
// the thread will exit
while (WaitForMultipleObjects(2, eventHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
{
// receive any messages in the queue
pNetworkGame->ReceiveMessages(&fSysMsg);
}
ExitThread(0);
return (0);
}
#endif // RECEIVETHREAD
/****************************************************************************\
*
* FUNCTION: ReceiveMessages
*
* PURPOSE: Reads messages queued by DPlay
*
* Returns: S_OK if message needs processing,
* S_FALSE message doesn't need processing,
* or failure code (including no more messages)
*
*\***************************************************************************/
HRESULT NetworkGame::ReceiveMessages(BOOL * pfSystemMsg)
{
DPID idFrom;
HRESULT hr;
*pfSystemMsg = FALSE;
hr = S_FALSE;
if (0 != mdpId)
{
idFrom = 0;
hr = FM.ReceiveMessages(mdpId, &idFrom);
if (SUCCEEDED(hr))
{
if (FM.PacketSize() >= sizeof(DPMSG_GENERIC))
{
mcMessagesReceived++;
if (idFrom == DPID_SYSMSG)
*pfSystemMsg = TRUE;
} else
hr = S_FALSE;
}
}
return (hr);
}
/****************************************************************************\
*
* FUNCTION: ShutdownConnection
*
* PURPOSE: Shut everything down and go home
*
*\***************************************************************************/
void NetworkGame::ShutdownConnection()
{
#ifdef RECEIVETHREAD
if (mhReceiveThread)
{
// wake up receive thread and wait for it to quit
SetEvent(mhKillReceiveEvent);
WaitForSingleObject(mhReceiveThread, INFINITE);
CloseHandle(mhReceiveThread);
mhReceiveThread = NULL;
}
if (mhKillReceiveEvent)
{
CloseHandle(mhKillReceiveEvent);
mhKillReceiveEvent = NULL;
}
#endif // RECEIVETHREAD
mDPlayWrap.FreeDirectPlaySession(mpDirectPlay);
mpDirectPlay = NULL;
#ifdef RECEIVETHREAD
if (mhPlayerEvent)
{
CloseHandle(mhPlayerEvent);
mhPlayerEvent = NULL;
}
#endif // RECEIVETHREAD
}
/****************************************************************************\
*
* FUNCTION: HandleSysMsg
*
* PURPOSE: Handle system message
*
*\***************************************************************************/
void NetworkGame::HandleSysMsg(DPID idTo, DPID idFrom)
{
// The body of each case is there so you can set a breakpoint and examine
// the contents of the message received.
LPDPMSG_GENERIC pMsg = (LPDPMSG_GENERIC) FM.BuffIn();
debugf("NWG: HandleSysMsg from %lu to %lu, dwType %lu\n", idFrom, idTo, pMsg->dwType);
switch (pMsg->dwType)
{
case DPSYS_CREATEPLAYERORGROUP:
{
// should never get this
}
break;
case DPSYS_DESTROYPLAYERORGROUP:
{
LPDPMSG_DESTROYPLAYERORGROUP lp = (LPDPMSG_DESTROYPLAYERORGROUP) pMsg;
}
break;
case DPSYS_ADDPLAYERTOGROUP:
{
LPDPMSG_ADDPLAYERTOGROUP lp = (LPDPMSG_ADDPLAYERTOGROUP) pMsg;
}
break;
case DPSYS_DELETEPLAYERFROMGROUP:
{
LPDPMSG_DELETEPLAYERFROMGROUP lp = (LPDPMSG_DELETEPLAYERFROMGROUP) pMsg;
}
break;
case DPSYS_SESSIONLOST:
{
LPDPMSG_SESSIONLOST lp = (LPDPMSG_SESSIONLOST) pMsg;
}
break;
case DPSYS_HOST:
{
LPDPMSG_HOST lp = (LPDPMSG_HOST) pMsg;
}
break;
case DPSYS_SETPLAYERORGROUPDATA:
{
LPDPMSG_SETPLAYERORGROUPDATA lp = (LPDPMSG_SETPLAYERORGROUPDATA) pMsg;
assert(false);
}
break;
case DPSYS_SETPLAYERORGROUPNAME:
{
LPDPMSG_SETPLAYERORGROUPNAME lp = (LPDPMSG_SETPLAYERORGROUPNAME) pMsg;
assert(false);
}
break;
case DPSYS_SECUREMESSAGE:
{
LPDPMSG_SECUREMESSAGE lp = (LPDPMSG_SECUREMESSAGE) pMsg;
assert(false);
}
break;
}
}
//
// We really want to use the same IDirectPlay pointer in all of
// our network game code (DPlay creates 3 threads per IDirectPlay).
// To do this, I've created these static functions to handle the
// initialization and termination of the one DPlay pointer.
//
DPlayWrapper::DPlayWrapper()
{
mpDirectPlay = NULL;
mpDirectPlayLobbyA = NULL;
}
DPlayWrapper::~DPlayWrapper()
{
ZAssert(NULL == mpDirectPlay);
if (NULL != mpDirectPlayLobbyA)
mpDirectPlayLobbyA->Release();
}
HRESULT DPlayWrapper::AllocDirectPlaySession(const CHAR * szServer,
IDirectPlayX ** ppDirectPlay)
{
BOOL fCreateSession;
VOID * pvAddress;
DWORD cbAddress;
HRESULT hr;
hr = S_OK;
fCreateSession = FALSE;
if (!CheckDPlayVersion())
hr = E_FAIL;
//
// Create a lobby if we don't already have one.
//
if (SUCCEEDED(hr) && NULL == mpDirectPlayLobbyA)
{
hr = DirectPlayLobbyCreate(NULL,
&mpDirectPlayLobbyA,
NULL,
NULL,
0);
}
//
// Create a directplay session if need be.
//
if (SUCCEEDED(hr) && (NULL == mpDirectPlay))
{
hr = CoCreateInstance(CLSID_DirectPlay,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDirectPlayX,
(VOID **) &mpDirectPlay);
if (SUCCEEDED(hr))
fCreateSession = TRUE;
}
if (TRUE == fCreateSession)
{
//
// At this point I have a new directplay pointer and lobby pointer.
//
//
// Need to find out how big of an address buffer we need, so
// we intentionally fail the first time.
//
pvAddress = NULL;
cbAddress = 0;
hr = mpDirectPlayLobbyA->CreateAddress(DPSPGUID_TCPIP,
DPAID_INet,
szServer,
strlen(szServer) + 1,
pvAddress,
&cbAddress);
if (DPERR_BUFFERTOOSMALL == hr)
{
pvAddress = new BYTE[cbAddress];
if (NULL != pvAddress)
{
hr = mpDirectPlayLobbyA->CreateAddress(DPSPGUID_TCPIP,
DPAID_INet,
szServer,
strlen(szServer) + 1,
pvAddress,
&cbAddress);
} else
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
hr = mpDirectPlay->InitializeConnection(pvAddress, 0);
if (SUCCEEDED(hr))
{
//
// Find the session.
//
DPSESSIONDESC2 sessionDesc;
GUID guidDPInstance, guidZero;
ZeroMemory(&sessionDesc, sizeof(sessionDesc));
sessionDesc.dwSize = sizeof(sessionDesc);
sessionDesc.guidApplication = FEDSRV_GUID;
ZeroMemory(&guidDPInstance, sizeof(guidDPInstance));
ZeroMemory(&guidZero, sizeof(guidZero));
hr = mpDirectPlay->EnumSessions(&sessionDesc,
5000,
EnumSessionsCallback2,
&guidDPInstance,
0);
if (FAILED(hr) || (guidZero == guidDPInstance))
{
//
// Maybe we hit a burp. Try again.
//
hr = mpDirectPlay->EnumSessions(&sessionDesc,
5000,
EnumSessionsCallback2,
&guidDPInstance,
0);
}
if (guidZero != guidDPInstance)
{
//
// Join the session.
//
sessionDesc.guidInstance = guidDPInstance;
hr = mpDirectPlay->SecureOpen(&sessionDesc,
DPOPEN_JOIN,
NULL,
NULL);
} else
hr = E_FAIL;
}
}
if (NULL != pvAddress)
delete[] pvAddress;
if (FAILED(hr))
{
mpDirectPlay->Release();
mpDirectPlay = NULL;
}
}
if (SUCCEEDED(hr))
{
*ppDirectPlay = mpDirectPlay;
if (FALSE == mfMultiplex)
mpDirectPlay = NULL;
else
mpDirectPlay->AddRef();
} else
*ppDirectPlay = NULL;
return(hr);
}
VOID DPlayWrapper::FreeDirectPlaySession(IDirectPlayX * pDirectPlay)
{
if (NULL != pDirectPlay)
{
if (FALSE == mfMultiplex)
{
//
// Not multiplexing. Shut the whole thing down.
//
pDirectPlay->Close();
pDirectPlay->Release();
} else if (1 == pDirectPlay->Release())
{
//
// We own the last reference. Shut down.
//
mpDirectPlay->Close();
mpDirectPlay->Release();
mpDirectPlay = NULL;
}
}
}