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.
542 lines
14 KiB
542 lines
14 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/NucleusLib/pnNetLog/pnNlCli.cpp |
|
* |
|
***/ |
|
|
|
#include "Pch.h" |
|
#pragma hdrstop |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Private |
|
* |
|
***/ |
|
|
|
struct NetLogConn : SrvConn { |
|
NetAddress netaddr; |
|
ESrvType srvType; |
|
|
|
NetLogConn (const NetAddress & addr, ESrvType srvType); |
|
~NetLogConn (); |
|
|
|
void Connect ( |
|
AsyncCancelId * cancelId, |
|
FAsyncNotifySocketProc notifyProc, |
|
void * param |
|
); |
|
}; |
|
|
|
struct LogMsgTrans : SrvTrans { |
|
Srv2Log_LogMsg *msgBuffer; |
|
|
|
LogMsgTrans(); |
|
~LogMsgTrans(); |
|
bool OnTransReply ( |
|
ENetError * error, |
|
SrvMsgHeader * msg |
|
); |
|
}; |
|
|
|
struct LogConnEventNode { |
|
LINK(LogConnEventNode) link; |
|
Srv2Log_LogMsg * msg; |
|
unsigned sendTimeMs; |
|
|
|
LogConnEventNode(Srv2Log_LogMsg *msg, unsigned timeStampMs); |
|
}; |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Private Data |
|
* |
|
***/ |
|
|
|
static NetLogConn * s_conn; |
|
static CCritSect s_critsect; |
|
static IniChangeReg * s_change; |
|
static ESrvType s_srvType; |
|
static LISTDECL(LogConnEventNode, link) s_eventQueue; |
|
static AsyncTimer * s_timer; |
|
static long s_perf[kNlCliNumPerf]; |
|
static bool s_running; |
|
|
|
static const unsigned kIssueSaveMs = 100; |
|
static const unsigned kMaxNumberOfTransactions = 2000; |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Private Functions |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
static NetLogConn * GetConnIncRef () { |
|
NetLogConn * conn; |
|
s_critsect.Enter(); |
|
{ |
|
if (nil != (conn = s_conn)) |
|
conn->IncRef(); |
|
} |
|
s_critsect.Leave(); |
|
return conn; |
|
} |
|
|
|
//============================================================================ |
|
static void NetLogConnConnect (NetAddress & addr, ESrvType srvType) { |
|
NetLogConn *conn = SRV_CONN_ALLOC(NetLogConn)(addr, srvType); |
|
|
|
s_critsect.Enter(); |
|
{ |
|
SWAP(s_conn, conn); |
|
} |
|
s_critsect.Leave(); |
|
|
|
if (conn) |
|
conn->Destroy(); |
|
} |
|
|
|
//============================================================================ |
|
static void NetLogConnDisconnect () { |
|
NetLogConn *conn = nil; |
|
|
|
s_critsect.Enter(); |
|
{ |
|
SWAP(s_conn, conn); |
|
} |
|
s_critsect.Leave(); |
|
|
|
if (conn) |
|
conn->Destroy(); |
|
} |
|
|
|
//============================================================================ |
|
static void AddEventNode (Srv2Log_LogMsg *msg) { |
|
LogConnEventNode *node = NEW(LogConnEventNode)(msg, TimeGetMs() + kIssueSaveMs); |
|
s_critsect.Enter(); |
|
{ |
|
s_eventQueue.Link(node); |
|
} |
|
s_critsect.Leave(); |
|
|
|
AsyncTimerUpdate( |
|
s_timer, |
|
kIssueSaveMs, |
|
kAsyncTimerUpdateSetPriorityHigher |
|
); |
|
} |
|
|
|
//============================================================================ |
|
static void ParseIni (Ini * ini) { |
|
unsigned iter; |
|
const IniValue * value = IniGetFirstValue( |
|
ini, |
|
L"Server Locations", |
|
L"LogSrv", |
|
&iter |
|
); |
|
|
|
if (value) { |
|
wchar addrStr[32]; |
|
IniGetString(value, addrStr, arrsize(addrStr), 0, nil); |
|
NetAddress addr; |
|
NetAddressFromString(&addr, addrStr, kNetDefaultServerPort); |
|
NetLogConnConnect(addr, s_srvType); |
|
} |
|
} |
|
|
|
//============================================================================ |
|
static void IniChangeCallback (const wchar fullPath[]) { |
|
Ini * ini = IniOpen(fullPath); |
|
ParseIni(ini); |
|
IniClose(ini); |
|
} |
|
|
|
//============================================================================ |
|
static unsigned TimerCallback (void *) { |
|
LISTDECL(LogConnEventNode, link) sendList; |
|
unsigned sleepMs = kAsyncTimeInfinite; |
|
|
|
s_critsect.Enter(); |
|
{ |
|
int allowedNumTrans = kMaxNumberOfTransactions - s_perf[kNlCliNumPendingSaves]; // find the number of transactions that we can send based on the number in transit, and the max number allowed |
|
if(allowedNumTrans < 0) // this could be negative, if so set to zero |
|
allowedNumTrans = 0; |
|
|
|
dword currTime = TimeGetMs(); |
|
LogConnEventNode *hash; |
|
int timeDiff; |
|
|
|
// Add pending saves, up to the max allowed per update |
|
for(;;) { |
|
if(!allowedNumTrans && s_running) { |
|
sleepMs = 5000; // we are at our max number of transactions sleep for 5 seconds and try again |
|
break; |
|
} |
|
|
|
hash = s_eventQueue.Head(); |
|
if(!hash) |
|
break; // no messages left. We will wait until another message comes in before another timer update |
|
|
|
timeDiff = hash->sendTimeMs - currTime; |
|
|
|
// nodes are naturally ordered by increasing sendTimeMs |
|
if(!s_running || (timeDiff <= 0)) { |
|
sendList.Link(hash); |
|
--allowedNumTrans; |
|
} |
|
else { |
|
sleepMs = timeDiff; |
|
break; |
|
} |
|
} |
|
} |
|
s_critsect.Leave(); |
|
|
|
while(LogConnEventNode *node = sendList.Head()) { |
|
LogMsgTrans * trans = SRV_TRANS_ALLOC(LogMsgTrans); |
|
trans->msgBuffer = node->msg; |
|
|
|
if (NetLogConn * conn = GetConnIncRef()) { |
|
conn->SendRequest(trans, node->msg); |
|
conn->DecRef(); |
|
} |
|
else { |
|
trans->TransCancel(kNetErrTimeout); |
|
} |
|
delete node; |
|
} |
|
return sleepMs; |
|
} |
|
|
|
//============================================================================ |
|
static unsigned CalcArgsLength (const NetLogEvent &event, va_list args) { |
|
unsigned length = 0; |
|
unsigned paramType = kNumLogParamTypes; // invalidate |
|
unsigned field = 0; |
|
|
|
for(unsigned i = 0; i < event.numFields * 2; ++i) { |
|
if(!(i % 2)) { |
|
paramType = va_arg(args, unsigned); |
|
continue; |
|
} |
|
|
|
//validate parameter type |
|
if(paramType != (unsigned)event.fields[field].type) { |
|
length = 0; |
|
LogMsg( kLogError, "Log parameter types do not match for event: %s parameter: %s?", event.eventName, event.fields[field].name); |
|
break; |
|
} |
|
|
|
switch(event.fields[field].type) { |
|
case kLogParamInt: { |
|
va_arg(args, int); |
|
length += sizeof(int); |
|
} |
|
break; |
|
|
|
case kLogParamUnsigned: { |
|
va_arg(args, unsigned); |
|
length += sizeof(unsigned); |
|
} |
|
break; |
|
|
|
case kLogParamFloat: { |
|
va_arg(args, float); |
|
length += sizeof(float); |
|
} |
|
break; |
|
|
|
case kLogParamLong: { |
|
va_arg(args, long); |
|
length += sizeof(long); |
|
} |
|
break; |
|
|
|
case kLogParamLongLong: { |
|
va_arg(args, long long); |
|
length += sizeof(long long); |
|
} |
|
break; |
|
|
|
case kLogParamUuid: { |
|
va_arg(args, Uuid); |
|
length += sizeof(Uuid); |
|
} |
|
break; |
|
|
|
case kLogParamStringW: { |
|
wchar *str = va_arg(args, wchar *); |
|
if(!str) |
|
str = L""; |
|
length += StrBytes(str); |
|
|
|
} |
|
break; |
|
|
|
default: |
|
hsAssert(false, "Unknown argument type in log statement"); |
|
return 0; |
|
} |
|
++field; |
|
} |
|
return length; |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* NetLogConn |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
NetLogConn::NetLogConn (const NetAddress & addr, ESrvType srvType) |
|
: netaddr(addr), |
|
srvType(srvType) |
|
{ |
|
AtomicAdd(&s_perf[kNlCliNumConn], 1); |
|
SetAutoPing(); |
|
AutoReconnect(); |
|
} |
|
|
|
//============================================================================ |
|
NetLogConn::~NetLogConn () { |
|
AtomicAdd(&s_perf[kNlCliNumConn], -1); |
|
} |
|
|
|
//============================================================================ |
|
void NetLogConn::Connect ( |
|
AsyncCancelId * cancelId, |
|
FAsyncNotifySocketProc notifyProc, |
|
void * param |
|
) { |
|
// Connect to remote server |
|
Srv2Log_Connect connect; |
|
connect.hdr.connType = kConnTypeSrvToLog; |
|
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); |
|
connect.data.buildId = BuildId(); |
|
connect.data.srvType = srvType; |
|
connect.data.buildType = BuildType(); |
|
connect.data.productId = ProductId(); |
|
|
|
AsyncSocketConnect( |
|
cancelId, |
|
netaddr, |
|
notifyProc, |
|
param, |
|
&connect, |
|
sizeof(connect) |
|
); |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* LogMsgTrans |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
LogMsgTrans::LogMsgTrans () |
|
: msgBuffer(nil) |
|
{ |
|
AtomicAdd(&s_perf[kNlCliNumTrans], 1); |
|
} |
|
|
|
//============================================================================ |
|
LogMsgTrans::~LogMsgTrans () { |
|
AtomicAdd(&s_perf[kNlCliNumTrans], -1); |
|
} |
|
|
|
//============================================================================ |
|
bool LogMsgTrans::OnTransReply ( |
|
ENetError * error, |
|
SrvMsgHeader * msg |
|
) { |
|
ref(msg); |
|
bool result; |
|
if (msg->protocolId != kNetProtocolSrv2Log) { |
|
result = SrvTrans::OnTransReply(error, msg); |
|
} |
|
else { |
|
result = true; |
|
} |
|
|
|
if(IS_NET_ERROR(*error) && s_running) { |
|
AddEventNode(msgBuffer); |
|
} |
|
else { |
|
FREE(msgBuffer); |
|
} |
|
return true; |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* LogConnEventNode |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
LogConnEventNode::LogConnEventNode (Srv2Log_LogMsg *msg, unsigned sendTimeMs) |
|
: msg(msg), |
|
sendTimeMs(sendTimeMs) |
|
{ |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Protected |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
void NetLogCliInitialize (ESrvType srvType) { |
|
s_running = true; |
|
s_srvType = srvType; |
|
AsyncTimerCreate( |
|
&s_timer, |
|
TimerCallback, |
|
kAsyncTimeInfinite, |
|
nil |
|
); |
|
IniChangeAdd(L"plServer", IniChangeCallback, &s_change); |
|
} |
|
|
|
//============================================================================ |
|
void NetLogCliShutdown () { |
|
s_running = false; |
|
if(s_change) { |
|
IniChangeRemove(s_change, true); |
|
s_change = false; |
|
} |
|
if(s_timer) { |
|
AsyncTimerDeleteCallback(s_timer, TimerCallback); |
|
s_timer = nil; |
|
} |
|
NetLogConnDisconnect(); |
|
} |
|
|
|
//============================================================================ |
|
void NetLogCliDestroy () { |
|
while(s_perf[kNlCliNumTrans]) |
|
AsyncSleep(10); |
|
while(s_perf[kNlCliNumConn]) |
|
AsyncSleep(10); |
|
} |
|
|
|
//============================================================================ |
|
void NetLogCliSendEvent (const NetLogEvent &event, va_list args) { |
|
Srv2Log_LogMsg *msg; |
|
unsigned length = CalcArgsLength(event, args); |
|
if(!length) |
|
return; |
|
|
|
CSrvPackBuffer pack( |
|
sizeof(*msg) + |
|
length |
|
); |
|
|
|
msg = (Srv2Log_LogMsg *) pack.Alloc(sizeof(*msg)); |
|
msg->transId = 0; |
|
msg->protocolId = kNetProtocolSrv2Log; |
|
msg->messageId = kSrv2Log_LogMsg; |
|
msg->eventType = event.logEventType; |
|
msg->timestamp = TimeGetLocalTime(); |
|
|
|
unsigned field = 0; |
|
unsigned paramType = kNumLogParamTypes; |
|
|
|
// if we get here the template parameters have already been validated by CalcLength, no need to do that again. |
|
for(unsigned i = 0; i < event.numFields * 2; ++i) { |
|
if(!(i % 2)) { |
|
paramType = va_arg(args, unsigned); |
|
continue; |
|
} |
|
switch(event.fields[field].type) { |
|
case kLogParamInt: { |
|
int i = va_arg(args, int); |
|
pack.AddData(&i, sizeof(int)); |
|
} |
|
break; |
|
|
|
case kLogParamUnsigned: { |
|
unsigned u = va_arg(args, unsigned); |
|
pack.AddData(&u, sizeof(unsigned)); |
|
} |
|
break; |
|
|
|
case kLogParamFloat: { |
|
float f = va_arg(args, float); |
|
pack.AddData(&f, sizeof(float)); |
|
} |
|
break; |
|
|
|
case kLogParamLong: { |
|
long l = va_arg(args, long); |
|
pack.AddData(&l, sizeof(long)); |
|
} |
|
break; |
|
|
|
case kLogParamLongLong: { |
|
long long ll = va_arg(args, long long); |
|
pack.AddData(&ll, sizeof(long long)); |
|
} |
|
break; |
|
|
|
case kLogParamUuid: { |
|
Uuid uuid = va_arg(args, Uuid); |
|
pack.AddData(&uuid, sizeof(Uuid)); |
|
} |
|
break; |
|
|
|
case kLogParamStringW: { |
|
wchar *str = va_arg(args, wchar *); |
|
if(!str) |
|
str = L""; |
|
pack.AddString(str); |
|
} |
|
break; |
|
} |
|
++field; |
|
} |
|
|
|
msg->messageBytes = pack.Size(); |
|
AddEventNode(msg); |
|
}
|
|
|