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.
359 lines
11 KiB
359 lines
11 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/PubUtilLib/plNetGameLib/Private/plNglTrans.cpp |
|
* |
|
***/ |
|
|
|
#include "../Pch.h" |
|
#pragma hdrstop |
|
|
|
|
|
namespace Ngl { |
|
/***************************************************************************** |
|
* |
|
* Private |
|
* |
|
***/ |
|
|
|
enum { |
|
kPerfCurrTransactions, |
|
kNumPerf |
|
}; |
|
|
|
static const unsigned kDefaultTimeoutMs = 5 * 60 * 1000; |
|
|
|
static bool s_running; |
|
static CCritSect s_critsect; |
|
static LISTDECL(NetTrans, m_link) s_transactions; |
|
static long s_perf[kNumPerf]; |
|
static unsigned s_timeoutMs = kDefaultTimeoutMs; |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Internal functions |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
static NetTrans * FindTransIncRef_CS (unsigned transId, const char tag[]) { |
|
// There shouldn't be more than a few transactions; just do a linear scan |
|
for (NetTrans * trans = s_transactions.Head(); trans; trans = s_transactions.Next(trans)) |
|
if (trans->m_transId == transId) { |
|
trans->IncRef(tag); |
|
return trans; |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
//============================================================================ |
|
static NetTrans * FindTransIncRef (unsigned transId, const char tag[]) { |
|
NetTrans * trans; |
|
s_critsect.Enter(); |
|
{ |
|
trans = FindTransIncRef_CS(transId, tag); |
|
} |
|
s_critsect.Leave(); |
|
return trans; |
|
} |
|
|
|
//============================================================================ |
|
static void CancelTrans_CS (NetTrans * trans, ENetError error) { |
|
ASSERT(IS_NET_ERROR(error)); |
|
if (trans->m_state != kTransStateComplete) { |
|
trans->m_result = error; |
|
trans->m_state = kTransStateComplete; |
|
} |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* NetTrans implementation |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
NetTrans::NetTrans (ENetProtocol protocol, ETransType transType) |
|
: m_state(kTransStateWaitServerConnect) |
|
, m_result(kNetPending) |
|
, m_transId(0) |
|
, m_connId(0) |
|
, m_protocol(protocol) |
|
, m_hasSubTrans(false) |
|
, m_timeoutAtMs(0) |
|
, m_transType(transType) |
|
{ |
|
AtomicAdd(&s_perf[kPerfCurrTransactions], 1); |
|
AtomicAdd(&s_perfTransCount[m_transType], 1); |
|
// DebugMsg("%s@%p created", s_transTypes[m_transType], this); |
|
} |
|
|
|
//============================================================================ |
|
NetTrans::~NetTrans () { |
|
ASSERT(!m_link.IsLinked()); |
|
AtomicAdd(&s_perfTransCount[m_transType], -1); |
|
AtomicAdd(&s_perf[kPerfCurrTransactions], -1); |
|
// DebugMsg("%s@%p destroyed", s_transTypes[m_transType], this); |
|
} |
|
|
|
//============================================================================ |
|
bool NetTrans::CanStart () const { |
|
switch (m_protocol) { |
|
case kNetProtocolCli2Auth: return AuthQueryConnected(); |
|
case kNetProtocolCli2Game: return GameQueryConnected(); |
|
case kNetProtocolCli2File: return FileQueryConnected(); |
|
case kNetProtocolCli2GateKeeper: return GateKeeperQueryConnected(); |
|
DEFAULT_FATAL(m_protocol); |
|
} |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
* |
|
* Module functions |
|
* |
|
***/ |
|
|
|
//============================================================================ |
|
void NetTransInitialize () { |
|
s_critsect.Enter(); |
|
{ |
|
s_running = true; |
|
} |
|
s_critsect.Leave(); |
|
} |
|
|
|
//============================================================================ |
|
void NetTransDestroy (bool wait) { |
|
s_critsect.Enter(); |
|
{ |
|
s_running = false; |
|
} |
|
s_critsect.Leave(); |
|
|
|
NetTransCancelAll(kNetErrRemoteShutdown); |
|
|
|
if (!wait) |
|
return; |
|
|
|
while (s_perf[kPerfCurrTransactions]) { |
|
NetTransUpdate(); |
|
AsyncSleep(10); |
|
} |
|
} |
|
|
|
//============================================================================ |
|
void NetTransSetTimeoutMs (unsigned ms) { |
|
s_timeoutMs = ms ? ms : kDefaultTimeoutMs; |
|
} |
|
|
|
//============================================================================ |
|
unsigned NetTransGetTimeoutMs () { |
|
return s_timeoutMs; |
|
} |
|
|
|
//============================================================================ |
|
void NetTransSend (NetTrans * trans) { |
|
trans->IncRef("Lifetime"); |
|
s_critsect.Enter(); |
|
{ |
|
static unsigned s_transId; |
|
while (!trans->m_transId) |
|
trans->m_transId = ++s_transId; |
|
s_transactions.Link(trans, kListTail); |
|
if (!s_running) |
|
CancelTrans_CS(trans, kNetErrRemoteShutdown); |
|
} |
|
s_critsect.Leave(); |
|
} |
|
|
|
//============================================================================ |
|
bool NetTransRecv (unsigned transId, const uint8_t msg[], unsigned bytes) { |
|
NetTrans * trans = FindTransIncRef(transId, "Recv"); |
|
|
|
if (!trans) |
|
return true; // transaction was canceled. |
|
|
|
// Update the timeout time |
|
trans->m_timeoutAtMs = TimeGetMs() + s_timeoutMs; |
|
|
|
bool result = trans->Recv(msg, bytes); |
|
|
|
if (!result) |
|
NetTransCancel(transId, kNetErrInternalError); |
|
|
|
trans->DecRef("Recv"); |
|
return result; |
|
} |
|
|
|
//============================================================================ |
|
void NetTransCancel (unsigned transId, ENetError error) { |
|
s_critsect.Enter(); |
|
{ |
|
NetTrans * trans = s_transactions.Head(); |
|
for (; trans; trans = trans->m_link.Next()) { |
|
if (trans->m_transId == transId) { |
|
CancelTrans_CS(trans, error); |
|
break; |
|
} |
|
} |
|
} |
|
s_critsect.Leave(); |
|
} |
|
|
|
//============================================================================ |
|
void NetTransCancelByProtocol (ENetProtocol protocol, ENetError error) { |
|
s_critsect.Enter(); |
|
{ |
|
NetTrans * trans = s_transactions.Head(); |
|
for (; trans; trans = trans->m_link.Next()) { |
|
if (trans->m_protocol == protocol) |
|
CancelTrans_CS(trans, error); |
|
} |
|
} |
|
s_critsect.Leave(); |
|
} |
|
|
|
//============================================================================ |
|
void NetTransCancelByConnId (unsigned connId, ENetError error) { |
|
s_critsect.Enter(); |
|
{ |
|
NetTrans * trans = s_transactions.Head(); |
|
for (; trans; trans = trans->m_link.Next()) { |
|
if (trans->m_connId == connId) |
|
CancelTrans_CS(trans, error); |
|
} |
|
} |
|
s_critsect.Leave(); |
|
} |
|
|
|
//============================================================================ |
|
void NetTransCancelAll (ENetError error) { |
|
s_critsect.Enter(); |
|
{ |
|
NetTrans * trans = s_transactions.Head(); |
|
for (; trans; trans = trans->m_link.Next()) |
|
CancelTrans_CS(trans, error); |
|
} |
|
s_critsect.Leave(); |
|
} |
|
|
|
//============================================================================ |
|
void NetTransUpdate () { |
|
LISTDECL(NetTrans, m_link) completed; |
|
LISTDECL(NetTrans, m_link) parentCompleted; |
|
|
|
s_critsect.Enter(); |
|
NetTrans * next, * trans = s_transactions.Head(); |
|
for (; trans; trans = next) { |
|
next = s_transactions.Next(trans); |
|
|
|
bool done = false; |
|
while (!done) { |
|
switch (trans->m_state) { |
|
case kTransStateComplete: |
|
if (trans->m_hasSubTrans) |
|
parentCompleted.Link(trans); |
|
else |
|
completed.Link(trans); |
|
done = true; |
|
break; |
|
|
|
case kTransStateWaitServerConnect: |
|
if (!trans->CanStart()) { |
|
done = true; |
|
break; |
|
} |
|
if (trans->m_protocol && 0 == (trans->m_connId = ConnGetId(trans->m_protocol))) { |
|
done = true; |
|
break; |
|
} |
|
// This is the default "next state", trans->Send() can override this |
|
trans->m_state = kTransStateWaitServerResponse; |
|
// Set timeout time before calling Send(), allowing Send() to change it if it wants to. |
|
trans->m_timeoutAtMs = TimeGetMs() + s_timeoutMs; |
|
if (!trans->Send()) { |
|
// Revert back to current state so that we'll attempt to send again |
|
trans->m_state = kTransStateWaitServerConnect; |
|
done = true; |
|
break; |
|
} |
|
break; |
|
|
|
case kTransStateWaitServerResponse: |
|
// Check for timeout |
|
if ((int)(TimeGetMs() - trans->m_timeoutAtMs) > 0) { |
|
// Check to see if the transaction wants to "abort" the timeout |
|
if (trans->TimedOut()) |
|
CancelTrans_CS(trans, kNetErrTimeout); |
|
else |
|
trans->m_timeoutAtMs = TimeGetMs() + s_timeoutMs; // Reset the timeout counter |
|
} |
|
done = true; |
|
break; |
|
|
|
DEFAULT_FATAL(trans->m_state); |
|
} |
|
} |
|
} |
|
s_critsect.Leave(); |
|
|
|
// Post completed transactions |
|
while (NetTrans * trans = completed.Head()) { |
|
completed.Unlink(trans); |
|
trans->Post(); |
|
trans->DecRef("Lifetime"); |
|
} |
|
// Post completed parent transactions |
|
while (NetTrans * trans = parentCompleted.Head()) { |
|
parentCompleted.Unlink(trans); |
|
trans->Post(); |
|
trans->DecRef("Lifetime"); |
|
} |
|
} |
|
|
|
|
|
} // namespace Ngl
|
|
|