// Filename: Human.cpp
//
// Description: Implementation of CHuman
// This file contains the code that handles the interaction between two
// human players.
//
// This file is provided as part of the Microsoft Transaction Server Samples
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT
// WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (C) 1997 Microsoft Corporation, All rights reserved
#include "stdafx.h"
#include "tServer.h"
#include "Human.h"
#include <mtx.h>
#include <mtxspm.h>
STDMETHODIMP CHuman::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_IHuman,
};
for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}
STDMETHODIMP CHuman::EnterNewGame (IN BSTR bstrLocalName, OUT VARIANT* pvGameID, OUT VARIANT* pvName,
OUT VARIANT* pvOrder) {
HRESULT hr = S_OK;
IObjectContext* pObjectContext = NULL;
ISharedPropertyGroupManager* spmMgr = NULL;
ISharedPropertyGroup* spmGroup = NULL;
ISharedProperty* spmPropCounter = NULL;
ISharedProperty* spmPropFirstName = NULL;
ISharedProperty* spmPropSecondName = NULL;
ISharedProperty* spmPropState = NULL;
pvGameID->vt = VT_I4;
pvName->vt = VT_BSTR;
pvOrder->vt = VT_I4;
long lGameID = 0;
long lOrder = 0;
try {
// Get the object context
THROW_ERR ( hr = GetObjectContext (&pObjectContext) );
if (pObjectContext == NULL) {
THROW_ERR ( E_FAIL );
}
// Create the SharedPropertyGroupManager
THROW_ERR ( pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr) );
// Create the SharedPropertyGroup
LONG lIsolationMode = LockMethod;
LONG lReleaseMode = Process;
VARIANT_BOOL bExists = VARIANT_FALSE;
THROW_ERR ( spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup) );
// Create SharedProperties
CComVariant vCounter;
THROW_ERR ( spmGroup->CreateProperty (L"Counter", &bExists, &spmPropCounter) );
THROW_ERR ( spmPropCounter->get_Value (&vCounter) );
CComVariant vSecondName;
CComVariant vState;
TCHAR szBuf [512];
BSTR bstrSecondName;
BSTR bstrFirstName;
BSTR bstrState;
// Find first open two-human game
if (vCounter.lVal == 0) {
vCounter.lVal = 1;
} else {
long lScan = 1;
bool bFlag = false;
while (lScan < vCounter.lVal && !bFlag) {
wsprintf (szBuf, _T("%dState"), lScan);
bstrState = TCHAR2BSTR (szBuf);
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) );
::SysFreeString (bstrState);
THROW_ERR ( spmPropState->get_Value (&vState) );
if (vState.lVal < 2) {
// Found a game
bFlag = true;
} else
lScan ++;
THROW_ERR ( spmPropState->Release() );
}
vCounter.lVal = lGameID = lScan;
}
wsprintf (szBuf, _T("%dSecondName"), vCounter.lVal);
bstrSecondName = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dState"), vCounter.lVal);
bstrState = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dFirstName"), vCounter.lVal);
bstrFirstName = TCHAR2BSTR (szBuf);
THROW_ERR ( spmGroup->CreateProperty (bstrFirstName, &bExists, &spmPropFirstName) );
THROW_ERR ( spmGroup->CreateProperty (bstrSecondName, &bExists, &spmPropSecondName) );
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) );
::SysFreeString (bstrFirstName);
::SysFreeString (bstrSecondName);
::SysFreeString (bstrState);
// Get current state
THROW_ERR ( spmPropState->get_Value (&vState) );
// State table
// ===========
// 0 - No one in game
// 1 - One player lurking
// 2 - Second player joined
// 3 - First player's turn
// 4 - Second player's turn
// Is this the first time this property has been accessed?
if (!bExists)
vState.lVal = 0;
// Is there someone lurking already?
if (vState.lVal == 1) {
CComVariant vFirstName;
// Get opponent's name
THROW_ERR ( spmPropFirstName->get_Value (&vFirstName) );
// Submit own name
CComVariant vSecondName2;
vSecondName2.vt = VT_BSTR;
vSecondName2.bstrVal = ::SysAllocString (bstrLocalName);
THROW_ERR ( spmPropSecondName->put_Value (vSecondName2) );
::SysFreeString (vSecondName2.bstrVal);
// Update state
vState.lVal = 2;
THROW_ERR ( spmPropState->put_Value (vState) );
// Assign gameID
lGameID = vCounter.lVal;
// Initialize the game screen array
InitArray();
THROW_ERR ( SaveArray(lGameID) );
// Prepare return values
pvName->bstrVal = ::SysAllocString (vFirstName.bstrVal);
pvOrder->lVal = 2;
pvGameID->lVal = lGameID;
} else {
CComVariant vFirstName;
vFirstName.vt = VT_BSTR;
// Update first name
vFirstName.bstrVal = ::SysAllocString (bstrLocalName);
THROW_ERR ( spmPropFirstName->put_Value (vFirstName) );
::SysFreeString (vFirstName.bstrVal);
// Update counter for next game
vCounter.lVal = vCounter.lVal + 1;
THROW_ERR ( spmPropCounter->put_Value (vCounter) );
// Update state
vState.lVal = 1;
THROW_ERR ( spmPropState->put_Value (vState) );
// Assign gameID
lGameID = vCounter.lVal - 1;
// Prepare return values
pvName->bstrVal = ::SysAllocString (L"No one");
pvOrder->lVal = 1;
pvGameID->lVal = lGameID;
}
// We're finished and happy
pObjectContext->SetComplete();
} catch (HRESULT hr) {
// Create an ErrorInfo object
ICreateErrorInfo* pCreateErrInfo = NULL;
IErrorInfo* pErrInfo = NULL;
CreateErrorInfo (&pCreateErrInfo);
pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo);
// Fill in error information
TCHAR szErr [512];
wsprintf (szErr, _T("Error %d occurred in Human::EnterNewGame()"), hr);
BSTR bstrDesc = TCHAR2BSTR (szErr);
pCreateErrInfo->SetGUID (IID_IComputer);
pCreateErrInfo->SetSource (L"Computer");
pCreateErrInfo->SetDescription (bstrDesc);
::SysFreeString (bstrDesc);
// Confirm error information
SetErrorInfo (0, pErrInfo);
// Clean up the error objects
if (pCreateErrInfo)
pCreateErrInfo->Release();
if (pErrInfo)
pErrInfo->Release();
// Indicate our unhappiness
if (pObjectContext)
pObjectContext->SetAbort();
hr = E_FAIL;
}
if (pObjectContext)
pObjectContext->Release();
if (spmMgr)
spmMgr->Release();
if (spmGroup)
spmGroup->Release();
if (spmPropCounter)
spmPropCounter->Release();
if (spmPropFirstName)
spmPropFirstName->Release();
if (spmPropSecondName)
spmPropSecondName->Release();
if (spmPropState)
spmPropState->Release();
return hr;
}
STDMETHODIMP CHuman::NewMove (IN long lGameID, IN long lOrder, IN long lX, IN long lY, OUT VARIANT* pvWin) {
// Win protocol
// ============
// 0 -> move did not end game
// 1 -> player won
// -1 -> remote player won (won't occur in this function)
// 2 -> player's move tied the game
// -2 -> remote player's move tied the game (won't occur in this function)
HRESULT hr = S_OK;
IObjectContext* pObjectContext = NULL;
ISharedPropertyGroupManager* spmMgr = NULL;
ISharedPropertyGroup* spmGroup = NULL;
ISharedProperty* spmPropState = NULL;
ISharedProperty* spmPropX = NULL;
ISharedProperty* spmPropY = NULL;
ISharedProperty* spmPropWin = NULL;
pvWin->vt = VT_I4;
long lWin = 0;
try {
// Get the object context
THROW_ERR ( hr = GetObjectContext(&pObjectContext) );
if (pObjectContext == NULL) {
THROW_ERR ( E_FAIL );
}
// Create the SharedPropertyGroupManager
THROW_ERR ( pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager,
IID_ISharedPropertyGroupManager, (void**)&spmMgr) );
// Create the SharedPropertyGroup
LONG lIsolationMode = LockMethod;
LONG lReleaseMode = Process;
VARIANT_BOOL bExists = VARIANT_FALSE;
THROW_ERR ( spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode,
&bExists, &spmGroup) );
// Create SharedProperties
BSTR bstrState;
BSTR bstrX;
BSTR bstrY;
BSTR bstrWin;
TCHAR szBuf[512];
wsprintf (szBuf, _T("%dState"), lGameID);
bstrState = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dX"), lGameID);
bstrX = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dY"), lGameID);
bstrY = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dWin"), lGameID);
bstrWin = TCHAR2BSTR (szBuf);
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) );
THROW_ERR ( spmGroup->CreateProperty (bstrX, &bExists, &spmPropX) );
THROW_ERR ( spmGroup->CreateProperty (bstrY, &bExists, &spmPropY) );
THROW_ERR ( spmGroup->CreateProperty (bstrWin, &bExists, &spmPropWin) );
::SysFreeString (bstrState);
::SysFreeString (bstrX);
::SysFreeString (bstrY);
::SysFreeString (bstrWin);
CComVariant vState;
CComVariant vX;
CComVariant vY;
CComVariant vWin;
vState.vt = VT_I4;
vX.vt = VT_I4;
vY.vt = VT_I4;
vWin.vt = VT_I4;
// Load screen data
THROW_ERR ( LoadArray(lGameID) );
// Save player's move to local storage and to Spam
plField[lX][lY] = lOrder;
vX.lVal = lX;
vY.lVal = lY;
THROW_ERR ( spmPropX->put_Value (vX) );
THROW_ERR ( spmPropY->put_Value (vY) );
// Submit new state
if (lOrder == 1)
vState.lVal = 4;
else
vState.lVal = 3;
THROW_ERR ( spmPropState->put_Value (vState) );
// Commit move to global storage
THROW_ERR ( SaveArray (lGameID) );
// Check for win
if (IsWin (lOrder)) {
// Player wins
lWin = 1;
// Other player loses
vWin.lVal = - 1;
THROW_ERR ( spmPropWin->put_Value (vWin) );
}
else if (HowManyTurns() == 9) {
// Player ties on his move
lWin = 2;
// Other player ties on opponent's move
vWin.lVal = - 2;
THROW_ERR ( spmPropWin->put_Value (vWin) );
}
// Prepare return values
pvWin->lVal = lWin;
// We're happy and finished
pObjectContext->SetComplete();
} catch (HRESULT hr) {
// Create an ErrorInfo object
ICreateErrorInfo* pCreateErrInfo = NULL;
IErrorInfo* pErrInfo = NULL;
CreateErrorInfo (&pCreateErrInfo);
pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo);
// Fill in error information
TCHAR szErr [512];
wsprintf (szErr, _T("Error %d occurred in Human::NewMove()"), hr);
BSTR bstrDesc = TCHAR2BSTR (szErr);
pCreateErrInfo->SetGUID (IID_IComputer);
pCreateErrInfo->SetSource (L"Computer");
pCreateErrInfo->SetDescription (bstrDesc);
::SysFreeString (bstrDesc);
// Confirm error information
SetErrorInfo (0, pErrInfo);
// Clean up the error objects
if (pCreateErrInfo)
pCreateErrInfo->Release();
if (pErrInfo)
pErrInfo->Release();
// Indicate our unhappiness
if (pObjectContext)
pObjectContext->SetAbort();
hr = E_FAIL;
}
if (pObjectContext)
pObjectContext->Release();
if (spmMgr)
spmMgr->Release();
if (spmGroup)
spmGroup->Release();
if (spmPropState)
spmPropState->Release();
if (spmPropX)
spmPropX->Release();
if (spmPropY)
spmPropY->Release();
if (spmPropWin)
spmPropWin->Release();
return hr;
}
STDMETHODIMP CHuman::GetNewMove (IN long lGameID, IN long lOrder, OUT VARIANT* pvName,
OUT VARIANT* pvFoeX, OUT VARIANT* pvFoeY, OUT VARIANT* pvWin,
OUT VARIANT* pvUpdate) {
// Win protocol
// ============
// 0 -> move did not end game
// 1 -> player won (won't occur in this function)
// -1 -> remote player won
// 2 -> player's move tied the game (won't occur in this function)
// -2 -> remote player's move tied the game
HRESULT hr = S_OK;
IObjectContext* pObjectContext = NULL;
ISharedPropertyGroupManager* spmMgr = NULL;
ISharedPropertyGroup* spmGroup = NULL;
ISharedProperty* spmPropState = NULL;
ISharedProperty* spmPropX = NULL;
ISharedProperty* spmPropY = NULL;
ISharedProperty* spmPropWin = NULL;
ISharedProperty* spmPropSecondName = NULL;
pvName->vt = VT_BSTR;
pvFoeX->vt = VT_I4;
pvFoeY->vt = VT_I4;
pvWin->vt = VT_I4;
pvUpdate->vt = VT_I4;
long lWin = 0;
long lUpdate = 0;
long lX = 0;
long lY = 0;
try {
// Get the object context
THROW_ERR ( hr = GetObjectContext(&pObjectContext) );
if (pObjectContext == NULL) {
THROW_ERR ( E_FAIL );
}
// Create the SharedPropertyGroupManager
THROW_ERR ( pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager,
IID_ISharedPropertyGroupManager, (void**)&spmMgr) );
// Create the SharedPropertyGroup
LONG lIsolationMode = LockMethod;
LONG lReleaseMode = Process;
VARIANT_BOOL bExists = VARIANT_FALSE;
THROW_ERR ( spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode,
&bExists, &spmGroup) );
// Create SharedProperties
BSTR bstrState;
BSTR bstrX;
BSTR bstrY;
BSTR bstrWin;
BSTR bstrSecondName;
TCHAR szBuf[512];
wsprintf (szBuf, _T("%dState"), lGameID);
bstrState = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dX"), lGameID);
bstrX = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dY"), lGameID);
bstrY = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dWin"), lGameID);
bstrWin = TCHAR2BSTR (szBuf);
wsprintf (szBuf, _T("%dSecondName"), lGameID);
bstrSecondName = TCHAR2BSTR (szBuf);
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) );
THROW_ERR ( spmGroup->CreateProperty (bstrX, &bExists, &spmPropX) );
THROW_ERR ( spmGroup->CreateProperty (bstrY, &bExists, &spmPropY) );
THROW_ERR ( spmGroup->CreateProperty (bstrWin, &bExists, &spmPropWin) );
THROW_ERR ( spmGroup->CreateProperty (bstrSecondName, &bExists, &spmPropSecondName) );
::SysFreeString (bstrState);
::SysFreeString (bstrX);
::SysFreeString (bstrY);
::SysFreeString (bstrWin);
::SysFreeString (bstrSecondName);
// Get state and move parameters
CComVariant vState;
CComVariant vX;
CComVariant vY;
CComVariant vWin;
CComVariant vSecondName;
THROW_ERR ( spmPropState->get_Value (&vState) );
THROW_ERR ( spmPropX->get_Value (&vX) );
THROW_ERR ( spmPropY->get_Value (&vY) );
THROW_ERR ( spmPropWin->get_Value (&vWin) );
THROW_ERR ( spmPropSecondName->get_Value (&vSecondName) );
switch (vState.lVal) {
case (0):
// Shouldn't happen
THROW_ERR ( E_FAIL);
break;
case (1):
// Still waiting for next player to join
lUpdate = 0;
break;
case (2):
// Other player just joined
if (lOrder == 1) {
lUpdate = 2;
pvName->bstrVal = ::SysAllocString (vSecondName.bstrVal);
// It's now the first player's turn
vState.lVal = 3;
THROW_ERR ( spmPropState->put_Value (vState) );
}
break;
case (3):
case (4):
long lTemp;
if (vState.lVal == 3)
lTemp = 1;
else
lTemp = 2;
// Has other player moved?
if (lOrder == lTemp) {
// Other player has moved
lX = vX.lVal;
lY = vY.lVal;
lWin = vWin.lVal;
lUpdate = 1;
} else {
// Waiting for other player to move
lUpdate = 0;
}
break;
default:
// Shouldn't happen
THROW_ERR ( E_FAIL );
break;
}
// Prepare return values
pvFoeX->lVal = lX;
pvFoeY->lVal = lY;
pvWin->lVal = lWin;
pvUpdate->lVal = lUpdate;
// We are finished and happy
pObjectContext->SetComplete();
} catch (HRESULT hr) {
// Create an ErrorInfo object
ICreateErrorInfo* pCreateErrInfo = NULL;
IErrorInfo* pErrInfo = NULL;
CreateErrorInfo (&pCreateErrInfo);
pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo);
// Fill in error information
TCHAR szErr [512];
wsprintf (szErr, _T("Error %d occurred in Human::GetNewMove()"), hr);
BSTR bstrDesc = TCHAR2BSTR (szErr);
pCreateErrInfo->SetGUID (IID_IComputer);
pCreateErrInfo->SetSource (L"Computer");
pCreateErrInfo->SetDescription (bstrDesc);
::SysFreeString (bstrDesc);
// Confirm error information
SetErrorInfo (0, pErrInfo);
// Clean up the error objects
if (pCreateErrInfo)
pCreateErrInfo->Release();
if (pErrInfo)
pErrInfo->Release();
// Indicate our unhappiness
if (pObjectContext)
pObjectContext->SetAbort();
hr = E_FAIL;
}
if (pObjectContext)
pObjectContext->Release();
if (spmMgr)
spmMgr->Release();
if (spmGroup)
spmGroup->Release();
if (spmPropState)
spmPropState->Release();
if (spmPropX)
spmPropX->Release();
if (spmPropY)
spmPropY->Release();
if (spmPropWin)
spmPropWin->Release();
if (spmPropSecondName)
spmPropSecondName->Release();
return hr;
}
bool CHuman::IsWin (long lPlayer) {
bool bWin = false;
for (int i = 0; i < 3; i ++) {
if (plField [i][0] == plField [i][1] && plField [i][1] == plField [i][2] && plField [i][0] == lPlayer)
bWin = true;
if (plField [0][i] == plField [1][i] && plField [1][i] == plField [2][i] && plField [0][i] == lPlayer)
bWin = true;
}
if (plField [0][0] == plField [1][1] && plField [1][1] == plField [2][2] && plField [1][1] == lPlayer)
bWin = true;
if (plField [2][0] == plField [1][1] && plField [1][1] == plField [0][2] && plField [1][1] == lPlayer)
bWin = true;
return bWin;
}
long CHuman::HowManyTurns () {
long lTurns = 0;
for (int i = 0; i < 3; i ++) {
for (int j = 0; j < 3; j ++) {
if (plField[i][j] > 0) {
lTurns ++;
}
}
}
return lTurns;
}
HRESULT CHuman::LoadArray (long lGameID) {
HRESULT hr = S_OK;
IObjectContext* pObjectContext = NULL;
ISharedPropertyGroupManager* spmMgr = NULL;
ISharedPropertyGroup* spmGroup = NULL;
ISharedProperty* spmPropField[3][3];
// Get context
hr = GetObjectContext(&pObjectContext);
// Create the SharedPropertyGroupManager
hr = pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr);
// Create the SharedPropertyGroup
LONG lIsolationMode = LockMethod;
LONG lReleaseMode = Process;
VARIANT_BOOL bExists = VARIANT_FALSE;
hr = spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup);
// Load the field SharedProperties
TCHAR szBuf [512];
BSTR bstrField;
CComVariant vField;
for (long i = 0; i < 3; i ++ ) {
for (long j = 0; j < 3; j ++) {
wsprintf (szBuf, _T("%dField%d%d"), lGameID, i, j);
bstrField = TCHAR2BSTR (szBuf);
hr = spmGroup->CreateProperty (bstrField, &bExists, &spmPropField[i][j]);
::SysFreeString (bstrField);
hr = spmPropField[i][j]->get_Value (&vField);
plField[i][j] = vField.lVal;
}
}
if (pObjectContext)
pObjectContext->Release();
if (spmMgr)
spmMgr->Release();
if (spmGroup)
spmGroup->Release();
for (i = 0; i < 3; i ++) {
for (long j = 0; j < 3; j ++) {
if (spmPropField[i][j]) {
spmPropField[i][j]->Release();
}
}
}
return hr;
}
HRESULT CHuman::SaveArray (long lGameID) {
HRESULT hr = S_OK;
IObjectContext* pObjectContext = NULL;
ISharedPropertyGroupManager* spmMgr = NULL;
ISharedPropertyGroup* spmGroup = NULL;
ISharedProperty* spmPropField[3][3];
// Get context
hr = GetObjectContext(&pObjectContext);
// Create the SharedPropertyGroupManager
hr = pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr);
// Create the SharedPropertyGroup
LONG lIsolationMode = LockMethod;
LONG lReleaseMode = Process;
VARIANT_BOOL bExists = VARIANT_FALSE;
hr = spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup);
// Save the field shared properties
TCHAR szBuf [512];
BSTR bstrField;
CComVariant vField;
vField.vt = VT_I4;
for (long i = 0; i < 3; i ++ ) {
for (long j = 0; j < 3; j ++) {
wsprintf (szBuf, _T("%dField%d%d"), lGameID, i, j);
bstrField = TCHAR2BSTR (szBuf);
hr = spmGroup->CreateProperty (bstrField, &bExists, &spmPropField[i][j]);
::SysFreeString (bstrField);
vField.lVal = plField[i][j];
hr = spmPropField[i][j]->put_Value (vField);
}
}
if (pObjectContext)
pObjectContext->Release();
if (spmMgr)
spmMgr->Release();
if (spmGroup)
spmGroup->Release();
for (i = 0; i < 3; i ++) {
for (long j = 0; j < 3; j ++) {
if (spmPropField[i][j]) {
spmPropField[i][j]->Release();
}
}
}
return hr;
}