diff --git a/source/network/NetClient.cpp b/source/network/NetClient.cpp index c4a416d8ff..cbdd197727 100644 --- a/source/network/NetClient.cpp +++ b/source/network/NetClient.cpp @@ -25,20 +25,22 @@ // INCLUDES #include "precompiled.h" + #include "NetClient.h" + #include "NetJsEvents.h" #include "Network.h" #include "NetServer.h" + #include "scripting/DOMEvent.h" #include "scripting/JSConversions.h" #include "scripting/ScriptableObject.h" -#include "ps/CStr.h" -#include "ps/CLogger.h" #include "ps/CConsole.h" +#include "ps/CLogger.h" +#include "ps/CStr.h" #include "ps/Game.h" #include "ps/Globals.h" #include "ps/GameAttributes.h" -#include "simulation/Simulation.h" #include "simulation2/Simulation2.h" // DECLARATIONS @@ -81,8 +83,8 @@ void CServerPlayer::ScriptingInit( void ) // Name: CNetClient() // Desc: Constructor //----------------------------------------------------------------------------- -CNetClient::CNetClient( CGame* pGame, CGameAttributes* pGameAttribs ) -: m_JsPlayers( &m_Players ) +CNetClient::CNetClient( ScriptInterface& scriptInterface, CGame* pGame, CGameAttributes* pGameAttribs ) +: CNetHost( scriptInterface ), m_JsPlayers( &m_Players ) { m_ClientTurnManager = NULL; @@ -167,8 +169,7 @@ bool CNetClient::SetupSession( CNetSession* pSession ) // Validate parameters if ( !pSession ) return false; - FsmActionCtx* pContext = new FsmActionCtx; // XXX: this gets leaked - if ( !pContext ) return false; + FsmActionCtx* pContext = pSession->GetFsmActionCtx(); pContext->pHost = this; pContext->pSession = pSession; @@ -291,7 +292,7 @@ void CNetClient::OnConnectComplete( ) { if ( m_OnConnectComplete.Defined() ) { - CConnectCompleteEvent connectComplete( ( CStrW )PS_OK, true ); + CConnectCompleteEvent connectComplete( "OK", true ); m_OnConnectComplete.DispatchEvent( GetScript(), &connectComplete ); } } diff --git a/source/network/NetClient.h b/source/network/NetClient.h index cb5499335c..2b9005a037 100644 --- a/source/network/NetClient.h +++ b/source/network/NetClient.h @@ -28,6 +28,7 @@ // INCLUDES #include "NetSession.h" +#include "NetHost.h" #include "NetTurnManager.h" #include "ps/CStr.h" #include "scripting/ScriptObject.h" @@ -80,7 +81,7 @@ class CNetClient: public CNetHost, public: - CNetClient( CGame* pGame, CGameAttributes* pGameAttributes ); + CNetClient( ScriptInterface& scriptInterface, CGame* pGame, CGameAttributes* pGameAttributes ); ~CNetClient( void ); bool CreateSession ( void ); diff --git a/source/network/NetHost.cpp b/source/network/NetHost.cpp new file mode 100644 index 0000000000..3e24bad7ba --- /dev/null +++ b/source/network/NetHost.cpp @@ -0,0 +1,467 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include "precompiled.h" +#include "NetHost.h" +#include "NetSession.h" +#include "NetMessage.h" + +#include "ps/CLogger.h" +#include "simulation2/Simulation2.h" + +#include + +static const int ENET_DEFAULT_CHANNEL = 0; +static const int CONNECT_TIMEOUT = 5000; +static const int DISCONNECT_TIMEOUT = 1000; + +//----------------------------------------------------------------------------- +// Name: CNetHost() +// Desc: Constructor +//----------------------------------------------------------------------------- +CNetHost::CNetHost(ScriptInterface& scriptInterface) : + m_ScriptInterface(scriptInterface) +{ + m_Host = NULL; +} + +//----------------------------------------------------------------------------- +// Name: ~CNetHost() +// Desc: Destructor +//----------------------------------------------------------------------------- +CNetHost::~CNetHost() +{ +// Shutdown(); // TODO: should do something like this except don't call HandleDisconnect() +} + +//----------------------------------------------------------------------------- +// Name: Create() +// Desc: Creates a client host +//----------------------------------------------------------------------------- +bool CNetHost::Create() +{ + debug_assert(!m_Host); + + // Create ENet host + m_Host = enet_host_create(NULL, 1, 0, 0); + if (!m_Host) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Name: Create() +// Desc: Creates a server host +//----------------------------------------------------------------------------- +bool CNetHost::Create(uint port, uint maxPeers) +{ + ENetAddress addr; + + // Bind to default host + addr.host = ENET_HOST_ANY; + addr.port = port; + + // Create ENet server + m_Host = enet_host_create(&addr, maxPeers, 0, 0); + if (!m_Host) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Name: Shutdown() +// Desc: Shuts down network server and releases any resources +//----------------------------------------------------------------------------- +void CNetHost::Shutdown() +{ + // Disconnect and release each peer + PeerSessionList::iterator it = m_PeerSessions.begin(); + for (; it != m_PeerSessions.end(); it++) + { + if (!it->pSession) + continue; + + Disconnect(it->pSession); + + delete it->pSession; + } + + m_PeerSessions.clear(); + + // Destroy server + if (m_Host) + enet_host_destroy(m_Host); + + m_Host = NULL; +} + +//----------------------------------------------------------------------------- +// Name: Connect() +// Desc: Connects to the specified remote host +// Note: Only clients use this method for connection to server +//----------------------------------------------------------------------------- +bool CNetHost::Connect(const CStr& host, uint port) +{ + debug_assert(m_Host); + + // Bind to specified host + ENetAddress addr; + addr.port = port; + if (enet_address_set_host(&addr, host.c_str()) < 0) + return false; + + // Initiate connection, allocate one channel + ENetPeer* pPeer = enet_host_connect(m_Host, &addr, 1); + if (!pPeer) + return false; + + // Wait a few seconds for the connection to succeed + // TODO: we ought to poll asynchronously so we can update the GUI while waiting + ENetEvent event; + if (enet_host_service(m_Host, &event, CONNECT_TIMEOUT) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) + { + // Connection succeeded + CNetSession* pNewSession = new CNetSession(this, event.peer); + + if (!SetupSession(pNewSession)) + { + delete pNewSession; + return false; + } + + LOGMESSAGE(L"Net: Successfully connected to server %hs:%d", host.c_str(), port); + + // Successfully handled? + if (!HandleConnect(pNewSession)) + { + delete pNewSession; + return false; + } + + // Store the only server session + PeerSession item; + item.pPeer = event.peer; + item.pSession = pNewSession; + m_PeerSessions.push_back(item); + + return true; + } + + LOGERROR(L"Net: Connection to server %hs:%d failed", host.c_str(), port); + + // Timed out or a host was disconnected + enet_peer_reset(pPeer); + + return false; +} + +//----------------------------------------------------------------------------- +// Name: ConnectAsync() +// Desc: Connects to the specified remote host +// Note: Only clients use this method for connection to server +//----------------------------------------------------------------------------- +bool CNetHost::ConnectAsync(const CStr& host, uint port) +{ + debug_assert(m_Host); + + // Bind to specified host + ENetAddress addr; + addr.port = port; + if (enet_address_set_host(&addr, host.c_str()) < 0) + return false; + + // Initiate connection, allocate one channel + ENetPeer* pPeer = enet_host_connect(m_Host, &addr, 1); + if (!pPeer) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Name: Disconnect() +// Desc: Disconnects the specified session from the host +//----------------------------------------------------------------------------- +bool CNetHost::Disconnect(CNetSession* pSession) +{ + // Validate parameters + if (!pSession) + return false; + + debug_assert(m_Host); + debug_assert(pSession->m_Peer); + + // Disconnect peer + enet_peer_disconnect(pSession->m_Peer, 0); + + // Allow a few seconds for the disconnect to succeed + ENetEvent event; + while (enet_host_service(m_Host, &event, DISCONNECT_TIMEOUT) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_RECEIVE: + + // Drop any received packets + enet_packet_destroy(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + + // Disconnect received for peer + if (!HandleDisconnect(pSession)) + return false; + break; + } + } + + // Disconnect attempt didn't succeed, force connection down + enet_peer_reset(pSession->m_Peer); + + return true; +} + +//----------------------------------------------------------------------------- +// Name: ProcessEvents() +// Desc: Wait for events and shuttles packets between the host and its peers +//----------------------------------------------------------------------------- +void CNetHost::Poll() +{ + debug_assert(m_Host); + + // Poll host for events + ENetEvent event; + while (enet_host_service(m_Host, &event, 0) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + { + // A new client has connected, handle it + CNetSession* pSession = new CNetSession(this, event.peer); + + // Setup new session + if (!SetupSession(pSession)) + { + delete pSession; + break; + } + + LOGMESSAGE(L"Net: A new client connected from %x:%u", event.peer->address.host, event.peer->address.port); + + // Successfully handled? + if (!HandleConnect(pSession)) + { + delete pSession; + break; + } + + event.peer->data = pSession; + + // Add new item to internal list + PeerSession item; + item.pPeer = event.peer; + item.pSession = pSession; + m_PeerSessions.push_back( item ); + + break; + } + + case ENET_EVENT_TYPE_DISCONNECT: + { + // Client has disconnected, handle it + PeerSessionList::iterator it = m_PeerSessions.begin(); + for (; it != m_PeerSessions.end(); it++) + { + // Is this our session? + if (it->pPeer == event.peer) + { + LOGMESSAGE(L"Net: %p disconnected", event.peer->data); + + // Successfully handled? + if (HandleDisconnect(it->pSession)) + m_PeerSessions.erase(it); + } + } + + break; + } + + case ENET_EVENT_TYPE_RECEIVE: + { + // A new data packet was received from client, handle message + PeerSessionList::iterator it = m_PeerSessions.begin(); + for (; it != m_PeerSessions.end(); it++) + { + // Is this our session? + if (it->pPeer == event.peer) + { + bool ok = false; + + // Create message from raw data + CNetMessage* pNewMessage = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, m_ScriptInterface); + if (pNewMessage) + { + LOGMESSAGE(L"Message %hs of size %lu was received from %p", pNewMessage->ToString().c_str(), (unsigned long)pNewMessage->GetSerializedLength(), event.peer->data); + + ok = HandleMessageReceive(pNewMessage, it->pSession); + + delete pNewMessage; + } + + // Done using the packet + enet_packet_destroy(event.packet); + + // TODO: what should we do if ok is false? + // For now, just carry on as if nothing bad happened + } + } + + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Name: Broadcast() +// Desc: Broadcast the specified message to connected clients +//----------------------------------------------------------------------------- +void CNetHost::Broadcast(const CNetMessage* pMessage) +{ + // Validate parameters + if (!pMessage) + return; + + // Loop through the list of sessions and send the message to each + for (uint i = 0; i < GetSessionCount(); i++) + { + CNetSession* pCurrSession = GetSession(i); + if (!pCurrSession) + continue; + + SendMessage(pCurrSession, pMessage); + } +} + +//----------------------------------------------------------------------------- +// Name: SendMessage() +// Desc: Sends the specified message to peer +//----------------------------------------------------------------------------- +bool CNetHost::SendMessage(const CNetSession* pSession, const CNetMessage* pMessage) +{ + // Validate parameters + if (!pMessage || !pSession) + return false; + + debug_assert(pSession->m_Peer); + debug_assert(m_Host); + + size_t size = pMessage->GetSerializedLength(); + + debug_assert(size); // else we'll fail when accessing the 0th element + + // Adjust buffer for message + std::vector buffer; + buffer.resize(size); + + // Save message to internal buffer + pMessage->Serialize(&buffer[0]); + + // Create a reliable packet + ENetPacket* pPacket = enet_packet_create(&buffer[0], size, ENET_PACKET_FLAG_RELIABLE); + if (!pPacket) + return false; + + // Let ENet send the message to peer + if (enet_peer_send(pSession->m_Peer, ENET_DEFAULT_CHANNEL, pPacket) < 0) + { + // ENet failed to send the packet + LOGERROR(L"Net: Failed to send ENet packet to peer"); + + return false; + } + else + { + LOGMESSAGE(L"Net: Message %hs of size %lu was sent to %p", + pMessage->ToString().c_str(), (unsigned long)size, pSession->m_Peer->data); + } + + // Don't call enet_host_flush - let it queue up all the packets + // and send them during the next frame + + return true; +} + +//----------------------------------------------------------------------------- +// Name: ReceiveMessage() +// Desc: Receives a message from client if incoming packets are available +//----------------------------------------------------------------------------- +CNetMessage* CNetHost::ReceiveMessage(const CNetSession* pSession) +{ + // Validate parameters + if (!pSession) + return NULL; + + debug_assert(pSession->m_Peer); + + // Let ENet receive a message from peer + ENetPacket* pPacket = enet_peer_receive(pSession->m_Peer, ENET_DEFAULT_CHANNEL); + if (!pPacket) + return NULL; + + // Create new message + return CNetMessageFactory::CreateMessage(pPacket->data, pPacket->dataLength, m_ScriptInterface); +} + +//----------------------------------------------------------------------------- +// Name: HandleMessageReceive() +// Desc: Allow application to handle message recive +//----------------------------------------------------------------------------- +bool CNetHost::HandleMessageReceive(CNetMessage* pMessage, CNetSession* pSession) +{ + // Validate parameters + if (!pSession || !pMessage) + return false; + + // Update FSM + return pSession->Update(pMessage->GetType(), pMessage); +} + +//----------------------------------------------------------------------------- +// Name: GetSessionCount() +// Desc: Returns the number of sessions the host manages +//----------------------------------------------------------------------------- +uint CNetHost::GetSessionCount() const +{ + return (uint)m_PeerSessions.size(); +} + +//----------------------------------------------------------------------------- +// Name: GetSession() +// Desc: Rteurns the session for the index +//----------------------------------------------------------------------------- +CNetSession* CNetHost::GetSession(uint index) +{ + // Validate parameter + if (index >= GetSessionCount()) + return NULL; + + return m_PeerSessions[index].pSession; +} diff --git a/source/network/NetHost.h b/source/network/NetHost.h new file mode 100644 index 0000000000..56655a0803 --- /dev/null +++ b/source/network/NetHost.h @@ -0,0 +1,147 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#ifndef NETHOST_H +#define NETHOST_H + +#include "fsm.h" + +#include "ps/CStr.h" + +#include + +typedef struct _ENetPeer ENetPeer; +typedef struct _ENetHost ENetHost; +class CNetSession; +class CNetHost; +class CNetMessage; +class ScriptInterface; + +struct PeerSession +{ + ENetPeer* pPeer; + CNetSession* pSession; +}; + +typedef std::vector PeerSessionList; + +/** + * Wrapper around ENet host concept + */ +class CNetHost +{ + NONCOPYABLE(CNetHost); + +public: + + CNetHost(ScriptInterface& scriptInterface); + virtual ~CNetHost(); + + bool Create(); + bool Create(uint port, uint maxPeers); + void Shutdown(); + + /** + * Indicates whether the host is currently a server + * + * @return Boolean indicating whether the host is a server + */ + virtual bool IsServer() const { return false; } + + /** + * Returns the number of sessions for the host + * + * @return The number of sessions + */ + uint GetSessionCount() const; + + /** + * Returns the session object for the specified index + * + * @param index Index for session + * @return Session object for index or NULL if not found + */ + CNetSession* GetSession(uint index); + + /** + * Connects to foreign host synchronously + * + * @param host Foreign host name + * @param port Port on which the foreign host listens + * @return true on success, false on failure + */ + bool Connect(const CStr& host, uint port); + + /** + * Connects to foreign host asynchronously (i.e. without waiting for the connection + * to succeed or to time out) + * + * @param host Foreign host name + * @param port Port on which the foreign host listens + * @return true on success, false on failure + */ + bool ConnectAsync(const CStr& host, uint port); + + /** + * Disconnects session from host + * + * @param pSession Session representing peer + * @return true on success, false otherwise + */ + bool Disconnect(CNetSession* pSession); + + /** + * Listens for incoming connections and dispatches host events + */ + void Poll(); + + /** + * Broadcast the specified message to connected clients + * + * @param pMessage Message to broadcast + */ + void Broadcast(const CNetMessage* pMessage); + + /** + * Send the specified message to client + * + * @param pMessage The message to send + */ + bool SendMessage(const CNetSession* pSession, const CNetMessage* pMessage); + +protected: + + // Allow application to handle new client connect + virtual bool SetupSession(CNetSession* pSession) = 0; + virtual bool HandleConnect(CNetSession* pSession) = 0; + virtual bool HandleDisconnect(CNetSession* pSession) = 0; + +private: + /** + * Receive a message from client if available + */ + CNetMessage* ReceiveMessage(const CNetSession* pSession); + + bool HandleMessageReceive(CNetMessage* pMessage, CNetSession* pSession); + + ScriptInterface& m_ScriptInterface; + + ENetHost* m_Host; // Represents this host + PeerSessionList m_PeerSessions; // Session list of connected peers +}; + +#endif // NETHOST_H diff --git a/source/network/NetLog.cpp b/source/network/NetLog.cpp deleted file mode 100644 index 4012840cbc..0000000000 --- a/source/network/NetLog.cpp +++ /dev/null @@ -1,1126 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -/** - *----------------------------------------------------------------------------- - * FILE : NetLog.cpp - * PROJECT : 0 A.D. - * DESCRIPTION : Network subsystem logging classes implementation - *----------------------------------------------------------------------------- - */ - -// INCLUDES -#include "precompiled.h" -#include "NetLog.h" -#include "ps/CConsole.h" -#include "lib/utf8.h" - -#include -#include -#include - -//----------------------------------------------------------------------------- -// Name: CNetLogEvent() -// Desc: Constructor -//----------------------------------------------------------------------------- -CNetLogEvent::CNetLogEvent( - LogLevel level, - const CStr& message, - const CStr& loggerName ) -{ - m_Level = level; - m_Message = message; - m_LoggerName = loggerName; -} - -//----------------------------------------------------------------------------- -// Name: ~CNetLogEvent() -// Desc: Destructor -//----------------------------------------------------------------------------- -CNetLogEvent::~CNetLogEvent( void ) -{ -} - -//----------------------------------------------------------------------------- -// Name: CNetLogSink() -// Desc: Constructor -//----------------------------------------------------------------------------- -CNetLogSink::CNetLogSink( void ) -{ - m_Active = false; -} - -//----------------------------------------------------------------------------- -// Name: ~CNetLogSink() -// Desc: Destructor -//----------------------------------------------------------------------------- -CNetLogSink::~CNetLogSink( void ) -{ -} - -//----------------------------------------------------------------------------- -// Name: SetName() -// Desc: Set the name for the sink -//----------------------------------------------------------------------------- -void CNetLogSink::SetName( const CStr& name ) -{ - m_Name = name; -} - -//----------------------------------------------------------------------------- -// Name: SetLevel() -// Desc: Set the level for the sink -//----------------------------------------------------------------------------- -void CNetLogSink::SetLevel( LogLevel level ) -{ - m_Level = level; -} - -//----------------------------------------------------------------------------- -// Name: SetHeader() -// Desc: Set new header text -//----------------------------------------------------------------------------- -void CNetLogSink::SetHeader( const CStr& header ) -{ - m_Header = header; -} - -//----------------------------------------------------------------------------- -// Name: SetFooter() -// Desc: Set new footer text -//----------------------------------------------------------------------------- -void CNetLogSink::SetFooter( const CStr& footer ) -{ - m_Footer = footer; -} - -//----------------------------------------------------------------------------- -// Name: Activate() -// Desc: Activates the sink -//----------------------------------------------------------------------------- -void CNetLogSink::Activate( void ) -{ - CScopeLock lock( m_Mutex ); - - if ( !m_Active ) - { - OnActivate(); - - m_Active = true; - } -} - -//----------------------------------------------------------------------------- -// Name: Close() -// Desc: Closes the sink -//----------------------------------------------------------------------------- -void CNetLogSink::Close( void ) -{ - CScopeLock lock( m_Mutex ); - - OnClose(); -} - -//----------------------------------------------------------------------------- -// Name: DoSink() -// Desc: Perform event logging -//----------------------------------------------------------------------------- -void CNetLogSink::DoSink( const CNetLogEvent& event ) -{ - CScopeLock lock( m_Mutex ); - - // Not activated? Nothing to log - if ( !m_Active ) return; - - if ( TestEvent( event ) ) - { - Sink( event ); - } -} - -//----------------------------------------------------------------------------- -// Name: DoBulkSink() -// Desc: Perform logging for a list of events -//----------------------------------------------------------------------------- -void CNetLogSink::DoBulkSink( const CNetLogEvent* pEvents, size_t eventCount ) -{ - size_t* pIndices = NULL; - size_t indexCount = 0; - size_t i; - - CScopeLock lock( m_Mutex ); - - // Validate parameters - if ( !pEvents ) return; - - // Not activated? Nothing to log - if ( m_Closed ) return; - - // Allocate new array which will store the events that will be logged - pIndices = new size_t[ eventCount ]; - if ( !pIndices ) - { - throw std::bad_alloc(); - return; - } - - // Filter each event and store the index for - // those passing the filter test - for ( i = 0; i < eventCount; i++ ) - { - if ( TestEvent( pEvents[ i ] ) ) - { - pIndices[ indexCount++ ] = i; - } - } - - // Log each event - for ( i = 0; i < indexCount; i++ ) - { - Sink( pEvents[ pIndices[ i ] ] ); - } - - delete [] pIndices; -} - -//----------------------------------------------------------------------------- -// Name: CNetLogSink() -// Desc: Test if the event can be logged -//----------------------------------------------------------------------------- -bool CNetLogSink::TestEvent( const CNetLogEvent& event ) -{ - return ( event.GetLevel() >= m_Level ); -} - -//----------------------------------------------------------------------------- -// Name: WriteHeader() -// Desc: Writes a header -//----------------------------------------------------------------------------- -void CNetLogSink::WriteHeader( void ) -{ - if ( !m_Header.empty() ) - { - Write( m_Header ); - } -} - -//----------------------------------------------------------------------------- -// Name: WriteFooter() -// Desc: Writes a footer -//----------------------------------------------------------------------------- -void CNetLogSink::WriteFooter( void ) -{ - if ( !m_Footer.empty() ) - { - Write( m_Footer ); - } -} - -//----------------------------------------------------------------------------- -// Name: OnActivate() -// Desc: Called on activation -//----------------------------------------------------------------------------- -void CNetLogSink::OnActivate( void ) -{ - // Does nothing by default -} - -//----------------------------------------------------------------------------- -// Name: OnClose() -// Desc: Called on sink closure -//----------------------------------------------------------------------------- -void CNetLogSink::OnClose( void ) -{ - // Does nothing by default -} - -//----------------------------------------------------------------------------- -// Name: CNetLogFileSink() -// Desc: Constructor -//----------------------------------------------------------------------------- -CNetLogFileSink::CNetLogFileSink( void ) -{ - // Get string time - CStr time; - CNetLogger::GetStringTime( time ); - - // Make relative path - fs::wpath path(psLogDir()/L"net_log"); - path /= CStrW(time)+L".txt"; - m_FileName = path.string(); - m_Append = true; -} - -//----------------------------------------------------------------------------- -// Name: CNetLogFileSink() -// Desc: Constructor -//----------------------------------------------------------------------------- -CNetLogFileSink::CNetLogFileSink( const fs::wpath& filename ) -{ - m_FileName = filename; - m_Append = true; -} - -//----------------------------------------------------------------------------- -// Name: CNetLogFileSink() -// Desc: Constructor -//----------------------------------------------------------------------------- -CNetLogFileSink::CNetLogFileSink( const fs::wpath& filename, bool append ) -{ - m_FileName = filename; - m_Append = append; -} - -//----------------------------------------------------------------------------- -// Name: ~CNetLogFileSink() -// Desc: Destructor -//----------------------------------------------------------------------------- -CNetLogFileSink::~CNetLogFileSink( void ) -{ -} - -//----------------------------------------------------------------------------- -// Name: Activate() -// Desc: Open the file which will be used for logging -//----------------------------------------------------------------------------- -void CNetLogFileSink::OnActivate( void ) -{ - OpenFile( m_FileName, m_Append ); - - WriteHeader(); -} - -//----------------------------------------------------------------------------- -// Name: OnClose() -// Desc: Closes the file used for logging -//----------------------------------------------------------------------------- -void CNetLogFileSink::OnClose( void ) -{ - WriteFooter(); - - CloseFile(); -} - -//----------------------------------------------------------------------------- -// Name: Sink() -// Desc: Log the event to file -//----------------------------------------------------------------------------- -void CNetLogFileSink::Sink( const CNetLogEvent& event ) -{ - Write( event.GetMessage() ); -} - -//----------------------------------------------------------------------------- -// Name: Write() -// Desc: Writes a message to log file -//----------------------------------------------------------------------------- -void CNetLogFileSink::Write( const CStr& message ) -{ - // File not opened? - if ( !m_File.is_open() ) - { - OpenFile( m_FileName, m_Append ); - - // If still not opened, ignore message - if ( !m_File.is_open() ) return; - } - - // Write message - if ( !message.empty() ) - { - m_File << message; - } -} - -//----------------------------------------------------------------------------- -// Name: Write() -// Desc: Writes a character to the file -//----------------------------------------------------------------------------- -void CNetLogFileSink::Write( char c ) -{ - // File not opened? - if ( !m_File.is_open() ) - { - OpenFile( m_FileName, m_Append ); - - // If still not opened, ignore character - if ( !m_File.is_open() ) return; - } - - // Write character - m_File << c; -} - -//----------------------------------------------------------------------------- -// Name: OpenFile() -// Desc: Open the file where the logging will output -//----------------------------------------------------------------------------- -void CNetLogFileSink::OpenFile( const fs::wpath& fileName, bool append ) -{ - // Close any open file - if ( m_File.is_open() ) m_File.close(); - - // Open the file and log start - m_File.open( utf8_from_wstring(fileName.string()).c_str(), append ? std::ios::app : std::ios::out ); - if ( !m_File.is_open() ) - { - // throw std::ios_base::failure - return; - } - - m_FileName = fileName; - m_Append = append; -} - -//----------------------------------------------------------------------------- -// Name: CloseFile() -// Desc: Closes the opened file -//----------------------------------------------------------------------------- -void CNetLogFileSink::CloseFile( void ) -{ - if ( m_File.is_open() ) - { - m_File.flush(); - m_File.close(); - } -} - -//----------------------------------------------------------------------------- -// Name: CNetLogConsoleSink() -// Desc: Constructor -//----------------------------------------------------------------------------- -CNetLogConsoleSink::CNetLogConsoleSink( void ) -{ -} - -//----------------------------------------------------------------------------- -// Name: ~CNetLogConsoleSink() -// Desc: Destructor -//----------------------------------------------------------------------------- -CNetLogConsoleSink::~CNetLogConsoleSink( void ) -{ -} - -//----------------------------------------------------------------------------- -// Name: Activate() -// Desc: Activates the game console -//----------------------------------------------------------------------------- -void CNetLogConsoleSink::OnActivate( void ) -{ - WriteHeader(); -} - -//----------------------------------------------------------------------------- -// Name: OnClose() -// Desc: Toggles off game console -//----------------------------------------------------------------------------- -void CNetLogConsoleSink::OnClose( void ) -{ - WriteFooter(); -} - -//----------------------------------------------------------------------------- -// Name: Sink() -// Desc: Log the event to file -//----------------------------------------------------------------------------- -void CNetLogConsoleSink::Sink( const CNetLogEvent& event ) -{ - Write( event.GetMessage() ); -} - -//----------------------------------------------------------------------------- -// Name: Write() -// Desc: Writes a message to game console -//----------------------------------------------------------------------------- -void CNetLogConsoleSink::Write( const CStr& message ) -{ - // Do we have a valid console? - if ( !g_Console ) return; - - // Write message - if ( !message.empty() ) - { - g_Console->InsertMessage( L"%ls", message.FromUTF8().c_str() ); - } -} - -//----------------------------------------------------------------------------- -// Name: Write() -// Desc: Writes a character to the file -//----------------------------------------------------------------------------- -void CNetLogConsoleSink::Write( char c ) -{ - // Do we have a valid console? - if ( !g_Console ) return; - - // Write character - Write( CStr( c ) ); -} - -//----------------------------------------------------------------------------- -// Name: CNetLogger() -// Desc: Constructor -//----------------------------------------------------------------------------- -CNetLogger::CNetLogger( const CStr& name ) -{ - m_Name = name; - m_Level = LOG_LEVEL_ALL; -} - -//----------------------------------------------------------------------------- -// Name: ~CNetLogger() -// Desc: Destructor -//----------------------------------------------------------------------------- -CNetLogger::~CNetLogger( void ) -{ -} - -//----------------------------------------------------------------------------- -// Name: SetLevel() -// Desc: Set logger level -//----------------------------------------------------------------------------- -void CNetLogger::SetLevel( LogLevel level ) -{ - m_Level = level; -} - -//----------------------------------------------------------------------------- -// Name: IsDebugEnabled() -// Desc: Check if enabled for DEBUG level -//----------------------------------------------------------------------------- -bool CNetLogger::IsDebugEnabled( void ) const -{ - return ( LOG_LEVEL_DEBUG >= m_Level ); -} - -//----------------------------------------------------------------------------- -// Name: IsErrorEnabled() -// Desc: Check if enabled for ERROR level -//----------------------------------------------------------------------------- -bool CNetLogger::IsErrorEnabled( void ) const -{ - return ( LOG_LEVEL_ERROR >= m_Level ); -} - -//----------------------------------------------------------------------------- -// Name: IsFatalEnabled() -// Desc: Check if enabled for FATAL level -//----------------------------------------------------------------------------- -bool CNetLogger::IsFatalEnabled( void ) const -{ - return ( LOG_LEVEL_FATAL >= m_Level ); -} - -//----------------------------------------------------------------------------- -// Name: IsInfoEnabled() -// Desc: Check if enabled for INFO level -//----------------------------------------------------------------------------- -bool CNetLogger::IsInfoEnabled( void ) const -{ - return ( LOG_LEVEL_INFO >= m_Level ); -} - -//----------------------------------------------------------------------------- -// Name: IsWarnEnabled() -// Desc: Check if enabled for WARN level -//----------------------------------------------------------------------------- -bool CNetLogger::IsWarnEnabled( void ) const -{ - return ( LOG_LEVEL_WARN >= m_Level ); -} - -//----------------------------------------------------------------------------- -// Name: Debug() -// Desc: Log a message with DEBUG level -//----------------------------------------------------------------------------- -void CNetLogger::Debug( const CStr& message ) -{ - if ( IsDebugEnabled() ) - { - // Get timestamp as a string - CStr timer; - GetStringTimeStamp( timer ); - - CStr eventMessage = "DEBUG"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += message; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_DEBUG, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Debug() -// Desc: Log a formatted message with DEBUG level -//----------------------------------------------------------------------------- -void CNetLogger::DebugFormat( const char* pFormat, ... ) -{ - if ( IsDebugEnabled() ) - { - char buffer[ 512 ] = { 0 }; - CStr timer; - va_list args; - - // Get timestamp as a string - GetStringTimeStamp( timer ); - - // Get arguments as a string - va_start ( args, pFormat ); - vsnprintf ( buffer, 512, pFormat, args ); - va_end ( args ); - - // Format message (e.g. ERROR - Hello World) - CStr eventMessage = "DEBUG"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += buffer; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_DEBUG, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Error() -// Desc: Log a message with ERROR level -//----------------------------------------------------------------------------- -void CNetLogger::Error( const CStr& message ) -{ - if ( IsErrorEnabled() ) - { - // Get timestamp as a string - CStr timer; - GetStringTimeStamp( timer ); - - CStr eventMessage = "ERROR"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += message; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_ERROR, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Error() -// Desc: Log a formatted message with ERROR level -//----------------------------------------------------------------------------- -void CNetLogger::ErrorFormat( const char* pFormat, ... ) -{ - if ( IsErrorEnabled() ) - { - char buffer[ 512 ] = { 0 }; - CStr timer; - va_list args; - - // Get timestamp as a string - GetStringTimeStamp( timer ); - - // Get arguments as a string - va_start ( args, pFormat ); - vsnprintf ( buffer, 512, pFormat, args ); - va_end ( args ); - - // Format message (e.g. ERROR - Hello World) - CStr eventMessage = "ERROR"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += buffer; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_ERROR, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Fatal() -// Desc: Log a message with FATAL level -//----------------------------------------------------------------------------- -void CNetLogger::Fatal( const CStr& message ) -{ - if ( IsFatalEnabled() ) - { - // Get timestamp as a string - CStr timer; - GetStringTimeStamp( timer ); - - CStr eventMessage = "FATAL"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += message; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_FATAL, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Fatal() -// Desc: Log a formatted message with FATAL error -//----------------------------------------------------------------------------- -void CNetLogger::FatalFormat( const char* pFormat, ... ) -{ - if ( IsFatalEnabled() ) - { - char buffer[ 512 ] = { 0 }; - CStr timer; - va_list args; - - // Get timestamp as a string - GetStringTimeStamp( timer ); - - // Get arguments as a string - va_start ( args, pFormat ); - vsnprintf ( buffer, 512, pFormat, args ); - va_end ( args ); - - // Format message (e.g. ERROR - Hello World) - CStr eventMessage = "FATAL"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += buffer; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_FATAL, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Info() -// Desc: Log a message with INFO level -//----------------------------------------------------------------------------- -void CNetLogger::Info( const CStr& message ) -{ - if ( IsInfoEnabled() ) - { - // Get timestamp as a string - CStr timer; - GetStringTimeStamp( timer ); - - CStr eventMessage = "INFO"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += message; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_INFO, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Info() -// Desc: Log a formatted message with INFO level -//----------------------------------------------------------------------------- -void CNetLogger::InfoFormat( const char* pFormat, ... ) -{ - if ( IsInfoEnabled() ) - { - char buffer[ 512 ] = { 0 }; - CStr timer; - va_list args; - - // Get timestamp as a string - GetStringTimeStamp( timer ); - - // Get arguments as a string - va_start ( args, pFormat ); - vsnprintf ( buffer, 512, pFormat, args ); - va_end ( args ); - - // Format message (e.g. ERROR - Hello World) - CStr eventMessage = "INFO"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += buffer; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_INFO, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Warn() -// Desc: Log a message with WARN level -//----------------------------------------------------------------------------- -void CNetLogger::Warn( const CStr& message ) -{ - if ( IsWarnEnabled() ) - { - // Get timestamp as a string - CStr timer; - GetStringTimeStamp( timer ); - - CStr eventMessage = "WARN"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += message; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_WARN, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: Warn() -// Desc: Log a formatted message with WARN level -//----------------------------------------------------------------------------- -void CNetLogger::WarnFormat( const char* pFormat, ... ) -{ - if ( IsWarnEnabled() ) - { - char buffer[ 512 ] = { 0 }; - CStr timer; - va_list args; - - // Get timestamp as a string - GetStringTimeStamp( timer ); - - // Get arguments as a string - va_start ( args, pFormat ); - vsnprintf ( buffer, 512, pFormat, args ); - va_end ( args ); - - // Format message (e.g. ERROR - Hello World) - CStr eventMessage = "WARN"; - eventMessage += " - "; - eventMessage += timer; - eventMessage += " "; - eventMessage += buffer; - eventMessage += "\n"; - - CNetLogEvent newEvent( LOG_LEVEL_WARN, eventMessage, m_Name ); - - CallSinks( newEvent ); - } -} - -//----------------------------------------------------------------------------- -// Name: AddSink() -// Desc: Attaches a new sink -//----------------------------------------------------------------------------- -void CNetLogger::AddSink( CNetLogSink* pSink ) -{ - size_t i; - CScopeLock lock( m_Mutex ); - - assert( pSink ); - - // Validate parameter - if ( !pSink ) return; - - // Check if already exists - for ( i = 0; i < GetSinkCount(); i++ ) - { - CNetLogSink* pCurrSink = GetSink( i ); - if ( pCurrSink == pSink ) break; - } - - // Already exists? - if ( i >= GetSinkCount() ) - { - // Activate new sink - pSink->Activate(); - - // Add new sink to list - m_Sinks.push_back( pSink ); - } -} - -//----------------------------------------------------------------------------- -// Name: RemoveSink() -// Desc: Removes the sink from the list of attached sinks -//----------------------------------------------------------------------------- -CNetLogSink* CNetLogger::RemoveSink( CNetLogSink* pSink ) -{ - CScopeLock lock( m_Mutex ); - - // Validate parameter - if ( !pSink ) return NULL; - - // Lookup the sink object - SinkList::iterator it = m_Sinks.begin(); - for ( ; it != m_Sinks.end(); it++ ) - { - CNetLogSink* pCurrSink = *it; - if ( pCurrSink == pSink ) - { - m_Sinks.erase( it ); - - return pCurrSink; - } - } - - return NULL; -} - -//----------------------------------------------------------------------------- -// Name: RemoveSink() -// Desc: Removes the named sink -//----------------------------------------------------------------------------- -CNetLogSink* CNetLogger::RemoveSink( const CStr& name ) -{ - CScopeLock lock( m_Mutex ); - - // Lookup sink by name - SinkList::iterator it = m_Sinks.begin(); - for ( ; it != m_Sinks.end(); it++ ) - { - CNetLogSink* pCurrSink = *it; - if ( !pCurrSink ) continue; - - if ( pCurrSink->GetName() == name ) - { - m_Sinks.erase( it ); - - return pCurrSink; - } - } - - // Sink not found - return NULL; -} - -//----------------------------------------------------------------------------- -// Name: RemoveAllSinks() -// Desc: Remove all attached sinks -//----------------------------------------------------------------------------- -void CNetLogger::RemoveAllSinks( void ) -{ - CScopeLock lock( m_Mutex ); - - for ( size_t i = 0; i < GetSinkCount(); i++ ) - delete m_Sinks[i]; - m_Sinks.clear(); -} - -//----------------------------------------------------------------------------- -// Name: GetSinkCount() -// Desc: Retrive the number of attached sinks -//----------------------------------------------------------------------------- -size_t CNetLogger::GetSinkCount( void ) -{ - return m_Sinks.size(); -} - -//----------------------------------------------------------------------------- -// Name: GetSink() -// Desc: Retrieves the sink by index -//----------------------------------------------------------------------------- -CNetLogSink* CNetLogger::GetSink( size_t index ) -{ - // Validate parameter - if ( index > m_Sinks.size() ) return NULL; - - return m_Sinks[ index ]; -} - -//----------------------------------------------------------------------------- -// Name: GetSink() -// Desc: Retrieve the sink by name -//----------------------------------------------------------------------------- -CNetLogSink* CNetLogger::GetSink( const CStr& name ) -{ - for ( size_t i = 0; i < GetSinkCount(); i++ ) - { - CNetLogSink* pCurrSink = GetSink( i ); - if ( !pCurrSink ) continue; - - if ( pCurrSink->GetName() == name ) - return pCurrSink; - } - - // Sink not found - return NULL; -} - -//----------------------------------------------------------------------------- -// Name: CallSinks() -// Desc: -//----------------------------------------------------------------------------- -void CNetLogger::CallSinks( const CNetLogEvent& event ) -{ - for ( size_t i = 0; i < GetSinkCount(); i++ ) - { - CNetLogSink* pCurrSink = GetSink( i ); - if ( !pCurrSink ) continue; - - pCurrSink->DoSink( event ); - } -} - -//----------------------------------------------------------------------------- -// Name: GetStringLocalTime() -// Desc: Returns the local date time into the passed in string -//----------------------------------------------------------------------------- -void CNetLogger::GetStringDateTime( CStr &str ) -{ - char buffer[ 128 ] = { 0 }; - time_t tm; - struct tm *now; - -#if OS_WIN - // Set timezone - _tzset(); - - // Get time and convert to tm structure - time( &tm ); - struct tm nowBuf; - localtime_s( &nowBuf, &tm ); - now = &nowBuf; -#else - time ( &tm ); - now = localtime( &tm ); -#endif - - // Build custom time string - strftime( buffer, 128, "%Y-%m-%d %H:%M:%S", now ); - - str = buffer; -} - -//----------------------------------------------------------------------------- -// Name: GetStringTime() -// Desc: Returns the local time into the passed string -//----------------------------------------------------------------------------- -void CNetLogger::GetStringTime( CStr& str ) -{ - char buffer[ 128 ] = { 0 }; - time_t tm; - struct tm *now; - -#if OS_WIN - // Set timezone - _tzset(); - - // Get time and convert to tm structure - time( &tm ); - struct tm nowBuf; - localtime_s( &nowBuf, &tm ); - now = &nowBuf; -#else - time ( &tm ); - now = localtime( &tm ); -#endif - - // Build custom time string - strftime( buffer, 128, "%H%M%S", now ); - - str = buffer; -} - -//----------------------------------------------------------------------------- -// Name: GetStringTimeStamp() -// Desc: Returns the formatted current time into the passed string -//----------------------------------------------------------------------------- -void CNetLogger::GetStringTimeStamp( CStr& str ) -{ - char buffer[ 128 ] = { 0 }; - double timestamp = timer_Time(); - sprintf_s( buffer, ARRAY_SIZE(buffer), "[%3u.%03u]", ( unsigned )timestamp, ( ( unsigned )( timestamp * 1000 ) % 1000 ) ); - str = buffer; -} - -// List of loggers under log manager -LoggerList CNetLogManager::m_Loggers; - -//----------------------------------------------------------------------------- -// Name: Shutdown() -// Desc: Shuts down the log manager -//----------------------------------------------------------------------------- -void CNetLogManager::Shutdown( void ) -{ - // Remove all loggers - LoggerList::iterator it = m_Loggers.begin(); - for ( ; it != m_Loggers.end(); it++ ) - { - CNetLogger *pCurrLogger = *it; - if ( !pCurrLogger ) continue; - - pCurrLogger->RemoveAllSinks(); - - delete pCurrLogger; - } - - m_Loggers.clear(); -} - -//----------------------------------------------------------------------------- -// Name: GetLogger() -// Desc: Retrieve or create a named logger -//----------------------------------------------------------------------------- -CNetLogger* CNetLogManager::GetLogger( const CStr& name ) -{ - LoggerList::const_iterator it = m_Loggers.begin(); - for ( ; it != m_Loggers.end(); it++ ) - { - CNetLogger* pCurrLogger = *it; - if ( !pCurrLogger ) continue; - - if ( pCurrLogger->GetName() == name ) - return pCurrLogger; - } - - // Logger not found, create it - CNetLogger* pNewLogger = new CNetLogger( name ); - if ( !pNewLogger ) return NULL; - - // Add new logger to list - m_Loggers.push_back( pNewLogger ); - - return pNewLogger; -} - -//----------------------------------------------------------------------------- -// Name: GetAllLoggers() -// Desc: Return the list of loggers -//----------------------------------------------------------------------------- -const LoggerList& CNetLogManager::GetAllLoggers( void ) -{ - return m_Loggers; -} - - diff --git a/source/network/NetLog.h b/source/network/NetLog.h deleted file mode 100644 index d2e957e3ee..0000000000 --- a/source/network/NetLog.h +++ /dev/null @@ -1,678 +0,0 @@ -/* Copyright (C) 2009 Wildfire Games. - * This file is part of 0 A.D. - * - * 0 A.D. 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 2 of the License, or - * (at your option) any later version. - * - * 0 A.D. 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 0 A.D. If not, see . - */ - -/** - *----------------------------------------------------------------------------- - * FILE : NetLog.h - * PROJECT : 0 A.D. - * DESCRIPTION : Network subsystem logging classes declarations - *----------------------------------------------------------------------------- - */ - -#ifndef NETLOG_H -#define NETLOG_H - -// INCLUDES -#include "ps/Pyrogenesis.h" -#include "ps/ThreadUtil.h" -#include "ps/CStr.h" -#include "lib/timer.h" - -#include -#include -#include - -// DECLARATIONS -typedef enum -{ - LOG_LEVEL_ALL = 0x0000, // Lowest level possible - LOG_LEVEL_DEBUG = 0x0001, // Informational events for app debugging - LOG_LEVEL_INFO = 0x0002, // Useful for highlighting app progress - LOG_LEVEL_WARN = 0x0004, // Potentially dangerous situations - LOG_LEVEL_ERROR = 0x0008, // Error events but the app can continue - LOG_LEVEL_FATAL = 0x0010, // Very severe errors, the app aborts - LOG_LEVEL_OFF = 0xFFFF, // The highest level possible -} LogLevel; - -class CNetLogger; -class CNetLogSink; - -typedef std::list< CNetLogger* > LoggerList; -typedef std::vector< CNetLogSink* > SinkList; - -/* - CLASS : CNetLogEvent - DESCRIPTION : CNetLogEvent represents an object passed between - different network logging components when a decision - for logging is made - NOTES : -*/ - -class CNetLogEvent -{ -public: - - CNetLogEvent( - LogLevel level, - const CStr& message, - const CStr& loggerName ); - ~CNetLogEvent( void ); - - /** - * Returns the level of the event - * - * @return Event level; - */ - inline LogLevel GetLevel( void ) const { return m_Level; } - - /** - * Returns the time of the event - * - * @return Event time - */ - inline TimerUnit GetTimeStamp( void ) const { return m_TimeStamp; } - - /** - * Returns the name of the logger which logged the event - * - * @return Logger name - */ - inline const CStr& GetLoggerName( void ) const { return m_LoggerName; } - - /** - * Returns the message used when the event was initialized - * - * @return Event message - */ - inline const CStr& GetMessage( void ) const { return m_Message; } - -protected: - -private: - - // Not implemented - CNetLogEvent( const CNetLogEvent& ); - CNetLogEvent& operator=( CNetLogEvent& ); - - LogLevel m_Level; // Current level - CStr m_LoggerName; // Logger which processed the event - CStr m_Message; // Application message for the event - TimerUnit m_TimeStamp; // Event logging time -}; - -/* - CLASS : CNetLogSink - DESCRIPTION : CNetLogSink is the basic interface for events logging - NOTES : -*/ - -class CNetLogSink -{ -public: - - CNetLogSink( ); - virtual ~CNetLogSink( ); - - /** - * Set the name of the sink - * - * @param name New sink name - */ - void SetName( const CStr& name ); - - /** - * Retrieves the name of the sink - * - * @return Sink name - */ - inline const CStr& GetName( void ) const { return m_Name; } - - /** - * Set the level of the sink - * - * @param level New sink level - */ - void SetLevel( LogLevel level ); - - /** - * Retrieves the current level of the sink - * - * @return Sink current level - */ - inline LogLevel GetLevel( void ) const { return m_Level; } - - /** - * Retrieves the header text - * - * @return The header text - */ - inline const CStr& GetHeader( void ) const { return m_Header; } - - /** - * Set header text which will be logged before an event logging - * - * @param header New header text - */ - void SetHeader( const CStr& header ); - - /** - * Retrieves the footer text - * - * @return The footer text - */ - inline const CStr& GetFooter( void ) const { return m_Footer; } - - /** - * Set footer text which will be logged after an event logging - * - * @param footer New footer text - */ - void SetFooter( const CStr& footer ); - - /** - * Activates the sink - */ - void Activate( void ); - - /** - * Closes the sink and release any resources - */ - void Close( void ); - - /** - * Check if the level of the event is greater than or equal to sink level - * and if it succeeds it performs the actual logging of the event. - * - * @param event Event to log - */ - void DoSink( const CNetLogEvent& event ); - - /** - * For each event from the passed array, check if its level is greater than - * or equal to sink level and performs the logging for it. - * - * @param pEvents List of events to log - * @param eventCount The number of events in pEvents list - */ - void DoBulkSink( const CNetLogEvent* pEvents, size_t eventCount ); - - /** - * Check if the sink can log the specified event - * - * @param event Event to check - * @return true if the event can be logged, - * false otherwise - */ - virtual bool TestEvent( const CNetLogEvent& event ); - -protected: - - /** - * Activates the sink object - */ - virtual void OnActivate( void ); - - /** - * Writes a header into the sink - */ - virtual void WriteHeader( void ); - - /** - * Writes a footer into the sink - */ - virtual void WriteFooter( void ); - - /** - * Writes a string message to the logging output - * - * @param message The message to log - */ - virtual void Write( const CStr& message ) = 0; - - /** - * Writes a single character to the logging output - * - * @param c The character to log - */ - virtual void Write( char c ) = 0; - - /** - * This is called by Close method. It can be overriden by specialized sinks - * if any resources needs to be released on close. - */ - virtual void OnClose( void ); - - /** - * This method is called by DoSink and DoBulkSink and it must be - * implemented by specialized sinks to perform actual logging - * - * @param event Event to log - */ - virtual void Sink( const CNetLogEvent& event ) = 0; - - LogLevel m_Level; // Current level - CMutex m_Mutex; // Multithreading synchronization object - CStr m_Header; // Header text - CStr m_Footer; // Footer text - CStr m_Name; // Sink name - bool m_Closed; // Indicates whether the sink is closed - bool m_Active; // Indicates whether the sink is active - -private: - - // Not implemented - CNetLogSink( const CNetLogSink& ); - CNetLogSink& operator=( const CNetLogSink& ); -}; - -/* - CLASS : CNetLogFileSink - DESCRIPTION : Log network events to a file - NOTES : -*/ - -class CNetLogFileSink : public CNetLogSink -{ -public: - - CNetLogFileSink( void ); - CNetLogFileSink( const fs::wpath& filename ); - CNetLogFileSink( const fs::wpath& filename, bool append ); - ~CNetLogFileSink( void ); - -protected: - - /** - * Activates the sink object and opens the file specified in constructor - */ - virtual void OnActivate( void ); - - /** - * Closes the log file - */ - virtual void OnClose( void ); - - /** - * Writes the event to the log file if opened - * - * @param event Event to log - */ - virtual void Sink( const CNetLogEvent& event ); - - /** - * Writes the message passed as parameter to file - * - * @param message The message to log - */ - virtual void Write( const CStr& message ); - - /** - * Writes the character passed as parameter to file - * - * @param c The character to log - */ - virtual void Write( char c ); - -private: - - // Not implemented - CNetLogFileSink( const CNetLogFileSink& ); - CNetLogFileSink& operator=( const CNetLogFileSink& ); - - /** - * Open the file where logging goes. The header text will be written each - * time the file is opened. If append parameter is true, then the file may - * contain the header many times. - * - * @param filename The path to the log file - * @param append Indicates whether logging should append to - * the file or truncate the file - */ - void OpenFile( const fs::wpath& fileName, bool append ); - - /** - * Close the previously opened file. The footer text will be written each time - * the file is closed. If the file was opened for appending, the footer might - * appera many times. - */ - void CloseFile( void ); - - std::ofstream m_File; // The log file handle - fs::wpath m_FileName; // The name of the log file - bool m_Append; // Logging should append to file -}; - -/* - CLASS : CNetLogConsoleSink - DESCRIPTION : Log network events to the game console - NOTES : -*/ - -class CNetLogConsoleSink : public CNetLogSink -{ -public: - - CNetLogConsoleSink( void ); - ~CNetLogConsoleSink( void ); - -protected: - - /** - * Activates the sink object and the game console - */ - virtual void OnActivate( void ); - - /** - * Toggle off game console - */ - virtual void OnClose( void ); - - /** - * Writes the event to the game console if active - * - * @param event Event to log - */ - virtual void Sink( const CNetLogEvent& event ); - - /** - * Writes the message passed as parameter to game console - * - * @param message The message to log - */ - virtual void Write( const CStr& message ); - - /** - * Writes the character passed as parameter to game console - * - * @param c The character to log - */ - virtual void Write( char c ); - -private: - - // Not implemented - CNetLogConsoleSink( const CNetLogConsoleSink& ); - CNetLogConsoleSink& operator=( const CNetLogConsoleSink& ); -}; - -/* - CLASS : CNetLogger - DESCRIPTION : CNetLogger serves for logging messages for network subsytem. - It contains methods for logging at different levels. - NOTES : CNetLogManager is used to obtain an instance of a logger. -*/ - -class CNetLogger -{ -public: - - CNetLogger( const CStr& name ); - virtual ~CNetLogger( void ); - - bool IsDebugEnabled ( void ) const; - bool IsInfoEnabled ( void ) const; - bool IsWarnEnabled ( void ) const; - bool IsErrorEnabled ( void ) const; - bool IsFatalEnabled ( void ) const; - - void Debug ( const CStr& message ); - void Warn ( const CStr& message ); - void Info ( const CStr& message ); - void Error ( const CStr& message ); - void Fatal ( const CStr& message ); - - void DebugFormat ( const char* pFormat, ... ) PRINTF_ARGS(2); - void WarnFormat ( const char* pFormat, ... ) PRINTF_ARGS(2); - void InfoFormat ( const char* pFormat, ... ) PRINTF_ARGS(2); - void ErrorFormat ( const char* pFormat, ... ) PRINTF_ARGS(2); - void FatalFormat ( const char* pFormat, ... ) PRINTF_ARGS(2); - - /** - * Retrieves the name of the logger - * - * @return Logger name - */ - const CStr& GetName( void ) const { return m_Name; } - - /** - * Retrieves the level of the logger - * - * @return Logger level - */ - LogLevel GetLevel( void ) const { return m_Level; } - - /** - * Set the level for the logger - * - * @param level New logger level - */ - void SetLevel( LogLevel level ); - - /** - * Attaches a new sink to the list of sinks. The sink will be activated. - * - * @param pSink The sink to add - */ - void AddSink( CNetLogSink* pSink ); - - /** - * Removes the specified sink from the list of attached sinks. The sink - * will not be closed. - * - * @param pSink The sink to remove - * @return The removed sink or NULL if not found - */ - CNetLogSink* RemoveSink( CNetLogSink* pSink ); - - /** - * Remove the named sink passed as parameter. The sink will not be closed. - * - * @param name The name of sink to remove - * @return The removed sink or NULL if not found - */ - CNetLogSink* RemoveSink( const CStr& name ); - - /** - * Removes all attached sinks - * - */ - void RemoveAllSinks( void ); - - /** - * Retrieve the number of attached sinks - * - * @return The number of sink objects - */ - size_t GetSinkCount( void ); - - /** - * Retrieves the sink by its index - * - * @param index The index of the sink - * @return NULL if index is out of boundaries or - * the sink at the specified index - */ - CNetLogSink* GetSink( size_t index ); - - /** - * Retrieves a sink by its name - * - * @param name The name of the sink - * @return NULL if the sink does not exists or - * the sink with the specified name - */ - CNetLogSink* GetSink( const CStr& name ); - - /** - * Helper function used to retrieve local date time in a string - */ - static void GetStringDateTime( CStr& str ); - - /** - * Helper function used to retrieve local time in a string - */ - static void GetStringTime( CStr& str ); - - /** - * Helper function used to retrieve the current timestamp in a string - */ - static void GetStringTimeStamp( CStr& str ); - -protected: - -private: - - // Not implemented - CNetLogger( const CNetLogger& ); - CNetLogger& operator=( const CNetLogger& ); - - /** - * Dispatch the event passed as parameter to all sinks - * - * @param event The event to log - */ - void CallSinks( const CNetLogEvent& event ); - - CMutex m_Mutex; // Multithread synchronization object - SinkList m_Sinks; // Holds the list of sink objects - LogLevel m_Level; // Logger level - CStr m_Name; // Logger name -}; - -/* - CLASS : CNetLogManager - DESCRIPTION : CNetLogManager serves clients requesting log instances - NOTES : The GetLogger method can be used to retrieve a log -*/ - -class CNetLogManager -{ -public: - - /** - * Shutdown the log manager, closes all sinks in the loggers. - * - */ - static void Shutdown( void ); - - /** - * Retrieves a named logger. If the logger does not exist, it is created. - * - * @param name Logger name - * @return A logger object - */ - static CNetLogger* GetLogger( const CStr& name ); - - /** - * Return the list of all defined loggers. - * - * @return The list of all loggers - */ - static const LoggerList& GetAllLoggers( void ); - -private: - - // Not implemented - CNetLogManager( void ); - ~CNetLogManager( void ); - CNetLogManager( const CNetLogManager& ); - CNetLogManager& operator=( const CNetLogManager& ); - - static LoggerList m_Loggers; // Holds the list of loggers -}; - -// TODO: Replace with better access to log manager -#define START_LOGGER( sinkName, sinkType )\ - CNetLogger *pLogger = CNetLogManager::GetLogger( "net.log" );\ - if ( pLogger )\ - {\ - CNetLogSink* pSink = pLogger->GetSink( sinkName );\ - if ( !pSink )\ - {\ - pSink = new sinkType();\ - if ( pSink )\ - {\ - CStr startTime;\ - CNetLogger::GetStringDateTime( startTime );\ - CStr header = "***************************************************\n";\ - header += "LOG STARTED: ";\ - header += startTime;\ - header += "\n";\ - header += "Timestamps are in seconds since engine startup\n";\ - pSink->SetHeader( header );\ - pSink->SetName( sinkName );\ - if ( strcmp(sinkName, "sink.console") == 0 )\ - pSink->SetLevel( LOG_LEVEL_ERROR );\ - else\ - pSink->SetLevel( LOG_LEVEL_INFO );\ - pLogger->AddSink( pSink );\ - }\ - } - -#define END_LOGGER\ - } - -#define NET_LOG( parameter )\ - {\ - START_LOGGER( "sink.file", CNetLogFileSink )\ - pLogger->Info( parameter );\ - END_LOGGER\ - }\ - {\ - START_LOGGER( "sink.console", CNetLogConsoleSink )\ - pLogger->Info( parameter );\ - END_LOGGER\ - } - -#define NET_LOG2( format, parameter )\ - {\ - START_LOGGER( "sink.file", CNetLogFileSink )\ - pLogger->InfoFormat( format, parameter );\ - END_LOGGER\ - }\ - {\ - START_LOGGER( "sink.console", CNetLogConsoleSink )\ - pLogger->InfoFormat( format, parameter );\ - END_LOGGER\ - } - -#define NET_LOG3( format, parameter1, parameter2 )\ - {\ - START_LOGGER( "sink.file", CNetLogFileSink )\ - pLogger->InfoFormat( format, parameter1, parameter2 );\ - END_LOGGER\ - }\ - {\ - START_LOGGER( "sink.console", CNetLogConsoleSink )\ - pLogger->InfoFormat( format, parameter1, parameter2 );\ - END_LOGGER\ - } - -#define NET_LOG4( format, parameter1, parameter2, parameter3 )\ - {\ - START_LOGGER( "sink.file", CNetLogFileSink )\ - pLogger->InfoFormat( format, parameter1, parameter2, parameter3 );\ - END_LOGGER\ - }\ - {\ - START_LOGGER( "sink.console", CNetLogConsoleSink )\ - pLogger->InfoFormat( format, parameter1, parameter2, parameter3 );\ - END_LOGGER\ - } - -#endif // NETLOG_H - diff --git a/source/network/NetServer.cpp b/source/network/NetServer.cpp index aae278cd4b..95e4a68756 100644 --- a/source/network/NetServer.cpp +++ b/source/network/NetServer.cpp @@ -25,12 +25,15 @@ // INCLUDES #include "precompiled.h" -#include "ps/CLogger.h" -#include "ps/CConsole.h" -#include "simulation/Simulation.h" + +#include "NetServer.h" + #include "NetJsEvents.h" #include "NetSession.h" -#include "NetServer.h" + +#include "ps/CConsole.h" +#include "ps/CLogger.h" +#include "ps/GameAttributes.h" #include "simulation2/Simulation2.h" #define LOG_CATEGORY L"net" @@ -42,8 +45,8 @@ CNetServer* g_NetServer = NULL; // Name: CNetServer() // Desc: Constructor //----------------------------------------------------------------------------- -CNetServer::CNetServer( CGame *pGame, CGameAttributes *pGameAttributes ) -: m_JsSessions( &m_IDSessions ) +CNetServer::CNetServer( ScriptInterface& scriptInterface, CGame *pGame, CGameAttributes *pGameAttributes ) +: CNetHost( scriptInterface ), m_JsSessions( &m_IDSessions ) { m_ServerTurnManager = NULL; @@ -125,8 +128,7 @@ bool CNetServer::SetupSession( CNetSession* pSession ) // Validate parameters if ( !pSession ) return false; - FsmActionCtx* pContext = new FsmActionCtx; - if ( !pContext ) return false; + FsmActionCtx* pContext = pSession->GetFsmActionCtx(); pContext->pHost = this; pContext->pSession = pSession; @@ -721,7 +723,7 @@ void CNetServer::AttributeUpdate( CNetServer* pServer = ( CNetServer* )pData; - g_Console->InsertMessage( L"AttributeUpdate: %ls = \"%ls\"", name.c_str(), newValue.c_str() ); + LOGMESSAGE( L"AttributeUpdate: %ls = \"%ls\"", name.c_str(), newValue.c_str() ); CGameSetupMessage gameSetup; gameSetup.m_Values.resize( 1 ); @@ -745,7 +747,7 @@ void CNetServer::PlayerAttributeUpdate( CNetServer* pServer = ( CNetServer* )pData; - g_Console->InsertMessage( L"PlayerAttributeUpdate(%ld): %ls = \"%ls\"", pPlayer->GetPlayerID(), name.c_str(), newValue.c_str() ); + LOGMESSAGE( L"PlayerAttributeUpdate(%ld): %ls = \"%ls\"", pPlayer->GetPlayerID(), name.c_str(), newValue.c_str() ); CPlayerConfigMessage* pNewMessage = new CPlayerConfigMessage; if ( !pNewMessage ) return; @@ -778,7 +780,7 @@ void CNetServer::PlayerSlotAssignment( pServer->BuildPlayerSlotAssignmentMessage( &assignPlayerSlot, pPlayerSlot ); - g_Console->InsertMessage( L"Player Slot Assignment: %hs\n", assignPlayerSlot.ToString().c_str() ); + LOGMESSAGE( L"Player Slot Assignment: %hs\n", assignPlayerSlot.ToString().c_str() ); pServer->Broadcast( &assignPlayerSlot ); } diff --git a/source/network/NetServer.h b/source/network/NetServer.h index 5bd6dbd12d..428a7a4404 100644 --- a/source/network/NetServer.h +++ b/source/network/NetServer.h @@ -28,10 +28,10 @@ // INCLUDES #include "Network.h" +#include "NetHost.h" #include "NetSession.h" #include "NetTurnManager.h" #include "scripting/ScriptableObject.h" -#include "ps/GameAttributes.h" #include "ps/scripting/JSMap.h" #include "ps/Player.h" #include "ps/Game.h" @@ -50,6 +50,8 @@ #define DEFAULT_WELCOME_MESSAGE L"Noname Server Welcome Message" #define DEFAULT_HOST_PORT 0x5073 +class CGameAttributes; + enum NetServerState { // We haven't opened the port yet, we're just setting some stuff up. @@ -102,7 +104,7 @@ class CNetServer : public CNetHost, NONCOPYABLE(CNetServer); public: - CNetServer( CGame* pGame, CGameAttributes* pGameAttributes ); + CNetServer( ScriptInterface& scriptInterface, CGame* pGame, CGameAttributes* pGameAttributes ); virtual ~CNetServer( void ); bool Start ( JSContext *pContext, uintN argc, jsval *argv ); @@ -246,9 +248,10 @@ protected: public: CGame* m_Game; // Pointer to actual game -private: - +protected: CGameAttributes* m_GameAttributes; // Stores game attributes + +private: CJSMap< IDSessionMap > m_JsSessions; /* diff --git a/source/network/NetSession.cpp b/source/network/NetSession.cpp index ce2f5af95f..c519a3ba8d 100644 --- a/source/network/NetSession.cpp +++ b/source/network/NetSession.cpp @@ -15,477 +15,37 @@ * along with 0 A.D. If not, see . */ -/** - *----------------------------------------------------------------------------- - * FILE : NetSession.cpp - * PROJECT : 0 A.D. - * DESCRIPTION : Network session class implementation - *----------------------------------------------------------------------------- - */ - -// INCLUDES #include "precompiled.h" #include "NetSession.h" -#include "NetLog.h" -#include "simulation2/Simulation2.h" -// DECLARATIONS - -//----------------------------------------------------------------------------- -// Name: CNetHost() -// Desc: Constructor -//----------------------------------------------------------------------------- -CNetHost::CNetHost( void ) -{ - m_Host = NULL; -} - -//----------------------------------------------------------------------------- -// Name: ~CNetHost() -// Desc: Destructor -//----------------------------------------------------------------------------- -CNetHost::~CNetHost( void ) -{ - // Release host - if ( m_Host ) enet_host_destroy( m_Host ); - - m_Host = NULL; -} - -//----------------------------------------------------------------------------- -// Name: Create() -// Desc: Creates a client host -//----------------------------------------------------------------------------- -bool CNetHost::Create( void ) -{ - // Create ENet host - m_Host = enet_host_create( NULL, 1, 0, 0 ); - if ( !m_Host ) return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Name: Create() -// Desc: Creates a server host -//----------------------------------------------------------------------------- -bool CNetHost::Create( uint port, uint maxPeers ) -{ - ENetAddress addr; - - // Bind to default host - addr.host = ENET_HOST_ANY; - addr.port = port; - - // Create ENet server - m_Host = enet_host_create( &addr, maxPeers, 0, 0 ); - if ( !m_Host ) return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Name: Shutdown() -// Desc: Shuts down network server and releases any resources -//----------------------------------------------------------------------------- -void CNetHost::Shutdown( void ) -{ - // Destroy server - if ( m_Host ) enet_host_destroy( m_Host ); - - // Disconnect and release each peer - PeerSessionList::iterator it = m_PeerSessions.begin(); - for ( ; it != m_PeerSessions.end(); it++ ) - { - if ( !it->pSession ) continue; - - Disconnect( it->pSession ); - - delete it->pSession; - } - - m_PeerSessions.clear(); - - m_Host = NULL; -} - -//----------------------------------------------------------------------------- -// Name: Connect() -// Desc: Connects to the specified remote host -// Note: Only clients use this method for connection to server -//----------------------------------------------------------------------------- -bool CNetHost::Connect( const CStr& host, uint port ) -{ - ENetEvent event; - ENetAddress addr; - PeerSession item; - - debug_assert( m_Host ); - - // Bind to specified host - addr.port = port; - if ( enet_address_set_host( &addr, host.c_str() ) < 0 ) return false; - - // Initiate connection, allocate one channel - ENetPeer* pPeer = enet_host_connect( m_Host, &addr, 1 ); - if ( !pPeer ) return false; - - // Wait 3 seconds for the connection to succeed - if ( enet_host_service( m_Host, &event, 5000 ) > 0 && - event.type == ENET_EVENT_TYPE_CONNECT ) - { - // Connection succeeded - CNetSession* pNewSession = new CNetSession( this, event.peer ); - if ( !pNewSession ) return false; - - if ( !SetupSession( pNewSession ) ) return false; - - NET_LOG3( "Successfully connected to server %s:%d succeeded", host.c_str(), port ); - - // Successfully handled? - if ( !HandleConnect( pNewSession ) ) - { - delete pNewSession; - - return false; - } - - // Store the only server session - item.pPeer = event.peer; - item.pSession = pNewSession; - m_PeerSessions.push_back( item ); - - return true; - } - - NET_LOG3( "Connection to server %s:%d failed", host.c_str(), port ); - - // 3 seconds are up or a host was disconnected - enet_peer_reset( pPeer ); - - return false; -} - -//----------------------------------------------------------------------------- -// Name: Disconnect() -// Desc: Disconnects the specified session from the host -//----------------------------------------------------------------------------- -bool CNetHost::Disconnect( CNetSession* pSession ) -{ - ENetEvent event; - - // Validate parameters - if ( !pSession ) return false; - - debug_assert( m_Host ); - debug_assert( pSession->m_Peer ); - - // Disconnect peer - enet_peer_disconnect( pSession->m_Peer, 0 ); - - // Allow up to 3 seconds for the disconnect to succeed - while ( enet_host_service( m_Host, &event, 5000 ) > 0 ) - { - switch ( event.type ) - { - case ENET_EVENT_TYPE_RECEIVE: - - // Drop any received packets - enet_packet_destroy( event.packet ); - break; - - case ENET_EVENT_TYPE_DISCONNECT: - - // Disconnect received for peer - if ( !HandleDisconnect( pSession ) ) return false; - break; - } - } - - // Disconnect attempt didn't succeed, force connection down - enet_peer_reset( pSession->m_Peer ); - - return true; -} - -//----------------------------------------------------------------------------- -// Name: ProcessEvents() -// Desc: Wait for events and shuttles packets between the host and its peers -//----------------------------------------------------------------------------- -bool CNetHost::Poll( void ) -{ - ENetEvent event; - CNetSession* pSession = NULL; - PeerSession item; - PeerSessionList::iterator it; - - debug_assert( m_Host ); - - // Poll host for events - while ( enet_host_service( m_Host, &event, 0 ) > 0 ) - { - // Handle occured event - switch( event.type ) - { - case ENET_EVENT_TYPE_CONNECT: - - // A new client has connected, handle it - pSession = new CNetSession( this, event.peer ); - if ( !pSession ) return false; - - // Setup new session - if ( !SetupSession( pSession ) ) return false; - - NET_LOG3( "A new client connected from %x:%u", event.peer->address.host, event.peer->address.port ); - - // Successfully handled? - if ( !HandleConnect( pSession ) ) return false; - - event.peer->data = pSession; - - // Add new item to internal list - item.pPeer = event.peer; - item.pSession = pSession; - m_PeerSessions.push_back( item ); - - break; - - case ENET_EVENT_TYPE_DISCONNECT: - - // Client has disconnected, handle it - it = m_PeerSessions.begin();; - for ( ; it != m_PeerSessions.end(); it++ ) - { - // Is this our session? - if ( it->pPeer == event.peer ) - { - NET_LOG2( "%p disconnected", event.peer->data ); - - // Successfully handled? - if ( !HandleDisconnect( it->pSession ) ) return false; - - m_PeerSessions.erase( it ); - - return true; - } - } - - break; - - case ENET_EVENT_TYPE_RECEIVE: - - // A new data packet was received from client, handle message - it = m_PeerSessions.begin(); - for ( ; it != m_PeerSessions.end(); it++ ) - { - // Is this our session? - if ( it->pPeer == event.peer ) - { - bool ok = false; - - // Create message from raw data - CNetMessage* pNewMessage = CNetMessageFactory::CreateMessage( event.packet->data, event.packet->dataLength, g_Game->GetSimulation2()->GetScriptInterface() ); - if ( pNewMessage ) - { - NET_LOG4( "Message %s of size %lu was received from %p", pNewMessage->ToString().c_str(), (unsigned long)pNewMessage->GetSerializedLength(), event.peer->data ); - - ok = HandleMessageReceive( pNewMessage, it->pSession ); - - delete pNewMessage; - } - - // Done using the packet - enet_packet_destroy( event.packet ); - - if (! ok) - return false; - } - } - - break; - } - } - - return true; -} - -//----------------------------------------------------------------------------- -// Name: Broadcast() -// Desc: Broadcast the specified message to connected clients -//----------------------------------------------------------------------------- -void CNetHost::Broadcast( const CNetMessage* pMessage ) -{ - // Validate parameters - if ( !pMessage ) return; - - // Loop through the list of sessions and send the message to each - for ( uint i = 0; i < GetSessionCount(); i++ ) - { - CNetSession* pCurrSession = GetSession( i ); - if ( !pCurrSession ) continue; - - SendMessage( pCurrSession, pMessage ); - } -} - -//----------------------------------------------------------------------------- -// Name: SendMessage() -// Desc: Sends the specified message to peer -//----------------------------------------------------------------------------- -bool CNetHost::SendMessage( - const CNetSession* pSession, - const CNetMessage* pMessage ) -{ - // Validate parameters - if ( !pMessage || !pSession ) return false; - - debug_assert( pSession->m_Peer ); - debug_assert( m_Host ); - - size_t size = pMessage->GetSerializedLength(); - debug_assert( size ); - - // Adjust buffer for message - m_Buffer.resize( size ); - - // Save message to internal buffer - pMessage->Serialize( &m_Buffer[0] ); - - // Create a reliable packet - ENetPacket* pPacket = enet_packet_create( &m_Buffer[0], size, ENET_PACKET_FLAG_RELIABLE ); - if ( !pPacket ) return false; - - // Let ENet send the message to peer - if ( enet_peer_send( pSession->m_Peer, ENET_DEFAULT_CHANNEL, pPacket ) < 0 ) - { - // ENet failed to send the packet - NET_LOG( "Failed to send ENet packet to peer" ); - - return false; - } - else - { - NET_LOG4( "Message %s of size %lu was sent to %p", - pMessage->ToString().c_str(), (unsigned long)size, pSession->m_Peer->data ); - } - - enet_host_flush( m_Host ); - - return true; -} - -//----------------------------------------------------------------------------- -// Name: ReceiveMessage() -// Desc: Receives a message from client if incoming packets are available -//----------------------------------------------------------------------------- -CNetMessage* CNetHost::ReceiveMessage( const CNetSession* pSession ) -{ - // Validate parameters - if ( !pSession ) return NULL; - - debug_assert( pSession->m_Peer ); - - // Let ENet receive a message from peer - ENetPacket* pPacket = enet_peer_receive( pSession->m_Peer, ENET_DEFAULT_CHANNEL ); - if ( !pPacket ) return NULL; - - // Create new message - return CNetMessageFactory::CreateMessage( pPacket->data, pPacket->dataLength, g_Game->GetSimulation2()->GetScriptInterface() ); -} - -//----------------------------------------------------------------------------- -// Name: SetupSession() -// Desc: Setup new session upon creation -//----------------------------------------------------------------------------- -bool CNetHost::SetupSession( CNetSession* UNUSED(pSession) ) -{ - return true; -} - -//----------------------------------------------------------------------------- -// Name: HandleConnect() -// Desc: Allow application to handle client connect -//----------------------------------------------------------------------------- -bool CNetHost::HandleConnect( CNetSession* UNUSED(pSession) ) -{ - return true; -} - -//----------------------------------------------------------------------------- -// Name: HandleDisconnect() -// Desc: Allow application to handle client disconnect -//----------------------------------------------------------------------------- -bool CNetHost::HandleDisconnect( CNetSession* UNUSED(pSession) ) -{ - return true; -} - -//----------------------------------------------------------------------------- -// Name: HandleMessageReceive() -// Desc: Allow application to handle message recive -//----------------------------------------------------------------------------- -bool CNetHost::HandleMessageReceive( - CNetMessage* pMessage, - CNetSession* pSession ) -{ - // Validate parameters - if ( !pSession || !pMessage ) return false; - - // Update FSM - return pSession->Update( pMessage->GetType(), pMessage ); -} - -//----------------------------------------------------------------------------- -// Name: GetSessionCount() -// Desc: Returns the number of sessions the host manages -//----------------------------------------------------------------------------- -uint CNetHost::GetSessionCount( void ) const -{ - return ( uint )m_PeerSessions.size(); -} - -//----------------------------------------------------------------------------- -// Name: GetSession() -// Desc: Rteurns the session for the index -//----------------------------------------------------------------------------- -CNetSession* CNetHost::GetSession( uint index ) -{ - // Validate parameter - if ( index >= GetSessionCount() ) return NULL; - - return m_PeerSessions[ index ].pSession; -}; +static const uint INVALID_SESSION = 0; //----------------------------------------------------------------------------- // Name: CNetSession() // Desc: Constructor //----------------------------------------------------------------------------- -CNetSession::CNetSession( CNetHost* pHost, ENetPeer* pPeer ) +CNetSession::CNetSession(CNetHost* pHost, ENetPeer* pPeer) { - m_Host = pHost; - m_Peer = pPeer; - m_ID = INVALID_SESSION; - m_PlayerSlot = NULL; + m_Host = pHost; + m_Peer = pPeer; + m_ID = INVALID_SESSION; + m_PlayerSlot = NULL; } //----------------------------------------------------------------------------- // Name: ~CNetSession() // Desc: Destructor //----------------------------------------------------------------------------- -CNetSession::~CNetSession( void ) +CNetSession::~CNetSession() { - // Release any resources - //if ( m_Host ) enet_host_destroy( m_Host ); - - //m_Host = NULL; - m_Peer = NULL; + m_Peer = NULL; } //----------------------------------------------------------------------------- // Name: SetName() // Desc: Set a new name for the session //----------------------------------------------------------------------------- -void CNetSession::SetName( const CStr& name ) +void CNetSession::SetName(const CStr& name) { m_Name = name; } @@ -494,7 +54,7 @@ void CNetSession::SetName( const CStr& name ) // Name: SetID() // Desc: Set new ID for this session //----------------------------------------------------------------------------- -void CNetSession::SetID( uint ID ) +void CNetSession::SetID(uint ID) { m_ID = ID; } @@ -503,7 +63,7 @@ void CNetSession::SetID( uint ID ) // Name: SetPlayerSlot() // Desc: Set the player slot for this session //----------------------------------------------------------------------------- -void CNetSession::SetPlayerSlot( CPlayerSlot* pPlayerSlot ) +void CNetSession::SetPlayerSlot(CPlayerSlot* pPlayerSlot) { m_PlayerSlot = pPlayerSlot; } @@ -512,10 +72,10 @@ void CNetSession::SetPlayerSlot( CPlayerSlot* pPlayerSlot ) // Name: ScriptingInit() // Desc: //----------------------------------------------------------------------------- -void CNetSession::ScriptingInit( void ) +void CNetSession::ScriptingInit() { - AddProperty( L"id", &CNetSession::m_ID ); - AddProperty( L"name", &CNetSession::m_Name ); + AddProperty(L"id", &CNetSession::m_ID); + AddProperty(L"name", &CNetSession::m_Name); - CJSObject::ScriptingInit( "NetSession" ); + CJSObject::ScriptingInit("NetSession"); } diff --git a/source/network/NetSession.h b/source/network/NetSession.h index 00e6408d4a..d06d0108a2 100644 --- a/source/network/NetSession.h +++ b/source/network/NetSession.h @@ -15,170 +15,39 @@ * along with 0 A.D. If not, see . */ -/** - *----------------------------------------------------------------------------- - * FILE : NetSession.h - * PROJECT : 0 A.D. - * DESCRIPTION : Network session class interface file - *----------------------------------------------------------------------------- - */ - #ifndef NETSESSION_H #define NETSESSION_H -// INCLUDES -#include "Network.h" -#include "ps/GameAttributes.h" #include "fsm.h" -#include +#include "scripting/ScriptableObject.h" -#include - -// DECLARATIONS -#define INVALID_SESSION 0 -#define ENET_DEFAULT_CHANNEL 0 - -class CNetSession; +class CPlayerSlot; class CNetHost; +class CNetSession; +typedef struct _ENetPeer ENetPeer; -typedef struct +struct FsmActionCtx { - ENetPeer* pPeer; - CNetSession* pSession; - -} PeerSession; - -typedef struct -{ - CNetHost* pHost; - CNetSession* pSession; - -} FsmActionCtx; - -typedef std::vector< PeerSession > PeerSessionList; - -/* - CLASS : CNetHost - DESCRIPTION : CNetHost is a wrapper around ENet host concept - NOTES : -*/ - -class CNetHost -{ - NONCOPYABLE(CNetHost); - -public: - - CNetHost( void ); - virtual ~CNetHost( void ); - - bool Create( void ); - bool Create( uint port, uint maxPeers ); - void Shutdown( void ); - - /** - * Indicates whether the host is currently a server - * - * @return Boolean indicating whether the host is a server - */ - virtual bool IsServer( void ) const { return false; } - - /** - * Returns the number of sessions for the host - * - * @return The number of sessions - */ - uint GetSessionCount( void ) const; - - /** - * Returns the session object for the specified index - * - * @param index Index for session - * @return Session object for index or NULL if not found - */ - CNetSession* GetSession( uint index ); - - /** - * Connects to foreign host - * - * @param host Foreign host name - * @param port Port on which the foreign host listens - * @return true on success, false on failure - */ - bool Connect( const CStr& host, uint port ); - - /** - * Disconnects session from host - * - * @param pSession Session representing peer - * @return true on success, false otherwise - */ - bool Disconnect( CNetSession* pSession ); - - /** - * Listens for incoming connections and dispatches host events - * - * @return true on exit, false dispatch failure - */ - //bool Run( void ); - bool Poll( void ); - - /** - * Broadcast the specified message to connected clients - * - * @param pMessage Message to broadcast - */ - void Broadcast( const CNetMessage* pMessage ); - - /** - * Send the specified message to client - * - * @param pMessage The message to send - */ - virtual bool SendMessage( - const CNetSession* pSession, - const CNetMessage* pMessage ); - - /** - * Receive a message from client if available - * - */ - virtual CNetMessage* ReceiveMessage( const CNetSession* pSession ); - -protected: - - // Allow application to handle new client connect - virtual bool SetupSession ( CNetSession* pSession ); - virtual bool HandleConnect ( CNetSession* pSession ); - virtual bool HandleDisconnect ( CNetSession* pSession ); - virtual bool HandleMessageReceive ( - CNetMessage* pMessage, - CNetSession* pSession ); -private: - - std::vector m_Buffer; // Serialize out messages buffer - ENetHost* m_Host; // Represents this host - PeerSessionList m_PeerSessions; // Session list of connected peers + CNetHost* pHost; + CNetSession* pSession; }; -/* - CLASS : CNetSession - DESCRIPTION : CNetSession is a wrapper class around ENet peer concept - which represents a peer from a network connection. A - network session is spawned by CNetServer each time a - client connects and destroyed when it disconnects. When a - new message is received from a client, its representing - session object's message handler is called for processing - that message. - CNetSession is also a state machine. All client requests - are delegated to the current state. The current - CNetSessionState object's methods will change the current - state as appropriate. - NOTES : -*/ +/** + * CNetSession is a wrapper class around the ENet peer concept + * which represents a peer from a network connection. A + * network session is spawned by CNetServer each time a + * client connects and destroyed when it disconnects. When a + * new message is received from a client, its representing + * session object's message handler is called for processing + * that message. + * CNetSession is also a state machine. All client requests + * are delegated to the current state. The current + * CNetSessionState object's methods will change the current + * state as appropriate. + */ class CNetSession : public CFsm, - public CJSObject< CNetSession > + public CJSObject { NONCOPYABLE(CNetSession); @@ -186,51 +55,53 @@ class CNetSession : public CFsm, public: - virtual ~CNetSession( void ); + virtual ~CNetSession(); /** * Retrieves the name of the session * * @return Session name */ - const CStrW& GetName( void ) const { return m_Name; } + const CStrW& GetName() const { return m_Name; } /** * Set the new name for the session * * @param name The session new name */ - void SetName( const CStr& name ); + void SetName(const CStr& name); /** * Retrieves the ID of the session * * @return Session ID */ - uint GetID( void ) const { return m_ID; } + uint GetID() const { return m_ID; } /** * Set the ID for this session * * @param New session ID */ - void SetID( uint ID ); + void SetID(uint ID); - void SetPlayerSlot( CPlayerSlot* pPlayerSlot ); - CPlayerSlot* GetPlayerSlot( void ) { return m_PlayerSlot; } - static void ScriptingInit( void ); + FsmActionCtx* GetFsmActionCtx() { return &m_FsmActionCtx; } + + void SetPlayerSlot(CPlayerSlot* pPlayerSlot); + CPlayerSlot* GetPlayerSlot() { return m_PlayerSlot; } + static void ScriptingInit(); private: // Only the hosts can create sessions - CNetSession( CNetHost* pHost, ENetPeer* pPeer ); + CNetSession(CNetHost* pHost, ENetPeer* pPeer); CNetHost* m_Host; // The associated local host ENetPeer* m_Peer; // Represents the peer host uint m_ID; // Session ID CStrW m_Name; // Session name CPlayerSlot* m_PlayerSlot; + FsmActionCtx m_FsmActionCtx; }; #endif // NETSESSION_H - diff --git a/source/network/Network.h b/source/network/Network.h index 87da0f4b10..d093f121b8 100644 --- a/source/network/Network.h +++ b/source/network/Network.h @@ -24,14 +24,14 @@ class CErrorMessage : public CNetMessage { public: - PS_RESULT m_Error; + const char* m_Error; - inline CErrorMessage() : - CNetMessage(NMT_ERROR) + CErrorMessage() : + CNetMessage(NMT_ERROR), m_Error(NULL) { } - inline CErrorMessage(PS_RESULT error) : + CErrorMessage(const char* error) : CNetMessage(NMT_ERROR), m_Error(error) { } @@ -41,7 +41,7 @@ public: struct CCloseRequestMessage : public CNetMessage { - inline CCloseRequestMessage() : + CCloseRequestMessage() : CNetMessage(NMT_CLOSE_REQUEST) { } @@ -51,7 +51,7 @@ struct CCloseRequestMessage : public CNetMessage struct CConnectCompleteMessage : public CNetMessage { - inline CConnectCompleteMessage() : + CConnectCompleteMessage() : CNetMessage(NMT_CONNECT_COMPLETE) { } diff --git a/source/network/fsm.h b/source/network/fsm.h index 3162c5a13d..63a4b80f74 100644 --- a/source/network/fsm.h +++ b/source/network/fsm.h @@ -56,13 +56,14 @@ typedef std::vector< CallbackFunction > CallbackList; /* CLASS : CFsmEvent DESCRIPTION : CFsmEvent class represents a signal in the state machine - that a change has occured. + that a change has occurred. NOTES : The CFsmEvent objects are under the control of CFsm so they are created and deleted via CFsm. */ class CFsmEvent { + NONCOPYABLE(CFsmEvent); public: CFsmEvent( unsigned int type ); @@ -72,14 +73,7 @@ public: void* GetParamRef ( void ) { return m_Param; } void SetParamRef ( void* pParam ); -protected: - private: - - // Not implemented - CFsmEvent( const CFsmEvent& ); - CFsmEvent& operator=( const CFsmEvent& ); - unsigned int m_Type; // Event type void* m_Param; // Event paramater }; @@ -94,6 +88,7 @@ private: class CFsmTransition { + NONCOPYABLE(CFsmTransition); public: CFsmTransition( unsigned int state ); @@ -115,14 +110,7 @@ public: bool ApplyConditions ( void ) const; bool RunActions ( void ) const; -protected: - private: - - // Not implemented - CFsmTransition( const CFsmTransition& ); - CFsmTransition& operator=( const CFsmTransition& ); - unsigned int m_CurrState; // Current state unsigned int m_NextState; // Next state CFsmEvent* m_Event; // Transition event @@ -144,6 +132,7 @@ private: class CFsm { + NONCOPYABLE(CFsm); public: CFsm( void ); @@ -188,14 +177,7 @@ public: bool IsValidEvent ( unsigned int eventType ) const; virtual bool IsDone ( void ) const; -protected: - private: - - // Not implemented - CFsm( const CFsm& ); - CFsm& operator=( const CFsm& ); - void SetCurrState ( unsigned int state ); bool IsFirstTime ( void ) const; diff --git a/source/network/tests/test_Net.h b/source/network/tests/test_Net.h new file mode 100644 index 0000000000..a415786a50 --- /dev/null +++ b/source/network/tests/test_Net.h @@ -0,0 +1,165 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. 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 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. 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 0 A.D. If not, see . + */ + +#include "lib/self_test.h" + +#include "lib/external_libraries/sdl.h" +#include "network/NetServer.h" +#include "network/NetClient.h" +#include "ps/ConfigDB.h" +#include "ps/Filesystem.h" +#include "ps/GameAttributes.h" +#include "ps/XML/Xeromyces.h" +#include "scriptinterface/ScriptInterface.h" + +class TestNetServer : public CNetServer +{ +public: + TestNetServer(ScriptInterface& scriptInterface, CGameAttributes& gameAttributes) : + CNetServer(scriptInterface, NULL, &gameAttributes), m_HasConnection(false) + { + } + + bool m_HasConnection; + +protected: + virtual void OnPlayerJoin(CNetSession* pSession) + { + debug_printf(L"# player joined\n"); + + for (size_t slot = 0; slot < m_GameAttributes->GetSlotCount(); ++slot) + { + if (m_GameAttributes->GetSlot(slot)->GetAssignment() == SLOT_OPEN) + { + debug_printf(L"# assigning slot %d\n", slot); + m_GameAttributes->GetSlot(slot)->AssignToSession(pSession); + m_HasConnection = true; + break; + } + } + } + + virtual void OnPlayerLeave(CNetSession* UNUSED(pSession)) + { + debug_printf(L"# player left\n"); + } +}; + +class TestNetClient : public CNetClient +{ +public: + TestNetClient(ScriptInterface& scriptInterface, CGameAttributes& gameAttributes) : + CNetClient(scriptInterface, NULL, &gameAttributes), m_HasConnection(false) + { + } + + bool m_HasConnection; + +protected: + virtual void OnConnectComplete() + { + debug_printf(L"# connect complete\n"); + m_HasConnection = true; + } + + virtual void OnStartGame() + { + debug_printf(L"# start game\n"); + } +}; + +class TestNetComms : public CxxTest::TestSuite +{ +public: + void setUp() + { + g_VFS = CreateVfs(20 * MiB); + TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/L"mods/public", VFS_MOUNT_MUST_EXIST)); + TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/L"_testcache")); + CXeromyces::Startup(); + + new ScriptingHost; + CGameAttributes::ScriptingInit(); + CNetServer::ScriptingInit(); + CNetClient::ScriptingInit(); + + new CConfigDB; + } + + void tearDown() + { + delete &g_ConfigDB; + + CGameAttributes::ScriptingShutdown(); + CNetServer::ScriptingShutdown(); + CNetClient::ScriptingShutdown(); + delete &g_ScriptingHost; + + CXeromyces::Terminate(); + g_VFS.reset(); + DeleteDirectory(DataDir()/L"_testcache"); + } + + void connect(TestNetServer& server, TestNetClient& client) + { + TS_ASSERT(server.Start(NULL, 0, NULL)); + TS_ASSERT(client.Create()); + TS_ASSERT(client.ConnectAsync("127.0.0.1", DEFAULT_HOST_PORT)); + for (size_t i = 0; ; ++i) + { + debug_printf(L"."); + server.Poll(); + client.Poll(); + + if (server.m_HasConnection && client.m_HasConnection) + break; + + if (i > 20) + { + TS_FAIL("connection timeout"); + break; + } + + SDL_Delay(100); + } + } + + void test_basic_DISABLED() + { + ScriptInterface scriptInterface("Engine"); + + CGameAttributes gameAttributesServer; + CGameAttributes gameAttributesClient; + TestNetServer server(scriptInterface, gameAttributesServer); + TestNetClient client(scriptInterface, gameAttributesClient); + connect(server, client); + client.CNetHost::Shutdown(); + server.CNetHost::Shutdown(); + } + + void TODO_test_destructor() + { + ScriptInterface scriptInterface("Engine"); + + CGameAttributes gameAttributesServer; + CGameAttributes gameAttributesClient; + TestNetServer server(scriptInterface, gameAttributesServer); + TestNetClient client(scriptInterface, gameAttributesClient); + connect(server, client); + // run in Valgrind; this shouldn't leak + } +}; diff --git a/source/ps/CLogger.h b/source/ps/CLogger.h index 256b008001..0452659126 100644 --- a/source/ps/CLogger.h +++ b/source/ps/CLogger.h @@ -32,9 +32,9 @@ extern CLogger* g_Logger; // Should become LOG_MESSAGE but this can only be changed once the LOG function is removed // from all of the files. LOG_INFO, LOG_WARNING and LOG_ERROR are currently existing macros. -#define LOGMESSAGE (g_Logger->LogMessage) -#define LOGWARNING (g_Logger->LogWarning) -#define LOGERROR (g_Logger->LogError) +#define LOGMESSAGE g_Logger->LogMessage +#define LOGWARNING g_Logger->LogWarning +#define LOGERROR g_Logger->LogError class CLogger { diff --git a/source/ps/ConfigDB.h b/source/ps/ConfigDB.h index 52e79f498e..d129ce24d4 100644 --- a/source/ps/ConfigDB.h +++ b/source/ps/ConfigDB.h @@ -50,7 +50,6 @@ #ifndef INCLUDED_CONFIGDB #define INCLUDED_CONFIGDB -#include "Pyrogenesis.h" #include "Parser.h" #include "CStr.h" #include "Singleton.h" diff --git a/source/ps/GameAttributes.cpp b/source/ps/GameAttributes.cpp index f35ed94726..eb24434e16 100644 --- a/source/ps/GameAttributes.cpp +++ b/source/ps/GameAttributes.cpp @@ -27,6 +27,7 @@ #include "ps/XML/Xeromyces.h" #include "simulation/LOSManager.h" +CGameAttributes *g_GameAttributes; CPlayerSlot::CPlayerSlot(size_t slotID, CPlayer *pPlayer): m_SlotID(slotID), diff --git a/source/ps/GameAttributes.h b/source/ps/GameAttributes.h index a95828947e..27355e4ca4 100644 --- a/source/ps/GameAttributes.h +++ b/source/ps/GameAttributes.h @@ -133,8 +133,7 @@ namespace PlayerSlotArray_JS } class CGameAttributes: - public CSynchedJSObject, - public Singleton + public CSynchedJSObject { public: typedef void (UpdateCallback)(const CStrW& name, const CStrW& newValue, void *data); @@ -210,6 +209,7 @@ public: static void ScriptingInit(); }; -#define g_GameAttributes CGameAttributes::GetSingleton() + +extern CGameAttributes *g_GameAttributes; #endif diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index c4dfd7e654..12ec7f3c92 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -37,6 +37,7 @@ #include "ps/Filesystem.h" #include "ps/Font.h" #include "ps/Game.h" +#include "ps/GameAttributes.h" #include "ps/Globals.h" #include "ps/Hotkey.h" #include "ps/Loader.h" @@ -85,7 +86,6 @@ #include "sound/JSI_Sound.h" -#include "network/NetLog.h" #include "network/NetServer.h" #include "network/NetClient.h" @@ -584,7 +584,7 @@ void Shutdown(int flags) if (! (flags & INIT_NO_SIM)) { - delete &g_GameAttributes; + SAFE_DELETE(g_GameAttributes); } // destroy actor related stuff @@ -611,10 +611,6 @@ void Shutdown(int flags) delete &g_ConfigDB; TIMER_END(L"shutdown ConfigDB"); - TIMER_BEGIN(L"shutdown CNetLogManager"); - CNetLogManager::Shutdown(); - TIMER_END(L"shutdown CNetLogManager"); - // Really shut down the i18n system. Any future calls // to translate() will crash. TIMER_BEGIN(L"shutdown I18N"); @@ -809,10 +805,10 @@ void Init(const CmdLineArgs& args, int flags) if (! (flags & INIT_NO_SIM)) { - new CGameAttributes; + g_GameAttributes = new CGameAttributes; // Register a few Game/Network JS globals - g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes.GetScript())); + g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes->GetScript())); } InitInput(); @@ -838,17 +834,17 @@ class AutostartNetServer : public CNetServer { public: AutostartNetServer(CGame *pGame, CGameAttributes *pGameAttributes, int maxPlayers) : - CNetServer(pGame, pGameAttributes), m_NumPlayers(1), m_MaxPlayers(maxPlayers) + CNetServer(pGame->GetSimulation2()->GetScriptInterface(), pGame, pGameAttributes), m_NumPlayers(1), m_MaxPlayers(maxPlayers) { } protected: virtual void OnPlayerJoin(CNetSession* pSession) { - for (size_t slot = 0; slot < g_GameAttributes.GetSlotCount(); ++slot) + for (size_t slot = 0; slot < m_GameAttributes->GetSlotCount(); ++slot) { - if (g_GameAttributes.GetSlot(slot)->GetAssignment() == SLOT_OPEN) + if (m_GameAttributes->GetSlot(slot)->GetAssignment() == SLOT_OPEN) { - g_GameAttributes.GetSlot(slot)->AssignToSession(pSession); + m_GameAttributes->GetSlot(slot)->AssignToSession(pSession); break; } } @@ -880,7 +876,7 @@ class AutostartNetClient : public CNetClient { public: AutostartNetClient(CGame *pGame, CGameAttributes *pGameAttributes) : - CNetClient(pGame, pGameAttributes) + CNetClient(pGame->GetSimulation2()->GetScriptInterface(), pGame, pGameAttributes) { } protected: @@ -912,11 +908,11 @@ static bool Autostart(const CmdLineArgs& args) g_Game = new CGame(); - g_GameAttributes.m_MapFile = autostartMap + ".pmp"; + g_GameAttributes->m_MapFile = autostartMap + ".pmp"; // Make the whole world visible - g_GameAttributes.m_LOSSetting = LOS_SETTING_ALL_VISIBLE; - g_GameAttributes.m_FogOfWar = false; + g_GameAttributes->m_LOSSetting = LOS_SETTING_ALL_VISIBLE; + g_GameAttributes->m_FogOfWar = false; if (args.Has("autostart-host")) { @@ -926,7 +922,7 @@ static bool Autostart(const CmdLineArgs& args) if (args.Has("autostart-players")) maxPlayers = args.Get("autostart-players").ToUInt(); - g_NetServer = new AutostartNetServer(g_Game, &g_GameAttributes, maxPlayers); + g_NetServer = new AutostartNetServer(g_Game, g_GameAttributes, maxPlayers); // TODO: player name, etc bool ok = g_NetServer->Start(NULL, 0, NULL); debug_assert(ok); @@ -936,7 +932,7 @@ static bool Autostart(const CmdLineArgs& args) InitPs(true, L"page_loading.xml"); bool ok; - g_NetClient = new AutostartNetClient(g_Game, &g_GameAttributes); + g_NetClient = new AutostartNetClient(g_Game, g_GameAttributes); // TODO: player name, etc ok = g_NetClient->Create(); debug_assert(ok); @@ -946,9 +942,9 @@ static bool Autostart(const CmdLineArgs& args) else { for (int i = 1; i < 8; ++i) - g_GameAttributes.GetSlot(i)->AssignLocal(); + g_GameAttributes->GetSlot(i)->AssignLocal(); - PSRETURN ret = g_Game->StartGame(&g_GameAttributes); + PSRETURN ret = g_Game->StartGame(g_GameAttributes); debug_assert(ret == PSRETURN_OK); LDR_NonprogressiveLoad(); ret = g_Game->ReallyStartGame(); diff --git a/source/ps/Pyrogenesis.cpp b/source/ps/Pyrogenesis.cpp index fc65bf07d8..f72154c2bc 100644 --- a/source/ps/Pyrogenesis.cpp +++ b/source/ps/Pyrogenesis.cpp @@ -26,10 +26,6 @@ #include "lib/path_util.h" #include "lib/svn_revision.h" -DEFINE_ERROR(PS_OK, "OK"); -DEFINE_ERROR(PS_FAIL, "Fail"); - - static const wchar_t* translate_no_mem = L"(no mem)"; // overrides ah_translate. registered in GameSetup.cpp diff --git a/source/ps/Pyrogenesis.h b/source/ps/Pyrogenesis.h index e13cab0fc5..3bc0850e75 100644 --- a/source/ps/Pyrogenesis.h +++ b/source/ps/Pyrogenesis.h @@ -24,19 +24,8 @@ Standard declarations which are included in all projects. #ifndef INCLUDED_PYROGENESIS #define INCLUDED_PYROGENESIS -typedef const char * PS_RESULT; - -#define DEFINE_ERROR(x, y) PS_RESULT x=y -#define DECLARE_ERROR(x) extern PS_RESULT x - -DECLARE_ERROR(PS_OK); -DECLARE_ERROR(PS_FAIL); - - - #define MICROLOG debug_wprintf_mem - // overrides ah_translate. registered in GameSetup.cpp extern const wchar_t* psTranslate(const wchar_t* text); extern void psTranslateFree(const wchar_t* text); diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index 3af0eb87bb..a57435a91e 100644 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -45,6 +45,7 @@ #include "ps/CLogger.h" #include "ps/CStr.h" #include "ps/Game.h" +#include "ps/GameAttributes.h" #include "ps/Globals.h" // g_frequencyFilter #include "ps/GameSetup/GameSetup.h" #include "ps/Hotkey.h" @@ -57,6 +58,7 @@ #include "renderer/Renderer.h" #include "renderer/SkyManager.h" #include "scriptinterface/ScriptInterface.h" +#include "simulation2/Simulation2.h" #define LOG_CATEGORY L"script" extern bool g_TerrainModified; @@ -202,7 +204,7 @@ JSBool CreateServer(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rv if( !g_Game ) g_Game = new CGame(); if( !g_NetServer ) - g_NetServer = new CNetServer(g_Game, &g_GameAttributes); + g_NetServer = new CNetServer(g_Game->GetSimulation2()->GetScriptInterface(), g_Game, g_GameAttributes); *rval = OBJECT_TO_JSVAL(g_NetServer->GetScript()); return( JS_TRUE ); @@ -219,7 +221,7 @@ JSBool CreateClient(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rv if( !g_Game ) g_Game = new CGame(); if( !g_NetClient ) - g_NetClient = new CNetClient(g_Game, &g_GameAttributes); + g_NetClient = new CNetClient(g_Game->GetSimulation2()->GetScriptInterface(), g_Game, g_GameAttributes); *rval = OBJECT_TO_JSVAL(g_NetClient->GetScript()); return( JS_TRUE ); @@ -255,7 +257,7 @@ JSBool StartGame(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rval) else if (!g_Game) { g_Game = new CGame(); - PSRETURN ret = g_Game->StartGame(&g_GameAttributes); + PSRETURN ret = g_Game->StartGame(g_GameAttributes); if (ret != PSRETURN_OK) { // Failed to start the game - destroy it, and return false @@ -291,7 +293,7 @@ JSBool GetGameMode(JSContext* cx, JSObject*, uintN argc, jsval* argv, jsval* rva { JSU_REQUIRE_NO_PARAMS(); - *rval = ToJSVal( g_GameAttributes.GetGameMode() ); + *rval = ToJSVal( g_GameAttributes->GetGameMode() ); return JS_TRUE; } diff --git a/source/scripting/ScriptableObject.h b/source/scripting/ScriptableObject.h index a9f8fbb9aa..c1347bbf74 100644 --- a/source/scripting/ScriptableObject.h +++ b/source/scripting/ScriptableObject.h @@ -256,6 +256,7 @@ public: PropertyTable::iterator it; for( it = m_NativeProperties.begin(); it != m_NativeProperties.end(); it++ ) delete( it->second ); + m_NativeProperties.clear(); } // JS Property access diff --git a/source/scripting/ScriptingHost.cpp b/source/scripting/ScriptingHost.cpp index 078cc38485..e872b1154c 100644 --- a/source/scripting/ScriptingHost.cpp +++ b/source/scripting/ScriptingHost.cpp @@ -334,6 +334,9 @@ void ScriptingHost::ErrorReporter(JSContext* UNUSED(cx), const char* pmessage, J void* ScriptingHost::jshook_script( JSContext* UNUSED(cx), JSStackFrame* UNUSED(fp), JSBool before, JSBool* UNUSED(ok), void* closure ) { + if (!CProfileManager::IsInitialised()) + return closure; + if( before ) { g_Profiler.StartScript( "script invocation" ); @@ -346,6 +349,9 @@ void* ScriptingHost::jshook_script( JSContext* UNUSED(cx), JSStackFrame* UNUSED( void* ScriptingHost::jshook_function( JSContext* cx, JSStackFrame* fp, JSBool before, JSBool* UNUSED(ok), void* closure ) { + if (!CProfileManager::IsInitialised()) + return closure; + JSFunction* fn = JS_GetFrameFunction( cx, fp ); if( before ) { diff --git a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp index a5b54c05e9..a5aa619498 100644 --- a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -50,19 +50,19 @@ namespace // Set attributes for the game: - g_GameAttributes.m_MapFile = map; + g_GameAttributes->m_MapFile = map; // Make all players locally controlled for (int i = 1; i < 8; ++i) - g_GameAttributes.GetSlot(i)->AssignLocal(); + g_GameAttributes->GetSlot(i)->AssignLocal(); // Make the whole world visible - g_GameAttributes.m_LOSSetting = LOS_SETTING_ALL_VISIBLE; - g_GameAttributes.m_FogOfWar = false; + g_GameAttributes->m_LOSSetting = LOS_SETTING_ALL_VISIBLE; + g_GameAttributes->m_FogOfWar = false; // Don't use screenshot mode, because we want working AI for the // simulation-testing. Outside that simulation-testing, we avoid having // the units move into attack mode by never calling CEntity::update. - g_GameAttributes.m_ScreenshotMode = false; + g_GameAttributes->m_ScreenshotMode = false; // Initialise the game: g_Game = new CGame(); @@ -86,7 +86,7 @@ namespace void StartGame() { - PSRETURN ret = g_Game->StartGame(&g_GameAttributes); + PSRETURN ret = g_Game->StartGame(g_GameAttributes); debug_assert(ret == PSRETURN_OK); LDR_NonprogressiveLoad(); ret = g_Game->ReallyStartGame();