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.

814 lines
21 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/PubUtilLib/plNetGameLib/Private/plNglGame.cpp
*
***/
#include "../Pch.h"
#pragma hdrstop
// This file excluded from pre-compiled header because it is auto-generated by the build server.
#include "pnNetBase/pnNbGameKey.hpp"
namespace Ngl { namespace Game {
/*****************************************************************************
*
* Private
*
***/
struct CliGmConn : AtomicRef {
LINK(CliGmConn) link;
CCritSect critsect;
AsyncSocket sock;
AsyncCancelId cancelId;
NetCli * cli;
NetAddress addr;
unsigned seq;
bool abandoned;
// ping
AsyncTimer * pingTimer;
unsigned pingSendTimeMs;
unsigned lastHeardTimeMs;
CliGmConn ();
~CliGmConn ();
void Send (const unsigned_ptr fields[], unsigned count);
};
//============================================================================
// JoinAgeRequestTrans
//============================================================================
struct JoinAgeRequestTrans : NetGameTrans {
FNetCliGameJoinAgeRequestCallback m_callback;
void * m_param;
// sent
unsigned m_ageMcpId;
Uuid m_accountUuid;
unsigned m_playerInt;
JoinAgeRequestTrans (
unsigned ageMcpId,
const Uuid & accountUuid,
unsigned playerInt,
FNetCliGameJoinAgeRequestCallback callback,
void * param
);
bool Send ();
void Post ();
bool Recv (
const byte msg[],
unsigned bytes
);
};
//============================================================================
// RcvdPropagatedBufferTrans
//============================================================================
struct RcvdPropagatedBufferTrans : NetNotifyTrans {
unsigned bufferType;
unsigned bufferBytes;
byte * bufferData;
RcvdPropagatedBufferTrans () : NetNotifyTrans(kGmRcvdPropagatedBufferTrans) {}
~RcvdPropagatedBufferTrans ();
void Post ();
};
//============================================================================
// RcvdGameMgrMsgTrans
//============================================================================
struct RcvdGameMgrMsgTrans : NetNotifyTrans {
unsigned bufferBytes;
byte * bufferData;
RcvdGameMgrMsgTrans () : NetNotifyTrans(kGmRcvdGameMgrMsgTrans) {}
~RcvdGameMgrMsgTrans ();
void Post ();
};
/*****************************************************************************
*
* Private data
*
***/
enum {
kPerfConnCount,
kPingDisabled,
kNumPerf
};
static bool s_running;
static CCritSect s_critsect;
static LISTDECL(CliGmConn, link) s_conns;
static CliGmConn * s_active;
static FNetCliGameRecvBufferHandler s_bufHandler;
static FNetCliGameRecvGameMgrMsgHandler s_gameMgrMsgHandler;
static long s_perf[kNumPerf];
/*****************************************************************************
*
* Internal functions
*
***/
//===========================================================================
static unsigned GetNonZeroTimeMs () {
if (unsigned ms = TimeGetMs())
return ms;
return 1;
}
//============================================================================
static CliGmConn * GetConnIncRef_CS (const char tag[]) {
if (CliGmConn * conn = s_active)
if (conn->cli) {
conn->IncRef(tag);
return conn;
}
return nil;
}
//============================================================================
static CliGmConn * GetConnIncRef (const char tag[]) {
CliGmConn * conn;
s_critsect.Enter();
{
conn = GetConnIncRef_CS(tag);
}
s_critsect.Leave();
return conn;
}
//============================================================================
static void UnlinkAndAbandonConn_CS (CliGmConn * conn) {
s_conns.Unlink(conn);
conn->abandoned = true;
if (conn->cancelId) {
AsyncSocketConnectCancel(nil, conn->cancelId);
conn->cancelId = 0;
}
else if (conn->sock) {
AsyncSocketDisconnect(conn->sock, true);
}
else {
conn->DecRef("Lifetime");
}
}
//============================================================================
static bool ConnEncrypt (ENetError error, void * param) {
CliGmConn * conn = (CliGmConn *) param;
if (IS_NET_SUCCESS(error)) {
s_critsect.Enter();
{
SWAP(s_active, conn);
}
s_critsect.Leave();
}
return IS_NET_SUCCESS(error);
}
//============================================================================
static void NotifyConnSocketConnect (CliGmConn * conn) {
conn->TransferRef("Connecting", "Connected");
conn->cli = NetCliConnectAccept(
conn->sock,
kNetProtocolCli2Game,
true,
ConnEncrypt,
0,
nil,
conn
);
}
//============================================================================
static void NotifyConnSocketConnectFailed (CliGmConn * conn) {
bool notify;
s_critsect.Enter();
{
conn->cancelId = 0;
s_conns.Unlink(conn);
notify
= s_running
&& !conn->abandoned
&& (!s_active || conn == s_active);
if (conn == s_active)
s_active = nil;
}
s_critsect.Leave();
NetTransCancelByConnId(conn->seq, kNetErrTimeout);
conn->DecRef("Connecting");
conn->DecRef("Lifetime");
if (notify)
ReportNetError(kNetProtocolCli2Game, kNetErrConnectFailed);
}
//============================================================================
static void NotifyConnSocketDisconnect (CliGmConn * conn) {
bool notify;
s_critsect.Enter();
{
s_conns.Unlink(conn);
notify
= s_running
&& !conn->abandoned
&& (!s_active || conn == s_active);
if (conn == s_active)
s_active = nil;
}
s_critsect.Leave();
// Cancel all transactions in process on this connection.
NetTransCancelByConnId(conn->seq, kNetErrTimeout);
conn->DecRef("Connected");
conn->DecRef("Lifetime");
if (notify)
ReportNetError(kNetProtocolCli2Game, kNetErrDisconnected);
}
//============================================================================
static bool NotifyConnSocketRead (CliGmConn * conn, AsyncNotifySocketRead * read) {
// TODO: Only dispatch messages from the active game server
conn->lastHeardTimeMs = GetNonZeroTimeMs();
bool result = NetCliDispatch(conn->cli, read->buffer, read->bytes, nil);
read->bytesProcessed += read->bytes;
return result;
}
//============================================================================
static bool SocketNotifyCallback (
AsyncSocket sock,
EAsyncNotifySocket code,
AsyncNotifySocket * notify,
void ** userState
) {
bool result = true;
CliGmConn * conn;
switch (code) {
case kNotifySocketConnectSuccess:
conn = (CliGmConn *) notify->param;
*userState = conn;
conn->TransferRef("Connecting", "Connected");
bool abandoned;
s_critsect.Enter();
{
conn->sock = sock;
conn->cancelId = 0;
abandoned = conn->abandoned;
}
s_critsect.Leave();
if (abandoned)
AsyncSocketDisconnect(sock, true);
else
NotifyConnSocketConnect(conn);
break;
case kNotifySocketConnectFailed:
conn = (CliGmConn *) notify->param;
NotifyConnSocketConnectFailed(conn);
break;
case kNotifySocketDisconnect:
conn = (CliGmConn *) *userState;
NotifyConnSocketDisconnect(conn);
break;
case kNotifySocketRead:
conn = (CliGmConn *) *userState;
result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify);
break;
}
return result;
}
//============================================================================
static void Connect (
const NetAddress & addr
) {
CliGmConn * conn = NEWZERO(CliGmConn);
conn->addr = addr;
conn->seq = ConnNextSequence();
conn->lastHeardTimeMs = GetNonZeroTimeMs();
conn->IncRef("Lifetime");
conn->IncRef("Connecting");
s_critsect.Enter();
{
while (CliGmConn * conn = s_conns.Head())
UnlinkAndAbandonConn_CS(conn);
s_conns.Link(conn);
}
s_critsect.Leave();
Cli2Game_Connect connect;
connect.hdr.connType = kConnTypeCliToGame;
connect.hdr.hdrBytes = sizeof(connect.hdr);
connect.hdr.buildId = BuildId();
connect.hdr.buildType = BuildType();
connect.hdr.branchId = BranchId();
connect.hdr.productId = ProductId();
connect.data.dataBytes = sizeof(connect.data);
AsyncSocketConnect(
&conn->cancelId,
addr,
SocketNotifyCallback,
conn,
&connect,
sizeof(connect),
0,
0
);
}
/*****************************************************************************
*
* CliGmConn
*
***/
//============================================================================
CliGmConn::CliGmConn () {
AtomicAdd(&s_perf[kPerfConnCount], 1);
}
//============================================================================
CliGmConn::~CliGmConn () {
if (cli)
NetCliDelete(cli, true);
AtomicAdd(&s_perf[kPerfConnCount], -1);
}
//============================================================================
void CliGmConn::Send (const unsigned_ptr fields[], unsigned count) {
critsect.Enter();
{
NetCliSend(cli, fields, count);
NetCliFlush(cli);
}
critsect.Leave();
}
/*****************************************************************************
*
* Cli2Game protocol
*
***/
//============================================================================
static bool Recv_PingReply (
const byte msg[],
unsigned bytes,
void * param
) {
ref(msg);
ref(bytes);
ref(param);
return true;
}
//============================================================================
static bool Recv_JoinAgeReply (
const byte msg[],
unsigned bytes,
void * param
) {
ref(bytes);
ref(param);
const Game2Cli_JoinAgeReply & reply = *(const Game2Cli_JoinAgeReply *)msg;
if (sizeof(reply) != bytes)
return false;
NetTransRecv(reply.transId, msg, bytes);
return true;
}
//============================================================================
static bool Recv_PropagateBuffer (
const byte msg[],
unsigned bytes,
void * param
) {
ref(bytes);
ref(param);
const Game2Cli_PropagateBuffer & reply = *(const Game2Cli_PropagateBuffer *)msg;
RcvdPropagatedBufferTrans * trans = NEW(RcvdPropagatedBufferTrans);
trans->bufferType = reply.type;
trans->bufferBytes = reply.bytes;
trans->bufferData = (byte *)ALLOC(reply.bytes);
MemCopy(trans->bufferData, reply.buffer, reply.bytes);
NetTransSend(trans);
return true;
}
//============================================================================
static bool Recv_GameMgrMsg (
const byte msg[],
unsigned bytes,
void * param
) {
ref(bytes);
ref(param);
const Game2Cli_GameMgrMsg & reply = *(const Game2Cli_GameMgrMsg *)msg;
RcvdGameMgrMsgTrans * trans = NEW(RcvdGameMgrMsgTrans);
trans->bufferBytes = reply.bytes;
trans->bufferData = (byte *)ALLOC(reply.bytes);
MemCopy(trans->bufferData, reply.buffer, reply.bytes);
NetTransSend(trans);
return true;
}
//============================================================================
// Send/Recv protocol handler init
//============================================================================
#define MSG(s) kNetMsg_Cli2Game_##s
static NetMsgInitSend s_send[] = {
{ MSG(PingRequest), },
{ MSG(JoinAgeRequest), },
{ MSG(PropagateBuffer), },
{ MSG(GameMgrMsg), },
};
#undef MSG
#define MSG(s) kNetMsg_Game2Cli_##s, Recv_##s
static NetMsgInitRecv s_recv[] = {
{ MSG(PingReply) },
{ MSG(JoinAgeReply), },
{ MSG(PropagateBuffer), },
{ MSG(GameMgrMsg), },
};
#undef MSG
/*****************************************************************************
*
* JoinAgeRequestTrans
*
***/
//============================================================================
JoinAgeRequestTrans::JoinAgeRequestTrans (
unsigned ageMcpId,
const Uuid & accountUuid,
unsigned playerInt,
FNetCliGameJoinAgeRequestCallback callback,
void * param
) : NetGameTrans(kJoinAgeRequestTrans)
, m_ageMcpId(ageMcpId)
, m_accountUuid(accountUuid)
, m_playerInt(playerInt)
, m_callback(callback)
, m_param(param)
{
}
//============================================================================
bool JoinAgeRequestTrans::Send () {
if (!AcquireConn())
return false;
const unsigned_ptr msg[] = {
kCli2Game_JoinAgeRequest,
m_transId,
m_ageMcpId,
(unsigned_ptr) &m_accountUuid,
m_playerInt,
};
m_conn->Send(msg, arrsize(msg));
return true;
}
//============================================================================
void JoinAgeRequestTrans::Post () {
m_callback(
m_result,
m_param
);
}
//============================================================================
bool JoinAgeRequestTrans::Recv (
const byte msg[],
unsigned bytes
) {
ref(bytes);
const Game2Cli_JoinAgeReply & reply = *(const Game2Cli_JoinAgeReply *) msg;
m_result = reply.result;
m_state = kTransStateComplete;
return true;
}
/*****************************************************************************
*
* RcvdPropagatedBufferTrans
*
***/
//============================================================================
RcvdPropagatedBufferTrans::~RcvdPropagatedBufferTrans () {
FREE(bufferData);
}
//============================================================================
void RcvdPropagatedBufferTrans::Post () {
if (s_bufHandler)
s_bufHandler(bufferType, bufferBytes, bufferData);
}
/*****************************************************************************
*
* RcvdGameMgrMsgTrans
*
***/
//============================================================================
RcvdGameMgrMsgTrans::~RcvdGameMgrMsgTrans () {
FREE(bufferData);
}
//============================================================================
void RcvdGameMgrMsgTrans::Post () {
if (s_gameMgrMsgHandler)
s_gameMgrMsgHandler((GameMsgHeader *)bufferData);
}
} using namespace Game;
/*****************************************************************************
*
* NetGameTrans
*
***/
//============================================================================
NetGameTrans::NetGameTrans (ETransType transType)
: NetTrans(kNetProtocolCli2Game, transType)
, m_conn(nil)
{
}
//============================================================================
NetGameTrans::~NetGameTrans () {
ReleaseConn();
}
//============================================================================
bool NetGameTrans::AcquireConn () {
if (!m_conn)
m_conn = GetConnIncRef("AcquireConn");
return m_conn != nil;
}
//============================================================================
void NetGameTrans::ReleaseConn () {
if (m_conn) {
m_conn->DecRef("AcquireConn");
m_conn = nil;
}
}
/*****************************************************************************
*
* Protected functions
*
***/
//============================================================================
void GameInitialize () {
s_running = true;
NetMsgProtocolRegister(
kNetProtocolCli2Game,
false,
s_send, arrsize(s_send),
s_recv, arrsize(s_recv),
kDhGValue,
BigNum(sizeof(kDhXData), kDhXData),
BigNum(sizeof(kDhNData), kDhNData)
);
}
//============================================================================
void GameDestroy (bool wait) {
s_running = false;
s_bufHandler = nil;
s_gameMgrMsgHandler = nil;
NetTransCancelByProtocol(
kNetProtocolCli2Game,
kNetErrRemoteShutdown
);
NetMsgProtocolDestroy(
kNetProtocolCli2Game,
false
);
s_critsect.Enter();
{
while (CliGmConn * conn = s_conns.Head())
UnlinkAndAbandonConn_CS(conn);
s_active = nil;
}
s_critsect.Leave();
if (!wait)
return;
while (s_perf[kPerfConnCount]) {
NetTransUpdate();
AsyncSleep(10);
}
}
//============================================================================
bool GameQueryConnected () {
bool result;
s_critsect.Enter();
{
if (nil != (result = s_active))
result &= (nil != s_active->cli);
}
s_critsect.Leave();
return result;
}
//============================================================================
unsigned GameGetConnId () {
unsigned connId;
s_critsect.Enter();
connId = (s_active) ? s_active->seq : 0;
s_critsect.Leave();
return connId;
}
//============================================================================
void GamePingEnable (bool enable) {
s_perf[kPingDisabled] = !enable;
}
} using namespace Ngl;
/*****************************************************************************
*
* Exported functions
*
***/
//============================================================================
void NetCliGameStartConnect (
const NetAddressNode & node
) {
NetAddress addr;
NetAddressFromNode(node, kNetDefaultClientPort, &addr);
Connect(addr);
}
//============================================================================
void NetCliGameDisconnect () {
s_critsect.Enter();
{
while (CliGmConn * conn = s_conns.Head())
UnlinkAndAbandonConn_CS(conn);
s_active = nil;
}
s_critsect.Leave();
}
//============================================================================
void NetCliGameJoinAgeRequest (
unsigned ageMcpId,
const Uuid & accountUuid,
unsigned playerInt,
FNetCliGameJoinAgeRequestCallback callback,
void * param
) {
JoinAgeRequestTrans * trans = NEWZERO(JoinAgeRequestTrans)(
ageMcpId,
accountUuid,
playerInt,
callback,
param
);
NetTransSend(trans);
}
//============================================================================
void NetCliGameSetRecvBufferHandler (
FNetCliGameRecvBufferHandler handler
) {
s_bufHandler = handler;
}
//============================================================================
void NetCliGamePropagateBuffer (
unsigned type,
unsigned bytes,
const byte buffer[]
) {
CliGmConn * conn = GetConnIncRef("PropBuffer");
if (!conn)
return;
const unsigned_ptr msg[] = {
kCli2Game_PropagateBuffer,
type,
bytes,
(unsigned_ptr) buffer,
};
conn->Send(msg, arrsize(msg));
conn->DecRef("PropBuffer");
}
//============================================================================
void NetCliGameSetRecvGameMgrMsgHandler (FNetCliGameRecvGameMgrMsgHandler handler) {
s_gameMgrMsgHandler = handler;
}
//============================================================================
void NetCliGameSendGameMgrMsg (GameMsgHeader * msgHdr) {
CliGmConn * conn = GetConnIncRef("GameMgrMsg");
if (!conn)
return;
const unsigned_ptr msg[] = {
kCli2Game_GameMgrMsg,
msgHdr->messageBytes,
(unsigned_ptr) msgHdr,
};
conn->Send(msg, arrsize(msg));
conn->DecRef("GameMgrMsg");
}