# Basic experimental multiplayer integration with new simulation system.
Add new turn manager that should be more correct and potentially more efficient. Remove evil CNetServer/CNetClient multiple inheritance of CTurnManager. Add multiplayer autostart. Various minor cleanups. This was SVN commit r7551.
This commit is contained in:
parent
8c1a3f7c8a
commit
c684c211a2
@ -143,7 +143,6 @@ Watch for updates or get involved in the development: http://wildfiregames.com/0
|
||||
sprite_over="pgMultiPlayerOver"
|
||||
sprite_disabled="pgMultiPlayerDisabled"
|
||||
tooltip="Tired of playing with yourself? Fight against one or more human players in a multiplayer game."
|
||||
enabled="false"
|
||||
>
|
||||
<action on="Press"><![CDATA[
|
||||
// Open Multiplayer connection window.
|
||||
|
@ -76,6 +76,11 @@ CGUIManager::~CGUIManager()
|
||||
{
|
||||
}
|
||||
|
||||
bool CGUIManager::HasPages()
|
||||
{
|
||||
return !m_PageStack.empty();
|
||||
}
|
||||
|
||||
void CGUIManager::SwitchPage(const CStrW& pageName, CScriptVal initData)
|
||||
{
|
||||
m_PageStack.clear();
|
||||
|
@ -49,16 +49,27 @@ public:
|
||||
|
||||
ScriptInterface& GetScriptInterface() { return m_ScriptInterface; }
|
||||
|
||||
// Load a new GUI page and make it active. All current pages will be destroyed.
|
||||
/**
|
||||
* Returns whether there are any current pages.
|
||||
*/
|
||||
bool HasPages();
|
||||
|
||||
/**
|
||||
* Load a new GUI page and make it active. All current pages will be destroyed.
|
||||
*/
|
||||
void SwitchPage(const CStrW& name, CScriptVal initData);
|
||||
|
||||
// Load a new GUI page and make it active. All current pages will be retained,
|
||||
// and will still be drawn and receive tick events, but will not receive
|
||||
// user inputs.
|
||||
/**
|
||||
* Load a new GUI page and make it active. All current pages will be retained,
|
||||
* and will still be drawn and receive tick events, but will not receive
|
||||
* user inputs.
|
||||
*/
|
||||
void PushPage(const CStrW& name, CScriptVal initData);
|
||||
|
||||
// Unload the currently active GUI page, and make the previous page active.
|
||||
// (There must be at least two pages when you call this.)
|
||||
/**
|
||||
* Unload the currently active GUI page, and make the previous page active.
|
||||
* (There must be at least two pages when you call this.)
|
||||
*/
|
||||
void PopPage();
|
||||
|
||||
/**
|
||||
|
@ -103,16 +103,9 @@ void PostNetworkCommand(void* cbdata, CScriptVal cmd)
|
||||
if (queue.null())
|
||||
return;
|
||||
|
||||
int player = -1;
|
||||
if (g_Game && g_Game->GetLocalPlayer())
|
||||
player = g_Game->GetLocalPlayer()->GetPlayerID();
|
||||
|
||||
jsval cmd2 = sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), cmd.get());
|
||||
|
||||
queue->PushClientCommand(player, cmd2);
|
||||
// TODO: This shouldn't call Push, it should send the message to the network layer
|
||||
// (which should propagate it across the network and eventually call Push on the
|
||||
// appropriate turn)
|
||||
queue->PostNetworkCommand(cmd2);
|
||||
}
|
||||
|
||||
std::vector<entity_id_t> PickEntitiesAtPoint(void* UNUSED(cbdata), int x, int y)
|
||||
|
@ -257,10 +257,15 @@ static void Frame()
|
||||
PROFILE_START("input");
|
||||
MICROLOG(L"input");
|
||||
PumpEvents();
|
||||
//g_SessionManager.Poll();
|
||||
if ( CNetHost::IsInitialised() )
|
||||
CNetHost::GetSingleton().Poll();
|
||||
PROFILE_END("input");
|
||||
//g_SessionManager.Poll();
|
||||
|
||||
PROFILE_START("network poll");
|
||||
if (g_NetServer)
|
||||
g_NetServer->Poll();
|
||||
if (g_NetClient)
|
||||
g_NetClient->Poll();
|
||||
PROFILE_END("network poll");
|
||||
|
||||
ogl_WarnIfError();
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -218,14 +218,6 @@ u8 *_nm::Serialize(u8 *buffer) const \
|
||||
#define START_NMT_CLASS(_nm, _tp) \
|
||||
START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp)
|
||||
#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \
|
||||
CNetMessage *Deserialize##_nm(const u8 *buffer, size_t length) \
|
||||
{ \
|
||||
_nm *ret=new _nm(); \
|
||||
if (ret->Deserialize(buffer, buffer+length)) \
|
||||
return ret; \
|
||||
else \
|
||||
{ delete ret; return NULL; } \
|
||||
} \
|
||||
const u8 *_nm::Deserialize(const u8 *pos, const u8 *end) \
|
||||
{ \
|
||||
pos=_base::Deserialize(pos, end); \
|
||||
@ -262,46 +254,7 @@ const u8 *_nm::Deserialize(const u8 *pos, const u8 *end) \
|
||||
#undef NMT_CREATOR_PASS_DESERIALIZE
|
||||
|
||||
/*************************************************************************/
|
||||
// Pass 5, Deserializer Registration
|
||||
|
||||
#define NMT_CREATOR_PASS_REGISTRATION
|
||||
|
||||
//#define START_NMTS() SNetMessageDeserializerRegistration g_DeserializerRegistrations[] = {
|
||||
//#define END_NMTS() { NMT_INVALID, NULL } };
|
||||
#define START_NMTS()
|
||||
#define END_NMTS()
|
||||
|
||||
#define START_NMT_CLASS(_nm, _tp) \
|
||||
START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp)
|
||||
|
||||
/*
|
||||
#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \
|
||||
{ _tp, Deserialize##_nm },
|
||||
*/
|
||||
|
||||
#define START_NMT_CLASS_DERIVED(_base, _nm, _tp)
|
||||
|
||||
#define NMT_START_ARRAY(_nm)
|
||||
|
||||
#define NMT_END_ARRAY()
|
||||
|
||||
#define NMT_FIELD_INT(_nm, _hosttp, _netsz)
|
||||
|
||||
#define NMT_FIELD(_tp, _nm)
|
||||
|
||||
#define END_NMT_CLASS()
|
||||
|
||||
#include "NMTCreator.h"
|
||||
|
||||
#undef NMT_CREATOR_PASS_REGISTRATION
|
||||
|
||||
/*************************************************************************/
|
||||
// Pass 6, String Representation
|
||||
|
||||
#define _T(s) s
|
||||
// PT: I'm not sure whether this is correct - it appears that this code
|
||||
// relied on CStr.h leaving _T defined in ASCII mode, so I'm not sure
|
||||
// why the macro is actually used at all...
|
||||
// Pass 5, String Representation
|
||||
|
||||
#define START_NMTS()
|
||||
#define END_NMTS()
|
||||
@ -309,8 +262,8 @@ const u8 *_nm::Deserialize(const u8 *pos, const u8 *end) \
|
||||
#define START_NMT_CLASS(_nm, _tp) \
|
||||
CStr _nm::ToString() const \
|
||||
{ \
|
||||
CStr ret=#_nm _T(" { "); \
|
||||
return ret + ToStringRaw() + _T(" }"); \
|
||||
CStr ret=#_nm " { "; \
|
||||
return ret + ToStringRaw() + " }"; \
|
||||
} \
|
||||
CStr _nm::ToStringRaw() const \
|
||||
{ \
|
||||
@ -321,39 +274,39 @@ CStr _nm::ToStringRaw() const \
|
||||
#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \
|
||||
CStr _nm::ToString() const \
|
||||
{ \
|
||||
CStr ret=#_nm _T(" { "); \
|
||||
return ret + ToStringRaw() + _T(" }"); \
|
||||
CStr ret=#_nm " { "; \
|
||||
return ret + ToStringRaw() + " }"; \
|
||||
} \
|
||||
CStr _nm::ToStringRaw() const \
|
||||
{ \
|
||||
CStr ret=_base::ToStringRaw() + _T(", "); \
|
||||
CStr ret=_base::ToStringRaw() + ", "; \
|
||||
const _nm *thiz=this;\
|
||||
UNUSED2(thiz); // preempt any "unused" warning
|
||||
|
||||
#define NMT_START_ARRAY(_nm) \
|
||||
ret+=#_nm _T(": { "); \
|
||||
ret+=#_nm ": { "; \
|
||||
std::vector < ARRAY_STRUCT_PREFIX(_nm) >::const_iterator it=_nm.begin(); \
|
||||
while (it != _nm.end()) \
|
||||
{ \
|
||||
ret+=_T(" { "); \
|
||||
ret+=" { "; \
|
||||
const ARRAY_STRUCT_PREFIX(_nm) *thiz=&*it;\
|
||||
UNUSED2(thiz); // preempt any "unused" warning
|
||||
|
||||
#define NMT_END_ARRAY() \
|
||||
++it; \
|
||||
ret=ret.substr(0, ret.length()-2)+_T(" }, "); \
|
||||
ret=ret.substr(0, ret.length()-2)+" }, "; \
|
||||
} \
|
||||
ret=ret.substr(0, ret.length()-2)+_T(" }, ");
|
||||
ret=ret.substr(0, ret.length()-2)+" }, ";
|
||||
|
||||
#define NMT_FIELD_INT(_nm, _hosttp, _netsz) \
|
||||
ret += #_nm _T(": "); \
|
||||
ret += #_nm ": "; \
|
||||
ret += NetMessageStringConvert(thiz->_nm); \
|
||||
ret += _T(", ");
|
||||
ret += ", ";
|
||||
|
||||
#define NMT_FIELD(_tp, _nm) \
|
||||
ret += #_nm _T(": "); \
|
||||
ret += #_nm ": "; \
|
||||
ret += NetMessageStringConvert(thiz->_nm); \
|
||||
ret += _T(", ");
|
||||
ret += ", ";
|
||||
|
||||
#define END_NMT_CLASS() \
|
||||
return ret.substr(0, ret.length()-2); \
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -39,13 +39,24 @@
|
||||
#include "ps/Globals.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "simulation/Simulation.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
// DECLARATIONS
|
||||
|
||||
#define LOG_CATEGORY L"net"
|
||||
|
||||
CNetClient *g_NetClient = NULL;
|
||||
extern int fps;
|
||||
|
||||
class CClientTurnManager : public CTurnManager
|
||||
{
|
||||
public:
|
||||
CClientTurnManager(CNetClient& client) : m_Client(client) { }
|
||||
virtual void QueueLocalCommand(CNetMessage* pMessage);
|
||||
virtual void NewTurn();
|
||||
virtual bool NewTurnReady() { return m_Client.m_TurnPending; }
|
||||
private:
|
||||
CNetClient& m_Client;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CServerPlayer()
|
||||
@ -84,6 +95,9 @@ void CServerPlayer::ScriptingInit( void )
|
||||
CNetClient::CNetClient( CGame* pGame, CGameAttributes* pGameAttribs )
|
||||
: m_JsPlayers( &m_Players )
|
||||
{
|
||||
m_TurnManager = new CClientTurnManager(*this);
|
||||
m_ClientTurnManager = NULL;
|
||||
|
||||
m_pLocalPlayerSlot = NULL;
|
||||
m_pGame = pGame;
|
||||
m_pGameAttributes = pGameAttribs;
|
||||
@ -91,7 +105,8 @@ CNetClient::CNetClient( CGame* pGame, CGameAttributes* pGameAttribs )
|
||||
|
||||
//ONCE( ScriptingInit(); );
|
||||
|
||||
m_pGame->GetSimulation()->SetTurnManager(this);
|
||||
if (!g_UseSimulation2)
|
||||
m_pGame->GetSimulation()->SetTurnManager(m_TurnManager);
|
||||
|
||||
g_ScriptingHost.SetGlobal("g_NetClient", OBJECT_TO_JSVAL(GetScript()));
|
||||
}
|
||||
@ -173,7 +188,7 @@ bool CNetClient::SetupSession( CNetSession* pSession )
|
||||
// Validate parameters
|
||||
if ( !pSession ) return false;
|
||||
|
||||
FsmActionCtx* pContext = new FsmActionCtx;
|
||||
FsmActionCtx* pContext = new FsmActionCtx; // XXX: this gets leaked
|
||||
if ( !pContext ) return false;
|
||||
|
||||
pContext->pHost = this;
|
||||
@ -193,7 +208,7 @@ bool CNetClient::SetupSession( CNetSession* pSession )
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_ASSIGN_PLAYER_SLOT, NCS_PREGAME, (void*)&OnPreGame, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_PLAYER_CONFIG, NCS_PREGAME, (void*)&OnPreGame, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_PLAYER_JOIN, NCS_PREGAME, (void*)&OnPlayerJoin, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_GAME_START, NCS_INGAME, (void*)&OnStartGame, pContext );
|
||||
pSession->AddTransition( NCS_PREGAME, ( uint )NMT_GAME_START, NCS_INGAME, (void*)&OnStartGame_, pContext );
|
||||
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_CHAT, NCS_INGAME, (void*)&OnChat, pContext );
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_GOTO, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
@ -208,6 +223,7 @@ bool CNetClient::SetupSession( CNetSession* pSession )
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_NOTIFY_REQUEST, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_FORMATION_GOTO, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_FORMATION_CONTACT_ACTION, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NCS_INGAME, ( uint )NMT_END_COMMAND_BATCH, NCS_INGAME, (void*)&OnInGame, pContext );
|
||||
|
||||
// Set first state
|
||||
@ -253,7 +269,7 @@ bool CNetClient::OnError( void* pContext, CFsmEvent* pEvent )
|
||||
if ( pEvent->GetType() != (uint)NMT_ERROR ) return true;
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
assert( pClient );
|
||||
debug_assert( pClient );
|
||||
|
||||
CErrorMessage* pMessage = ( CErrorMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
@ -283,7 +299,7 @@ bool CNetClient::OnPlayerJoin( void* pContext, CFsmEvent* pEvent )
|
||||
if ( pEvent->GetType() != NMT_PLAYER_JOIN ) return true;
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
assert( pClient );
|
||||
debug_assert( pClient );
|
||||
|
||||
CPlayerJoinMessage* pMessage = ( CPlayerJoinMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
@ -293,16 +309,25 @@ bool CNetClient::OnPlayerJoin( void* pContext, CFsmEvent* pEvent )
|
||||
pClient->OnPlayer( pMessage->m_Clients[ i ].m_SessionID, pMessage->m_Clients[ i ].m_Name );
|
||||
}
|
||||
|
||||
if ( pClient->m_OnConnectComplete.Defined() )
|
||||
{
|
||||
CConnectCompleteEvent connectComplete( ( CStrW )PS_OK, true );
|
||||
pClient->m_OnConnectComplete.DispatchEvent( pClient->GetScript(), &connectComplete );
|
||||
}
|
||||
pClient->OnConnectComplete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnConnectComplete()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetClient::OnConnectComplete( )
|
||||
{
|
||||
if ( m_OnConnectComplete.Defined() )
|
||||
{
|
||||
CConnectCompleteEvent connectComplete( ( CStrW )PS_OK, true );
|
||||
m_OnConnectComplete.DispatchEvent( GetScript(), &connectComplete );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnHandshake()
|
||||
// Desc:
|
||||
@ -315,8 +340,8 @@ bool CNetClient::OnHandshake( void* pContext, CFsmEvent* pEvent )
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
||||
|
||||
assert( pClient );
|
||||
assert( pSession );
|
||||
debug_assert( pClient );
|
||||
debug_assert( pSession );
|
||||
|
||||
switch ( pEvent->GetType() )
|
||||
{
|
||||
@ -331,7 +356,7 @@ bool CNetClient::OnHandshake( void* pContext, CFsmEvent* pEvent )
|
||||
handshake.m_MagicResponse = PS_PROTOCOL_MAGIC_RESPONSE;
|
||||
handshake.m_ProtocolVersion = PS_PROTOCOL_VERSION;
|
||||
handshake.m_SoftwareVersion = PS_PROTOCOL_VERSION;
|
||||
( ( CNetHost* )pClient )->SendMessage( pSession, &handshake );
|
||||
pClient->SendMessage( pSession, &handshake );
|
||||
}
|
||||
break;
|
||||
|
||||
@ -340,7 +365,7 @@ bool CNetClient::OnHandshake( void* pContext, CFsmEvent* pEvent )
|
||||
CAuthenticateMessage authenticate;
|
||||
authenticate.m_Name = pClient->m_Nickname;
|
||||
authenticate.m_Password = pClient->m_Password;
|
||||
( ( CNetHost* )pClient )->SendMessage( pSession, &authenticate );
|
||||
pClient->SendMessage( pSession, &authenticate );
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -361,8 +386,8 @@ bool CNetClient::OnAuthenticate( void* pContext, CFsmEvent* pEvent )
|
||||
UNUSED2(pClient);
|
||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
||||
|
||||
assert( pClient );
|
||||
assert( pSession );
|
||||
debug_assert( pClient );
|
||||
debug_assert( pSession );
|
||||
|
||||
if ( pEvent->GetType() == (uint)NMT_ERROR )
|
||||
{
|
||||
@ -395,8 +420,8 @@ bool CNetClient::OnPreGame( void* pContext, CFsmEvent* pEvent )
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetSession* pSession = ( CNetSession* )( ( FsmActionCtx* )pContext )->pSession;
|
||||
|
||||
assert( pClient );
|
||||
assert( pSession );
|
||||
debug_assert( pClient );
|
||||
debug_assert( pSession );
|
||||
|
||||
switch ( pEvent->GetType() )
|
||||
{
|
||||
@ -499,6 +524,13 @@ bool CNetClient::OnInGame( void *pContext, CFsmEvent* pEvent )
|
||||
CNetMessage* pMessage = ( CNetMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
{
|
||||
if (pMessage->GetType() == NMT_SIMULATION_COMMAND)
|
||||
{
|
||||
CSimulationMessage* simMessage = static_cast<CSimulationMessage*> (pMessage);
|
||||
pClient->m_ClientTurnManager->OnSimulationMessage(simMessage);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( pMessage->GetType() >= NMT_COMMAND_FIRST && pMessage->GetType() < NMT_COMMAND_LAST )
|
||||
{
|
||||
pClient->QueueIncomingMessage( pMessage );
|
||||
@ -511,7 +543,13 @@ bool CNetClient::OnInGame( void *pContext, CFsmEvent* pEvent )
|
||||
CEndCommandBatchMessage* pMessage = ( CEndCommandBatchMessage* )pEvent->GetParamRef();
|
||||
if ( !pMessage ) return false;
|
||||
|
||||
pClient->SetTurnLength( 1, pMessage->m_TurnLength );
|
||||
if (g_UseSimulation2)
|
||||
{
|
||||
CEndCommandBatchMessage* endMessage = static_cast<CEndCommandBatchMessage*> (pMessage);
|
||||
pClient->m_ClientTurnManager->FinishedAllCommands(endMessage->m_Turn);
|
||||
}
|
||||
|
||||
pClient->m_TurnManager->SetTurnLength( 1, pMessage->m_TurnLength );
|
||||
|
||||
pClient->m_TurnPending = true;
|
||||
|
||||
@ -563,19 +601,35 @@ bool CNetClient::OnChat( void* pContext, CFsmEvent* pEvent )
|
||||
// Name: OnStartGame()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnStartGame( void* pContext, CFsmEvent* pEvent )
|
||||
void CNetClient::OnStartGame( void )
|
||||
{
|
||||
if ( m_OnStartGame.Defined() )
|
||||
{
|
||||
CStartGameEvent event;
|
||||
m_OnStartGame.DispatchEvent( GetScript(), &event );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: OnStartGame_()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetClient::OnStartGame_( void* pContext, CFsmEvent* pEvent )
|
||||
{
|
||||
// Validate parameters
|
||||
if ( !pEvent || !pContext ) return false;
|
||||
|
||||
CNetClient* pClient = ( CNetClient* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
||||
|
||||
if ( pClient->m_OnStartGame.Defined() )
|
||||
if (g_UseSimulation2)
|
||||
{
|
||||
CStartGameEvent event;
|
||||
pClient->m_OnStartGame.DispatchEvent( pClient->GetScript(), &event );
|
||||
pClient->m_ClientTurnManager = new CNetClientTurnManager(*pClient->m_pGame->GetSimulation2(), *pClient, pClient->GetLocalPlayer()->GetPlayerID(), pSession->GetID());
|
||||
pClient->m_pGame->SetTurnManager(pClient->m_ClientTurnManager);
|
||||
}
|
||||
|
||||
pClient->OnStartGame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -635,12 +689,15 @@ int CNetClient::StartGame( void )
|
||||
|
||||
if ( m_pGame->StartGame( m_pGameAttributes ) != PSRETURN_OK ) return -1;
|
||||
|
||||
if (!g_UseSimulation2)
|
||||
{
|
||||
// Send an end-of-batch message for turn 0 to signal that we're ready.
|
||||
CEndCommandBatchMessage endBatch;
|
||||
endBatch.m_TurnLength = 1000 / g_frequencyFilter->StableFrequency();
|
||||
|
||||
CNetSession* pSession = GetSession( 0 );
|
||||
CNetHost::SendMessage( pSession, &endBatch );
|
||||
SendMessage( pSession, &endBatch );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -658,33 +715,33 @@ CPlayer* CNetClient::GetLocalPlayer()
|
||||
// Name: NewTurn()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetClient::NewTurn()
|
||||
void CClientTurnManager::NewTurn()
|
||||
{
|
||||
CScopeLock lock(m_Mutex);
|
||||
CScopeLock lock(m_Client.m_Mutex);
|
||||
|
||||
RotateBatches();
|
||||
ClearBatch(2);
|
||||
m_TurnPending = false;
|
||||
m_Client.m_TurnPending = false;
|
||||
|
||||
//debug_printf(L"In NewTurn - sending ack\n");
|
||||
CEndCommandBatchMessage* pMsg = new CEndCommandBatchMessage;
|
||||
pMsg->m_TurnLength=1000/g_frequencyFilter->StableFrequency(); // JW: it'd probably be nicer to get the FPS as a parameter
|
||||
|
||||
CNetSession* pSession = GetSession( 0 );
|
||||
CNetHost::SendMessage( pSession, pMsg );
|
||||
CNetSession* pSession = m_Client.GetSession( 0 );
|
||||
m_Client.SendMessage( pSession, pMsg );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: QueueLocalCommand()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetClient::QueueLocalCommand( CNetMessage* pMessage )
|
||||
void CClientTurnManager::QueueLocalCommand( CNetMessage* pMessage )
|
||||
{
|
||||
if ( !pMessage ) return;
|
||||
|
||||
// Don't save these locally, since they'll be bounced by the server anyway
|
||||
CNetSession* pSession = GetSession( 0 );
|
||||
CNetHost::SendMessage( pSession, pMessage );
|
||||
CNetSession* pSession = m_Client.GetSession( 0 );
|
||||
m_Client.SendMessage( pSession, pMessage );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -695,5 +752,5 @@ void CNetClient::QueueIncomingMessage( CNetMessage* pMessage )
|
||||
{
|
||||
CScopeLock lock( m_Mutex );
|
||||
|
||||
QueueMessage( 2, pMessage );
|
||||
m_TurnManager->QueueMessage( 2, pMessage );
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -28,6 +28,7 @@
|
||||
|
||||
// INCLUDES
|
||||
#include "NetSession.h"
|
||||
#include "NetTurnManager.h"
|
||||
#include "ps/CStr.h"
|
||||
#include "simulation/TurnManager.h"
|
||||
#include "simulation/ScriptObject.h"
|
||||
@ -88,9 +89,10 @@ private:
|
||||
*/
|
||||
|
||||
class CNetClient: public CNetHost,
|
||||
public CJSObject<CNetClient>,
|
||||
protected CTurnManager
|
||||
public CJSObject<CNetClient>
|
||||
{
|
||||
NONCOPYABLE(CNetClient);
|
||||
|
||||
public:
|
||||
|
||||
CNetClient( CGame* pGame, CGameAttributes* pGameAttributes );
|
||||
@ -139,22 +141,20 @@ public:
|
||||
static void ScriptingInit( void );
|
||||
int StartGame( void );
|
||||
|
||||
CNetTurnManager* GetTurnManager() { debug_assert(m_ClientTurnManager); return m_ClientTurnManager; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool SetupSession ( CNetSession* pSession );
|
||||
virtual bool HandleConnect ( CNetSession* pSession );
|
||||
virtual bool HandleDisconnect ( CNetSession *pSession );
|
||||
|
||||
virtual void NewTurn ( void );
|
||||
virtual bool NewTurnReady ( void ) { return m_TurnPending; }
|
||||
virtual void QueueLocalCommand ( CNetMessage* pMessage );
|
||||
void QueueIncomingMessage ( CNetMessage* pMessage );
|
||||
|
||||
private:
|
||||
virtual void OnConnectComplete ( void );
|
||||
virtual void OnStartGame ( void );
|
||||
|
||||
// Not implemented
|
||||
CNetClient( const CNetClient& );
|
||||
CNetClient& operator=( const CNetClient& );
|
||||
private:
|
||||
|
||||
static bool OnError ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnPlayerJoin ( void* pContext, CFsmEvent* pEvent );
|
||||
@ -163,13 +163,16 @@ private:
|
||||
static bool OnPreGame ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnInGame ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnChat ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnStartGame ( void* pContext, CFsmEvent* pEvent );
|
||||
static bool OnStartGame_ ( void* pContext, CFsmEvent* pEvent );
|
||||
|
||||
|
||||
bool SetupConnection( JSContext *cx, uintN argc, jsval *argv );
|
||||
|
||||
//CNetSession* m_Session; // Server session
|
||||
PlayerMap m_Players; // List of online players
|
||||
|
||||
CTurnManager* m_TurnManager;
|
||||
CNetClientTurnManager* m_ClientTurnManager;
|
||||
};
|
||||
|
||||
extern CNetClient *g_NetClient;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -31,6 +31,9 @@
|
||||
#include "Network.h"
|
||||
#include "NetMessage.h"
|
||||
|
||||
#include "ps/Game.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
#undef ALLNETMSGS_DONT_CREATE_NMTS
|
||||
#define ALLNETMSGS_IMPLEMENT
|
||||
#include "NetMessages.h"
|
||||
@ -41,10 +44,6 @@
|
||||
// DEFINES
|
||||
#define LOG_CATEGORY L"net"
|
||||
|
||||
// Please don't modify the deserializer map outside the ONCE-block in DeserializeMessage
|
||||
//typedef std::map< NetMessageType, NetMessageDeserializer > MessageDeserializerMap;
|
||||
//MessageDeserializerMap g_DeserializerMap;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CNetMessage()
|
||||
// Desc: Constructor
|
||||
@ -110,7 +109,7 @@ const u8* CNetMessage::Deserialize( const u8* pStart, const u8* pEnd )
|
||||
m_Type = *( ( NetMessageType* )pStart );
|
||||
uint size = *( ( uint* )( pStart + sizeof( NetMessageType ) ) );
|
||||
|
||||
assert( pStart + size == pEnd );
|
||||
debug_assert( pStart + size == pEnd );
|
||||
|
||||
return pStart + sizeof( NetMessageType ) + sizeof( size );
|
||||
}
|
||||
@ -1054,6 +1053,10 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
||||
pNewMessage = new CFormationContactActionMessage;
|
||||
break;
|
||||
|
||||
case NMT_SIMULATION_COMMAND:
|
||||
pNewMessage = new CSimulationMessage(g_Game->GetSimulation2()->GetScriptInterface());
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG(CLogger::Error, LOG_CATEGORY, L"CNetMessageFactory::CreateMessage(): Unknown message received" );
|
||||
break;
|
||||
|
@ -47,6 +47,8 @@
|
||||
|
||||
class CNetMessage : public ISerializable
|
||||
{
|
||||
NONCOPYABLE(CNetMessage);
|
||||
|
||||
friend class CNetSession;
|
||||
|
||||
public:
|
||||
@ -132,18 +134,10 @@ public:
|
||||
* @return The message as a string
|
||||
*/
|
||||
virtual CStr ToString( void ) const;
|
||||
// virtual CStr GetString( void ) const;
|
||||
// operator CStr() const { return GetString(); }
|
||||
|
||||
private:
|
||||
|
||||
// Not implemented
|
||||
CNetMessage( const CNetMessage& );
|
||||
CNetMessage& operator=( const CNetMessage& );
|
||||
|
||||
bool m_Dirty; // Message has been modified
|
||||
NetMessageType m_Type; // Message type
|
||||
//u16 m_SerializeSize; // Serialized message size in bytes
|
||||
|
||||
public:
|
||||
|
||||
@ -214,6 +208,28 @@ private:
|
||||
CNetMessageFactory& operator=( const CNetMessageFactory& );
|
||||
};
|
||||
|
||||
/**
|
||||
* Special message type for simulation commands.
|
||||
* These commands are exposed as arbitrary JS objects, associated with a specific player.
|
||||
*/
|
||||
class CSimulationMessage : public CNetMessage
|
||||
{
|
||||
public:
|
||||
CSimulationMessage(ScriptInterface& scriptInterface);
|
||||
CSimulationMessage(ScriptInterface& scriptInterface, u32 client, i32 player, u32 turn, jsval data);
|
||||
virtual u8* Serialize(u8* pBuffer) const;
|
||||
virtual const u8* Deserialize(const u8* pStart, const u8* pEnd);
|
||||
virtual size_t GetSerializedLength() const;
|
||||
virtual CStr ToString() const;
|
||||
|
||||
u32 m_Client;
|
||||
i32 m_Player;
|
||||
u32 m_Turn;
|
||||
CScriptValRooted m_Data;
|
||||
private:
|
||||
ScriptInterface& m_ScriptInterface;
|
||||
};
|
||||
|
||||
// This time, the classes are created
|
||||
#include "NetMessages.h"
|
||||
|
||||
|
126
source/network/NetMessageSim.cpp
Normal file
126
source/network/NetMessageSim.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/* 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 "NetMessage.h"
|
||||
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation2/serialization/BinarySerializer.h"
|
||||
#include "simulation2/serialization/StdDeserializer.h"
|
||||
|
||||
/**
|
||||
* Serializer instance that writes directly to a buffer (which must be long enough).
|
||||
*/
|
||||
class CBufferBinarySerializer : public CBinarySerializer
|
||||
{
|
||||
public:
|
||||
CBufferBinarySerializer(ScriptInterface& scriptInterface, u8* buffer) :
|
||||
CBinarySerializer(scriptInterface), m_Buffer(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
void Put(const char* UNUSED(name), const u8* data, size_t len)
|
||||
{
|
||||
memcpy(m_Buffer, data, len);
|
||||
m_Buffer += len;
|
||||
}
|
||||
|
||||
u8* m_Buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serializer instance that simply counts how many bytes would be written.
|
||||
*/
|
||||
class CLengthBinarySerializer : public CBinarySerializer
|
||||
{
|
||||
public:
|
||||
CLengthBinarySerializer(ScriptInterface& scriptInterface) :
|
||||
CBinarySerializer(scriptInterface), m_Length(0)
|
||||
{
|
||||
}
|
||||
|
||||
void Put(const char* UNUSED(name), const u8* UNUSED(data), size_t len)
|
||||
{
|
||||
m_Length += len;
|
||||
}
|
||||
|
||||
size_t m_Length;
|
||||
};
|
||||
|
||||
CSimulationMessage::CSimulationMessage(ScriptInterface& scriptInterface) :
|
||||
CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface)
|
||||
{
|
||||
}
|
||||
|
||||
CSimulationMessage::CSimulationMessage(ScriptInterface& scriptInterface, u32 client, i32 player, u32 turn, jsval data) :
|
||||
CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface),
|
||||
m_Client(client), m_Player(player), m_Turn(turn), m_Data(scriptInterface.GetContext(), data)
|
||||
{
|
||||
}
|
||||
|
||||
u8* CSimulationMessage::Serialize(u8* pBuffer) const
|
||||
{
|
||||
// TODO: ought to handle serialization exceptions
|
||||
// TODO: ought to represent common commands more efficiently
|
||||
|
||||
u8* pos = CNetMessage::Serialize(pBuffer);
|
||||
CBufferBinarySerializer serializer(m_ScriptInterface, pos);
|
||||
serializer.NumberU32_Unbounded("client", m_Client);
|
||||
serializer.NumberI32_Unbounded("player", m_Player);
|
||||
serializer.NumberU32_Unbounded("turn", m_Turn);
|
||||
serializer.ScriptVal("command", m_Data);
|
||||
return serializer.m_Buffer;
|
||||
}
|
||||
|
||||
const u8* CSimulationMessage::Deserialize(const u8* pStart, const u8* pEnd)
|
||||
{
|
||||
// TODO: ought to handle serialization exceptions
|
||||
// TODO: ought to represent common commands more efficiently
|
||||
|
||||
const u8* pos = CNetMessage::Deserialize(pStart, pEnd);
|
||||
std::istringstream stream(std::string(pos, pEnd));
|
||||
CStdDeserializer deserializer(m_ScriptInterface, stream);
|
||||
deserializer.NumberU32_Unbounded(m_Client);
|
||||
deserializer.NumberI32_Unbounded(m_Player);
|
||||
deserializer.NumberU32_Unbounded(m_Turn);
|
||||
deserializer.ScriptVal(m_Data);
|
||||
return pEnd;
|
||||
}
|
||||
|
||||
size_t CSimulationMessage::GetSerializedLength() const
|
||||
{
|
||||
// TODO: serializing twice is stupidly inefficient - we should just
|
||||
// do it once, store the result, and use it here and in Serialize
|
||||
|
||||
CLengthBinarySerializer serializer(m_ScriptInterface);
|
||||
serializer.NumberU32_Unbounded("client", m_Client);
|
||||
serializer.NumberI32_Unbounded("player", m_Player);
|
||||
serializer.NumberU32_Unbounded("turn", m_Turn);
|
||||
serializer.ScriptVal("command", m_Data);
|
||||
return CNetMessage::GetSerializedLength() + serializer.m_Length;
|
||||
}
|
||||
|
||||
CStr CSimulationMessage::ToString() const
|
||||
{
|
||||
std::string source;
|
||||
|
||||
if (!m_ScriptInterface.CallFunction(m_Data.get(), "toSource", source))
|
||||
source = "ERROR";
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "CSimulationMessage { m_Client: " << m_Client << ", m_Player: " << m_Player << ", m_Turn: " << m_Turn << ", m_Data: " << source << " }";
|
||||
return stream.str();
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -29,6 +29,7 @@
|
||||
// INCLUDES
|
||||
#include "ps/CStr.h"
|
||||
#include "scripting/JSSerialization.h"
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
#include "simulation/EntityHandles.h"
|
||||
|
||||
// DEFINES
|
||||
@ -79,6 +80,7 @@ enum NetMessageType
|
||||
NMT_NOTIFY_REQUEST,
|
||||
NMT_FORMATION_GOTO,
|
||||
NMT_FORMATION_CONTACT_ACTION,
|
||||
NMT_SIMULATION_COMMAND,
|
||||
NMT_COMMAND_LAST,
|
||||
NMT_LAST // Last message in the list
|
||||
};
|
||||
@ -190,6 +192,7 @@ START_NMT_CLASS_(GameStart, NMT_GAME_START)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(EndCommandBatch, NMT_END_COMMAND_BATCH)
|
||||
NMT_FIELD_INT(m_Turn, u32, 4)
|
||||
NMT_FIELD_INT(m_TurnLength, u32, 2)
|
||||
END_NMT_CLASS()
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -31,12 +31,24 @@
|
||||
#include "NetJsEvents.h"
|
||||
#include "NetSession.h"
|
||||
#include "NetServer.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
#define LOG_CATEGORY L"net"
|
||||
|
||||
// DECLARATIONS
|
||||
CNetServer* g_NetServer = NULL;
|
||||
|
||||
class CServerTurnManager : public CTurnManager
|
||||
{
|
||||
public:
|
||||
CServerTurnManager(CNetServer& server) : m_Server(server) { }
|
||||
virtual void QueueLocalCommand(CNetMessage* pMessage);
|
||||
virtual void NewTurn();
|
||||
virtual bool NewTurnReady();
|
||||
private:
|
||||
CNetServer& m_Server;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: CNetServer()
|
||||
// Desc: Constructor
|
||||
@ -44,7 +56,8 @@ CNetServer* g_NetServer = NULL;
|
||||
CNetServer::CNetServer( CGame *pGame, CGameAttributes *pGameAttributes )
|
||||
: m_JsSessions( &m_IDSessions )
|
||||
{
|
||||
//ONCE( ScriptingInit(); );
|
||||
m_TurnManager = new CServerTurnManager(*this);
|
||||
m_ServerTurnManager = NULL;
|
||||
|
||||
m_Game = pGame;
|
||||
m_GameAttributes = pGameAttributes;
|
||||
@ -60,15 +73,14 @@ CNetServer::CNetServer( CGame *pGame, CGameAttributes *pGameAttributes )
|
||||
m_GameAttributes->SetPlayerUpdateCallback( PlayerAttributeUpdate, this );
|
||||
m_GameAttributes->SetPlayerSlotAssignmentCallback( PlayerSlotAssignment, this );
|
||||
|
||||
// Set turn manager
|
||||
CSimulation* pSimulation = m_Game->GetSimulation();
|
||||
if ( pSimulation ) pSimulation->SetTurnManager( this );
|
||||
if (!g_UseSimulation2)
|
||||
m_Game->GetSimulation()->SetTurnManager(m_TurnManager);
|
||||
|
||||
// Set an incredibly long turn length for debugging
|
||||
// (e.g. less command batch spam that way)
|
||||
for ( uint i = 0; i < 3; i++ )
|
||||
{
|
||||
CTurnManager::SetTurnLength( i, DEFAULT_TURN_LENGTH );
|
||||
m_TurnManager->SetTurnLength( i, CTurnManager::DEFAULT_TURN_LENGTH );
|
||||
}
|
||||
|
||||
g_ScriptingHost.SetGlobal( "g_NetServer", OBJECT_TO_JSVAL( GetScript() ) );
|
||||
@ -167,6 +179,7 @@ bool CNetServer::SetupSession( CNetSession* pSession )
|
||||
pSession->AddTransition( NSS_INGAME, ( uint )NMT_NOTIFY_REQUEST, NSS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NSS_INGAME, ( uint )NMT_FORMATION_GOTO, NSS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NSS_INGAME, ( uint )NMT_FORMATION_CONTACT_ACTION, NSS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NSS_INGAME, ( uint )NMT_SIMULATION_COMMAND, NSS_INGAME, (void*)&OnInGame, pContext );
|
||||
pSession->AddTransition( NSS_INGAME, ( uint )NMT_END_COMMAND_BATCH, NSS_INGAME, (void*)&OnInGame, pContext );
|
||||
|
||||
// Set first state
|
||||
@ -189,7 +202,7 @@ bool CNetServer::HandleConnect( CNetSession* pSession )
|
||||
handshake.m_Magic = PS_PROTOCOL_MAGIC;
|
||||
handshake.m_ProtocolVersion = PS_PROTOCOL_VERSION;
|
||||
handshake.m_SoftwareVersion = PS_PROTOCOL_VERSION;
|
||||
CNetHost::SendMessage( pSession, &handshake );
|
||||
SendMessage( pSession, &handshake );
|
||||
|
||||
// Store new session into the map
|
||||
m_IDSessions[ pSession->GetID() ] = pSession;
|
||||
@ -230,11 +243,11 @@ void CNetServer::SetupPlayer( CNetSession* pSession )
|
||||
// Validate parameters
|
||||
if ( !pSession ) return;
|
||||
|
||||
assert( m_GameAttributes );
|
||||
debug_assert( m_GameAttributes );
|
||||
|
||||
// Send a new config message to the connected client
|
||||
BuildGameSetupMessage( &gameSetup );
|
||||
CNetHost::SendMessage( pSession, &gameSetup );
|
||||
SendMessage( pSession, &gameSetup );
|
||||
|
||||
// Add information for already connected clients and the server
|
||||
playerJoin.m_Clients.resize( GetSessionCount() );
|
||||
@ -252,7 +265,7 @@ void CNetServer::SetupPlayer( CNetSession* pSession )
|
||||
playerJoin.m_Clients[ i + 1 ].m_Name = pCurrSession->GetName();
|
||||
}
|
||||
|
||||
CNetHost::SendMessage( pSession, &playerJoin );
|
||||
SendMessage( pSession, &playerJoin );
|
||||
|
||||
// TODO: Handle observers
|
||||
|
||||
@ -268,7 +281,7 @@ void CNetServer::SetupPlayer( CNetSession* pSession )
|
||||
// Skip the player being setup
|
||||
if ( !pCurrSession || pCurrSession == pSession ) continue;
|
||||
|
||||
CNetHost::SendMessage( pCurrSession, &playerJoin );
|
||||
SendMessage( pCurrSession, &playerJoin );
|
||||
}
|
||||
//Broadcast( &playerJoin );
|
||||
|
||||
@ -298,13 +311,13 @@ void CNetServer::SetupPlayer( CNetSession* pSession )
|
||||
break;
|
||||
}
|
||||
|
||||
CNetHost::SendMessage( pSession, &assignSlot );
|
||||
SendMessage( pSession, &assignSlot );
|
||||
|
||||
if ( pCurrSlot->GetAssignment() == SLOT_SESSION )
|
||||
{
|
||||
// Setup player
|
||||
BuildPlayerConfigMessage( &playerConfig, pCurrSlot->GetPlayer() );
|
||||
CNetHost::SendMessage( pSession, &playerConfig );
|
||||
SendMessage( pSession, &playerConfig );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -358,7 +371,7 @@ void CNetServer::OnPlayerLeave( CNetSession* pSession )
|
||||
// TODO Set everything up for re-connect and resume
|
||||
if ( pPlayerSlot )
|
||||
{
|
||||
SetClientPipe( pPlayerSlot->GetSlotID(), NULL );
|
||||
m_TurnManager->SetClientPipe( pPlayerSlot->GetSlotID(), NULL );
|
||||
pPlayerSlot->AssignClosed();
|
||||
}
|
||||
break;
|
||||
@ -435,8 +448,8 @@ bool CNetServer::OnHandshake( void* pContext, CFsmEvent* pEvent )
|
||||
CNetServer* pServer = ( CNetServer* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
||||
|
||||
assert( pServer );
|
||||
assert( pSession );
|
||||
debug_assert( pServer );
|
||||
debug_assert( pSession );
|
||||
|
||||
CCliHandshakeMessage* pMessage = ( CCliHandshakeMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
@ -444,7 +457,7 @@ bool CNetServer::OnHandshake( void* pContext, CFsmEvent* pEvent )
|
||||
if ( pMessage->m_ProtocolVersion != PS_PROTOCOL_VERSION )
|
||||
{
|
||||
CCloseRequestMessage closeRequest;
|
||||
( ( CNetHost* )pServer )->SendMessage( pSession, &closeRequest );
|
||||
pServer->SendMessage( pSession, &closeRequest );
|
||||
|
||||
CErrorMessage error( PS_OK, SS_UNCONNECTED );
|
||||
pSession->Update( ( uint )NMT_ERROR, &error );
|
||||
@ -455,7 +468,7 @@ bool CNetServer::OnHandshake( void* pContext, CFsmEvent* pEvent )
|
||||
handshakeResponse.m_UseProtocolVersion = PS_PROTOCOL_VERSION;
|
||||
handshakeResponse.m_Message = pServer->m_WelcomeMessage;
|
||||
handshakeResponse.m_Flags = 0;
|
||||
( ( CNetHost* )pServer )->SendMessage( pSession, &handshakeResponse );
|
||||
pServer->SendMessage( pSession, &handshakeResponse );
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,7 +507,7 @@ bool CNetServer::OnAuthenticate( void* pContext, CFsmEvent* pEvent )
|
||||
authenticateResult.m_Code = ARC_OK;
|
||||
authenticateResult.m_SessionID = pSession->GetID();
|
||||
authenticateResult.m_Message = L"Logged in";
|
||||
( ( CNetHost* )pServer )->SendMessage( pSession, &authenticateResult );
|
||||
pServer->SendMessage( pSession, &authenticateResult );
|
||||
|
||||
//pServer->AddSession( pSession );
|
||||
|
||||
@ -518,7 +531,7 @@ bool CNetServer::OnAuthenticate( void* pContext, CFsmEvent* pEvent )
|
||||
authenticateResult.m_Code = ARC_PASSWORD_INVALID;
|
||||
authenticateResult.m_SessionID = 0;
|
||||
authenticateResult.m_Message = L"Invalid Password";
|
||||
( ( CNetHost* )pServer )->SendMessage( pSession, &authenticateResult );
|
||||
pServer->SendMessage( pSession, &authenticateResult );
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,6 +565,13 @@ bool CNetServer::OnInGame( void* pContext, CFsmEvent* pEvent )
|
||||
CNetMessage* pMessage = ( CNetMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
{
|
||||
if (pMessage->GetType() == NMT_SIMULATION_COMMAND)
|
||||
{
|
||||
CSimulationMessage* simMessage = static_cast<CSimulationMessage*> (pMessage);
|
||||
pServer->m_ServerTurnManager->OnSimulationMessage(simMessage);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( pMessage->GetType() >= NMT_COMMAND_FIRST && pMessage->GetType() < NMT_COMMAND_LAST )
|
||||
{
|
||||
//pSession->m_pPlayer->ValidateCommand(pMsg);
|
||||
@ -562,6 +582,12 @@ bool CNetServer::OnInGame( void* pContext, CFsmEvent* pEvent )
|
||||
|
||||
if ( pMessage->GetType() == NMT_END_COMMAND_BATCH )
|
||||
{
|
||||
if (g_UseSimulation2)
|
||||
{
|
||||
CEndCommandBatchMessage* endMessage = static_cast<CEndCommandBatchMessage*> (pMessage);
|
||||
pServer->m_ServerTurnManager->NotifyFinishedClientCommands(pSession->GetID(), endMessage->m_Turn);
|
||||
}
|
||||
|
||||
// TODO Update client timing information and recalculate turn length
|
||||
pSession->SetReadyForTurn( true );
|
||||
}
|
||||
@ -585,8 +611,8 @@ bool CNetServer::OnChat( void* pContext, CFsmEvent* pEvent )
|
||||
CNetServer* pServer = ( CNetServer* )( ( FsmActionCtx* )pContext )->pHost;
|
||||
CNetSession* pSession = ( ( FsmActionCtx* )pContext )->pSession;
|
||||
|
||||
assert( pSession );
|
||||
assert( pServer );
|
||||
debug_assert( pSession );
|
||||
debug_assert( pServer );
|
||||
|
||||
CChatMessage* pMessage = ( CChatMessage* )pEvent->GetParamRef();
|
||||
if ( pMessage )
|
||||
@ -734,12 +760,18 @@ int CNetServer::StartGame( void )
|
||||
{
|
||||
uint i;
|
||||
|
||||
assert( m_Game );
|
||||
assert( m_GameAttributes );
|
||||
debug_assert( m_Game );
|
||||
debug_assert( m_GameAttributes );
|
||||
|
||||
if ( m_Game->StartGame( m_GameAttributes ) != PSRETURN_OK ) return -1;
|
||||
|
||||
CTurnManager::Initialize( m_GameAttributes->GetSlotCount() );
|
||||
if (g_UseSimulation2)
|
||||
{
|
||||
m_ServerTurnManager = new CNetServerTurnManager(*m_Game->GetSimulation2(), *this, m_Game->GetLocalPlayer()->GetPlayerID(), SERVER_SESSIONID);
|
||||
m_Game->SetTurnManager(m_ServerTurnManager);
|
||||
}
|
||||
|
||||
m_TurnManager->Initialize( m_GameAttributes->GetSlotCount() );
|
||||
|
||||
for ( i = 0; i < m_GameAttributes->GetSlotCount(); i++ )
|
||||
{
|
||||
@ -747,7 +779,11 @@ int CNetServer::StartGame( void )
|
||||
if ( !pCurrSlot ) continue;
|
||||
|
||||
if ( pCurrSlot->GetAssignment() == SLOT_SESSION )
|
||||
CTurnManager::SetClientPipe( i, pCurrSlot->GetSession() );
|
||||
{
|
||||
m_TurnManager->SetClientPipe( i, pCurrSlot->GetSession() );
|
||||
if (g_UseSimulation2)
|
||||
m_ServerTurnManager->InitialiseClient(pCurrSlot->GetSessionID());
|
||||
}
|
||||
}
|
||||
|
||||
m_State = SERVER_STATE_INGAME;
|
||||
@ -778,7 +814,7 @@ void CNetServer::BuildGameSetupMessage( CGameSetupMessage* pMessage )
|
||||
// Validate parameters
|
||||
if ( !pMessage ) return;
|
||||
|
||||
assert( m_GameAttributes );
|
||||
debug_assert( m_GameAttributes );
|
||||
|
||||
// Iterate through game properties and load them into message
|
||||
m_GameAttributes->IterateSynchedProperties( GameSetupMessageCallback, pMessage );
|
||||
@ -964,12 +1000,12 @@ bool CNetServer::AllowObserver( CNetSession* UNUSED( pSession ) )
|
||||
// Name: NewTurnReady()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CNetServer::NewTurnReady()
|
||||
bool CServerTurnManager::NewTurnReady()
|
||||
{
|
||||
// Check whether all sessions are ready for the next turn
|
||||
for ( uint i = 0; i < GetSessionCount(); i++ )
|
||||
for ( uint i = 0; i < m_Server.GetSessionCount(); i++ )
|
||||
{
|
||||
CNetSession* pCurrSession = GetSession( i );
|
||||
CNetSession* pCurrSession = m_Server.GetSession( i );
|
||||
if ( !pCurrSession ) continue;
|
||||
|
||||
if ( !pCurrSession->IsReadyForTurn() )
|
||||
@ -983,14 +1019,14 @@ bool CNetServer::NewTurnReady()
|
||||
// Name: NewTurn()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetServer::NewTurn()
|
||||
void CServerTurnManager::NewTurn()
|
||||
{
|
||||
CScopeLock lock(m_Mutex);
|
||||
CScopeLock lock(m_Server.m_Mutex);
|
||||
|
||||
// Reset session ready for next turn flag
|
||||
for ( uint i = 0; i < GetSessionCount(); i++ )
|
||||
for ( uint i = 0; i < m_Server.GetSessionCount(); i++ )
|
||||
{
|
||||
CNetSession* pCurrSession = GetSession( i );
|
||||
CNetSession* pCurrSession = m_Server.GetSession( i );
|
||||
if ( !pCurrSession ) continue;
|
||||
|
||||
pCurrSession->SetReadyForTurn( false );
|
||||
@ -999,7 +1035,7 @@ void CNetServer::NewTurn()
|
||||
RecordBatch( 2 );
|
||||
RotateBatches();
|
||||
ClearBatch( 2 );
|
||||
IterateBatch( 1, CSimulation::GetMessageMask, m_Game->GetSimulation() );
|
||||
IterateBatch( 1, CSimulation::GetMessageMask, m_Server.m_Game->GetSimulation() );
|
||||
SendBatch( 1 );
|
||||
//IterateBatch( 1, SendToObservers, this );
|
||||
}
|
||||
@ -1008,9 +1044,14 @@ void CNetServer::NewTurn()
|
||||
// Name: QueueLocalCommand()
|
||||
// Desc:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetServer::QueueLocalCommand( CNetMessage *pMessage )
|
||||
void CServerTurnManager::QueueLocalCommand( CNetMessage *pMessage )
|
||||
{
|
||||
QueueIncomingCommand( pMessage );
|
||||
// Validate parameters
|
||||
if ( !pMessage ) return;
|
||||
|
||||
//LOG( NORMAL, LOG_CATEGORY, L"CServerTurnManager::QueueLocalCommand(): %hs.", pMessage->ToString().c_str() );
|
||||
|
||||
QueueMessage( 2, pMessage );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -1024,7 +1065,7 @@ void CNetServer::QueueIncomingCommand( CNetMessage* pMessage )
|
||||
|
||||
//LOG( NORMAL, LOG_CATEGORY, L"CNetServer::QueueIncomingCommand(): %hs.", pMessage->ToString().c_str() );
|
||||
|
||||
QueueMessage( 2, pMessage );
|
||||
m_TurnManager->QueueMessage( 2, pMessage );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -1069,5 +1110,3 @@ void CNetServer::BuildPlayerSlotAssignmentMessage(
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -29,6 +29,7 @@
|
||||
// INCLUDES
|
||||
#include "Network.h"
|
||||
#include "NetSession.h"
|
||||
#include "NetTurnManager.h"
|
||||
#include "simulation/TurnManager.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
@ -45,7 +46,6 @@
|
||||
#define CLIENT_MIN_SESSIONID 100
|
||||
#define MAX_CLIENTS 8
|
||||
#define MAX_OBSERVERS 5
|
||||
#define DEFAULT_SERVER_SESSION_ID 1
|
||||
#define DEFAULT_SERVER_NAME L"Noname Server"
|
||||
#define DEFAULT_PLAYER_NAME L"Noname Player"
|
||||
#define DEFAULT_WELCOME_MESSAGE L"Noname Server Welcome Message"
|
||||
@ -98,9 +98,9 @@ typedef std::vector< CNetSession* > SessionList;
|
||||
*/
|
||||
|
||||
class CNetServer : public CNetHost,
|
||||
public CJSObject<CNetServer>,
|
||||
public CTurnManager
|
||||
public CJSObject<CNetServer>
|
||||
{
|
||||
NONCOPYABLE(CNetServer);
|
||||
public:
|
||||
|
||||
CNetServer( CGame* pGame, CGameAttributes* pGameAttributes );
|
||||
@ -154,6 +154,8 @@ public:
|
||||
*/
|
||||
CNetSession* GetSessionByID( uint sessionID );
|
||||
|
||||
CNetTurnManager* GetTurnManager() { debug_assert(m_ServerTurnManager); return m_ServerTurnManager; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool SetupSession ( CNetSession* pSession );
|
||||
@ -162,10 +164,6 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
// Not implemented
|
||||
CNetServer( const CNetServer& );
|
||||
CNetServer& operator=( const CNetServer& );
|
||||
|
||||
//void ClientConnect ( ENetPeer* pPeer );
|
||||
//void ClientDisconnect ( ENetPeer* pPeer );
|
||||
//void ClientReceive ( ENetPeer* pPeer, ENetPacket* pPacket );
|
||||
@ -251,7 +249,6 @@ private:
|
||||
//CScriptObject m_ScriptConnect; // Script client connect dispatch
|
||||
//CScriptObject m_ScriptDisconnect; // Script client disconnect dispatch
|
||||
//CScriptObject m_ScriptChat; // Script client chat dispatch
|
||||
CPlayer* m_Player; // Server player
|
||||
|
||||
|
||||
public:
|
||||
@ -288,8 +285,8 @@ protected:
|
||||
|
||||
// Call the JS callback for incoming events
|
||||
void OnPlayerChat ( const CStrW& from, const CStrW& message );
|
||||
void OnPlayerJoin ( CNetSession* pSession );
|
||||
void OnPlayerLeave ( CNetSession* pSession );
|
||||
virtual void OnPlayerJoin ( CNetSession* pSession );
|
||||
virtual void OnPlayerLeave ( CNetSession* pSession );
|
||||
void SetupPlayer ( CNetSession* pSession );
|
||||
|
||||
//static bool OnPlayerJoin ( void* pContext, CFsmEvent* pEvent );
|
||||
@ -303,11 +300,6 @@ protected:
|
||||
// OVERRIDES FROM CServerSocket
|
||||
//virtual void OnAccept( const CSocketAddress& address );
|
||||
|
||||
// OVERRIDES FROM CTurnManager
|
||||
virtual void NewTurn( void );
|
||||
virtual bool NewTurnReady( void );
|
||||
virtual void QueueLocalCommand( CNetMessage* pMessage );
|
||||
|
||||
// Will only be called from the Network Thread, by the OnAccept handler
|
||||
//virtual CNetServerSession* CreateSession( CSocketInternal* pSocketInternal);
|
||||
|
||||
@ -318,13 +310,16 @@ protected:
|
||||
// false otherwise
|
||||
virtual bool AllowObserver( CNetSession* pSession );
|
||||
|
||||
public:
|
||||
CMutex m_Mutex; // Synchronization object for batches
|
||||
CGame* m_Game; // Pointer to actual game
|
||||
|
||||
private:
|
||||
|
||||
CGameAttributes* m_GameAttributes; // Stores game attributes
|
||||
//int m_LastSessionID; // Stores the last session ID
|
||||
//SessionMap m_Sessions; // Managed sessions
|
||||
CJSMap< IDSessionMap > m_JsSessions;
|
||||
CMutex m_Mutex; // Synchronization object for batches
|
||||
|
||||
/*
|
||||
All sessions that have observer status (observer as in watcher - simple
|
||||
@ -334,7 +329,6 @@ private:
|
||||
*/
|
||||
SessionList m_Observers;
|
||||
uint m_MaxObservers; // Maximum number of observers
|
||||
CGame* m_Game; // Pointer to actual game
|
||||
NetServerState m_State; // Holds server state
|
||||
CStrW m_Name; // Server name
|
||||
CStrW m_WelcomeMessage; // Nice welcome message
|
||||
@ -353,10 +347,11 @@ private:
|
||||
static void AttributeUpdate ( const CStrW& name, const CStrW& newValue, void* pData);
|
||||
static void PlayerAttributeUpdate ( const CStrW& name, const CStrW& value, CPlayer* pPlayer, void* pData );
|
||||
static void PlayerSlotAssignment ( void* pData, CPlayerSlot* pPlayerSlot );
|
||||
|
||||
CTurnManager* m_TurnManager;
|
||||
CNetServerTurnManager* m_ServerTurnManager;
|
||||
};
|
||||
|
||||
extern CNetServer *g_NetServer;
|
||||
|
||||
#endif // NETSERVER_H
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -139,7 +139,7 @@ bool CNetHost::Connect( const CStr& host, uint port )
|
||||
ENetAddress addr;
|
||||
PeerSession item;
|
||||
|
||||
assert( m_Host );
|
||||
debug_assert( m_Host );
|
||||
|
||||
// Bind to specified host
|
||||
addr.port = port;
|
||||
@ -196,8 +196,8 @@ bool CNetHost::Disconnect( CNetSession* pSession )
|
||||
// Validate parameters
|
||||
if ( !pSession ) return false;
|
||||
|
||||
assert( m_Host );
|
||||
assert( pSession->m_Peer );
|
||||
debug_assert( m_Host );
|
||||
debug_assert( pSession->m_Peer );
|
||||
|
||||
// Disconnect peer
|
||||
enet_peer_disconnect( pSession->m_Peer, 0 );
|
||||
@ -233,7 +233,7 @@ bool CNetHost::Disconnect( CNetSession* pSession )
|
||||
//-----------------------------------------------------------------------------
|
||||
/*bool CNetHost::Run( void )
|
||||
{
|
||||
assert( m_Host );
|
||||
debug_assert( m_Host );
|
||||
|
||||
// Host created?
|
||||
if ( !m_Host ) return false;
|
||||
@ -354,7 +354,7 @@ bool CNetHost::Poll( void )
|
||||
PeerSession item;
|
||||
PeerSessionList::iterator it;
|
||||
|
||||
assert( m_Host );
|
||||
debug_assert( m_Host );
|
||||
|
||||
// Poll host for events
|
||||
while ( enet_host_service( m_Host, &event, 0 ) > 0 )
|
||||
@ -416,20 +416,24 @@ bool CNetHost::Poll( void )
|
||||
// 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 );
|
||||
if ( !pNewMessage ) return false;
|
||||
|
||||
if ( pNewMessage )
|
||||
{
|
||||
NET_LOG4( "Message %s of size %lu was received from %p", pNewMessage->ToString().c_str(), (unsigned long)pNewMessage->GetSerializedLength(), event.peer->data );
|
||||
|
||||
// Successfully handled?
|
||||
if ( !HandleMessageReceive( pNewMessage, it->pSession ) ) {
|
||||
enet_packet_destroy( event.packet );
|
||||
return false;
|
||||
ok = HandleMessageReceive( pNewMessage, it->pSession );
|
||||
|
||||
delete pNewMessage;
|
||||
}
|
||||
|
||||
// Done using the packet
|
||||
enet_packet_destroy( event.packet );
|
||||
|
||||
if (! ok)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,8 +447,6 @@ bool CNetHost::Poll( void )
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: Broadcast()
|
||||
// Desc: Broadcast the specified message to connected clients
|
||||
// Note: Reference counting for a sending message requires multithreading
|
||||
// locking mechanisms so a clone of the message is made and sent out
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetHost::Broadcast( const CNetMessage* pMessage )
|
||||
{
|
||||
@ -501,8 +503,8 @@ bool CNetHost::SendMessage(
|
||||
// Validate parameters
|
||||
if ( !pMessage || !pSession ) return false;
|
||||
|
||||
assert( pSession->m_Peer );
|
||||
assert( m_Host );
|
||||
debug_assert( pSession->m_Peer );
|
||||
debug_assert( m_Host );
|
||||
|
||||
size_t size = pMessage->GetSerializedLength();
|
||||
|
||||
@ -544,7 +546,7 @@ CNetMessage* CNetHost::ReceiveMessage( const CNetSession* pSession )
|
||||
// Validate parameters
|
||||
if ( !pSession ) return NULL;
|
||||
|
||||
assert( pSession->m_Peer );
|
||||
debug_assert( pSession->m_Peer );
|
||||
|
||||
// Let ENet receive a message from peer
|
||||
ENetPacket* pPacket = enet_peer_receive( pSession->m_Peer, ENET_DEFAULT_CHANNEL );
|
||||
@ -705,7 +707,7 @@ void CNetSession::Push( CNetMessage* pMessage )
|
||||
// Validate parameters
|
||||
if ( !pMessage ) return;
|
||||
|
||||
assert( m_Host );
|
||||
debug_assert( m_Host );
|
||||
|
||||
m_Host->SendMessage( this, pMessage );
|
||||
}
|
||||
@ -716,7 +718,7 @@ void CNetSession::Push( CNetMessage* pMessage )
|
||||
//-----------------------------------------------------------------------------
|
||||
CNetMessage* CNetSession::TryPop( void )
|
||||
{
|
||||
assert( m_Host );
|
||||
debug_assert( m_Host );
|
||||
|
||||
return m_Host->ReceiveMessage( this );
|
||||
}
|
||||
@ -839,8 +841,8 @@ bool CNetServerSession::BaseHandler(
|
||||
CNetMessage* pMessage,
|
||||
CNetSession* pSession )
|
||||
{
|
||||
assert( pMessage );
|
||||
assert( pSession );
|
||||
debug_assert( pMessage );
|
||||
debug_assert( pSession );
|
||||
|
||||
// Validate parameters
|
||||
if ( !pMessage || !pSession ) return false;
|
||||
@ -882,9 +884,9 @@ bool CNetServerSession::HandshakeHandler(
|
||||
CNetMessage* pMessage,
|
||||
CNetSession* pSession )
|
||||
{
|
||||
assert( pMessage );
|
||||
assert( pSession );
|
||||
assert( m_Server );
|
||||
debug_assert( pMessage );
|
||||
debug_assert( pSession );
|
||||
debug_assert( m_Server );
|
||||
|
||||
// Validate parameters
|
||||
if ( !pMessage || !pSession ) return false;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -28,7 +28,6 @@
|
||||
|
||||
// INCLUDES
|
||||
#include "Network.h"
|
||||
#include "ps/Singleton.h"
|
||||
#include "ps/GameAttributes.h"
|
||||
#include "ps/Player.h"
|
||||
#include "fsm.h"
|
||||
@ -61,11 +60,11 @@ typedef std::vector< PeerSession > PeerSessionList;
|
||||
|
||||
/*
|
||||
CLASS : CNetHost
|
||||
DESCRIPTION : CNetHost is a wrapper around ENet host conecept
|
||||
DESCRIPTION : CNetHost is a wrapper around ENet host concept
|
||||
NOTES :
|
||||
*/
|
||||
|
||||
class CNetHost : public Singleton< CNetHost >
|
||||
class CNetHost
|
||||
{
|
||||
public:
|
||||
|
||||
@ -198,7 +197,7 @@ private:
|
||||
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 fom a client, its representing
|
||||
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
|
||||
@ -212,6 +211,8 @@ class CNetSession : public CFsm,
|
||||
public CJSObject< CNetSession >,
|
||||
public IMessagePipeEnd
|
||||
{
|
||||
NONCOPYABLE(CNetSession);
|
||||
|
||||
friend class CNetHost;
|
||||
|
||||
public:
|
||||
@ -289,11 +290,6 @@ private:
|
||||
// Only the hosts can create sessions
|
||||
CNetSession( CNetHost* pHost, ENetPeer* pPeer );
|
||||
|
||||
// Not implemented
|
||||
CNetSession( void );
|
||||
CNetSession( const CNetSession& );
|
||||
CNetSession& operator=( const CNetSession& );
|
||||
|
||||
CNetHost* m_Host; // The associated local host
|
||||
ENetPeer* m_Peer; // Represents the peer host
|
||||
uint m_ID; // Session ID
|
||||
|
265
source/network/NetTurnManager.cpp
Normal file
265
source/network/NetTurnManager.cpp
Normal file
@ -0,0 +1,265 @@
|
||||
/* 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 "NetTurnManager.h"
|
||||
|
||||
#include "NetServer.h"
|
||||
#include "NetClient.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
|
||||
static const int TURN_LENGTH = 200; // TODO: this should be a variable controlled by the server depending on latency
|
||||
|
||||
static const int COMMAND_DELAY = 2;
|
||||
|
||||
//#define NETTURN_LOG debug_printf
|
||||
|
||||
CNetTurnManager::CNetTurnManager(CSimulation2& simulation, int playerId, int clientId) :
|
||||
m_Simulation2(simulation), m_CurrentTurn(0), m_ReadyTurn(1), m_DeltaTime(0),
|
||||
m_PlayerId(playerId), m_ClientId(clientId)
|
||||
{
|
||||
// When we are on turn n, we schedule new commands for n+2.
|
||||
// We know that all other clients have finished scheduling commands for n (else we couldn't have got here).
|
||||
// We know we have not yet finished scheduling commands for n+2.
|
||||
// Hence other clients can be on turn n-1, n, n+1, and no other.
|
||||
// So they can be sending us commands scheduled for n+1, n+2, n+3.
|
||||
// So we need a 3-element buffer:
|
||||
m_QueuedCommands.resize(COMMAND_DELAY + 1);
|
||||
}
|
||||
|
||||
bool CNetTurnManager::Update(float frameLength)
|
||||
{
|
||||
m_DeltaTime += frameLength;
|
||||
|
||||
// If we haven't reached the next turn yet, do nothing
|
||||
if (m_DeltaTime < 0)
|
||||
return false;
|
||||
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"Update current=%d ready=%d\n", m_CurrentTurn, m_ReadyTurn);
|
||||
#endif
|
||||
|
||||
// Check that the next turn is ready for execution
|
||||
if (m_ReadyTurn > m_CurrentTurn)
|
||||
{
|
||||
NotifyFinishedOwnCommands(m_CurrentTurn + COMMAND_DELAY);
|
||||
m_CurrentTurn += 1;
|
||||
|
||||
// Put all the client commands into a single list, in a globally consistent order
|
||||
std::vector<SimulationCommand> commands;
|
||||
for (std::map<u32, std::vector<SimulationCommand> >::iterator it = m_QueuedCommands[0].begin(); it != m_QueuedCommands[0].end(); ++it)
|
||||
{
|
||||
commands.insert(commands.end(), it->second.begin(), it->second.end());
|
||||
}
|
||||
m_QueuedCommands.pop_front();
|
||||
m_QueuedCommands.resize(m_QueuedCommands.size() + 1);
|
||||
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"Running %d cmds\n", commands.size());
|
||||
#endif
|
||||
|
||||
m_Simulation2.Update(TURN_LENGTH, commands);
|
||||
|
||||
// TODO: Compute state hash, send SyncCheck message
|
||||
|
||||
// Set the time for the next turn update
|
||||
m_DeltaTime -= TURN_LENGTH / 1000.f;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oops, we wanted to start the next turn but it's not ready yet -
|
||||
// there must be too much network lag.
|
||||
// TODO: complain to the user.
|
||||
// TODO: send feedback to the server to increase the turn length.
|
||||
|
||||
// Reset the next-turn timer to 0 so we try again next update but
|
||||
// so we don't rush to catch up in subsequent turns.
|
||||
// TODO: we should do clever rate adjustment instead of just pausing like this.
|
||||
m_DeltaTime = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CNetTurnManager::Interpolate(float frameLength)
|
||||
{
|
||||
float offset = clamp(m_DeltaTime / (TURN_LENGTH / 1000.f) + 1.0, 0.0, 1.0);
|
||||
m_Simulation2.Interpolate(frameLength, offset);
|
||||
}
|
||||
|
||||
void CNetTurnManager::AddCommand(int client, int player, CScriptValRooted data, u32 turn)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"AddCommand(client=%d player=%d turn=%d)\n", client, player, turn);
|
||||
#endif
|
||||
|
||||
if (!(m_CurrentTurn < turn && turn <= m_CurrentTurn + COMMAND_DELAY + 1))
|
||||
{
|
||||
debug_warn(L"Received command for invalid turn");
|
||||
return;
|
||||
}
|
||||
|
||||
SimulationCommand cmd;
|
||||
cmd.player = player;
|
||||
cmd.data = data;
|
||||
m_QueuedCommands[turn - (m_CurrentTurn+1)][client].push_back(cmd);
|
||||
}
|
||||
|
||||
void CNetTurnManager::FinishedAllCommands(u32 turn)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"FinishedAllCommands(%d)\n", turn);
|
||||
#endif
|
||||
|
||||
debug_assert(turn == m_ReadyTurn + 1);
|
||||
m_ReadyTurn = turn;
|
||||
}
|
||||
|
||||
void CNetClientTurnManager::PostCommand(CScriptValRooted data)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"PostCommand()\n");
|
||||
#endif
|
||||
|
||||
// Transmit command to server
|
||||
CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data.get());
|
||||
CNetSession* session = m_NetClient.GetSession(0);
|
||||
m_NetClient.SendMessage(session, &msg);
|
||||
|
||||
// Add to our local queue
|
||||
//AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + COMMAND_DELAY);
|
||||
// TODO: we should do this when the server stops sending our commands back to us
|
||||
}
|
||||
|
||||
void CNetClientTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"NotifyFinishedOwnCommands(%d)\n", turn);
|
||||
#endif
|
||||
|
||||
CEndCommandBatchMessage msg;
|
||||
msg.m_TurnLength = TURN_LENGTH;
|
||||
msg.m_Turn = turn;
|
||||
CNetSession* session = m_NetClient.GetSession(0);
|
||||
m_NetClient.SendMessage(session, &msg);
|
||||
}
|
||||
|
||||
void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage* msg)
|
||||
{
|
||||
// Command received from the server - store it for later execution
|
||||
AddCommand(msg->m_Client, msg->m_Player, msg->m_Data, msg->m_Turn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CNetServerTurnManager::PostCommand(CScriptValRooted data)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"PostCommand()\n");
|
||||
#endif
|
||||
|
||||
// Transmit command to all clients
|
||||
CSimulationMessage msg(m_Simulation2.GetScriptInterface(), m_ClientId, m_PlayerId, m_CurrentTurn + COMMAND_DELAY, data.get());
|
||||
m_NetServer.Broadcast(&msg);
|
||||
|
||||
// Add to our local queue
|
||||
AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + COMMAND_DELAY);
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"NotifyFinishedOwnCommands(%d)\n", turn);
|
||||
#endif
|
||||
|
||||
NotifyFinishedClientCommands(m_ClientId, turn);
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::NotifyFinishedClientCommands(int client, u32 turn)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L"NotifyFinishedClientCommands(client=%d, turn=%d)\n", client, turn);
|
||||
#endif
|
||||
|
||||
// Must be a client we've already heard of
|
||||
debug_assert(m_ClientsReady.find(client) != m_ClientsReady.end());
|
||||
|
||||
// Clients must advance one turn at a time
|
||||
debug_assert(turn == m_ClientsReady[client] + 1);
|
||||
m_ClientsReady[client] = turn;
|
||||
|
||||
// See if all clients (including self) are ready for a new turn
|
||||
for (std::map<int, u32>::iterator it = m_ClientsReady.begin(); it != m_ClientsReady.end(); ++it)
|
||||
{
|
||||
#ifdef NETTURN_LOG
|
||||
NETTURN_LOG(L" %d: %d <=? %d\n", it->first, it->second, m_ReadyTurn);
|
||||
#endif
|
||||
if (it->second <= m_ReadyTurn)
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell all clients that the next turn is ready
|
||||
CEndCommandBatchMessage msg;
|
||||
msg.m_TurnLength = TURN_LENGTH;
|
||||
msg.m_Turn = turn;
|
||||
m_NetServer.Broadcast(&msg);
|
||||
|
||||
// Move ourselves to the next turn
|
||||
FinishedAllCommands(m_ReadyTurn + 1);
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::InitialiseClient(int client)
|
||||
{
|
||||
debug_assert(m_ClientsReady.find(client) == m_ClientsReady.end());
|
||||
m_ClientsReady[client] = 1;
|
||||
|
||||
// TODO: do we need some kind of UninitialiseClient in case they leave?
|
||||
}
|
||||
|
||||
void CNetServerTurnManager::OnSimulationMessage(CSimulationMessage* msg)
|
||||
{
|
||||
// Send it back to all clients immediately
|
||||
m_NetServer.Broadcast(msg);
|
||||
|
||||
// TODO: we should do some validation of ownership (clients can't send commands on behalf of opposing players)
|
||||
|
||||
// TODO: we shouldn't send the message back to the client that first sent it
|
||||
|
||||
// Process it ourselves
|
||||
AddCommand(msg->m_Client, msg->m_Player, msg->m_Data, msg->m_Turn);
|
||||
}
|
||||
|
||||
void CNetLocalTurnManager::PostCommand(CScriptValRooted data)
|
||||
{
|
||||
// Add directly to the next turn, ignoring COMMAND_DELAY,
|
||||
// because we don't need to compensate for network latency
|
||||
AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + 1);
|
||||
}
|
||||
|
||||
void CNetLocalTurnManager::NotifyFinishedOwnCommands(u32 turn)
|
||||
{
|
||||
FinishedAllCommands(turn);
|
||||
}
|
||||
|
||||
void CNetLocalTurnManager::OnSimulationMessage(CSimulationMessage* UNUSED(msg))
|
||||
{
|
||||
debug_warn(L"This should never be called");
|
||||
}
|
177
source/network/NetTurnManager.h
Normal file
177
source/network/NetTurnManager.h
Normal file
@ -0,0 +1,177 @@
|
||||
/* 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 INCLUDED_NETTURNMANAGER
|
||||
#define INCLUDED_NETTURNMANAGER
|
||||
|
||||
#include "simulation2/helpers/SimulationCommand.h"
|
||||
|
||||
class CNetServer;
|
||||
class CNetClient;
|
||||
class CSimulationMessage;
|
||||
class CSimulation2;
|
||||
|
||||
/*
|
||||
* This file deals with the logic of the network turn system. The basic idea is as in
|
||||
* http://www.gamasutra.com/view/feature/3094/1500_archers_on_a_288_network_.php?print=1
|
||||
*
|
||||
* Each player performs the simulation for turn N.
|
||||
* User input is translated into commands scheduled for execution in turn N+2 which are
|
||||
* distributed to all other clients.
|
||||
* After a while, a player want to perform the simulation for turn N+1,
|
||||
* which first requires that it has all the other clients' commands for turn N+1.
|
||||
* In that case, it does the simulation and tells all the other clients it has finished
|
||||
* sending commands for turn N+2, and it starts sending commands for turn N+3.
|
||||
*
|
||||
* Commands are redistributed immediately by the server.
|
||||
* To ensure a consistent execution of commands, they are each associated with a
|
||||
* client session ID (which is globally unique and consistent), which is used to sort them.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common network turn system (used by clients, servers, and offline games).
|
||||
*/
|
||||
class CNetTurnManager
|
||||
{
|
||||
NONCOPYABLE(CNetTurnManager);
|
||||
public:
|
||||
/**
|
||||
* Construct for a given player ID, and a given network session ID.
|
||||
*/
|
||||
CNetTurnManager(CSimulation2& simulation, int playerId, int clientId);
|
||||
|
||||
virtual ~CNetTurnManager() { }
|
||||
|
||||
/**
|
||||
* Advance the simulation by a certain time. If this brings us past the current
|
||||
* turn length, the next turn is processed and the function returns true.
|
||||
* Otherwise, nothing happens and it returns false.
|
||||
* @param frameLength length of the previous frame in seconds
|
||||
*/
|
||||
bool Update(float frameLength);
|
||||
|
||||
/**
|
||||
* Advance the graphics by a certain time.
|
||||
* @param frameLength length of the previous frame in seconds
|
||||
*/
|
||||
void Interpolate(float frameLength);
|
||||
|
||||
/**
|
||||
* Called by networking code when a simulation message is received.
|
||||
*/
|
||||
virtual void OnSimulationMessage(CSimulationMessage* msg) = 0;
|
||||
|
||||
/**
|
||||
* Called by simulation code, to add a new command to be distributed to all clients and executed soon.
|
||||
*/
|
||||
virtual void PostCommand(CScriptValRooted data) = 0;
|
||||
|
||||
/**
|
||||
* Called when all commands for a given turn have been received.
|
||||
* This allows Update to progress to that turn.
|
||||
*/
|
||||
void FinishedAllCommands(u32 turn);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Store a command to be executed at a given turn.
|
||||
*/
|
||||
void AddCommand(int client, int player, CScriptValRooted data, u32 turn);
|
||||
|
||||
/**
|
||||
* Called when this client has finished sending all its commands scheduled for the given turn.
|
||||
*/
|
||||
virtual void NotifyFinishedOwnCommands(u32 turn) = 0;
|
||||
|
||||
CSimulation2& m_Simulation2;
|
||||
|
||||
/// The turn that we have most recently executed
|
||||
u32 m_CurrentTurn;
|
||||
|
||||
/// The latest turn for which we have received all commands from all clients
|
||||
u32 m_ReadyTurn;
|
||||
|
||||
/// Commands queued at each turn (index 0 is for m_CurrentTurn+1)
|
||||
std::deque<std::map<u32, std::vector<SimulationCommand> > > m_QueuedCommands;
|
||||
|
||||
int m_PlayerId;
|
||||
uint m_ClientId;
|
||||
|
||||
/// Time remaining until we ought to execute the next turn
|
||||
float m_DeltaTime;
|
||||
};
|
||||
|
||||
class CNetClientTurnManager : public CNetTurnManager
|
||||
{
|
||||
public:
|
||||
CNetClientTurnManager(CSimulation2& simulation, CNetClient& client, int playerId, int clientId) :
|
||||
CNetTurnManager(simulation, playerId, clientId), m_NetClient(client)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnSimulationMessage(CSimulationMessage* msg);
|
||||
|
||||
virtual void PostCommand(CScriptValRooted data);
|
||||
|
||||
protected:
|
||||
virtual void NotifyFinishedOwnCommands(u32 turn);
|
||||
|
||||
CNetClient& m_NetClient;
|
||||
};
|
||||
|
||||
class CNetServerTurnManager : public CNetTurnManager
|
||||
{
|
||||
public:
|
||||
CNetServerTurnManager(CSimulation2& simulation, CNetServer& server, int playerId, int clientId) :
|
||||
CNetTurnManager(simulation, playerId, clientId), m_NetServer(server)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnSimulationMessage(CSimulationMessage* msg);
|
||||
|
||||
virtual void PostCommand(CScriptValRooted data);
|
||||
|
||||
void NotifyFinishedClientCommands(int client, u32 turn);
|
||||
|
||||
void InitialiseClient(int client);
|
||||
|
||||
protected:
|
||||
virtual void NotifyFinishedOwnCommands(u32 turn);
|
||||
|
||||
// Client ID -> ready turn number (the latest turn for which all commands have been received)
|
||||
std::map<int, u32> m_ClientsReady;
|
||||
|
||||
CNetServer& m_NetServer;
|
||||
};
|
||||
|
||||
class CNetLocalTurnManager : public CNetTurnManager
|
||||
{
|
||||
public:
|
||||
CNetLocalTurnManager(CSimulation2& simulation) :
|
||||
CNetTurnManager(simulation, 1, 0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnSimulationMessage(CSimulationMessage* msg);
|
||||
|
||||
virtual void PostCommand(CScriptValRooted data);
|
||||
|
||||
protected:
|
||||
virtual void NotifyFinishedOwnCommands(u32 turn);
|
||||
};
|
||||
|
||||
#endif // INCLUDED_NETTURNMANAGER
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -84,6 +84,8 @@ CGame::CGame():
|
||||
|
||||
if (g_UseSimulation2)
|
||||
{
|
||||
m_TurnManager = new CNetLocalTurnManager(*m_Simulation2); // this will get replaced if we're a net server/client
|
||||
|
||||
m_Simulation2->LoadDefaultScripts();
|
||||
m_Simulation2->ResetState();
|
||||
|
||||
@ -105,12 +107,19 @@ CGame::~CGame()
|
||||
// Again, the in-game call tree is going to be different to the main menu one.
|
||||
g_Profiler.StructuralReset();
|
||||
|
||||
delete m_TurnManager;
|
||||
delete m_GameView;
|
||||
delete m_Simulation2;
|
||||
delete m_Simulation;
|
||||
delete m_World;
|
||||
}
|
||||
|
||||
void CGame::SetTurnManager(CNetTurnManager* turnManager)
|
||||
{
|
||||
if (m_TurnManager)
|
||||
delete m_TurnManager;
|
||||
m_TurnManager = turnManager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -145,11 +154,12 @@ PSRETURN CGame::RegisterInit(CGameAttributes* pAttribs)
|
||||
PSRETURN CGame::ReallyStartGame()
|
||||
{
|
||||
// Call the reallyStartGame GUI function, but only if it exists
|
||||
if (g_GUI->HasPages())
|
||||
{
|
||||
jsval fval, rval;
|
||||
JSBool ok = JS_GetProperty(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), "reallyStartGame", &fval);
|
||||
debug_assert(ok);
|
||||
if (ok && !JSVAL_IS_VOID(fval))
|
||||
{
|
||||
ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), g_GUI->GetScriptObject(), fval, 0, NULL, &rval);
|
||||
}
|
||||
|
||||
@ -162,9 +172,6 @@ PSRETURN CGame::ReallyStartGame()
|
||||
// Mark terrain as modified so the minimap can repaint (is there a cleaner way of handling this?)
|
||||
g_GameRestarted = true;
|
||||
|
||||
|
||||
g_GUI->SendEventToAll("sessionstart");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -240,20 +247,25 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
|
||||
deltaTime *= m_SimRate;
|
||||
|
||||
bool ok = true;
|
||||
if (deltaTime)
|
||||
{
|
||||
if (g_UseSimulation2)
|
||||
{
|
||||
if (m_Simulation2->Update(deltaTime))
|
||||
PROFILE("update");
|
||||
if (m_TurnManager->Update(deltaTime))
|
||||
g_GUI->SendEventToAll("SimulationUpdate");
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = m_Simulation->Update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (doInterpolate)
|
||||
{
|
||||
PROFILE("interpolate");
|
||||
if (g_UseSimulation2)
|
||||
m_Simulation2->Interpolate(deltaTime);
|
||||
m_TurnManager->Interpolate(deltaTime);
|
||||
else
|
||||
m_Simulation->Interpolate(deltaTime);
|
||||
}
|
||||
@ -272,6 +284,12 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
|
||||
return ok;
|
||||
}
|
||||
|
||||
void CGame::Interpolate(float frameLength)
|
||||
{
|
||||
m_TurnManager->Interpolate(frameLength);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test player statistics and update game status as required.
|
||||
*
|
||||
|
@ -34,6 +34,7 @@ class CGameView;
|
||||
class CSimulation;
|
||||
class CPlayer;
|
||||
class CGameAttributes;
|
||||
class CNetTurnManager;
|
||||
|
||||
/**
|
||||
* Default player limit (not counting the Gaia player)
|
||||
@ -98,6 +99,8 @@ class CGame
|
||||
EOG_WIN /// Game is over, local player wins
|
||||
} GameStatus;
|
||||
|
||||
CNetTurnManager* m_TurnManager;
|
||||
|
||||
public:
|
||||
CGame();
|
||||
~CGame();
|
||||
@ -121,6 +124,8 @@ public:
|
||||
*/
|
||||
bool Update(double deltaTime, bool doInterpolate = true);
|
||||
|
||||
void Interpolate(float frameLength);
|
||||
|
||||
void UpdateGameStatus();
|
||||
void EndGame();
|
||||
|
||||
@ -217,6 +222,17 @@ public:
|
||||
inline float GetSimRate() const
|
||||
{ return m_SimRate; }
|
||||
|
||||
/**
|
||||
* Replace the current turn manager.
|
||||
* This class will take ownership of the pointer.
|
||||
*/
|
||||
void SetTurnManager(CNetTurnManager* turnManager);
|
||||
|
||||
CNetTurnManager* GetTurnManager() const
|
||||
{
|
||||
return m_TurnManager;
|
||||
}
|
||||
|
||||
private:
|
||||
PSRETURN RegisterInit(CGameAttributes* pAttribs);
|
||||
};
|
||||
|
@ -122,9 +122,6 @@ static void ProcessCommandLineArgs(const CmdLineArgs& args)
|
||||
// TODO: all these options (and the ones processed elsewhere) should
|
||||
// be documented somewhere for users.
|
||||
|
||||
if (args.Has("autostart"))
|
||||
g_AutostartMap = args.Get("autostart");
|
||||
|
||||
if (args.Has("buildarchive"))
|
||||
{
|
||||
// note: VFS init is sure to have been completed by now
|
||||
|
@ -63,9 +63,6 @@ extern bool g_VSync;
|
||||
|
||||
extern bool g_Quickstart;
|
||||
|
||||
// If non-empty, specified map will be automatically loaded
|
||||
extern CStr g_AutostartMap;
|
||||
|
||||
extern CStrW g_CursorName;
|
||||
|
||||
class CmdLineArgs;
|
||||
|
@ -550,10 +550,16 @@ static void InitVfs(const CmdLineArgs& args)
|
||||
}
|
||||
|
||||
|
||||
static void InitPs(bool setup_gui)
|
||||
static void InitPs(bool setup_gui, const CStrW& gui_page)
|
||||
{
|
||||
if (setup_gui)
|
||||
if (!setup_gui)
|
||||
{
|
||||
// We do actually need *some* kind of GUI loaded, so use the
|
||||
// (currently empty) Atlas one
|
||||
g_GUI->SwitchPage(L"page_atlas.xml", JSVAL_VOID);
|
||||
return;
|
||||
}
|
||||
|
||||
// The things here aren't strictly GUI, but they're unnecessary when in Atlas
|
||||
// because the game doesn't draw any text or handle keys or anything
|
||||
|
||||
@ -584,10 +590,7 @@ static void InitPs(bool setup_gui)
|
||||
}
|
||||
|
||||
// GUI uses VFS, so this must come after VFS init.
|
||||
if (g_AutostartMap.empty())
|
||||
g_GUI->SwitchPage(L"page_pregame.xml", JSVAL_VOID);
|
||||
else
|
||||
g_GUI->SwitchPage(L"page_session_new.xml", JSVAL_VOID);
|
||||
g_GUI->SwitchPage(gui_page, JSVAL_VOID);
|
||||
|
||||
// Warn nicely about missing S3TC support
|
||||
if (!ogl_tex_has_s3tc())
|
||||
@ -605,13 +608,6 @@ static void InitPs(bool setup_gui)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We do actually need *some* kind of GUI loaded, so use the
|
||||
// (currently empty) Atlas one
|
||||
g_GUI->SwitchPage(L"page_atlas.xml", JSVAL_VOID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void InitInput()
|
||||
@ -643,8 +639,7 @@ static void ShutdownPs()
|
||||
{
|
||||
SAFE_DELETE(g_GUI);
|
||||
|
||||
delete g_Console;
|
||||
g_Console = 0;
|
||||
SAFE_DELETE(g_Console);
|
||||
|
||||
// disable the special Windows cursor, or free textures for OGL cursors
|
||||
cursor_draw(0, g_mouse_x, g_mouse_y);
|
||||
@ -716,19 +711,9 @@ static void InitSDL()
|
||||
|
||||
void EndGame()
|
||||
{
|
||||
if (g_NetServer)
|
||||
{
|
||||
delete g_NetServer;
|
||||
g_NetServer=NULL;
|
||||
}
|
||||
else if (g_NetClient)
|
||||
{
|
||||
delete g_NetClient;
|
||||
g_NetClient=NULL;
|
||||
}
|
||||
|
||||
delete g_Game;
|
||||
g_Game=NULL;
|
||||
SAFE_DELETE(g_NetServer);
|
||||
SAFE_DELETE(g_NetClient);
|
||||
SAFE_DELETE(g_Game);
|
||||
}
|
||||
|
||||
|
||||
@ -851,6 +836,8 @@ void EarlyInit()
|
||||
srand(time(NULL)); // NOTE: this rand should *not* be used for simulation!
|
||||
}
|
||||
|
||||
static bool Autostart(const CmdLineArgs& args);
|
||||
|
||||
void Init(const CmdLineArgs& args, int flags)
|
||||
{
|
||||
const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0;
|
||||
@ -1005,9 +992,6 @@ void Init(const CmdLineArgs& args, int flags)
|
||||
pwglSwapIntervalEXT(g_VSync? 1 : 0);
|
||||
#endif
|
||||
|
||||
MICROLOG(L"init ps");
|
||||
InitPs(setup_gui);
|
||||
|
||||
ogl_WarnIfError();
|
||||
InitRenderer();
|
||||
|
||||
@ -1040,31 +1024,142 @@ void Init(const CmdLineArgs& args, int flags)
|
||||
|
||||
ogl_WarnIfError();
|
||||
|
||||
if (! g_AutostartMap.empty())
|
||||
if (!Autostart(args))
|
||||
{
|
||||
// Code copied mostly from atlas/GameInterface/Handlers/Map.cpp -
|
||||
// maybe should be refactored to avoid duplication
|
||||
g_GameAttributes.m_MapFile = g_AutostartMap+".pmp";
|
||||
|
||||
// Make the whole world visible
|
||||
g_GameAttributes.m_LOSSetting = LOS_SETTING_ALL_VISIBLE;
|
||||
g_GameAttributes.m_FogOfWar = false;
|
||||
|
||||
for (int i = 1; i < 8; ++i)
|
||||
g_GameAttributes.GetSlot(i)->AssignLocal();
|
||||
|
||||
g_Game = new CGame();
|
||||
|
||||
PSRETURN ret = g_Game->StartGame(&g_GameAttributes);
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
LDR_NonprogressiveLoad();
|
||||
ret = g_Game->ReallyStartGame();
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
InitPs(setup_gui, L"page_pregame.xml");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RenderGui(bool RenderingState)
|
||||
{
|
||||
g_DoRenderGui = RenderingState;
|
||||
}
|
||||
|
||||
|
||||
// Network autostart:
|
||||
|
||||
class AutostartNetServer : public CNetServer
|
||||
{
|
||||
public:
|
||||
AutostartNetServer(CGame *pGame, CGameAttributes *pGameAttributes, int maxPlayers) :
|
||||
CNetServer(pGame, pGameAttributes), m_NumPlayers(1), m_MaxPlayers(maxPlayers)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
virtual void OnPlayerJoin(CNetSession* pSession)
|
||||
{
|
||||
for (size_t slot = 0; slot < g_GameAttributes.GetSlotCount(); ++slot)
|
||||
{
|
||||
if (g_GameAttributes.GetSlot(slot)->GetAssignment() == SLOT_OPEN)
|
||||
{
|
||||
g_GameAttributes.GetSlot(slot)->AssignToSession(pSession);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_NumPlayers++;
|
||||
|
||||
debug_printf(L"# player joined (got %d, need %d)\n", (int)m_NumPlayers, (int)m_MaxPlayers);
|
||||
|
||||
if (m_NumPlayers >= m_MaxPlayers)
|
||||
{
|
||||
g_GUI->SwitchPage(L"page_loading.xml", JSVAL_VOID);
|
||||
int ret = StartGame();
|
||||
debug_assert(ret == 0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnPlayerLeave(CNetSession* UNUSED(pSession))
|
||||
{
|
||||
debug_warn(L"client left?!");
|
||||
m_NumPlayers--;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_NumPlayers;
|
||||
size_t m_MaxPlayers;
|
||||
};
|
||||
|
||||
class AutostartNetClient : public CNetClient
|
||||
{
|
||||
public:
|
||||
AutostartNetClient(CGame *pGame, CGameAttributes *pGameAttributes) :
|
||||
CNetClient(pGame, pGameAttributes)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
virtual void OnConnectComplete()
|
||||
{
|
||||
debug_printf(L"# connect complete\n");
|
||||
}
|
||||
|
||||
virtual void OnStartGame()
|
||||
{
|
||||
g_GUI->SwitchPage(L"page_loading.xml", JSVAL_VOID);
|
||||
int ret = StartGame();
|
||||
debug_assert(ret == 0);
|
||||
}
|
||||
};
|
||||
|
||||
static bool Autostart(const CmdLineArgs& args)
|
||||
{
|
||||
/*
|
||||
* Handle various command-line options, for quick testing of various features:
|
||||
* -autostart=mapname -- single-player
|
||||
* -autostart=mapname -autostart-playername=Player -autostart-host -autostart-players=2 -- multiplayer host, wait for 2 players
|
||||
* -autostart=mapname -autostart-playername=Player -autostart-client -autostart-ip=127.0.0.1 -- multiplayer client, connect to 127.0.0.1
|
||||
*/
|
||||
|
||||
CStr autostartMap = args.Get("autostart");
|
||||
if (autostartMap.empty())
|
||||
return false;
|
||||
|
||||
g_Game = new CGame();
|
||||
|
||||
g_GameAttributes.m_MapFile = autostartMap + ".pmp";
|
||||
|
||||
// Make the whole world visible
|
||||
g_GameAttributes.m_LOSSetting = LOS_SETTING_ALL_VISIBLE;
|
||||
g_GameAttributes.m_FogOfWar = false;
|
||||
|
||||
if (args.Has("autostart-host"))
|
||||
{
|
||||
InitPs(true, L"page_loading.xml");
|
||||
|
||||
size_t maxPlayers = 2;
|
||||
if (args.Has("autostart-players"))
|
||||
maxPlayers = args.Get("autostart-players").ToUInt();
|
||||
|
||||
g_NetServer = new AutostartNetServer(g_Game, &g_GameAttributes, maxPlayers);
|
||||
// TODO: player name, etc
|
||||
bool ok = g_NetServer->Start(NULL, 0, NULL);
|
||||
debug_assert(ok);
|
||||
}
|
||||
else if (args.Has("autostart-client"))
|
||||
{
|
||||
InitPs(true, L"page_loading.xml");
|
||||
|
||||
bool ok;
|
||||
g_NetClient = new AutostartNetClient(g_Game, &g_GameAttributes);
|
||||
// TODO: player name, etc
|
||||
ok = g_NetClient->Create();
|
||||
debug_assert(ok);
|
||||
ok = g_NetClient->Connect(args.Get("autostart-ip"), DEFAULT_HOST_PORT);
|
||||
debug_assert(ok);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 1; i < 8; ++i)
|
||||
g_GameAttributes.GetSlot(i)->AssignLocal();
|
||||
|
||||
PSRETURN ret = g_Game->StartGame(&g_GameAttributes);
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
LDR_NonprogressiveLoad();
|
||||
ret = g_Game->ReallyStartGame();
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
|
||||
InitPs(true, g_UseSimulation2 ? L"page_session_new.xml" : L"page_session.xml");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -166,15 +166,6 @@ jsval ScriptingHost::ExecuteScript(const CStrW& script, const CStrW& calledFrom,
|
||||
return rval;
|
||||
}
|
||||
|
||||
// unused
|
||||
void ScriptingHost::RegisterFunction(const std::string & functionName, JSNative function, int numArgs)
|
||||
{
|
||||
JSFunction * func = JS_DefineFunction(m_Context, m_GlobalObject, functionName.c_str(), function, numArgs, 0);
|
||||
|
||||
if (func == NULL)
|
||||
throw PSERROR_Scripting_RegisterFunctionFailed();
|
||||
}
|
||||
|
||||
void ScriptingHost::DefineConstant(const std::string & name, int value)
|
||||
{
|
||||
// First remove this constant if it already exists
|
||||
|
@ -109,8 +109,6 @@ public:
|
||||
|
||||
jsval ExecuteScript(const CStrW& script, const CStrW& calledFrom = L"Console", JSObject* contextObject = NULL );
|
||||
|
||||
void RegisterFunction(const std::string & functionName, JSNative function, int numArgs);
|
||||
|
||||
void DefineConstant(const std::string & name, int value);
|
||||
|
||||
void DefineCustomObjectType(JSClass *clasp, JSNative constructor, uintN nargs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
|
||||
|
@ -37,5 +37,6 @@
|
||||
|
||||
class ScriptInterface;
|
||||
class CScriptVal;
|
||||
class CScriptValRooted;
|
||||
|
||||
#endif // INCLUDED_SCRIPTTYPES
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -84,15 +84,15 @@ void CTurnManager::SendBatch(uintptr_t batch)
|
||||
MsgVector::iterator it=messages.begin();
|
||||
while (it != messages.end())
|
||||
{
|
||||
SendMessage(it->m_pMessage, it->m_ClientMask);
|
||||
SendMessageMasked(it->m_pMessage, it->m_ClientMask);
|
||||
++it;
|
||||
}
|
||||
CEndCommandBatchMessage *pMsg=new CEndCommandBatchMessage();
|
||||
pMsg->m_TurnLength=m_Batches[batch].m_TurnLength;
|
||||
SendMessage(pMsg, (size_t)-1);
|
||||
SendMessageMasked(pMsg, (size_t)-1);
|
||||
}
|
||||
|
||||
void CTurnManager::SendMessage(CNetMessage *pMsg, size_t clientMask)
|
||||
void CTurnManager::SendMessageMasked(CNetMessage *pMsg, size_t clientMask)
|
||||
{
|
||||
for (size_t i=0;i<m_Clients.size();i++)
|
||||
{
|
||||
|
@ -83,7 +83,7 @@ private:
|
||||
|
||||
CGameRecord *m_pRecord;
|
||||
|
||||
protected:
|
||||
public:
|
||||
// Rotate the three batches: {0, 1, 2} => {1, 2, 0}
|
||||
void RotateBatches();
|
||||
|
||||
@ -97,7 +97,7 @@ protected:
|
||||
// void UpdateTimingData(size_t client, int fps, int currentLatency);
|
||||
void SetTurnLength(uintptr_t batch, int turnLength);
|
||||
|
||||
void SendMessage(CNetMessage *pMsg, uintptr_t clientMask);
|
||||
void SendMessageMasked(CNetMessage *pMsg, uintptr_t clientMask);
|
||||
|
||||
// Add the message to the specified batch. The message is assumed to be
|
||||
// validated before passed here, and will be blindly trusted.
|
||||
|
@ -27,9 +27,14 @@
|
||||
#include "simulation2/components/ICmpCommandQueue.h"
|
||||
|
||||
#include "lib/file/file_system_util.h"
|
||||
#include "lib/utf8.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/Pyrogenesis.h"
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
bool g_UseSimulation2 = true;
|
||||
|
||||
@ -37,12 +42,14 @@ class CSimulation2Impl
|
||||
{
|
||||
public:
|
||||
CSimulation2Impl(CUnitManager* unitManager, CTerrain* terrain) :
|
||||
m_SimContext(), m_ComponentManager(m_SimContext)
|
||||
m_SimContext(), m_ComponentManager(m_SimContext), m_EnableOOSLog(false)
|
||||
{
|
||||
m_SimContext.m_UnitManager = unitManager;
|
||||
m_SimContext.m_Terrain = terrain;
|
||||
m_ComponentManager.LoadComponentTypes();
|
||||
|
||||
// m_EnableOOSLog = true; // TODO: this should be a command-line flag or similar
|
||||
|
||||
// (can't call ResetState here since the scripts haven't been loaded yet)
|
||||
}
|
||||
|
||||
@ -86,8 +93,10 @@ public:
|
||||
bool LoadScripts(const VfsPath& path);
|
||||
LibError ReloadChangedFile(const VfsPath& path);
|
||||
|
||||
bool Update(float frameTime);
|
||||
void Interpolate(float frameTime);
|
||||
bool Update(int turnLength, const std::vector<SimulationCommand>& commands);
|
||||
void Interpolate(float frameLength, float frameOffset);
|
||||
|
||||
void DumpState();
|
||||
|
||||
CSimContext m_SimContext;
|
||||
CComponentManager m_ComponentManager;
|
||||
@ -99,7 +108,7 @@ public:
|
||||
|
||||
uint32_t m_TurnNumber;
|
||||
|
||||
static const int TURN_LENGTH = 300; // TODO: Use CTurnManager
|
||||
bool m_EnableOOSLog;
|
||||
};
|
||||
|
||||
bool CSimulation2Impl::LoadScripts(const VfsPath& path)
|
||||
@ -141,15 +150,9 @@ LibError CSimulation2Impl::ReloadChangedFile(const VfsPath& path)
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
bool CSimulation2Impl::Update(float frameTime)
|
||||
bool CSimulation2Impl::Update(int turnLength, const std::vector<SimulationCommand>& commands)
|
||||
{
|
||||
// TODO: Use CTurnManager
|
||||
m_DeltaTime += frameTime;
|
||||
if (m_DeltaTime >= 0.0)
|
||||
{
|
||||
double turnLength = TURN_LENGTH / 1000.0;
|
||||
fixed turnLengthFixed = fixed::FromInt(TURN_LENGTH) / 1000;
|
||||
m_DeltaTime -= turnLength;
|
||||
fixed turnLengthFixed = fixed::FromInt(turnLength) / 1000;
|
||||
|
||||
CMessageTurnStart msgTurnStart;
|
||||
m_ComponentManager.BroadcastMessage(msgTurnStart);
|
||||
@ -159,7 +162,7 @@ bool CSimulation2Impl::Update(float frameTime)
|
||||
|
||||
CmpPtr<ICmpCommandQueue> cmpCommandQueue(m_SimContext, SYSTEM_ENTITY);
|
||||
if (!cmpCommandQueue.null())
|
||||
cmpCommandQueue->ProcessCommands();
|
||||
cmpCommandQueue->FlushTurn(commands);
|
||||
|
||||
CMessageUpdate msgUpdate(turnLengthFixed);
|
||||
m_ComponentManager.BroadcastMessage(msgUpdate);
|
||||
@ -170,22 +173,42 @@ bool CSimulation2Impl::Update(float frameTime)
|
||||
// if (m_TurnNumber == 0)
|
||||
// m_ComponentManager.GetScriptInterface().DumpHeap();
|
||||
|
||||
if (m_EnableOOSLog)
|
||||
DumpState();
|
||||
|
||||
++m_TurnNumber;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return true; // TODO: don't bother with bool return
|
||||
}
|
||||
|
||||
void CSimulation2Impl::Interpolate(float frameTime)
|
||||
void CSimulation2Impl::Interpolate(float frameLength, float frameOffset)
|
||||
{
|
||||
// TODO: Use CTurnManager
|
||||
double turnLength = TURN_LENGTH / 1000.0;
|
||||
float offset = clamp(m_DeltaTime / turnLength + 1.0, 0.0, 1.0);
|
||||
CMessageInterpolate msg(frameTime, offset);
|
||||
CMessageInterpolate msg(frameLength, frameOffset);
|
||||
m_ComponentManager.BroadcastMessage(msg);
|
||||
}
|
||||
|
||||
void CSimulation2Impl::DumpState()
|
||||
{
|
||||
PROFILE("DumpState");
|
||||
|
||||
std::wstringstream name;
|
||||
name << L"sim_log/" << getpid() << L"/" << std::setw(5) << std::setfill(L'0') << m_TurnNumber << L".txt";
|
||||
fs::wpath path (psLogDir()/name.str());
|
||||
CreateDirectories(path.branch_path(), 0700);
|
||||
std::ofstream file (path.external_file_string().c_str(), std::ofstream::out | std::ofstream::trunc);
|
||||
|
||||
file << "State hash: " << std::hex;
|
||||
std::string hashRaw;
|
||||
m_ComponentManager.ComputeStateHash(hashRaw);
|
||||
for (size_t i = 0; i < hashRaw.size(); ++i)
|
||||
file << std::setfill('0') << std::setw(2) << (int)(unsigned char)hashRaw[i];
|
||||
file << std::dec << "\n";
|
||||
|
||||
file << "\n";
|
||||
|
||||
m_ComponentManager.DumpDebugState(file);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
CSimulation2::CSimulation2(CUnitManager* unitManager, CTerrain* terrain) :
|
||||
@ -200,6 +223,11 @@ CSimulation2::~CSimulation2()
|
||||
|
||||
// Forward all method calls to the appropriate CSimulation2Impl/CComponentManager methods:
|
||||
|
||||
void CSimulation2::EnableOOSLog()
|
||||
{
|
||||
m->m_EnableOOSLog = true;
|
||||
}
|
||||
|
||||
entity_id_t CSimulation2::AddEntity(const std::wstring& templateName)
|
||||
{
|
||||
return m->m_ComponentManager.AddEntity(templateName, m->m_ComponentManager.AllocateNewEntity());
|
||||
@ -261,14 +289,20 @@ void CSimulation2::InitGame(const CScriptVal& data)
|
||||
GetScriptInterface().CallFunction(GetScriptInterface().GetGlobalObject(), "InitGame", data, ret);
|
||||
}
|
||||
|
||||
bool CSimulation2::Update(float frameTime)
|
||||
bool CSimulation2::Update(int turnLength)
|
||||
{
|
||||
return m->Update(frameTime);
|
||||
std::vector<SimulationCommand> commands;
|
||||
return m->Update(turnLength, commands);
|
||||
}
|
||||
|
||||
void CSimulation2::Interpolate(float frameTime)
|
||||
bool CSimulation2::Update(int turnLength, const std::vector<SimulationCommand>& commands)
|
||||
{
|
||||
m->Interpolate(frameTime);
|
||||
return m->Update(turnLength, commands);
|
||||
}
|
||||
|
||||
void CSimulation2::Interpolate(float frameLength, float frameOffset)
|
||||
{
|
||||
m->Interpolate(frameLength, frameOffset);
|
||||
}
|
||||
|
||||
void CSimulation2::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include "simulation2/system/CmpPtr.h"
|
||||
#include "simulation2/system/Components.h"
|
||||
#include "simulation2/helpers/SimulationCommand.h"
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
|
||||
#include "lib/file/vfs/vfs_path.h"
|
||||
|
||||
@ -32,7 +34,6 @@ class CTerrain;
|
||||
class IComponent;
|
||||
class ScriptInterface;
|
||||
class CMessage;
|
||||
class CScriptVal;
|
||||
class SceneCollector;
|
||||
class CFrustum;
|
||||
|
||||
@ -41,6 +42,7 @@ extern bool g_UseSimulation2;
|
||||
|
||||
/**
|
||||
* Public API for simulation system.
|
||||
* Most code should interact with the simulation only through this API.
|
||||
*/
|
||||
class CSimulation2
|
||||
{
|
||||
@ -50,6 +52,8 @@ public:
|
||||
CSimulation2(CUnitManager*, CTerrain*);
|
||||
~CSimulation2();
|
||||
|
||||
void EnableOOSLog();
|
||||
|
||||
/**
|
||||
* Load all scripts in the specified directory (non-recursively),
|
||||
* so they can register new component types and functions. This
|
||||
@ -89,8 +93,9 @@ public:
|
||||
*/
|
||||
void InitGame(const CScriptVal& data);
|
||||
|
||||
bool Update(float frameTime);
|
||||
void Interpolate(float frameTime);
|
||||
bool Update(int turnLength);
|
||||
bool Update(int turnLength, const std::vector<SimulationCommand>& commands);
|
||||
void Interpolate(float frameLength, float frameOffset);
|
||||
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include "ICmpCommandQueue.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Game.h"
|
||||
#include "network/NetTurnManager.h"
|
||||
|
||||
class CCmpCommandQueue : public ICmpCommandQueue
|
||||
{
|
||||
@ -31,13 +33,7 @@ public:
|
||||
|
||||
DEFAULT_COMPONENT_ALLOCATOR(CommandQueue)
|
||||
|
||||
struct Command
|
||||
{
|
||||
int player;
|
||||
CScriptValRooted data;
|
||||
};
|
||||
|
||||
std::vector<Command> m_CmdQueue;
|
||||
std::vector<SimulationCommand> m_LocalQueue;
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
@ -54,11 +50,11 @@ public:
|
||||
|
||||
virtual void Serialize(ISerializer& serialize)
|
||||
{
|
||||
serialize.NumberU32_Unbounded("num commands", (u32)m_CmdQueue.size());
|
||||
for (size_t i = 0; i < m_CmdQueue.size(); ++i)
|
||||
serialize.NumberU32_Unbounded("num commands", (u32)m_LocalQueue.size());
|
||||
for (size_t i = 0; i < m_LocalQueue.size(); ++i)
|
||||
{
|
||||
serialize.NumberI32_Unbounded("player", m_CmdQueue[i].player);
|
||||
serialize.ScriptVal("data", m_CmdQueue[i].data.get());
|
||||
serialize.NumberI32_Unbounded("player", m_LocalQueue[i].player);
|
||||
serialize.ScriptVal("data", m_LocalQueue[i].data.get());
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,31 +70,47 @@ public:
|
||||
jsval data;
|
||||
deserialize.NumberI32_Unbounded(player);
|
||||
deserialize.ScriptVal(data);
|
||||
Command c = { player, CScriptValRooted(cx, data) };
|
||||
m_CmdQueue.push_back(c);
|
||||
SimulationCommand c = { player, CScriptValRooted(cx, data) };
|
||||
m_LocalQueue.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void PushClientCommand(int player, CScriptVal cmd)
|
||||
virtual void PushLocalCommand(int player, CScriptVal cmd)
|
||||
{
|
||||
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
|
||||
|
||||
Command c = { player, CScriptValRooted(cx, cmd) };
|
||||
m_CmdQueue.push_back(c);
|
||||
SimulationCommand c = { player, CScriptValRooted(cx, cmd) };
|
||||
m_LocalQueue.push_back(c);
|
||||
}
|
||||
|
||||
virtual void ProcessCommands()
|
||||
virtual void PostNetworkCommand(CScriptVal cmd)
|
||||
{
|
||||
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
|
||||
|
||||
// TODO: would be nicer to not use globals
|
||||
g_Game->GetTurnManager()->PostCommand(CScriptValRooted(cx, cmd));
|
||||
}
|
||||
|
||||
virtual void FlushTurn(const std::vector<SimulationCommand>& commands)
|
||||
{
|
||||
ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
|
||||
|
||||
for (size_t i = 0; i < m_CmdQueue.size(); ++i)
|
||||
std::vector<SimulationCommand> localCommands;
|
||||
m_LocalQueue.swap(localCommands);
|
||||
|
||||
for (size_t i = 0; i < localCommands.size(); ++i)
|
||||
{
|
||||
bool ok = scriptInterface.CallFunctionVoid(scriptInterface.GetGlobalObject(), "ProcessCommand", m_CmdQueue[i].player, m_CmdQueue[i].data);
|
||||
bool ok = scriptInterface.CallFunctionVoid(scriptInterface.GetGlobalObject(), "ProcessCommand", localCommands[i].player, localCommands[i].data);
|
||||
if (!ok)
|
||||
LOGERROR(L"Failed to call ProcessCommand() global script function");
|
||||
}
|
||||
|
||||
m_CmdQueue.clear();
|
||||
for (size_t i = 0; i < commands.size(); ++i)
|
||||
{
|
||||
bool ok = scriptInterface.CallFunctionVoid(scriptInterface.GetGlobalObject(), "ProcessCommand", commands[i].player, commands[i].data);
|
||||
if (!ok)
|
||||
LOGERROR(L"Failed to call ProcessCommand() global script function");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(CommandQueue)
|
||||
DEFINE_INTERFACE_METHOD_2("PushClientCommand", void, ICmpCommandQueue, PushClientCommand, int, CScriptVal)
|
||||
// Excluded: ProcessCommands (doesn't make sense for scripts to call it)
|
||||
DEFINE_INTERFACE_METHOD_2("PushLocalCommand", void, ICmpCommandQueue, PushLocalCommand, int, CScriptVal)
|
||||
DEFINE_INTERFACE_METHOD_1("PostNetworkCommand", void, ICmpCommandQueue, PostNetworkCommand, CScriptVal)
|
||||
// Excluded: FlushTurn (doesn't make sense for scripts to call it)
|
||||
END_INTERFACE_WRAPPER(CommandQueue)
|
||||
|
@ -20,35 +20,38 @@
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
#include "simulation2/helpers/SimulationCommand.h"
|
||||
|
||||
/**
|
||||
* Command queue, for sending orders to entities.
|
||||
* Each command is associated with a player ID (who triggered the command, in some sense)
|
||||
* and an arbitrary script value.
|
||||
* Commands can be added to the queue at any time, and will all be executed at the start
|
||||
* of the next turn.
|
||||
*
|
||||
* Typically commands will be sent by the GUI to the networking system, which will eventually
|
||||
* add them to this queue; the player ID identifies the client who initiated the action, and
|
||||
* the command processing code ought to check the player has permission to perform that command
|
||||
* (e.g. make sure they only move their own units). The networking system will add a few turns of
|
||||
* latency before the commands reach the queue.
|
||||
* Commands can be added to the local queue at any time, and will all be executed at the start
|
||||
* of the next turn. (This will typically be used by AI scripts.)
|
||||
*
|
||||
* Alternatively, commands may be added directly to the queue by AI scripts, to emulate a player,
|
||||
* in which case the ID identifies the AI player and the network is not involved.
|
||||
* Alternatively, commands can be sent to the networking system, and they will be executed
|
||||
* at the start of some later turn by all players simultaneously. (This will typically be
|
||||
* used for user inputs.)
|
||||
*/
|
||||
class ICmpCommandQueue : public IComponent
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Pushes a new command onto the queue. @p cmd does not need to be rooted.
|
||||
* Pushes a new command onto the local queue. @p cmd does not need to be rooted.
|
||||
*/
|
||||
virtual void PushClientCommand(int player, CScriptVal cmd) = 0;
|
||||
virtual void PushLocalCommand(int player, CScriptVal cmd) = 0;
|
||||
|
||||
/**
|
||||
* Calls the ProcessCommand(player, cmd) global script function for each command in the queue,
|
||||
* in order of adding to the queue, and empties the queue.
|
||||
* Send a command associated with the current player to the networking system.
|
||||
*/
|
||||
virtual void ProcessCommands() = 0;
|
||||
virtual void PostNetworkCommand(CScriptVal cmd) = 0;
|
||||
|
||||
/**
|
||||
* Calls the ProcessCommand(player, cmd) global script function for each command in the
|
||||
* local queue and in @p commands, and empties the local queue.
|
||||
*/
|
||||
virtual void FlushTurn(const std::vector<SimulationCommand>& commands) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(CommandQueue)
|
||||
};
|
||||
|
@ -36,6 +36,8 @@ public:
|
||||
{
|
||||
ComponentTestHelper test;
|
||||
|
||||
std::vector<SimulationCommand> empty;
|
||||
|
||||
ICmpCommandQueue* cmp = test.Add<ICmpCommandQueue>(CID_CommandQueue, "");
|
||||
|
||||
TS_ASSERT(test.GetScriptInterface().Eval("var cmds = []; function ProcessCommand(player, cmd) { cmds.push([player, cmd]); }"));
|
||||
@ -43,24 +45,24 @@ public:
|
||||
CScriptVal cmd;
|
||||
|
||||
TS_ASSERT(test.GetScriptInterface().Eval("([1,2,3])", cmd));
|
||||
cmp->PushClientCommand(1, cmd);
|
||||
cmp->PushLocalCommand(1, cmd);
|
||||
|
||||
TS_ASSERT(test.GetScriptInterface().Eval("({x:4})", cmd));
|
||||
cmp->PushClientCommand(-1, cmd);
|
||||
cmp->PushLocalCommand(-1, cmd);
|
||||
|
||||
test.Roundtrip();
|
||||
|
||||
// Process the first two commands
|
||||
cmp->ProcessCommands();
|
||||
cmp->FlushTurn(empty);
|
||||
|
||||
TS_ASSERT(test.GetScriptInterface().Eval("({y:5})", cmd));
|
||||
cmp->PushClientCommand(10, cmd);
|
||||
cmp->PushLocalCommand(10, cmd);
|
||||
|
||||
// Process the next command
|
||||
cmp->ProcessCommands();
|
||||
cmp->FlushTurn(empty);
|
||||
|
||||
// Process no commands
|
||||
cmp->ProcessCommands();
|
||||
cmp->FlushTurn(empty);
|
||||
|
||||
test.Roundtrip();
|
||||
|
||||
|
32
source/simulation2/helpers/SimulationCommand.h
Normal file
32
source/simulation2/helpers/SimulationCommand.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* 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 INCLUDED_SIMULATIONCOMMAND
|
||||
#define INCLUDED_SIMULATIONCOMMAND
|
||||
|
||||
#include "scriptinterface/ScriptVal.h"
|
||||
|
||||
/**
|
||||
* Simulation command, typically received over the network in multiplayer games.
|
||||
*/
|
||||
struct SimulationCommand
|
||||
{
|
||||
int player;
|
||||
CScriptValRooted data;
|
||||
};
|
||||
|
||||
#endif // INCLUDED_SIMULATIONCOMMAND
|
@ -116,6 +116,11 @@ void ISerializer::ScriptVal(const char* name, CScriptVal value)
|
||||
PutScriptVal(name, value.get());
|
||||
}
|
||||
|
||||
void ISerializer::ScriptVal(const char* name, CScriptValRooted value)
|
||||
{
|
||||
PutScriptVal(name, value.get());
|
||||
}
|
||||
|
||||
void ISerializer::RawBytes(const char* name, const u8* data, size_t len)
|
||||
{
|
||||
Put(name, data, len);
|
||||
|
@ -184,6 +184,12 @@ public:
|
||||
*/
|
||||
void ScriptVal(const char* name, CScriptVal value);
|
||||
|
||||
/**
|
||||
* Serialize a CScriptValRooted.
|
||||
* The value must not contain any unserializable values (like functions).
|
||||
*/
|
||||
void ScriptVal(const char* name, CScriptValRooted value);
|
||||
|
||||
/**
|
||||
* Serialize a stream of bytes.
|
||||
* It is the caller's responsibility to deal with portability (padding, endianness, etc);
|
||||
|
@ -185,6 +185,11 @@ void CStdDeserializer::ScriptVal(CScriptVal& out)
|
||||
out = ReadScriptVal(NULL);
|
||||
}
|
||||
|
||||
void CStdDeserializer::ScriptVal(CScriptValRooted& out)
|
||||
{
|
||||
out = CScriptValRooted(m_ScriptInterface.GetContext(), ReadScriptVal(NULL));
|
||||
}
|
||||
|
||||
void CStdDeserializer::ScriptObjectAppend(jsval& obj)
|
||||
{
|
||||
if (!JSVAL_IS_OBJECT(obj))
|
||||
|
@ -31,6 +31,7 @@ public:
|
||||
|
||||
virtual void ScriptVal(jsval& out);
|
||||
virtual void ScriptVal(CScriptVal& out);
|
||||
virtual void ScriptVal(CScriptValRooted& out);
|
||||
virtual void ScriptObjectAppend(jsval& obj);
|
||||
virtual void ScriptString(JSString*& out);
|
||||
|
||||
|
@ -305,8 +305,8 @@ void ActorViewer::Render()
|
||||
|
||||
void ActorViewer::Update(float dt)
|
||||
{
|
||||
m.Simulation2.Update(dt);
|
||||
m.Simulation2.Interpolate(dt);
|
||||
m.Simulation2.Update((int)(dt*1000));
|
||||
m.Simulation2.Interpolate(dt, 0);
|
||||
|
||||
if (m.WalkEnabled && m.CurrentSpeed)
|
||||
{
|
||||
|
@ -179,7 +179,7 @@ void ViewGame::Update(float frameLength)
|
||||
{
|
||||
// Update unit interpolation
|
||||
if (g_UseSimulation2)
|
||||
g_Game->GetSimulation2()->Interpolate(0.0);
|
||||
g_Game->Interpolate(0.0);
|
||||
else
|
||||
g_Game->GetSimulation()->Interpolate(0.0);
|
||||
}
|
||||
@ -205,7 +205,7 @@ void ViewGame::Update(float frameLength)
|
||||
// not in every call to g_Game->Update
|
||||
if (g_UseSimulation2)
|
||||
{
|
||||
g_Game->GetSimulation2()->Interpolate(actualFrameLength);
|
||||
g_Game->Interpolate(actualFrameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user