# Various networking code cleanups.

Don't flush network queue after every message.
Desingletonify CGameAttributes to allow testing.
Make network server/client basically testable.
Add very basic tests for network server/client.
Fix FsmActionCtx memory leak.
Split CNetHost into a separate file.
Convert CNetHost, CNetSession to a more conventional coding style.
Delete CNetLogger, since it's a lot of complexity and is barely used and
is redundant with CLogger.
Other minor simplifications.

This was SVN commit r7623.
This commit is contained in:
Ykkrosh 2010-06-07 22:19:05 +00:00
parent 2c160e5bd8
commit dfb10c8209
24 changed files with 910 additions and 2525 deletions

View File

@ -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 );
}
}

View File

@ -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 );

467
source/network/NetHost.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "NetHost.h"
#include "NetSession.h"
#include "NetMessage.h"
#include "ps/CLogger.h"
#include "simulation2/Simulation2.h"
#include <enet/enet.h>
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<u8> 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;
}

147
source/network/NetHost.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef NETHOST_H
#define NETHOST_H
#include "fsm.h"
#include "ps/CStr.h"
#include <vector>
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<PeerSession> 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

File diff suppressed because it is too large Load Diff

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/**
*-----------------------------------------------------------------------------
* 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 <list>
#include <vector>
#include <fstream>
// 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

View File

@ -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 );
}

View File

@ -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;
/*

View File

@ -15,477 +15,37 @@
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*-----------------------------------------------------------------------------
* 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<CNetSession>::ScriptingInit( "NetSession" );
CJSObject<CNetSession>::ScriptingInit("NetSession");
}

View File

@ -15,170 +15,39 @@
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*-----------------------------------------------------------------------------
* 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 <enet/enet.h>
#include "scripting/ScriptableObject.h"
#include <vector>
// 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<u8> 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<CNetSession>
{
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

View File

@ -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)
{
}

View File

@ -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;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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
}
};

View File

@ -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
{

View File

@ -50,7 +50,6 @@
#ifndef INCLUDED_CONFIGDB
#define INCLUDED_CONFIGDB
#include "Pyrogenesis.h"
#include "Parser.h"
#include "CStr.h"
#include "Singleton.h"

View File

@ -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),

View File

@ -133,8 +133,7 @@ namespace PlayerSlotArray_JS
}
class CGameAttributes:
public CSynchedJSObject<CGameAttributes>,
public Singleton<CGameAttributes>
public CSynchedJSObject<CGameAttributes>
{
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

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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 )
{

View File

@ -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();