/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/*****************************************************************************
*
* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.cpp
*
***/
#include "Pch.h"
#pragma hdrstop
/*****************************************************************************
*
* Local types
*
***/
//============================================================================
// pfGameCli factory
//============================================================================
struct Factory : THashKeyVal {
HASHLINK(Factory) link;
const GameTypeReg & reg;
Factory (const GameTypeReg & reg);
Factory & operator= (const Factory &); // not impl
};
//============================================================================
// pfGameCli internal state
//============================================================================
struct IGameCli : THashKeyVal {
HASHLINK(IGameCli) link;
pfGameCli * gameCli;
Factory * factory;
plKey receiver;
unsigned playerCount;
IGameCli (
pfGameCli * gameCli,
unsigned gameId,
plKey receiver
);
void Recv (GameMsgHeader * msg, void * param);
void RecvPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg, void * param);
void RecvPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg, void * param);
void RecvInviteFailed (const Srv2Cli_Game_InviteFailed & msg, void * param);
void RecvOwnerChange (const Srv2Cli_Game_OwnerChange & msg, void * param);
};
//============================================================================
// Transaction states
//============================================================================
struct TransState : THashKeyVal {
HASHLINK(TransState) link;
void * param;
TransState (unsigned transId, void * param);
};
struct JoinTransState {
plKey receiver;
JoinTransState (plKey receiver)
: receiver(receiver)
{ }
};
//============================================================================
// IGameMgr
//============================================================================
struct IGameMgr {
pfGameCli * CreateGameCli (const Uuid & gameTypeId, unsigned gameId, plKey receiver);
void Recv (GameMsgHeader * msg);
void RecvGameInstance (const Srv2Cli_GameMgr_GameInstance & msg, void * param);
void RecvInviteReceived (const Srv2Cli_GameMgr_InviteReceived & msg, void * param);
void RecvInviteRevoked (const Srv2Cli_GameMgr_InviteRevoked & msg, void * param);
static void StaticRecv (GameMsgHeader * msg);
};
/*****************************************************************************
*
* Local data
*
***/
static HASHTABLEDECL(TransState, THashKeyVal, link) s_trans;
static HASHTABLEDECL(IGameCli, THashKeyVal, link) s_games;
static HASHTABLEDECL(Factory, THashKeyVal, link) s_factories;
static long s_transId;
static ARRAYOBJ(plKey) s_receivers;
/*****************************************************************************
*
* Local functions
*
***/
//============================================================================
static void ShutdownFactories () {
while (Factory * factory = s_factories.Head())
DEL(factory);
}
//============================================================================
AUTO_INIT_FUNC (SetGameMgrMsgHandler) {
NetCliGameSetRecvGameMgrMsgHandler(IGameMgr::StaticRecv);
atexit(ShutdownFactories);
}
//============================================================================
static inline unsigned INextTransId () {
unsigned transId = AtomicAdd(&s_transId, 1);
while (!transId)
transId = AtomicAdd(&s_transId, 1);
return transId;
}
/*****************************************************************************
*
* IGameMgr
*
***/
//============================================================================
pfGameCli * IGameMgr::CreateGameCli (const Uuid & gameTypeId, unsigned gameId, plKey receiver) {
if (Factory * factory = s_factories.Find(gameTypeId)) {
pfGameCli * gameCli = factory->reg.create(gameId, receiver);
gameCli->internal->factory = factory;
return gameCli;
}
return nil;
}
//============================================================================
void IGameMgr::RecvGameInstance (const Srv2Cli_GameMgr_GameInstance & msg, void * param) {
JoinTransState * state = (JoinTransState *)param;
pfGameCli * cli = nil;
IGameCli * internal = nil;
if (msg.result == kGameJoinSuccess) {
if (nil == (internal = s_games.Find(msg.newGameId)))
cli = CreateGameCli(msg.gameTypeId, msg.newGameId, state->receiver);
else
cli = internal->gameCli;
}
DEL(state);
}
//============================================================================
void IGameMgr::RecvInviteReceived (const Srv2Cli_GameMgr_InviteReceived & msg, void * param) {
REF(param);
pfGameMgrMsg * gameMgrMsg = NEWZERO(pfGameMgrMsg);
gameMgrMsg->Set(msg);
for (unsigned i = 0; i < s_receivers.Count(); ++i)
gameMgrMsg->AddReceiver(s_receivers[i]);
gameMgrMsg->Send();
}
//============================================================================
void IGameMgr::RecvInviteRevoked (const Srv2Cli_GameMgr_InviteRevoked & msg, void * param) {
REF(param);
pfGameMgrMsg * gameMgrMsg = NEWZERO(pfGameMgrMsg);
gameMgrMsg->Set(msg);
for (unsigned i = 0; i < s_receivers.Count(); ++i)
gameMgrMsg->AddReceiver(s_receivers[i]);
gameMgrMsg->Send();
}
//============================================================================
void IGameMgr::Recv (GameMsgHeader * msg) {
// Look for transaction state associated with this message
void * param;
if (TransState * trans = s_trans.Find(msg->transId)) {
param = trans->param;
DEL(trans);
}
else {
param = nil;
}
// If the message has a receiver gameId specified, then
// hand it off to that game client for dispatch.
if (unsigned gameId = msg->recvGameId) {
if (IGameCli * node = s_games.Find(gameId)) {
node->Recv(msg, param);
}
return;
}
// The message was meant for the game manager (that's us); dispatch it.
#define DISPATCH(a) case kSrv2Cli_GameMgr_##a: { \
const Srv2Cli_GameMgr_##a & m = *(const Srv2Cli_GameMgr_##a *)msg; \
Recv##a(m, param); \
} \
break; //
switch (msg->messageId) {
DISPATCH(GameInstance);
DISPATCH(InviteReceived);
DISPATCH(InviteRevoked);
DEFAULT_FATAL(msg->messageId);
}
#undef DISPATCH
}
//============================================================================
void IGameMgr::StaticRecv (GameMsgHeader * msg) {
pfGameMgr::GetInstance()->internal->Recv(msg);
}
/*****************************************************************************
*
* pfGameMgrMsg
*
***/
//============================================================================
pfGameMgrMsg::pfGameMgrMsg () {
netMsg = nil;
}
//============================================================================
pfGameMgrMsg::~pfGameMgrMsg () {
FREE(netMsg);
}
//============================================================================
void pfGameMgrMsg::Set (const GameMsgHeader & msg) {
netMsg = (GameMsgHeader *)ALLOC(msg.messageBytes);
MemCopy(netMsg, &msg, msg.messageBytes);
}
/*****************************************************************************
*
* pfGameCliMsg
*
***/
//============================================================================
pfGameCliMsg::pfGameCliMsg () {
gameCli = nil;
netMsg = nil;
}
//============================================================================
pfGameCliMsg::~pfGameCliMsg () {
FREE(netMsg);
}
//============================================================================
void pfGameCliMsg::Set (pfGameCli * cli, const GameMsgHeader & msg) {
netMsg = (GameMsgHeader *)ALLOC(msg.messageBytes);
MemCopy(netMsg, &msg, msg.messageBytes);
gameCli = cli;
}
/*****************************************************************************
*
* pfGameMgr
*
***/
//============================================================================
pfGameMgr::pfGameMgr () {
}
//============================================================================
pfGameMgr * pfGameMgr::GetInstance () {
static pfGameMgr s_instance;
return &s_instance;
}
//============================================================================
void pfGameMgr::GetGameIds (ARRAY(unsigned) * arr) const {
for (IGameCli * node = s_games.Head(); node; node = s_games.Next(node))
arr->Add(node->GetValue());
}
//============================================================================
pfGameCli * pfGameMgr::GetGameCli (unsigned gameId) const {
if (IGameCli * node = s_games.Find(gameId))
return node->gameCli;
return nil;
}
//============================================================================
const wchar * pfGameMgr::GetGameNameByTypeId (const Uuid & gameTypeId) const {
if (Factory * factory = s_factories.Find(gameTypeId))
return factory->reg.name;
return nil;
}
//============================================================================
void pfGameMgr::RemoveReceiver (plKey receiver) {
for (unsigned i = 0; i < s_receivers.Count(); ++i) {
if (s_receivers[i] == receiver) {
s_receivers.DeleteUnordered(i);
break;
}
}
}
//============================================================================
void pfGameMgr::AddReceiver (plKey receiver) {
RemoveReceiver(receiver);
s_receivers.Add(receiver);
}
//============================================================================
void pfGameMgr::JoinGame (
plKey receiver,
unsigned gameId
) {
Cli2Srv_GameMgr_JoinGame msg;
ZERO(msg);
msg.messageId = kCli2Srv_GameMgr_JoinGame;
msg.recvGameId = 0; // send to GameMgr on server
msg.newGameId = gameId; // the GameSrv we wish to join
// Don't send "common game" message fields
unsigned msgBytes
= sizeof(msg)
- sizeof(msg.gameTypeId)
- sizeof(msg.createDataBytes)
- sizeof(msg.createData);
msg.messageBytes = msgBytes;
GameMgrSend(&msg, NEWZERO(JoinTransState)(receiver));
}
//============================================================================
void pfGameMgr::CreateGame (
plKey receiver,
const Uuid & gameTypeId,
unsigned createOptions,
unsigned initBytes,
const void * initData
) {
Cli2Srv_GameMgr_CreateGame * msg;
unsigned msgBytes
= sizeof(*msg)
- sizeof(msg->createData)
+ initBytes;
msg = (Cli2Srv_GameMgr_CreateGame *)_alloca(msgBytes);
msg->messageId = kCli2Srv_GameMgr_CreateGame;
msg->recvGameId = 0; // send to GameMgr on server
msg->gameTypeId = gameTypeId; // The type of game we wish to create
msg->createOptions = createOptions;
msg->messageBytes = msgBytes;
msg->createDataBytes = initBytes;
MemCopy(msg->createData, initData, initBytes);
GameMgrSend(msg, NEWZERO(JoinTransState)(receiver));
}
//============================================================================
void pfGameMgr::JoinCommonGame (
plKey receiver,
const Uuid & gameTypeId,
unsigned gameNumber,
unsigned initBytes,
const void * initData
) {
Cli2Srv_GameMgr_JoinGame * msg;
unsigned msgBytes
= sizeof(*msg)
- sizeof(msg->createData)
+ initBytes;
msg = (Cli2Srv_GameMgr_JoinGame *)_alloca(msgBytes);
msg->messageId = kCli2Srv_GameMgr_JoinGame;
msg->recvGameId = 0; // send to GameMgr on server
msg->gameTypeId = gameTypeId; // the type of common game we with to join
msg->newGameId = gameNumber; // the "table number" of th common game we wish to join
msg->createOptions = kGameJoinCommon;
msg->messageBytes = msgBytes;
msg->createDataBytes = initBytes;
MemCopy(msg->createData, initData, initBytes);
GameMgrSend(msg, NEWZERO(JoinTransState)(receiver));
}
/*****************************************************************************
*
* pfGameCli
*
***/
//============================================================================
pfGameCli::pfGameCli (
unsigned gameId,
plKey receiver
) {
internal = NEWZERO(IGameCli)(this, gameId, receiver);
}
//============================================================================
pfGameCli::~pfGameCli () {
DEL(internal);
}
//============================================================================
unsigned pfGameCli::GetGameId () const {
return internal->GetValue();
}
//============================================================================
const Uuid & pfGameCli::GetGameTypeId () const {
return internal->factory->GetValue();
}
//============================================================================
const wchar * pfGameCli::GetName () const {
return internal->factory->reg.name;
}
//============================================================================
plKey pfGameCli::GetReceiver () const {
return internal->receiver;
}
//============================================================================
unsigned pfGameCli::GetPlayerCount () const {
return internal->playerCount;
}
//============================================================================
void pfGameCli::InvitePlayer (unsigned playerId) {
Cli2Srv_Game_Invite msg;
msg.messageId = kCli2Srv_Game_Invite;
msg.recvGameId = GetGameId(); // send to GameSrv on server
msg.playerId = playerId;
msg.messageBytes = sizeof(msg);
GameMgrSend(&msg);
}
//============================================================================
void pfGameCli::UninvitePlayer (unsigned playerId) {
Cli2Srv_Game_Uninvite msg;
msg.messageId = kCli2Srv_Game_Uninvite;
msg.recvGameId = GetGameId(); // send to GameSrv on server
msg.playerId = playerId;
msg.messageBytes = sizeof(msg);
GameMgrSend(&msg);
}
//============================================================================
void pfGameCli::LeaveGame () {
Cli2Srv_Game_LeaveGame msg;
msg.messageId = kCli2Srv_Game_LeaveGame;
msg.recvGameId = GetGameId(); // send to GameSrv on server
msg.messageBytes = sizeof(msg);
GameMgrSend(&msg);
}
/*****************************************************************************
*
* IGameCli
*
***/
//============================================================================
IGameCli::IGameCli (
pfGameCli * gameCli,
unsigned gameId,
plKey receiver
) : THashKeyVal(gameId)
, gameCli(gameCli)
, receiver(receiver)
{
s_games.Add(this);
}
//============================================================================
void IGameCli::Recv (GameMsgHeader * msg, void * param) {
#define DISPATCH(a) case kSrv2Cli_Game_##a: { \
const Srv2Cli_Game_##a & m = *(const Srv2Cli_Game_##a *)msg; \
Recv##a(m, param); \
} \
break;
switch (msg->messageId) {
DISPATCH(PlayerJoined);
DISPATCH(PlayerLeft);
DISPATCH(InviteFailed);
DISPATCH(OwnerChange);
default:
gameCli->Recv(msg, param);
}
#undef DISPATCH
}
//============================================================================
void IGameCli::RecvPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg, void * param) {
REF(param);
++playerCount;
gameCli->OnPlayerJoined(msg);
}
//============================================================================
void IGameCli::RecvPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg, void * param) {
REF(param);
--playerCount;
gameCli->OnPlayerLeft(msg);
}
//============================================================================
void IGameCli::RecvInviteFailed (const Srv2Cli_Game_InviteFailed & msg, void * param) {
REF(param);
gameCli->OnInviteFailed(msg);
}
//============================================================================
void IGameCli::RecvOwnerChange (const Srv2Cli_Game_OwnerChange & msg, void * param) {
REF(param);
gameCli->OnOwnerChange(msg);
}
/*****************************************************************************
*
* Factory
*
***/
//============================================================================
Factory::Factory (const GameTypeReg & reg)
: reg(reg)
, THashKeyVal(reg.typeId)
{
s_factories.Add(this);
}
/*****************************************************************************
*
* TransState
*
***/
//============================================================================
TransState::TransState (unsigned transId, void * param)
: THashKeyVal(transId)
, param(param)
{
s_trans.Add(this);
}
/*****************************************************************************
*
* Module functions
*
***/
//============================================================================
void GameMgrRegisterGameType (const GameTypeReg & reg) {
(void)NEWZERO(Factory)(reg);
}
//============================================================================
void GameMgrSend (GameMsgHeader * msg, void * param) {
if (param) {
msg->transId = INextTransId();
(void)NEW(TransState)(msg->transId, param);
}
else {
msg->transId = 0;
}
NetCliGameSendGameMgrMsg(msg);
}