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.
603 lines
19 KiB
603 lines
19 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/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
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/pnNlSrv.cpp |
|
* |
|
***/ |
|
|
|
#include "Pch.h" |
|
#pragma hdrstop |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Private |
|
* |
|
***/ |
|
|
|
struct EventHash { |
|
unsigned eventType; |
|
ESrvType srvType; |
|
|
|
inline EventHash ( |
|
unsigned eventType, |
|
ESrvType srvType |
|
); |
|
|
|
inline uint32_t GetHash () const; |
|
inline bool operator== (const EventHash & rhs) const; |
|
}; |
|
|
|
struct NetLogEventHash : EventHash { |
|
const NetLogEvent * event; |
|
|
|
NetLogEventHash( |
|
unsigned eventType, |
|
ESrvType srvType, |
|
const NetLogEvent *event |
|
); |
|
HASHLINK(NetLogEventHash) link; |
|
}; |
|
|
|
struct LogConn : SrvConn { |
|
LINK(LogConn) link; |
|
ESrvType srvType; |
|
unsigned buildId; |
|
NetAddressNode addr; |
|
unsigned buildType; |
|
unsigned productId; |
|
|
|
// Ctor |
|
LogConn ( |
|
AsyncSocket sock, |
|
void ** userState, |
|
NetAddressNode nodeNumber, |
|
unsigned buildId, |
|
ESrvType srvType, |
|
unsigned buildType, |
|
unsigned productId |
|
); |
|
~LogConn (); |
|
|
|
bool OnSrvMsg (SrvMsgHeader * msg); |
|
void OnDisconnect (); |
|
|
|
bool Recv_Srv2Log_LogMsg( const Srv2Log_LogMsg & msg ); |
|
}; |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Private Data |
|
* |
|
***/ |
|
|
|
static IniChangeReg * s_change; |
|
static long s_perf[kNlSrvNumPerf]; |
|
static CCritSect s_critsect; |
|
static bool s_running; |
|
static LISTDECL(LogConn, link) s_conns; |
|
void (*NetLogSrvCallback)(const NetLogEvent *event, const ARRAY(wchar_t) &, unsigned, NetAddressNode &, uint64_t, unsigned, unsigned ) ; |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Private Functions |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
static void ParseIni (Ini * ini) { |
|
unsigned iter; |
|
const IniValue *value; |
|
|
|
value = IniGetFirstValue( |
|
ini, |
|
L"", |
|
L"", |
|
&iter |
|
); |
|
} |
|
|
|
//============================================================================ |
|
static void IniChangeCallback (const wchar_t fullPath[]) { |
|
Ini * ini = IniOpen(fullPath); |
|
ParseIni(ini); |
|
IniClose(ini); |
|
} |
|
|
|
//=========================================================================== |
|
static bool SocketNotifyProc ( |
|
AsyncSocket sock, |
|
EAsyncNotifySocket code, |
|
AsyncNotifySocket * notify, |
|
void ** userState |
|
) { |
|
// If the socket is successfully connected then only |
|
// kNotifySocketListenSuccess will arrive here, as |
|
// the service routine will be changed by SrvConn. |
|
if (code != kNotifySocketListenSuccess) { |
|
if (code == kNotifySocketDisconnect) |
|
AsyncSocketDelete(sock); |
|
return false; |
|
} |
|
|
|
// TODO: Verify this is from a valid server |
|
AsyncNotifySocketListen * listen = (AsyncNotifySocketListen *) notify; |
|
EServerRights rights = SrvIniGetServerRights(listen->remoteAddr); |
|
if (rights < kSrvRightsServer) { |
|
LogMsgDebug("LogConn: insufficient server rights"); |
|
AtomicAdd(&s_perf[kNlSrvPerfConnDenied], 1); |
|
return false; |
|
} |
|
|
|
NetAddressNode nodeNumber = NetAddressGetNode(listen->remoteAddr); |
|
|
|
// Parse the connect message |
|
Srv2Log_ConnData connect; |
|
if (!Srv2LogValidateConnect(listen, &connect)) { |
|
LogMsgDebug("LogConn: invalid connect packet"); |
|
AtomicAdd(&s_perf[kNlSrvPerfConnDenied], 1); |
|
return false; |
|
} |
|
|
|
// Create connection record |
|
LogConn * conn = SRV_CONN_ALLOC(LogConn)( |
|
sock, |
|
userState, |
|
nodeNumber, |
|
connect.buildId, |
|
(ESrvType) connect.srvType, |
|
connect.buildType, |
|
connect.productId |
|
); |
|
|
|
// Add this connection |
|
bool result; |
|
s_critsect.Enter(); |
|
{ |
|
s_conns.Link(conn); |
|
result = s_running; |
|
} |
|
s_critsect.Leave(); |
|
|
|
return result; |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* LogConn implementation |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
LogConn::LogConn ( |
|
AsyncSocket sock, |
|
void ** userState, |
|
NetAddressNode nodeNumber, |
|
unsigned buildId, |
|
ESrvType srvType, |
|
unsigned buildType, |
|
unsigned productId |
|
) : srvType(srvType), |
|
buildId(buildId), |
|
buildType(buildType), |
|
productId(productId) |
|
{ |
|
ref(nodeNumber); |
|
AtomicAdd(&s_perf[kNlSrvPerfConnCount], 1); |
|
SetAutoTimeout(); |
|
NotifyListen(sock, userState); |
|
addr = nodeNumber; |
|
} |
|
|
|
//============================================================================ |
|
LogConn::~LogConn () { |
|
AtomicAdd(&s_perf[kNlSrvPerfConnCount], -1); |
|
} |
|
|
|
//============================================================================ |
|
void LogConn::OnDisconnect () { |
|
// Accepting side just destroys the connection. Connecting side |
|
// will reconnect if that's what should happen. |
|
Destroy(); |
|
} |
|
|
|
//============================================================================ |
|
bool LogConn::OnSrvMsg (SrvMsgHeader * msg) { |
|
// Pass along messages not intended for us |
|
if (msg->protocolId != kNetProtocolSrv2Log) |
|
return SrvConn::OnSrvMsg(msg); |
|
|
|
bool status = false; |
|
#define DISPATCH(a) case k##a: status = Recv_##a(*(const a*)msg); break |
|
switch (msg->messageId) { |
|
DISPATCH(Srv2Log_LogMsg); |
|
|
|
default: |
|
status = false; |
|
break; |
|
} |
|
#undef DISPATCH |
|
|
|
return status; |
|
} |
|
|
|
//============================================================================ |
|
bool LogConn::Recv_Srv2Log_LogMsg(const Srv2Log_LogMsg & msg ) { |
|
CSrvUnpackBuffer unpack(&msg, msg.messageBytes); |
|
(void)unpack.GetData(sizeof(msg)); |
|
unsigned length = 0; |
|
ARRAY(wchar_t) databuf; |
|
wchar_t data[256]; |
|
const void *pData = 0; |
|
|
|
SendReply(msg.transId, kNetSuccess); |
|
if(!s_running) |
|
return true; |
|
|
|
// WARNING: each parameter type needs to be able to handle the case where GetData returns a nil pointer for backward compatability |
|
|
|
const NetLogEvent *event = NetLogFindEvent(msg.eventType, srvType); |
|
if(event) { |
|
for(unsigned i = 0; i < event->numFields; ++i) { |
|
if(!event->fields[i].name) |
|
{ |
|
LogMsg(kLogError, "Failed logging event because of nil param name: %s", event->eventName); |
|
return true; |
|
} |
|
|
|
switch(event->fields[i].type) { |
|
case kLogParamInt: { |
|
int i = 0; |
|
pData = unpack.GetData(sizeof(int)); |
|
if(!pData) |
|
continue; |
|
|
|
i = *(int *)pData; |
|
StrPrintf(data, arrsize(data), L"%d", i); |
|
length += StrLen(data) + 1; |
|
} |
|
break; |
|
|
|
case kLogParamUnsigned: { |
|
unsigned u = 0; |
|
pData = unpack.GetData(sizeof(unsigned)); |
|
if(!pData) |
|
continue; |
|
|
|
u = *(unsigned *)pData; |
|
StrPrintf(data, arrsize(data), L"%d", u); |
|
length += StrLen(data) + 1; |
|
} |
|
break; |
|
|
|
case kLogParamFloat: { |
|
float f = 0; |
|
pData = unpack.GetData(sizeof(float)); |
|
if(!pData) |
|
continue; |
|
|
|
f = *(float *)pData; |
|
StrPrintf(data, arrsize(data), L"%f", f); |
|
length += StrLen(data) + 1; |
|
} |
|
break; |
|
|
|
case kLogParamLong: { |
|
long l = 0; |
|
pData = unpack.GetData(sizeof(long)); |
|
if(!pData) |
|
continue; |
|
|
|
l = *(long *)pData; |
|
StrPrintf(data, arrsize(data), L"%ld", l); |
|
length += StrLen(data) + 1; |
|
} |
|
break; |
|
|
|
case kLogParamLongLong: { |
|
long long ll = 0; |
|
pData = unpack.GetData(sizeof(long)); |
|
if(!pData) |
|
continue; |
|
ll = *(long *)pData; |
|
StrPrintf(data, arrsize(data), L"%lld", ll); |
|
length += StrLen(data) + 1; |
|
} |
|
break; |
|
|
|
case kLogParamUuid: { |
|
Uuid uuid = 0; |
|
pData = unpack.GetData(sizeof(Uuid)); |
|
if(!pData) |
|
continue; |
|
uuid = *(Uuid *)pData; |
|
StrPrintf(data, arrsize(data), L"%s", uuid.data); |
|
length += StrLen(data) + 1; |
|
} |
|
break; |
|
|
|
case kLogParamStringW: { |
|
const wchar_t *str = unpack.GetString(); |
|
if(!str) { |
|
continue; |
|
} |
|
length += StrLen(str) + 1; |
|
} |
|
break; |
|
} |
|
length += StrLen(event->fields[i].name) + 1; // this must happen after the parameter check so we can opt out of saving a non existant parameter |
|
} |
|
|
|
databuf.Reserve(length + 1); |
|
|
|
{ |
|
CSrvUnpackBuffer unpack(&msg, msg.messageBytes); |
|
(void)unpack.GetData(sizeof(msg)); |
|
for(unsigned i = 0; i < event->numFields; ++i){ |
|
|
|
// the parameter name needs to be written before the data. Also, we need to be able to opt out of writing a parameter. |
|
switch(event->fields[i].type) { |
|
case kLogParamInt: { |
|
pData = unpack.GetData(sizeof(int)); |
|
if(!pData) |
|
continue; |
|
|
|
int val = *(int *)pData; |
|
|
|
// log event name |
|
databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); |
|
databuf.Add(0); |
|
|
|
// log event data |
|
StrPrintf(data, arrsize(data), L"%d", val); |
|
databuf.Add(data, StrLen(data)); |
|
databuf.Add(0); |
|
} |
|
break; |
|
|
|
case kLogParamUnsigned: { |
|
pData = unpack.GetData(sizeof(unsigned)); |
|
if(!pData) |
|
continue; |
|
|
|
unsigned u = *(unsigned *)pData; |
|
|
|
// log event name |
|
databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); |
|
databuf.Add(0); |
|
|
|
// log event data |
|
StrPrintf(data, arrsize(data), L"%d", u); |
|
databuf.Add(data, StrLen(data)); |
|
databuf.Add(0); |
|
} |
|
break; |
|
|
|
case kLogParamFloat: { |
|
pData = unpack.GetData(sizeof(float)); |
|
if(!pData) |
|
continue; |
|
|
|
float f = *(float *)pData; |
|
|
|
// log event name |
|
databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); |
|
databuf.Add(0); |
|
|
|
// log event data |
|
StrPrintf(data, arrsize(data), L"%f", f); |
|
databuf.Add(data, StrLen(data)); |
|
databuf.Add(0); |
|
} |
|
break; |
|
|
|
case kLogParamLong: { |
|
pData = unpack.GetData(sizeof(long)); |
|
if(!pData) |
|
continue; |
|
|
|
long l = *(long *)pData; |
|
|
|
// log event name |
|
databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); |
|
databuf.Add(0); |
|
|
|
// log event data |
|
StrPrintf(data, arrsize(data), L"%ld", l); |
|
databuf.Add(data, StrLen(data)); |
|
databuf.Add(0); |
|
} |
|
break; |
|
|
|
case kLogParamLongLong: { |
|
pData = unpack.GetData(sizeof(long long)); |
|
if(!pData) |
|
continue; |
|
|
|
long long ll = *(long long *)pData; |
|
|
|
// log event name |
|
databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); |
|
databuf.Add(0); |
|
|
|
// log event data |
|
StrPrintf(data, arrsize(data), L"%lld", ll); |
|
databuf.Add(data, StrLen(data)); |
|
databuf.Add(0); |
|
} |
|
break; |
|
|
|
case kLogParamUuid: { |
|
pData = unpack.GetData(sizeof(Uuid)); |
|
if(!pData) |
|
continue; |
|
|
|
Uuid uuid = *(Uuid *)pData; |
|
|
|
// log event name |
|
databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); |
|
databuf.Add(0); |
|
|
|
// log event data |
|
GuidToString(uuid, data, arrsize(data)); |
|
databuf.Add(data, StrLen(data)); |
|
databuf.Add(0); |
|
} |
|
break; |
|
|
|
case kLogParamStringW: { |
|
const wchar_t *str = unpack.GetString(); |
|
if(!str) { |
|
continue; |
|
} |
|
|
|
// log event name |
|
databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); |
|
databuf.Add(0); |
|
|
|
// log event data |
|
databuf.Add(str, StrLen(str)); |
|
databuf.Add(0); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
databuf.Add(0); |
|
|
|
if(NetLogSrvCallback) { |
|
NetLogSrvCallback( |
|
event, |
|
databuf, |
|
buildId, |
|
addr, |
|
msg.timestamp, |
|
productId, |
|
buildType |
|
); |
|
} |
|
} |
|
else |
|
{ |
|
LogMsg(kLogError, "Unable to log event - event not found. type: %d from server: %d. If it is a new event the log server needs to be updated.", msg.eventType, srvType); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Module exports |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
void NetLogSrvInitialize () { |
|
s_running = true; |
|
AsyncSocketRegisterNotifyProc( |
|
kConnTypeSrvToLog, |
|
SocketNotifyProc, |
|
0, // Accept all buildIds |
|
0, // Accept all buildTypes |
|
0, // Accept all branchIds |
|
0 // Accept all product Ids |
|
); |
|
IniChangeAdd(L"Log", IniChangeCallback, &s_change); |
|
} |
|
|
|
//============================================================================ |
|
void NetLogSrvShutdown () { |
|
if(s_change) { |
|
IniChangeRemove(s_change, true); |
|
s_change = false; |
|
} |
|
|
|
s_running = false; |
|
AsyncSocketUnregisterNotifyProc( |
|
kConnTypeSrvToLog, |
|
SocketNotifyProc, |
|
0, // Accept all buildIds |
|
0, |
|
0, // Accept all branchIds |
|
0 |
|
); |
|
} |
|
|
|
//============================================================================ |
|
void NetLogSrvDestroy () { |
|
|
|
while(s_perf[kNlSrvPerfConnCount]) |
|
AsyncSleep(10); |
|
} |
|
|
|
//============================================================================ |
|
void NetLogSrvRegisterCallback( void (*NlSrvCallback)(const NetLogEvent *, const ARRAY(wchar_t) &, unsigned, NetAddressNode &, uint64_t, unsigned, unsigned )) { |
|
NetLogSrvCallback = NlSrvCallback; |
|
} |
|
|
|
//============================================================================ |
|
void LogConnIncRef (LogConn * conn) { |
|
conn->IncRef(); |
|
} |
|
|
|
//============================================================================ |
|
void LogConnDecRef (LogConn * conn) { |
|
conn->DecRef(); |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Public exports |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
long NetLogSrvGetPerf (unsigned index) { |
|
ASSERT(index < kNlSrvNumPerf); |
|
return s_perf[index]; |
|
}
|
|
|