/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include //rcg10242001 #include #include #include #include #include #include #include #include #include // define this to randomly drop messages (for debugging of packet-loss recovery) //#define LOSEPACKETS_THRESHOLD (RAND_MAX/10) extern INDEX net_bReportTraffic; extern BOOL _bTempNetwork; ///////////////////////////////////////////////////////////////////// // CMessageBuffer helper class+implementation // class for holding received messages (thread locally) class CMessageBuffer { public: // size of message buffer for one message ULONG mb_ulMessageBufferSize; // pointer to message buffer for one message void *mb_pvMessageBuffer; void Allocate(void); void Free(void); }; // the thread's local buffer THREADLOCAL(CMessageBuffer, mbReceivedMessage, CMessageBuffer()); void CMessageBuffer::Allocate(void) { if (mb_ulMessageBufferSize==0) { ASSERT(mb_pvMessageBuffer == NULL); // allocate message buffer mb_ulMessageBufferSize = 16000; mb_pvMessageBuffer = AllocMemory(mb_ulMessageBufferSize); } } void CMessageBuffer::Free(void) { // if message buffer is allocated if (mb_ulMessageBufferSize != 0) { ASSERT(mb_pvMessageBuffer != NULL); // free it FreeMemory(mb_pvMessageBuffer); mb_ulMessageBufferSize = 0; mb_pvMessageBuffer = NULL; } } ///////////////////////////////////////////////////////////////////// // CNetworkProvider implementation /* * Default constructor. */ CNetworkProvider::CNetworkProvider(void) { } /* * Create a human description of driver. */ const CTString &CNetworkProvider::GetDescription(void) const { return np_Description; } ///////////////////////////////////////////////////////////////////// // CNetworkSession implementation /* * Default constructor. */ CNetworkSession::CNetworkSession(void) { } /* Construct a session for connecting to certain server. */ CNetworkSession::CNetworkSession(const CTString &strAddress) { ns_strAddress = strAddress; } void CNetworkSession::Copy(const CNetworkSession &nsOriginal) { ns_strAddress = nsOriginal.ns_strAddress ; ns_strSession = nsOriginal.ns_strSession ; ns_strWorld = nsOriginal.ns_strWorld ; ns_tmPing = nsOriginal.ns_tmPing ; ns_ctPlayers = nsOriginal.ns_ctPlayers ; ns_ctMaxPlayers = nsOriginal.ns_ctMaxPlayers ; ns_strGameType = nsOriginal.ns_strGameType ; ns_strMod = nsOriginal.ns_strMod ; ns_strVer = nsOriginal.ns_strVer ; } ///////////////////////////////////////////////////////////////////// // CMessageDispatcher -- construction/destruction /* * Default constructor. */ CMessageDispatcher::CMessageDispatcher(void) { if (!_bTempNetwork) { _cmiComm.Init(); } // enumerate network providers EnumNetworkProviders_startup(md_lhProviders); } /* * Destructor. */ CMessageDispatcher::~CMessageDispatcher(void) { if (!_bTempNetwork) { _cmiComm.Close(); } // destroy the list of network providers FORDELETELIST(CNetworkProvider, np_Node, md_lhProviders, litProviders) { delete &*litProviders; } } /* * Initialize for a given game. */ void CMessageDispatcher::Init(const CTString &strGameID) { md_strGameID = strGameID; } ///////////////////////////////////////////////////////////////////// // CMessageDispatcher -- network provider management /* * Enumerate all providers at startup (later enumeration just copies this list). */ void CMessageDispatcher::EnumNetworkProviders_startup(CListHead &lh) { // create local connection provider CNetworkProvider *pnpLocal = new CNetworkProvider; pnpLocal->np_Description = "Local"; lh.AddTail(pnpLocal->np_Node); // create TCP/IP connection provider CNetworkProvider *pnpTCP = new CNetworkProvider; pnpTCP->np_Description = "TCP/IP Server"; lh.AddTail(pnpTCP->np_Node); CNetworkProvider *pnpTCPCl = new CNetworkProvider; pnpTCPCl->np_Description = "TCP/IP Client"; lh.AddTail(pnpTCPCl->np_Node); } /* * Enumerate all providers. */ void CMessageDispatcher::EnumNetworkProviders(CListHead &lh) { // for each provider enumerated at startup FOREACHINLIST(CNetworkProvider, np_Node, md_lhProviders, litProvider) { // create a copy CNetworkProvider *pnpNew = new CNetworkProvider(*litProvider); // add the copy to the list lh.AddTail(pnpNew->np_Node); } } /* * Start using a service provider. */ void CMessageDispatcher::StartProvider_t(const CNetworkProvider &npProvider) { if (npProvider.np_Description=="Local") { _cmiComm.PrepareForUse(FALSE, FALSE); } else if (npProvider.np_Description=="TCP/IP Server") { _cmiComm.PrepareForUse(TRUE, FALSE); } else { _cmiComm.PrepareForUse(TRUE, TRUE); } } /* * Stop using current service provider. */ void CMessageDispatcher::StopProvider(void) { _cmiComm.Unprepare(); } ///////////////////////////////////////////////////////////////////// // CMessageDispatcher -- network message management static void UpdateSentMessageStats(const CNetworkMessage &nmMessage) { // increment profile counters _pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_MESSAGESSENT); _pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_BYTESSENT, nmMessage.nm_slSize); switch (nmMessage.GetType()) { case MSG_GAMESTREAMBLOCKS: _pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_GAMESTREAM_BYTES_SENT, nmMessage.nm_slSize); break; case MSG_ACTION: _pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_ACTION_BYTES_SENT, nmMessage.nm_slSize); break; } if (net_bReportTraffic) { CPrintF("Sent: %d\n", nmMessage.nm_slSize); } } static void UpdateSentStreamStats(SLONG slSize) { if (net_bReportTraffic) { CPrintF("STREAM Sent: %d\n", slSize); } } static void UpdateReceivedMessageStats(const CNetworkMessage &nmMessage) { // increment profile counters _pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_MESSAGESRECEIVED); _pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_BYTESRECEIVED, nmMessage.nm_slSize); switch (nmMessage.GetType()) { case MSG_GAMESTREAMBLOCKS: _pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_GAMESTREAM_BYTES_RECEIVED, nmMessage.nm_slSize); break; case MSG_ACTION: _pfNetworkProfile.IncrementCounter(CNetworkProfile::PCI_ACTION_BYTES_RECEIVED, nmMessage.nm_slSize); break; } if (net_bReportTraffic) { CPrintF("Rcvd: %d\n", nmMessage.nm_slSize); } } static void UpdateReceivedStreamStats(SLONG slSize) { if (net_bReportTraffic) { CPrintF("STREAM Rcvd: %d\n", slSize); } } /* Send a message from server to client. */ void CMessageDispatcher::SendToClient(INDEX iClient, const CNetworkMessage &nmMessage) { // if testing for packet-loss recovery #ifdef LOSEPACKETS_THRESHOLD // every once a while if (rand()