/**************************************************************************** * 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 #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; } } }