/*==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 .
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) {
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) {
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) {
++playerCount;
gameCli->OnPlayerJoined(msg);
}
//============================================================================
void IGameCli::RecvPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg, void * param) {
--playerCount;
gameCli->OnPlayerLeft(msg);
}
//============================================================================
void IGameCli::RecvInviteFailed (const Srv2Cli_Game_InviteFailed & msg, void * param) {
gameCli->OnInviteFailed(msg);
}
//============================================================================
void IGameCli::RecvOwnerChange (const Srv2Cli_Game_OwnerChange & msg, void * 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);
}