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.

1121 lines
31 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/plNetGateKeeperLib/Private/plNglGateKeeper.cpp
*
***/
#include "../Pch.h"
#pragma hdrstop
// This file excluded from pre-compiled header because it is auto-generated by the build server.
#include "pnNetBase/pnNbGateKeeperKey.hpp"
namespace Ngl { namespace GateKeeper {
/*****************************************************************************
*
* Private
*
***/
struct CliGkConn : AtomicRef {
CliGkConn ();
~CliGkConn ();
// Reconnection
AsyncTimer * reconnectTimer;
unsigned reconnectStartMs;
// Ping
AsyncTimer * pingTimer;
unsigned pingSendTimeMs;
unsigned lastHeardTimeMs;
// This function should be called during object construction
// to initiate connection attempts to the remote host whenever
// the socket is disconnected.
void AutoReconnect ();
bool AutoReconnectEnabled ();
void StopAutoReconnect (); // call before destruction
void StartAutoReconnect ();
void TimerReconnect ();
// ping
void AutoPing ();
void StopAutoPing ();
void TimerPing ();
void Send (const unsigned_ptr fields[], unsigned count);
CCritSect critsect;
LINK(CliGkConn) link;
AsyncSocket sock;
NetCli * cli;
wchar name[MAX_PATH];
NetAddress addr;
Uuid token;
unsigned seq;
unsigned serverChallenge;
AsyncCancelId cancelId;
bool abandoned;
};
//============================================================================
// PingRequestTrans
//============================================================================
struct PingRequestTrans : NetGateKeeperTrans {
FNetCliGateKeeperPingRequestCallback m_callback;
void * m_param;
unsigned m_pingAtMs;
unsigned m_replyAtMs;
ARRAY(byte) m_payload;
PingRequestTrans (
FNetCliGateKeeperPingRequestCallback callback,
void * param,
unsigned pingAtMs,
unsigned payloadBytes,
const void * payload
);
bool Send ();
void Post ();
bool Recv (
const byte msg[],
unsigned bytes
);
};
//============================================================================
// FileSrvIpAddressRequestTrans
//============================================================================
struct FileSrvIpAddressRequestTrans : NetGateKeeperTrans {
FNetCliGateKeeperFileSrvIpAddressRequestCallback m_callback;
void * m_param;
wchar m_addr[64];
bool m_isPatcher;
FileSrvIpAddressRequestTrans (
FNetCliGateKeeperFileSrvIpAddressRequestCallback callback,
void * param,
bool isPatcher
);
bool Send ();
void Post ();
bool Recv (
const byte msg[],
unsigned bytes
);
};
//============================================================================
// AuthSrvIpAddressRequestTrans
//============================================================================
struct AuthSrvIpAddressRequestTrans : NetGateKeeperTrans {
FNetCliGateKeeperAuthSrvIpAddressRequestCallback m_callback;
void * m_param;
wchar m_addr[64];
AuthSrvIpAddressRequestTrans (
FNetCliGateKeeperAuthSrvIpAddressRequestCallback callback,
void * param
);
bool Send ();
void Post ();
bool Recv (
const byte msg[],
unsigned bytes
);
};
/*****************************************************************************
*
* Private data
*
***/
enum {
kPerfConnCount,
kPingDisabled,
kAutoReconnectDisabled,
kNumPerf
};
static bool s_running;
static CCritSect s_critsect;
static LISTDECL(CliGkConn, link) s_conns;
static CliGkConn * s_active;
static long s_perf[kNumPerf];
/*****************************************************************************
*
* Internal functions
*
***/
//===========================================================================
static unsigned GetNonZeroTimeMs () {
if (unsigned ms = TimeGetMs())
return ms;
return 1;
}
//============================================================================
static CliGkConn * GetConnIncRef_CS (const char tag[]) {
if (CliGkConn * conn = s_active) {
conn->IncRef(tag);
return conn;
}
return nil;
}
//============================================================================
static CliGkConn * GetConnIncRef (const char tag[]) {
CliGkConn * conn;
s_critsect.Enter();
{
conn = GetConnIncRef_CS(tag);
}
s_critsect.Leave();
return conn;
}
//============================================================================
static void UnlinkAndAbandonConn_CS (CliGkConn * conn) {
s_conns.Unlink(conn);
conn->abandoned = true;
conn->StopAutoReconnect();
if (conn->cancelId) {
AsyncSocketConnectCancel(nil, conn->cancelId);
conn->cancelId = 0;
}
else if (conn->sock) {
AsyncSocketDisconnect(conn->sock, true);
}
else {
conn->DecRef("Lifetime");
}
}
//============================================================================
static void SendClientRegisterRequest (CliGkConn * conn) {
/* const unsigned_ptr msg[] = {
kCli2GateKeeper_ClientRegisterRequest,
BuildId(),
};
conn->Send(msg, arrsize(msg));*/
}
//============================================================================
static bool ConnEncrypt (ENetError error, void * param) {
CliGkConn * conn = (CliGkConn *) param;
if (IS_NET_SUCCESS(error)) {
//SendClientRegisterRequest(conn);
if (!s_perf[kPingDisabled])
conn->AutoPing();
s_critsect.Enter();
{
SWAP(s_active, conn);
}
s_critsect.Leave();
// AuthConnectedNotifyTrans * trans = NEW(AuthConnectedNotifyTrans);
// NetTransSend(trans);
}
return IS_NET_SUCCESS(error);
}
//============================================================================
static void NotifyConnSocketConnect (CliGkConn * conn) {
conn->TransferRef("Connecting", "Connected");
conn->cli = NetCliConnectAccept(
conn->sock,
kNetProtocolCli2GateKeeper,
false,
ConnEncrypt,
0,
nil,
conn
);
}
//============================================================================
static void CheckedReconnect (CliGkConn * conn, ENetError error) {
unsigned disconnectedMs = GetNonZeroTimeMs() - conn->lastHeardTimeMs;
// no auto-reconnect or haven't heard from the server in a while?
if (!conn->AutoReconnectEnabled() || (int)disconnectedMs >= (int)kDisconnectedTimeoutMs) {
// Cancel all transactions in progress on this connection.
NetTransCancelByConnId(conn->seq, kNetErrTimeout);
// conn is dead.
conn->DecRef("Lifetime");
ReportNetError(kNetProtocolCli2GateKeeper, error);
}
else {
if (conn->token != kNilGuid)
// previously encrypted; reconnect quickly
conn->reconnectStartMs = GetNonZeroTimeMs() + 500;
else
// never encrypted; reconnect slowly
conn->reconnectStartMs = GetNonZeroTimeMs() + kMaxReconnectIntervalMs;
// clean up the socket and start reconnect
if (conn->cli)
NetCliDelete(conn->cli, true);
conn->cli = nil;
conn->sock = nil;
conn->StartAutoReconnect();
}
}
//============================================================================
static void NotifyConnSocketConnectFailed (CliGkConn * conn) {
s_critsect.Enter();
{
conn->cancelId = 0;
s_conns.Unlink(conn);
if (conn == s_active)
s_active = nil;
}
s_critsect.Leave();
CheckedReconnect(conn, kNetErrConnectFailed);
conn->DecRef("Connecting");
}
//============================================================================
static void NotifyConnSocketDisconnect (CliGkConn * conn) {
conn->StopAutoPing();
s_critsect.Enter();
{
conn->cancelId = 0;
s_conns.Unlink(conn);
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");
}
//============================================================================
static bool NotifyConnSocketRead (CliGkConn * conn, AsyncNotifySocketRead * read) {
// TODO: Only dispatch messages from the active auth server
conn->lastHeardTimeMs = GetNonZeroTimeMs();
bool result = NetCliDispatch(conn->cli, read->buffer, read->bytes, conn);
read->bytesProcessed += read->bytes;
return result;
}
//============================================================================
static bool SocketNotifyCallback (
AsyncSocket sock,
EAsyncNotifySocket code,
AsyncNotifySocket * notify,
void ** userState
) {
bool result = true;
CliGkConn * conn;
switch (code) {
case kNotifySocketConnectSuccess:
conn = (CliGkConn *) notify->param;
*userState = conn;
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 = (CliGkConn *) notify->param;
NotifyConnSocketConnectFailed(conn);
break;
case kNotifySocketDisconnect:
conn = (CliGkConn *) *userState;
NotifyConnSocketDisconnect(conn);
break;
case kNotifySocketRead:
conn = (CliGkConn *) *userState;
result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify);
break;
}
return result;
}
//============================================================================
static void Connect (
CliGkConn * conn
) {
ASSERT(s_running);
conn->pingSendTimeMs = 0;
s_critsect.Enter();
{
while (CliGkConn * oldConn = s_conns.Head()) {
if (oldConn != conn)
UnlinkAndAbandonConn_CS(oldConn);
else
s_conns.Unlink(oldConn);
}
s_conns.Link(conn);
}
s_critsect.Leave();
Cli2GateKeeper_Connect connect;
connect.hdr.connType = kConnTypeCliToGateKeeper;
connect.hdr.hdrBytes = sizeof(connect.hdr);
connect.hdr.buildId = BuildId();
connect.hdr.buildType = BuildType();
connect.hdr.branchId = BranchId();
connect.hdr.productId = ProductId();
connect.data.token = conn->token;
connect.data.dataBytes = sizeof(connect.data);
AsyncSocketConnect(
&conn->cancelId,
conn->addr,
SocketNotifyCallback,
conn,
&connect,
sizeof(connect),
0,
0
);
}
//============================================================================
static void Connect (
const wchar name[],
const NetAddress & addr
) {
ASSERT(s_running);
CliGkConn * conn = NEWZERO(CliGkConn);
conn->addr = addr;
conn->seq = ConnNextSequence();
conn->lastHeardTimeMs = GetNonZeroTimeMs(); // used in connect timeout, and ping timeout
StrCopy(conn->name, name, arrsize(conn->name));
conn->IncRef("Lifetime");
conn->AutoReconnect();
}
//============================================================================
static void AsyncLookupCallback (
void * param,
const wchar name[],
unsigned addrCount,
const NetAddress addrs[]
) {
if (!addrCount) {
ReportNetError(kNetProtocolCli2GateKeeper, kNetErrNameLookupFailed);
return;
}
for (unsigned i = 0; i < addrCount; ++i) {
Connect(name, addrs[i]);
}
}
/*****************************************************************************
*
* CliGkConn
*
***/
//===========================================================================
static unsigned CliGkConnTimerDestroyed (void * param) {
CliGkConn * conn = (CliGkConn *) param;
conn->DecRef("TimerDestroyed");
return kAsyncTimeInfinite;
}
//===========================================================================
static unsigned CliGkConnReconnectTimerProc (void * param) {
((CliGkConn *) param)->TimerReconnect();
return kAsyncTimeInfinite;
}
//===========================================================================
static unsigned CliGkConnPingTimerProc (void * param) {
((CliGkConn *) param)->TimerPing();
return kPingIntervalMs;
}
//============================================================================
CliGkConn::CliGkConn () {
AtomicAdd(&s_perf[kPerfConnCount], 1);
}
//============================================================================
CliGkConn::~CliGkConn () {
if (cli)
NetCliDelete(cli, true);
AtomicAdd(&s_perf[kPerfConnCount], -1);
}
//===========================================================================
void CliGkConn::TimerReconnect () {
ASSERT(!sock);
ASSERT(!cancelId);
if (!s_running) {
s_critsect.Enter();
UnlinkAndAbandonConn_CS(this);
s_critsect.Leave();
}
else {
IncRef("Connecting");
// Remember the time we started the reconnect attempt, guarding against
// TimeGetMs() returning zero (unlikely), as a value of zero indicates
// a first-time connect condition to StartAutoReconnect()
reconnectStartMs = GetNonZeroTimeMs();
Connect(this);
}
}
//===========================================================================
// This function is called when after a disconnect to start a new connection
void CliGkConn::StartAutoReconnect () {
critsect.Enter();
if (reconnectTimer && !s_perf[kAutoReconnectDisabled]) {
// Make reconnect attempts at regular intervals. If the last attempt
// took more than the specified max interval time then reconnect
// immediately; otherwise wait until the time interval is up again
// then reconnect.
unsigned remainingMs = 0;
if (reconnectStartMs) {
remainingMs = reconnectStartMs - GetNonZeroTimeMs();
if ((signed)remainingMs < 0)
remainingMs = 0;
LogMsg(kLogPerf, L"GateKeeper auto-reconnecting in %u ms", remainingMs);
}
AsyncTimerUpdate(reconnectTimer, remainingMs);
}
critsect.Leave();
}
//===========================================================================
// This function should be called during object construction
// to initiate connection attempts to the remote host whenever
// the socket is disconnected.
void CliGkConn::AutoReconnect () {
ASSERT(!reconnectTimer);
IncRef("ReconnectTimer");
critsect.Enter();
{
AsyncTimerCreate(
&reconnectTimer,
CliGkConnReconnectTimerProc,
0, // immediate callback
this
);
}
critsect.Leave();
}
//============================================================================
void CliGkConn::StopAutoReconnect () {
critsect.Enter();
{
if (AsyncTimer * timer = reconnectTimer) {
reconnectTimer = nil;
AsyncTimerDeleteCallback(timer, CliGkConnTimerDestroyed);
}
}
critsect.Leave();
}
//============================================================================
bool CliGkConn::AutoReconnectEnabled () {
return (reconnectTimer != nil) && !s_perf[kAutoReconnectDisabled];
}
//============================================================================
void CliGkConn::AutoPing () {
ASSERT(!pingTimer);
IncRef("PingTimer");
critsect.Enter();
{
AsyncTimerCreate(
&pingTimer,
CliGkConnPingTimerProc,
sock ? 0 : kAsyncTimeInfinite,
this
);
}
critsect.Leave();
}
//============================================================================
void CliGkConn::StopAutoPing () {
critsect.Enter();
{
if (AsyncTimer * timer = pingTimer) {
pingTimer = nil;
AsyncTimerDeleteCallback(timer, CliGkConnTimerDestroyed);
}
}
critsect.Leave();
}
//============================================================================
void CliGkConn::TimerPing () {
#if 0
// if the time difference between when we last sent a ping and when we last
// heard from the server is >= 3x the ping interval, the socket is stale.
if (pingSendTimeMs && abs(int(pingSendTimeMs - lastHeardTimeMs)) >= kPingTimeoutMs) {
// ping timed out, disconnect the socket
AsyncSocketDisconnect(sock, true);
}
else
#endif
{
// Send a ping request
pingSendTimeMs = GetNonZeroTimeMs();
const unsigned_ptr msg[] = {
kCli2GateKeeper_PingRequest,
pingSendTimeMs,
0, // not a transaction
0, // no payload
nil
};
//Send(msg, arrsize(msg));
}
}
//============================================================================
void CliGkConn::Send (const unsigned_ptr fields[], unsigned count) {
critsect.Enter();
{
NetCliSend(cli, fields, count);
NetCliFlush(cli);
}
critsect.Leave();
}
/*****************************************************************************
*
* Cli2GateKeeper message handlers
*
***/
//============================================================================
static bool Recv_PingReply (
const byte msg[],
unsigned bytes,
void *
) {
const GateKeeper2Cli_PingReply & reply = *(const GateKeeper2Cli_PingReply *)msg;
if (reply.transId)
NetTransRecv(reply.transId, msg, bytes);
return true;
}
//============================================================================
static bool Recv_FileSrvIpAddressReply (
const byte msg[],
unsigned bytes,
void *
) {
const GateKeeper2Cli_FileSrvIpAddressReply & reply = *(const GateKeeper2Cli_FileSrvIpAddressReply *)msg;
if (reply.transId)
NetTransRecv(reply.transId, msg, bytes);
return true;
}
//============================================================================
static bool Recv_AuthSrvIpAddressReply (
const byte msg[],
unsigned bytes,
void *
) {
const GateKeeper2Cli_AuthSrvIpAddressReply & reply = *(const GateKeeper2Cli_AuthSrvIpAddressReply *)msg;
if (reply.transId)
NetTransRecv(reply.transId, msg, bytes);
return true;
}
/*****************************************************************************
*
* Cli2Auth protocol
*
***/
#define MSG(s) kNetMsg_Cli2GateKeeper_##s
static NetMsgInitSend s_send[] = {
{ MSG(PingRequest) },
{ MSG(FileSrvIpAddressRequest) },
{ MSG(AuthSrvIpAddressRequest) },
};
#undef MSG
#define MSG(s) kNetMsg_GateKeeper2Cli_##s, Recv_##s
static NetMsgInitRecv s_recv[] = {
{ MSG(PingReply) },
{ MSG(FileSrvIpAddressReply) },
};
#undef MSG
/*****************************************************************************
*
* PingRequestTrans
*
***/
//============================================================================
PingRequestTrans::PingRequestTrans (
FNetCliGateKeeperPingRequestCallback callback,
void * param,
unsigned pingAtMs,
unsigned payloadBytes,
const void * payload
) : NetGateKeeperTrans(kPingRequestTrans)
, m_callback(callback)
, m_param(param)
, m_pingAtMs(pingAtMs)
{
m_payload.Set((const byte *)payload, payloadBytes);
}
//============================================================================
bool PingRequestTrans::Send () {
if (!AcquireConn())
return false;
const unsigned_ptr msg[] = {
kCli2GateKeeper_PingRequest,
m_pingAtMs,
m_transId,
m_payload.Count(),
(unsigned_ptr) m_payload.Ptr(),
};
m_conn->Send(msg, arrsize(msg));
return true;
}
//============================================================================
void PingRequestTrans::Post () {
m_callback(
m_result,
m_param,
m_pingAtMs,
m_replyAtMs,
m_payload.Count(),
m_payload.Ptr()
);
}
//============================================================================
bool PingRequestTrans::Recv (
const byte msg[],
unsigned bytes
) {
const GateKeeper2Cli_PingReply & reply = *(const GateKeeper2Cli_PingReply *)msg;
m_payload.Set(reply.payload, reply.payloadBytes);
m_replyAtMs = TimeGetMs();
m_result = kNetSuccess;
m_state = kTransStateComplete;
return true;
}
/*****************************************************************************
*
* FileSrvIpAddressRequestTrans
*
***/
//============================================================================
FileSrvIpAddressRequestTrans::FileSrvIpAddressRequestTrans (
FNetCliGateKeeperFileSrvIpAddressRequestCallback callback,
void * param,
bool isPatcher
) : NetGateKeeperTrans(kGkFileSrvIpAddressRequestTrans)
, m_callback(callback)
, m_param(param)
, m_isPatcher(isPatcher)
{
}
//============================================================================
bool FileSrvIpAddressRequestTrans::Send () {
if (!AcquireConn())
return false;
const unsigned_ptr msg[] = {
kCli2GateKeeper_FileSrvIpAddressRequest,
m_transId,
m_isPatcher == true ? 1 : 0
};
m_conn->Send(msg, arrsize(msg));
return true;
}
//============================================================================
void FileSrvIpAddressRequestTrans::Post () {
m_callback(
m_result,
m_param,
m_addr
);
}
//============================================================================
bool FileSrvIpAddressRequestTrans::Recv (
const byte msg[],
unsigned bytes
) {
const GateKeeper2Cli_FileSrvIpAddressReply & reply = *(const GateKeeper2Cli_FileSrvIpAddressReply *)msg;
m_result = kNetSuccess;
m_state = kTransStateComplete;
StrCopy(m_addr, reply.address, 64);
return true;
}
/*****************************************************************************
*
* AuthSrvIpAddressRequestTrans
*
***/
//============================================================================
AuthSrvIpAddressRequestTrans::AuthSrvIpAddressRequestTrans (
FNetCliGateKeeperFileSrvIpAddressRequestCallback callback,
void * param
) : NetGateKeeperTrans(kGkAuthSrvIpAddressRequestTrans)
, m_callback(callback)
, m_param(param)
{
}
//============================================================================
bool AuthSrvIpAddressRequestTrans::Send () {
if (!AcquireConn())
return false;
const unsigned_ptr msg[] = {
kCli2GateKeeper_AuthSrvIpAddressRequest,
m_transId,
};
m_conn->Send(msg, arrsize(msg));
return true;
}
//============================================================================
void AuthSrvIpAddressRequestTrans::Post () {
m_callback(
m_result,
m_param,
m_addr
);
}
//============================================================================
bool AuthSrvIpAddressRequestTrans::Recv (
const byte msg[],
unsigned bytes
) {
const GateKeeper2Cli_AuthSrvIpAddressReply & reply = *(const GateKeeper2Cli_AuthSrvIpAddressReply *)msg;
m_result = kNetSuccess;
m_state = kTransStateComplete;
StrCopy(m_addr, reply.address, 64);
return true;
}
} using namespace GateKeeper;
/*****************************************************************************
*
* NetGateKeeperTrans
*
***/
//============================================================================
NetGateKeeperTrans::NetGateKeeperTrans (ETransType transType)
: NetTrans(kNetProtocolCli2GateKeeper, transType)
, m_conn(nil)
{
}
//============================================================================
NetGateKeeperTrans::~NetGateKeeperTrans () {
ReleaseConn();
}
//============================================================================
bool NetGateKeeperTrans::AcquireConn () {
if (!m_conn)
m_conn = GetConnIncRef("AcquireConn");
return m_conn != nil;
}
//============================================================================
void NetGateKeeperTrans::ReleaseConn () {
if (m_conn) {
m_conn->DecRef("AcquireConn");
m_conn = nil;
}
}
/*****************************************************************************
*
* Protected functions
*
***/
//============================================================================
void GateKeeperInitialize () {
s_running = true;
NetMsgProtocolRegister(
kNetProtocolCli2GateKeeper,
false,
s_send, arrsize(s_send),
s_recv, arrsize(s_recv),
kDhGValue,
BigNum(sizeof(kDhXData), kDhXData),
BigNum(sizeof(kDhNData), kDhNData)
);
}
//============================================================================
void GateKeeperDestroy (bool wait) {
s_running = false;
NetTransCancelByProtocol(
kNetProtocolCli2GateKeeper,
kNetErrRemoteShutdown
);
NetMsgProtocolDestroy(
kNetProtocolCli2GateKeeper,
false
);
s_critsect.Enter();
{
while (CliGkConn * conn = s_conns.Head())
UnlinkAndAbandonConn_CS(conn);
s_active = nil;
}
s_critsect.Leave();
if (!wait)
return;
while (s_perf[kPerfConnCount]) {
NetTransUpdate();
AsyncSleep(10);
}
}
//============================================================================
bool GateKeeperQueryConnected () {
bool result;
s_critsect.Enter();
{
if (nil != (result = s_active))
result &= (nil != s_active->cli);
}
s_critsect.Leave();
return result;
}
//============================================================================
unsigned GateKeeperGetConnId () {
unsigned connId;
s_critsect.Enter();
connId = (s_active) ? s_active->seq : 0;
s_critsect.Leave();
return connId;
}
} using namespace Ngl;
/*****************************************************************************
*
* Exported functions
*
***/
//============================================================================
void NetCliGateKeeperStartConnect (
const wchar * gateKeeperAddrList[],
unsigned gateKeeperAddrCount
) {
gateKeeperAddrCount = min(gateKeeperAddrCount, 1);
for (unsigned i = 0; i < gateKeeperAddrCount; ++i) {
// Do we need to lookup the address?
const wchar * name = gateKeeperAddrList[i];
while (unsigned ch = *name) {
++name;
if (!(isdigit(ch) || ch == L'.' || ch == L':')) {
AsyncCancelId cancelId;
AsyncAddressLookupName(
&cancelId,
AsyncLookupCallback,
gateKeeperAddrList[i],
kNetDefaultClientPort,
nil
);
break;
}
}
if (!name[0]) {
NetAddress addr;
NetAddressFromString(&addr, gateKeeperAddrList[i], kNetDefaultClientPort);
Connect(gateKeeperAddrList[i], addr);
}
}
}
//============================================================================
void NetCliGateKeeperDisconnect () {
s_critsect.Enter();
{
while (CliGkConn * conn = s_conns.Head())
UnlinkAndAbandonConn_CS(conn);
s_active = nil;
}
s_critsect.Leave();
}
//============================================================================
void NetCliGateKeeperPingRequest (
unsigned pingTimeMs,
unsigned payloadBytes,
const void * payload,
FNetCliGateKeeperPingRequestCallback callback,
void * param
) {
PingRequestTrans * trans = NEW(PingRequestTrans)(
callback,
param,
pingTimeMs,
payloadBytes,
payload
);
NetTransSend(trans);
}
//============================================================================
void NetCliGateKeeperFileSrvIpAddressRequest (
FNetCliGateKeeperFileSrvIpAddressRequestCallback callback,
void * param,
bool isPatcher
) {
FileSrvIpAddressRequestTrans * trans = NEW(FileSrvIpAddressRequestTrans)(callback, param, isPatcher);
NetTransSend(trans);
}