You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
645 lines
18 KiB
645 lines
18 KiB
/*==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 <http://www.gnu.org/licenses/>. |
|
|
|
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<Uuid> { |
|
|
|
HASHLINK(Factory) link; |
|
const GameTypeReg & reg; |
|
|
|
Factory (const GameTypeReg & reg); |
|
operator= (const Factory &); // not impl |
|
}; |
|
|
|
//============================================================================ |
|
// pfGameCli internal state |
|
//============================================================================ |
|
struct IGameCli : THashKeyVal<unsigned> { |
|
|
|
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<unsigned> { |
|
|
|
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<unsigned>, link) s_trans; |
|
static HASHTABLEDECL(IGameCli, THashKeyVal<unsigned>, link) s_games; |
|
static HASHTABLEDECL(Factory, THashKeyVal<Uuid>, 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<unsigned>(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<Uuid>(reg.typeId) |
|
{ |
|
s_factories.Add(this); |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* TransState |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
TransState::TransState (unsigned transId, void * param) |
|
: THashKeyVal<unsigned>(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); |
|
}
|
|
|