Revamped CNetClient/CNetServer JS Interface and new CGameAttributes
implementation (moving towards a merger of CGameAttribs into CGame). This was SVN commit r1746.
This commit is contained in:
parent
b391fb89ef
commit
ae38aee3d2
@ -121,7 +121,6 @@ static bool g_EntGraph = false;
|
||||
|
||||
static float g_Gamma = 1.0f;
|
||||
|
||||
CGameAttributes g_GameAttributes;
|
||||
extern int game_view_handler(const SDL_Event* ev);
|
||||
|
||||
static CMusicPlayer MusicPlayer;
|
||||
@ -568,12 +567,6 @@ static void Render()
|
||||
oglCheck();
|
||||
}
|
||||
|
||||
static void InitDefaultGameAttributes()
|
||||
{
|
||||
g_GameAttributes.SetValue( "mapFile", L"combattest.pmp" );
|
||||
}
|
||||
|
||||
|
||||
static void LoadProfile( CStr profile )
|
||||
{
|
||||
CStr filename = CStr( "profiles/" ) + profile + CStr( ".cfg" );
|
||||
@ -658,10 +651,6 @@ static void ParseArgs(int argc, char* argv[])
|
||||
if(strncmp(name, "listfiles", 9) == 0)
|
||||
vfs_enable_file_listing(true);
|
||||
break;
|
||||
case 'm':
|
||||
if(strncmp(name, "m=", 2) == 0)
|
||||
g_GameAttributes.SetValue( "mapFile", CStr( argv[i] + 3 ) );
|
||||
break;
|
||||
case 'n':
|
||||
if(strncmp(name, "novbo", 5) == 0)
|
||||
g_ConfigDB.CreateValue(CFG_COMMAND, "novbo")->m_String="true";
|
||||
@ -734,8 +723,6 @@ TIMER(InitScripting)
|
||||
|
||||
JSI_Camera::init();
|
||||
JSI_Console::init();
|
||||
|
||||
CNetClient::ScriptingInit();
|
||||
}
|
||||
|
||||
|
||||
@ -873,8 +860,6 @@ TIMER(InitConfig)
|
||||
g_ConfigDB.SetConfigFile(CFG_MOD, true, "config/mod.cfg");
|
||||
// No point in reloading mod.cfg here - we haven't mounted mods yet
|
||||
|
||||
// We init the defaults here; command line options might want to override
|
||||
InitDefaultGameAttributes();
|
||||
ParseArgs(argc, argv);
|
||||
|
||||
LoadGlobals(); // Collects information from system.cfg, the profile file, and any command-line overrides
|
||||
@ -935,9 +920,6 @@ static void Shutdown()
|
||||
{
|
||||
psShutdown(); // Must delete g_GUI before g_ScriptingHost
|
||||
|
||||
// Release script references to the globals before ScriptingHost shuts down
|
||||
// g_GameAttributes.ReleaseScriptObject();
|
||||
|
||||
if (g_Game)
|
||||
delete g_Game;
|
||||
|
||||
@ -952,6 +934,8 @@ static void Shutdown()
|
||||
// Managed by CWorld
|
||||
// delete &g_EntityManager;
|
||||
|
||||
delete &g_GameAttributes;
|
||||
|
||||
delete &g_EntityTemplateCollection;
|
||||
|
||||
delete &g_ScriptingHost;
|
||||
@ -1130,9 +1114,10 @@ TIMER(init_after_InitRenderer);
|
||||
|
||||
new CSessionManager;
|
||||
|
||||
new CGameAttributes;
|
||||
|
||||
// Register a few Game/Network JS globals
|
||||
g_ScriptingHost.SetGlobal("g_NetServerAttributes", OBJECT_TO_JSVAL(g_NetServerAttributes.GetJSObject()));
|
||||
g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes.GetJSObject()));
|
||||
g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes.GetScript()));
|
||||
|
||||
// Check for heap corruption after every allocation. Very, very slowly.
|
||||
// (And it highlights the allocation just after the one you care about,
|
||||
|
@ -16,20 +16,22 @@ namespace PlayerArray_JS
|
||||
if (!JSVAL_IS_INT(id))
|
||||
return JS_FALSE;
|
||||
uint index=g_ScriptingHost.ValueToInt(id);
|
||||
uint numPlayers=pInstance->GetValue("numPlayers").ToUInt();
|
||||
uint numPlayers=pInstance->m_NumPlayers;
|
||||
|
||||
if (numPlayers > pInstance->m_PlayerAttribs.size())
|
||||
if (numPlayers+1 > pInstance->m_Players.size())
|
||||
{
|
||||
for (size_t i=pInstance->m_PlayerAttribs.size();i<=index;i++)
|
||||
for (size_t i=pInstance->m_Players.size();i<=index;i++)
|
||||
{
|
||||
pInstance->m_PlayerAttribs.push_back(new CGameAttributes::CPlayerAttributes());
|
||||
CPlayer *pNewPlayer=new CPlayer(i);
|
||||
pNewPlayer->SetUpdateCallback(pInstance->m_PlayerUpdateCB, pInstance->m_PlayerUpdateCBData);
|
||||
pInstance->m_Players.push_back(pNewPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
if (index > numPlayers)
|
||||
return JS_FALSE;
|
||||
|
||||
*vp=OBJECT_TO_JSVAL(pInstance->m_PlayerAttribs[index]->GetJSObject());
|
||||
*vp=OBJECT_TO_JSVAL(pInstance->m_Players[index]->GetScript());
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -57,57 +59,82 @@ namespace PlayerArray_JS
|
||||
}
|
||||
};
|
||||
|
||||
namespace PlayerAttribs_JS
|
||||
{
|
||||
void Finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
CAttributeMap *pAttribMap=(CAttributeMap *)JS_GetPrivate(cx, obj);
|
||||
delete pAttribMap;
|
||||
}
|
||||
|
||||
JSClass Class = {
|
||||
"PlayerAttributes", JSCLASS_HAS_PRIVATE,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
AttributeMap_JS::GetProperty, AttributeMap_JS::SetProperty,
|
||||
JS_EnumerateStub, JS_ResolveStub,
|
||||
JS_ConvertStub, Finalize
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
void CGameAttributes::CPlayerAttributes::CreateJSObject()
|
||||
CGameAttributes::CGameAttributes():
|
||||
m_UpdateCB(NULL),
|
||||
m_MapFile("test01.pmp"),
|
||||
m_NumPlayers(2)
|
||||
{
|
||||
ONCE(
|
||||
g_ScriptingHost.DefineCustomObjectType(&PlayerAttribs_JS::Class, AttributeMap_JS::Construct, 0, NULL, NULL, NULL, NULL);
|
||||
g_ScriptingHost.DefineCustomObjectType(&PlayerArray_JS::Class,
|
||||
PlayerArray_JS::Construct, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
ScriptingInit("GameAttributes");
|
||||
);
|
||||
m_JSObject=g_ScriptingHost.CreateCustomObject("PlayerAttributes");
|
||||
JS_SetPrivate(g_ScriptingHost.getContext(), m_JSObject, (CAttributeMap *)this);
|
||||
CAttributeMap::CreateJSObject();
|
||||
}
|
||||
|
||||
CGameAttributes::CPlayerAttributes::CPlayerAttributes()
|
||||
{
|
||||
AddValue("name", L"Player Default Name");
|
||||
}
|
||||
m_PlayerArrayJS=g_ScriptingHost.CreateCustomObject("PlayerArray");
|
||||
JS_SetPrivate(g_ScriptingHost.GetContext(), m_PlayerArrayJS, this);
|
||||
|
||||
CGameAttributes::CGameAttributes():
|
||||
m_PlayerArrayJS(NULL)
|
||||
{
|
||||
AddValue("mapFile", L"test01.pmp");
|
||||
AddValue("numPlayers", L"2");
|
||||
AddSynchedProperty(L"mapFile", &m_MapFile);
|
||||
AddSynchedProperty(L"numPlayers", &m_NumPlayers);
|
||||
|
||||
AddProperty(L"players", (GetFn)&CGameAttributes::JSGetPlayers);
|
||||
|
||||
m_Players.resize(3);
|
||||
for (int i=0;i<3;i++)
|
||||
m_Players[i]=new CPlayer(i);
|
||||
|
||||
m_Players[0]->SetName(L"Gaia");
|
||||
m_Players[0]->SetColour(SPlayerColour(0.2f, 0.7f, 0.2f));
|
||||
|
||||
m_Players[1]->SetName(L"Acumen");
|
||||
m_Players[1]->SetColour(SPlayerColour(1.0f, 0.0f, 0.0f));
|
||||
|
||||
m_Players[2]->SetName(L"Boco the Insignificant");
|
||||
m_Players[2]->SetColour(SPlayerColour(0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
CGameAttributes::~CGameAttributes()
|
||||
{
|
||||
std::vector<CPlayerAttributes *>::iterator it=m_PlayerAttribs.begin();
|
||||
while (it != m_PlayerAttribs.end())
|
||||
std::vector<CPlayer *>::iterator it=m_Players.begin();
|
||||
while (it != m_Players.end())
|
||||
{
|
||||
delete *it;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void CGameAttributes::CreateJSObject()
|
||||
jsval CGameAttributes::JSGetPlayers()
|
||||
{
|
||||
return OBJECT_TO_JSVAL(m_PlayerArrayJS);
|
||||
}
|
||||
|
||||
void CGameAttributes::SetValue(CStrW name, CStrW value)
|
||||
{
|
||||
ISynchedJSProperty *prop=GetSynchedProperty(name);
|
||||
if (prop)
|
||||
{
|
||||
prop->FromString(value);
|
||||
}
|
||||
}
|
||||
|
||||
void CGameAttributes::Update(CStrW name, ISynchedJSProperty *attrib)
|
||||
{
|
||||
if (m_UpdateCB)
|
||||
m_UpdateCB(name, attrib->ToString(), m_UpdateCBData);
|
||||
}
|
||||
|
||||
void CGameAttributes::SetPlayerUpdateCallback(CPlayer::UpdateCallback *cb, void *userdata)
|
||||
{
|
||||
m_PlayerUpdateCB=cb;
|
||||
m_PlayerUpdateCBData=userdata;
|
||||
|
||||
for (int i=0;i<m_Players.size();i++)
|
||||
{
|
||||
m_Players[i]->SetUpdateCallback(cb, userdata);
|
||||
}
|
||||
}
|
||||
|
||||
/*void CGameAttributes::CreateJSObject()
|
||||
{
|
||||
CAttributeMap::CreateJSObject();
|
||||
|
||||
@ -129,7 +156,7 @@ JSBool CGameAttributes::GetJSProperty(jsval id, jsval *ret)
|
||||
if (name == CStr("players"))
|
||||
return JS_TRUE;
|
||||
return CAttributeMap::GetJSProperty(id, ret);
|
||||
}
|
||||
}*/
|
||||
|
||||
// Disable "warning C4355: 'this' : used in base member initializer list".
|
||||
// "The base-class constructors and class member constructors are called before
|
||||
@ -172,7 +199,8 @@ PSRETURN CGame::StartGame(CGameAttributes *pAttribs)
|
||||
for (size_t i=0; i<m_Players.size(); ++i)
|
||||
delete m_Players[i];
|
||||
|
||||
m_NumPlayers=pAttribs->GetValue("numPlayers").ToUInt();
|
||||
//m_NumPlayers=pAttribs->GetValue("numPlayers").ToUInt();
|
||||
m_NumPlayers=pAttribs->m_NumPlayers;
|
||||
|
||||
// Note: If m_Players is resized after this point (causing a reallocation)
|
||||
// various bits of code will still contain pointers to data at the original
|
||||
@ -183,31 +211,7 @@ PSRETURN CGame::StartGame(CGameAttributes *pAttribs)
|
||||
m_Players.resize(m_NumPlayers + 1);
|
||||
|
||||
for (uint i=0;i <= m_NumPlayers;i++)
|
||||
m_Players[i]=new CPlayer(i);
|
||||
|
||||
// FIXME If the GUI hasn't set attributes for all players, the CPlayer
|
||||
// object's state will be whatever is set by the CPlayer constructor.
|
||||
for (uint i=0;i<m_NumPlayers && i<pAttribs->m_PlayerAttribs.size();i++)
|
||||
{
|
||||
CGameAttributes::CPlayerAttributes *pPlayerAttribs=
|
||||
pAttribs->m_PlayerAttribs[i];
|
||||
// TODO Set player attributes in the player object
|
||||
}
|
||||
|
||||
m_Players[0]->m_Name = L"Gaia";
|
||||
m_Players[0]->m_Colour.r = 0.2f;
|
||||
m_Players[0]->m_Colour.g = 0.7f;
|
||||
m_Players[0]->m_Colour.b = 0.2f;
|
||||
|
||||
m_Players[1]->m_Name = L"Acumen";
|
||||
m_Players[1]->m_Colour.r = 1.0f;
|
||||
m_Players[1]->m_Colour.g = 0.0f;
|
||||
m_Players[1]->m_Colour.b = 0.0f;
|
||||
|
||||
m_Players[2]->m_Name = L"Boco the Insignificant";
|
||||
m_Players[2]->m_Colour.r = 0.0f;
|
||||
m_Players[2]->m_Colour.g = 0.0f;
|
||||
m_Players[2]->m_Colour.b = 1.0f;
|
||||
m_Players[i]=pAttribs->m_Players[i];
|
||||
|
||||
m_pLocalPlayer=m_Players[1];
|
||||
|
||||
|
@ -11,31 +11,55 @@ ERROR_GROUP(Game);
|
||||
#include "Player.h"
|
||||
#include "GameView.h"
|
||||
|
||||
#include "AttributeMap.h"
|
||||
#include "scripting/SynchedJSObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class CGameAttributes: public CAttributeMap
|
||||
namespace PlayerArray_JS
|
||||
{
|
||||
protected:
|
||||
virtual void CreateJSObject();
|
||||
virtual JSBool GetJSProperty(jsval id, jsval *ret);
|
||||
JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
};
|
||||
|
||||
#define g_GameAttributes CGameAttributes::GetSingleton()
|
||||
class CGameAttributes:
|
||||
public CSynchedJSObject<CGameAttributes>,
|
||||
public Singleton<CGameAttributes>
|
||||
{
|
||||
public:
|
||||
typedef void (UpdateCallback)(CStrW name, CStrW newValue, void *data);
|
||||
|
||||
private:
|
||||
friend JSBool PlayerArray_JS::GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
|
||||
virtual void Update(CStrW name, ISynchedJSProperty *attrib);
|
||||
|
||||
UpdateCallback *m_UpdateCB;
|
||||
void *m_UpdateCBData;
|
||||
|
||||
CPlayer::UpdateCallback *m_PlayerUpdateCB;
|
||||
void *m_PlayerUpdateCBData;
|
||||
|
||||
jsval JSGetPlayers();
|
||||
|
||||
public:
|
||||
CStrW m_MapFile;
|
||||
uint m_NumPlayers;
|
||||
|
||||
CGameAttributes();
|
||||
virtual ~CGameAttributes();
|
||||
|
||||
// NOTE: Public only for JS interface
|
||||
class CPlayerAttributes: public CAttributeMap
|
||||
{
|
||||
protected:
|
||||
virtual void CreateJSObject();
|
||||
public:
|
||||
CPlayerAttributes();
|
||||
};
|
||||
void SetValue(CStrW name, CStrW value);
|
||||
|
||||
inline void SetUpdateCallback(UpdateCallback *cb, void *userdata)
|
||||
{
|
||||
m_UpdateCB=cb;
|
||||
m_UpdateCBData=userdata;
|
||||
}
|
||||
|
||||
void SetPlayerUpdateCallback(CPlayer::UpdateCallback *cb, void *userdata);
|
||||
|
||||
std::vector <CPlayer *> m_Players;
|
||||
JSObject *m_PlayerArrayJS;
|
||||
std::vector <CPlayerAttributes *> m_PlayerAttribs;
|
||||
};
|
||||
|
||||
class CGame
|
||||
@ -73,7 +97,15 @@ public:
|
||||
{ m_pLocalPlayer=pLocalPlayer; }
|
||||
|
||||
inline CPlayer *GetPlayer(uint idx)
|
||||
{ if (idx >= 0 && idx < m_NumPlayers) return m_Players[idx]; else { debug_warn("Invalid player ID"); return m_Players[0]; } }
|
||||
{
|
||||
if (idx >= 0 && idx < m_NumPlayers)
|
||||
return m_Players[idx];
|
||||
else
|
||||
{
|
||||
debug_warn("Invalid player ID");
|
||||
return m_Players[0];
|
||||
}
|
||||
}
|
||||
|
||||
inline std::vector<CPlayer*>* GetPlayers()
|
||||
{ return( &m_Players ); }
|
||||
|
@ -133,7 +133,7 @@ END_NMT_CLASS()
|
||||
START_NMT_CLASS_(Authenticate)
|
||||
NMT_FIELD(CStrW, m_Name)
|
||||
//NMT_FIELD(CPasswordHash, m_Password)
|
||||
NMT_FIELD(CStr, m_Password)
|
||||
NMT_FIELD(CStrW, m_Password)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(ChatMessage)
|
||||
@ -144,7 +144,7 @@ END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(SetGameConfig)
|
||||
NMT_START_ARRAY(m_Values)
|
||||
NMT_FIELD(CStr, m_Name)
|
||||
NMT_FIELD(CStrW, m_Name)
|
||||
NMT_FIELD(CStrW, m_Value)
|
||||
NMT_END_ARRAY()
|
||||
END_NMT_CLASS()
|
||||
@ -152,7 +152,7 @@ END_NMT_CLASS()
|
||||
START_NMT_CLASS_(SetPlayerConfig)
|
||||
NMT_FIELD_INT(m_PlayerID, u32, 2)
|
||||
NMT_START_ARRAY(m_Values)
|
||||
NMT_FIELD(CStr, m_Name)
|
||||
NMT_FIELD(CStrW, m_Name)
|
||||
NMT_FIELD(CStrW, m_Value)
|
||||
NMT_END_ARRAY()
|
||||
END_NMT_CLASS()
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "lib.h"
|
||||
|
||||
#include <scripting/DOMEvent.h>
|
||||
#include <scripting/JSConversions.h>
|
||||
#include <scripting/ScriptableObject.h>
|
||||
@ -18,7 +20,7 @@ enum CClientEvents
|
||||
{
|
||||
CLIENT_EVENT_START_GAME,
|
||||
CLIENT_EVENT_CHAT,
|
||||
CLIENT_EVENT_CONNECT,
|
||||
CLIENT_EVENT_CONNECT_COMPLETE,
|
||||
CLIENT_EVENT_LAST
|
||||
};
|
||||
|
||||
@ -45,13 +47,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CConnectEvent: public CScriptEvent
|
||||
class CConnectCompleteEvent: public CScriptEvent
|
||||
{
|
||||
CStrW m_Message;
|
||||
bool m_Success;
|
||||
public:
|
||||
CConnectEvent(CStrW message, bool success):
|
||||
CScriptEvent(L"connect", false, CLIENT_EVENT_CONNECT),
|
||||
CConnectCompleteEvent(CStrW message, bool success):
|
||||
CScriptEvent(L"connectComplete", false, CLIENT_EVENT_CONNECT_COMPLETE),
|
||||
m_Message(message),
|
||||
m_Success(success)
|
||||
{
|
||||
@ -66,11 +68,20 @@ CNetClient::CNetClient(CGame *pGame, CGameAttributes *pGameAttribs):
|
||||
m_pGame(pGame),
|
||||
m_pGameAttributes(pGameAttribs)
|
||||
{
|
||||
ONCE(
|
||||
// This one's funny: if you remove the parantheses around this stmt
|
||||
// the preprocessor will take the comma inside the template decl and
|
||||
// interpret it as a macro argument delimiter ;-)
|
||||
(AddMethod<bool, &CNetClient::JSI_BeginConnect>("beginConnect", 1));
|
||||
|
||||
CJSObject<CNetClient>::ScriptingInit("NetClient");
|
||||
);
|
||||
|
||||
m_pGame->GetSimulation()->SetTurnManager(this);
|
||||
|
||||
AddProperty(L"onStartGame", &m_OnStartGame);
|
||||
AddProperty(L"onChat", &m_OnChat);
|
||||
AddProperty(L"onConnect", &m_OnConnect);
|
||||
AddProperty(L"onConnectComplete", &m_OnConnectComplete);
|
||||
|
||||
AddProperty(L"password", &m_Password);
|
||||
AddProperty(L"playerName", &m_Name);
|
||||
@ -83,13 +94,6 @@ CNetClient::~CNetClient()
|
||||
g_ScriptingHost.SetGlobal("g_NetClient", JSVAL_NULL);
|
||||
}
|
||||
|
||||
void CNetClient::ScriptingInit()
|
||||
{
|
||||
AddMethod<bool, &CNetClient::JSI_BeginConnect>("beginConnect", 1);
|
||||
|
||||
CJSObject<CNetClient>::ScriptingInit("NetClient");
|
||||
}
|
||||
|
||||
bool CNetClient::JSI_BeginConnect(JSContext *cx, uintN argc, jsval *argv)
|
||||
{
|
||||
CStr connectHostName;
|
||||
@ -143,20 +147,20 @@ bool CNetClient::ConnectHandler(CNetMessage *pMsg, CNetSession *pSession)
|
||||
{
|
||||
case NMT_CONNECT_COMPLETE:
|
||||
pClient->m_pMessageHandler=HandshakeHandler;
|
||||
if (pClient->m_OnConnect.Defined())
|
||||
if (pClient->m_OnConnectComplete.Defined())
|
||||
{
|
||||
CConnectEvent evt=CConnectEvent(CStr(PS_OK), true);
|
||||
pClient->m_OnConnect.DispatchEvent(pClient->GetScript(), &evt);
|
||||
CConnectCompleteEvent evt=CConnectCompleteEvent(CStr(PS_OK), true);
|
||||
pClient->m_OnConnectComplete.DispatchEvent(pClient->GetScript(), &evt);
|
||||
}
|
||||
break;
|
||||
case NMT_ERROR:
|
||||
{
|
||||
CNetErrorMessage *msg=(CNetErrorMessage *)pMsg;
|
||||
LOG(ERROR, LOG_CAT_NET, "CNetClient::ConnectHandler(): Connect Failed: %s", msg->m_Error);
|
||||
if (pClient->m_OnConnect.Defined())
|
||||
if (pClient->m_OnConnectComplete.Defined())
|
||||
{
|
||||
CConnectEvent evt=CConnectEvent(CStr(msg->m_Error), false);
|
||||
pClient->m_OnConnect.DispatchEvent(pClient->GetScript(), &evt);
|
||||
CConnectCompleteEvent evt=CConnectCompleteEvent(CStr(msg->m_Error), false);
|
||||
pClient->m_OnConnectComplete.DispatchEvent(pClient->GetScript(), &evt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class CNetClient: public CNetSession, protected CTurnManager, public CJSObject<C
|
||||
// JS event scripts
|
||||
CScriptObject m_OnStartGame;
|
||||
CScriptObject m_OnChat;
|
||||
CScriptObject m_OnConnect;
|
||||
CScriptObject m_OnConnectComplete;
|
||||
|
||||
protected:
|
||||
virtual void NewTurn();
|
||||
@ -46,7 +46,6 @@ public:
|
||||
static MessageHandler ChatHandler;
|
||||
static MessageHandler InGameHandler;
|
||||
|
||||
static void ScriptingInit();
|
||||
bool JSI_BeginConnect(JSContext *cx, uintN argc, jsval *argv);
|
||||
};
|
||||
|
||||
|
@ -67,7 +67,7 @@ public:
|
||||
|
||||
typedef CNetMessage * (*NetMessageDeserializer) (const u8 *buffer, uint length);
|
||||
|
||||
#include "Entity.h"
|
||||
#include "EntityHandles.h"
|
||||
|
||||
struct SNetMessageDeserializerRegistration
|
||||
{
|
||||
|
@ -15,7 +15,6 @@
|
||||
extern CConsole *g_Console;
|
||||
|
||||
CNetServer *g_NetServer=NULL;
|
||||
CNetServerAttributes g_NetServerAttributes;
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -40,227 +39,55 @@ void CNetServer::OnAccept(const CSocketAddress &addr)
|
||||
CNetServerSession *pSession=CreateSession(pInt);
|
||||
}
|
||||
|
||||
CNetServerSession::~CNetServerSession()
|
||||
{
|
||||
m_pServer->RemoveSession(this);
|
||||
}
|
||||
|
||||
void CNetServerSession::StartGame()
|
||||
{
|
||||
if (m_pMessageHandler==PreGameHandler)
|
||||
m_pMessageHandler=InGameHandler;
|
||||
}
|
||||
|
||||
#define UNHANDLED(_pMsg) return false;
|
||||
#define HANDLED(_pMsg) delete _pMsg; return true;
|
||||
#define TAKEN(_pMsg) return true;
|
||||
|
||||
bool CNetServerSession::BaseHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
switch (pMsg->GetType())
|
||||
{
|
||||
case NMT_ERROR:
|
||||
{
|
||||
CNetErrorMessage *msg=(CNetErrorMessage *)pMsg;
|
||||
LOG(WARNING, LOG_CAT_NET, "CNetServerSession::BaseHandler(): NMT_ERROR: %s", msg->GetString().c_str());
|
||||
if (msg->m_State == SS_UNCONNECTED)
|
||||
{
|
||||
/* We were disconnected... What happens with our session?
|
||||
*
|
||||
* Note that deleting a session also removes it from m_Sessions
|
||||
* in CNetServer. If the session is an observer it is also
|
||||
* removed from m_Observers.
|
||||
*
|
||||
* Sessions that are observers or chatters should perhaps
|
||||
* generate an exit message that is sent to all other sessions.
|
||||
*
|
||||
* Player sessions require more care. In Pre-Game, each player
|
||||
* session has an associated CPlayer object and player session
|
||||
* slot requiring special care when deleting.
|
||||
*/
|
||||
if (!pSession->m_pPlayer)
|
||||
{
|
||||
delete pSession;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(ERROR, LOG_CAT_NET, "CNetServerSession::BaseHandler(): Player disconnection not implemented!!");
|
||||
}
|
||||
}
|
||||
HANDLED(pMsg);
|
||||
}
|
||||
}
|
||||
UNHANDLED(pMsg);
|
||||
}
|
||||
|
||||
bool CNetServerSession::HandshakeHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::HandshakeHandler(): %s.", pMsg->GetString().c_str());
|
||||
switch (pMsg->GetType())
|
||||
{
|
||||
case NMT_ClientHandshake:
|
||||
{
|
||||
CClientHandshake * /*SmartPointer<CClientHandshake>*/ msg=(CClientHandshake *)pMsg/*.GetRawPointer()*/;
|
||||
|
||||
if (msg->m_ProtocolVersion != PS_PROTOCOL_VERSION)
|
||||
do {} while(0); // This will never happen to us here, but anyways ;-)
|
||||
|
||||
CServerHandshakeResponse *retmsg=new CServerHandshakeResponse();
|
||||
retmsg->m_UseProtocolVersion=PS_PROTOCOL_VERSION;
|
||||
retmsg->m_Flags=0;
|
||||
retmsg->m_Message=pSession->m_pServer->GetAttributes()->GetValue("welcomeMessage");
|
||||
pSession->Push(retmsg);
|
||||
|
||||
pSession->m_pMessageHandler=AuthenticateHandler;
|
||||
|
||||
HANDLED(pMsg);
|
||||
}
|
||||
}
|
||||
return BaseHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::AuthenticateHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
CNetServer *pServer=pSession->m_pServer;
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): %s.", pMsg->GetString().c_str());
|
||||
if (pMsg->GetType() == NMT_Authenticate)
|
||||
{
|
||||
CAuthenticate *msg=(CAuthenticate *)pMsg;
|
||||
|
||||
if (msg->m_Password == pSession->m_pServer->m_Password)
|
||||
{
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): Login Successful");
|
||||
pSession->m_Name=msg->m_Name;
|
||||
|
||||
pSession->m_pServer->m_Sessions.push_back(pSession);
|
||||
|
||||
CResult *msg=new CResult();
|
||||
msg->m_Code=NRC_OK;
|
||||
msg->m_Message=L"Logged in";
|
||||
pSession->Push(msg);
|
||||
if (pServer->GetServerState() == NSS_PreGame)
|
||||
{
|
||||
// We're in pre-game. The connected client becomes a Player.
|
||||
// Find the first free player slot and assign the player to the
|
||||
// client.
|
||||
// If no free player slots could be found - demote to chatter/
|
||||
// observer.
|
||||
pSession->m_pMessageHandler=PreGameHandler;
|
||||
if (!pServer->AddNewPlayer(pSession))
|
||||
pSession->m_pMessageHandler=ObserverHandler;
|
||||
}
|
||||
else // We're not in pre-game. The session becomes a chatter/observer here.
|
||||
{
|
||||
pSession->m_pMessageHandler=ObserverHandler;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(WARNING, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): Login Failed");
|
||||
CResult *msg=new CResult();
|
||||
msg->m_Code=NRC_PasswordInvalid;
|
||||
msg->m_Message=L"Invalid Password";
|
||||
pSession->Push(msg);
|
||||
}
|
||||
|
||||
HANDLED(pMsg);
|
||||
}
|
||||
return BaseHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::PreGameHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::PreGameHandler(): %s.", pMsg->GetString().c_str());
|
||||
|
||||
return ChatHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::ObserverHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
printf("CNetServerSession::ObserverHandler(): %s.\n", pMsg->GetString().c_str());
|
||||
|
||||
// TODO Implement observers and chatter => observer promotion
|
||||
/*
|
||||
if (pMsg->GetType() == NMT_RequestObserve)
|
||||
*/
|
||||
|
||||
return ChatHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::ChatHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
if (pMsg->GetType() == NMT_ChatMessage)
|
||||
{
|
||||
CChatMessage *msg=(CChatMessage *)pMsg;
|
||||
msg->m_Sender=pSession->m_Name;
|
||||
CStrW wstr=msg->m_Message;
|
||||
g_Console->ReceivedChatMessage(pSession->GetName().c_str(), wstr.c_str());
|
||||
pSession->m_pServer->Broadcast(msg);
|
||||
|
||||
TAKEN(pMsg);
|
||||
}
|
||||
|
||||
return BaseHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::InGameHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
if (pMsg->GetType() != NMT_EndCommandBatch)
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::InGameHandler(): %s.", pMsg->GetString().c_str());
|
||||
|
||||
if (BaseHandler(pMsg, pNetSession))
|
||||
return true;
|
||||
|
||||
if (ChatHandler(pMsg, pNetSession))
|
||||
return true;
|
||||
|
||||
if (pMsg->GetType() >= NMT_COMMAND_FIRST && pMsg->GetType() <= NMT_COMMAND_LAST)
|
||||
{
|
||||
// All Command Messages (i.e. simulation turn synchronized messages)
|
||||
//pSession->m_pPlayer->ValidateCommand(pMsg);
|
||||
pSession->m_pServer->QueueIncomingCommand(pMsg);
|
||||
TAKEN(pMsg);
|
||||
}
|
||||
|
||||
switch (pMsg->GetType())
|
||||
{
|
||||
case NMT_EndCommandBatch:
|
||||
// TODO Update client timing information and recalculate turn length
|
||||
HANDLED(pMsg);
|
||||
|
||||
default:
|
||||
UNHANDLED(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Replace with next generation CNetServer JS Interface
|
||||
CNetServerAttributes::CNetServerAttributes()
|
||||
{
|
||||
AddValue("serverPlayerName", L"Noname Server Player");
|
||||
AddValue("serverName", L"Noname Server");
|
||||
AddValue("welcomeMessage", L"Noname Server Welcome Message");
|
||||
}
|
||||
|
||||
CNetServer::CNetServer(CNetServerAttributes *pServerAttribs, CGame *pGame, CGameAttributes *pGameAttribs):
|
||||
m_pServerAttributes(pServerAttribs),
|
||||
CNetServer::CNetServer(CGame *pGame, CGameAttributes *pGameAttribs):
|
||||
m_pGame(pGame),
|
||||
m_pGameAttributes(pGameAttribs),
|
||||
m_MaxObservers(5)
|
||||
m_MaxObservers(5),
|
||||
m_ServerPlayerName(L"Noname Server Player"),
|
||||
m_ServerName(L"Noname Server"),
|
||||
m_WelcomeMessage(L"Noname Server Welcome Message"),
|
||||
m_Port(-1)
|
||||
{
|
||||
ONCE(
|
||||
(AddMethod<bool, &CNetServer::JSI_Open>("open", 0));
|
||||
|
||||
CJSObject<CNetServer>::ScriptingInit("NetServer");
|
||||
);
|
||||
|
||||
AddProperty(L"serverPlayerName", &m_ServerPlayerName);
|
||||
AddProperty(L"serverName", &m_ServerName);
|
||||
AddProperty(L"welcomeMessage", &m_WelcomeMessage);
|
||||
|
||||
AddProperty(L"port", &m_Port);
|
||||
|
||||
m_pGameAttributes->SetUpdateCallback(AttributeUpdate, this);
|
||||
m_ServerPlayerName=m_pServerAttributes->GetValue("serverPlayerName");
|
||||
m_NumPlayers=m_pGameAttributes->GetValue("numPlayers").ToUInt();
|
||||
m_pGameAttributes->SetPlayerUpdateCallback(PlayerAttributeUpdate, this);
|
||||
m_NumPlayers=m_pGameAttributes->m_NumPlayers;
|
||||
|
||||
m_pGame->GetSimulation()->SetTurnManager(this);
|
||||
// Set an incredibly long turn length - less command batch spam that way
|
||||
for (int i=0;i<3;i++)
|
||||
CTurnManager::SetTurnLength(i, 3000);
|
||||
|
||||
g_ScriptingHost.SetGlobal("g_NetServer", OBJECT_TO_JSVAL(GetScript()));
|
||||
}
|
||||
|
||||
bool CNetServer::JSI_Open(JSContext *cx, uintN argc, jsval *argv)
|
||||
{
|
||||
CSocketAddress addr;
|
||||
if (m_Port == -1)
|
||||
GetDefaultListenAddress(addr);
|
||||
else
|
||||
addr=CSocketAddress(m_Port, /*m_UseIPv6?IPv6:*/IPv4);
|
||||
|
||||
PS_RESULT res=Bind(addr);
|
||||
if (res != PS_OK)
|
||||
{
|
||||
LOG(ERROR, LOG_CAT_NET, "CNetServer::JSI_Open(): Bind error: %s", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PS_RESULT CNetServer::Bind(const CSocketAddress &address)
|
||||
@ -271,20 +98,24 @@ PS_RESULT CNetServer::Bind(const CSocketAddress &address)
|
||||
return res;
|
||||
}
|
||||
|
||||
void FillSetGameConfigCB(CStrW name, ISynchedJSProperty *prop, void *userdata)
|
||||
{
|
||||
CSetGameConfig *pMsg=(CSetGameConfig *)userdata;
|
||||
uint size=pMsg->m_Values.size();
|
||||
pMsg->m_Values.resize(size+1);
|
||||
pMsg->m_Values[size].m_Name=name;
|
||||
pMsg->m_Values[size].m_Value=prop->ToString();
|
||||
}
|
||||
|
||||
void CNetServer::FillSetGameConfig(CSetGameConfig *pMsg)
|
||||
{
|
||||
m_pGameAttributes->IterateSynchedProperties(FillSetGameConfigCB, pMsg);
|
||||
}
|
||||
|
||||
bool CNetServer::AddNewPlayer(CNetServerSession *pSession)
|
||||
{
|
||||
CAttributeMap::MapType &attribs=m_pGameAttributes->GetInternalValueMap();
|
||||
CAttributeMap::MapType::iterator it=attribs.begin();
|
||||
|
||||
CSetGameConfig *pMsg=new CSetGameConfig();
|
||||
uint n=0;
|
||||
while (it != attribs.end())
|
||||
{
|
||||
pMsg->m_Values.resize(++n);
|
||||
pMsg->m_Values.back().m_Name=it->first;
|
||||
pMsg->m_Values.back().m_Value=it->second;
|
||||
++it;
|
||||
}
|
||||
FillSetGameConfig(pMsg);
|
||||
pSession->Push(pMsg);
|
||||
|
||||
if (m_PlayerSessions.size() < m_NumPlayers-1)
|
||||
@ -294,8 +125,7 @@ bool CNetServer::AddNewPlayer(CNetServerSession *pSession)
|
||||
// Broadcast a message for the newly added player session
|
||||
CPlayerConnect *pMsg=new CPlayerConnect();
|
||||
pMsg->m_Players.resize(1);
|
||||
// The player ID is the player session index plus one
|
||||
pMsg->m_Players[0].m_PlayerID=(u32)m_PlayerSessions.size();
|
||||
pMsg->m_Players[0].m_PlayerID=pSession->m_pPlayer->GetPlayerID();
|
||||
pMsg->m_Players[0].m_Nick=pSession->GetName();
|
||||
Broadcast(pMsg);
|
||||
|
||||
@ -303,7 +133,7 @@ bool CNetServer::AddNewPlayer(CNetServerSession *pSession)
|
||||
|
||||
// Server Player
|
||||
pMsg->m_Players.resize(1);
|
||||
pMsg->m_Players.back().m_PlayerID=0;
|
||||
pMsg->m_Players.back().m_PlayerID=m_pServerPlayer->GetPlayerID();
|
||||
pMsg->m_Players.back().m_Nick=m_ServerPlayerName;
|
||||
|
||||
// All the other players
|
||||
@ -321,11 +151,12 @@ bool CNetServer::AddNewPlayer(CNetServerSession *pSession)
|
||||
return false;
|
||||
}
|
||||
|
||||
void CNetServer::AttributeUpdate(CStr name, CStrW newValue, void *userdata)
|
||||
void CNetServer::AttributeUpdate(CStrW name, CStrW newValue, void *userdata)
|
||||
{
|
||||
CNetServer *pServer=(CNetServer *)userdata;
|
||||
g_Console->InsertMessage(L"AttributeUpdate: %ls = \"%ls\"", name.c_str(), newValue.c_str());
|
||||
|
||||
if (name == CStr("numPlayers"))
|
||||
if (name == CStrW(L"numPlayers"))
|
||||
{
|
||||
pServer->m_NumPlayers=newValue.ToUInt();
|
||||
}
|
||||
@ -338,6 +169,20 @@ void CNetServer::AttributeUpdate(CStr name, CStrW newValue, void *userdata)
|
||||
pServer->Broadcast(pMsg);
|
||||
}
|
||||
|
||||
void CNetServer::PlayerAttributeUpdate(CStrW name, CStrW newValue, CPlayer *pPlayer, void *userdata)
|
||||
{
|
||||
CNetServer *pServer=(CNetServer *)userdata;
|
||||
g_Console->InsertMessage(L"PlayerAttributeUpdate(%d): %ls = \"%ls\"", pPlayer->GetPlayerID(), name.c_str(), newValue.c_str());
|
||||
|
||||
CSetPlayerConfig *pMsg=new CSetPlayerConfig;
|
||||
pMsg->m_PlayerID=pPlayer->GetPlayerID();
|
||||
pMsg->m_Values.resize(1);
|
||||
pMsg->m_Values[0].m_Name=name;
|
||||
pMsg->m_Values[0].m_Value=newValue;
|
||||
|
||||
pServer->Broadcast(pMsg);
|
||||
}
|
||||
|
||||
bool CNetServer::AllowObserver(CNetServerSession *pSession)
|
||||
{
|
||||
return m_Observers.size() < m_MaxObservers;
|
||||
|
@ -2,50 +2,10 @@
|
||||
#define _Network_NetServer_H
|
||||
|
||||
#include "Network/Session.h"
|
||||
#include "Network/ServerSession.h"
|
||||
#include "Game.h"
|
||||
#include "TurnManager.h"
|
||||
|
||||
class CNetServer;
|
||||
class CPlayer;
|
||||
|
||||
class CNetServerSession: public CNetSession
|
||||
{
|
||||
CNetServer *m_pServer;
|
||||
CPlayer *m_pPlayer;
|
||||
bool m_IsObserver;
|
||||
|
||||
protected:
|
||||
friend class CNetServer;
|
||||
|
||||
inline void SetPlayer(CPlayer *pPlayer)
|
||||
{ m_pPlayer=pPlayer; }
|
||||
|
||||
public:
|
||||
virtual ~CNetServerSession();
|
||||
|
||||
inline CNetServerSession(CNetServer *pServer, CSocketInternal *pInt, MessageHandler *pMsgHandler=HandshakeHandler):
|
||||
CNetSession(pInt, pMsgHandler),
|
||||
m_pServer(pServer),
|
||||
m_pPlayer(NULL),
|
||||
m_IsObserver(false)
|
||||
{}
|
||||
|
||||
inline bool IsObserver()
|
||||
{ return m_IsObserver; }
|
||||
|
||||
// Called by server when starting the game, after sending NMT_StartGame to
|
||||
// all connected clients.
|
||||
void StartGame();
|
||||
|
||||
static MessageHandler BaseHandler;
|
||||
static MessageHandler HandshakeHandler;
|
||||
static MessageHandler AuthenticateHandler;
|
||||
static MessageHandler PreGameHandler;
|
||||
static MessageHandler ObserverHandler;
|
||||
static MessageHandler ChatHandler;
|
||||
static MessageHandler InGameHandler;
|
||||
};
|
||||
|
||||
enum ENetServerState
|
||||
{
|
||||
// We haven't opened the port yet, we're just setting some stuff up.
|
||||
@ -55,19 +15,17 @@ enum ENetServerState
|
||||
// rules are set up by the operator and where players join and select civs
|
||||
// and stuff.
|
||||
NSS_PreGame,
|
||||
// In-Game state: the one with all the killing ;-)
|
||||
NSS_InGame,
|
||||
// The game is over and someone has won. Players might linger to chat or
|
||||
// download the replay log.
|
||||
NSS_PostGame
|
||||
};
|
||||
|
||||
class CNetServerAttributes: public CAttributeMap
|
||||
{
|
||||
public:
|
||||
CNetServerAttributes();
|
||||
};
|
||||
|
||||
class CNetServer: protected CServerSocket, protected CTurnManager
|
||||
class CNetServer:
|
||||
protected CServerSocket,
|
||||
protected CTurnManager,
|
||||
public CJSObject<CNetServer>
|
||||
{
|
||||
private:
|
||||
/*
|
||||
@ -94,12 +52,20 @@ private:
|
||||
|
||||
CGame *m_pGame;
|
||||
CGameAttributes *m_pGameAttributes;
|
||||
CNetServerAttributes *m_pServerAttributes;
|
||||
|
||||
CStr m_Password;
|
||||
CPlayer *m_pServerPlayer;
|
||||
|
||||
CStrW m_Password;
|
||||
CStrW m_ServerPlayerName;
|
||||
CStrW m_ServerName;
|
||||
CStrW m_WelcomeMessage;
|
||||
|
||||
static CAttributeMap::UpdateCallback AttributeUpdate;
|
||||
int m_Port;
|
||||
|
||||
static CGameAttributes::UpdateCallback AttributeUpdate;
|
||||
static CPlayer::UpdateCallback PlayerAttributeUpdate;
|
||||
|
||||
void FillSetGameConfig(CSetGameConfig *pMsg);
|
||||
|
||||
protected:
|
||||
friend class CNetServerSession;
|
||||
@ -120,14 +86,6 @@ protected:
|
||||
// by the caller.
|
||||
void QueueIncomingCommand(CNetMessage *pMsg);
|
||||
|
||||
// Simple accessor. NOTE: Most attributes are read in when creating the
|
||||
// server object, so changing the attributes should not have any effect on
|
||||
// the server's operation. Hence, return const-pointer.
|
||||
inline const CNetServerAttributes *GetAttributes() const
|
||||
{
|
||||
return m_pServerAttributes;
|
||||
}
|
||||
|
||||
// OVERRIDES FROM CServerSocket
|
||||
virtual void OnAccept(const CSocketAddress &);
|
||||
|
||||
@ -147,7 +105,7 @@ protected:
|
||||
virtual bool AllowObserver(CNetServerSession *pSession);
|
||||
|
||||
public:
|
||||
CNetServer(CNetServerAttributes *pServerAttribs, CGame *pGame, CGameAttributes *pGameAttribs);
|
||||
CNetServer(CGame *pGame, CGameAttributes *pGameAttribs);
|
||||
|
||||
static void GetDefaultListenAddress(CSocketAddress &address);
|
||||
PS_RESULT Bind(const CSocketAddress &address);
|
||||
@ -163,11 +121,12 @@ public:
|
||||
|
||||
int StartGame();
|
||||
|
||||
// Synchronized, safe to call from any thread
|
||||
void Broadcast(CNetMessage *);
|
||||
|
||||
// JS Interface Methods
|
||||
bool JSI_Open(JSContext *cx, uintN argc, jsval *argv);
|
||||
};
|
||||
|
||||
extern CNetServer *g_NetServer;
|
||||
extern CNetServerAttributes g_NetServerAttributes;
|
||||
|
||||
#endif // _Network_NetServer_H
|
||||
|
214
source/ps/Network/ServerSession.cpp
Normal file
214
source/ps/Network/ServerSession.cpp
Normal file
@ -0,0 +1,214 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "Network/ServerSession.h"
|
||||
#include "Network/Server.h"
|
||||
#include "CLogger.h"
|
||||
#include "CConsole.h"
|
||||
|
||||
extern CConsole *g_Console;
|
||||
|
||||
CNetServerSession::~CNetServerSession()
|
||||
{
|
||||
m_pServer->RemoveSession(this);
|
||||
}
|
||||
|
||||
void CNetServerSession::StartGame()
|
||||
{
|
||||
if (m_pMessageHandler==PreGameHandler)
|
||||
m_pMessageHandler=InGameHandler;
|
||||
}
|
||||
|
||||
#define UNHANDLED(_pMsg) return false;
|
||||
#define HANDLED(_pMsg) delete _pMsg; return true;
|
||||
#define TAKEN(_pMsg) return true;
|
||||
|
||||
bool CNetServerSession::BaseHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
switch (pMsg->GetType())
|
||||
{
|
||||
case NMT_ERROR:
|
||||
{
|
||||
CNetErrorMessage *msg=(CNetErrorMessage *)pMsg;
|
||||
LOG(WARNING, LOG_CAT_NET, "CNetServerSession::BaseHandler(): NMT_ERROR: %s", msg->GetString().c_str());
|
||||
if (msg->m_State == SS_UNCONNECTED)
|
||||
{
|
||||
/* We were disconnected... What happens with our session?
|
||||
*
|
||||
* Note that deleting a session also removes it from m_Sessions
|
||||
* in CNetServer. If the session is an observer it is also
|
||||
* removed from m_Observers.
|
||||
*
|
||||
* Sessions that are observers or chatters should perhaps
|
||||
* generate an exit message that is sent to all other sessions.
|
||||
*
|
||||
* Player sessions require more care.
|
||||
* Pre-Game: each player session has an associated CPlayer
|
||||
* object and player session slot requiring special care when
|
||||
* deleting.
|
||||
* In-Game: Revert all player's entities to Gaia control,
|
||||
* awaiting the client's reconnect attempts [IF we implement it]
|
||||
* Post-Game: Just do your basic clean-up
|
||||
*/
|
||||
if (!pSession->m_pPlayer)
|
||||
{
|
||||
delete pSession;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(ERROR, LOG_CAT_NET, "CNetServerSession::BaseHandler(): Player disconnection not implemented!!");
|
||||
}
|
||||
}
|
||||
HANDLED(pMsg);
|
||||
}
|
||||
}
|
||||
UNHANDLED(pMsg);
|
||||
}
|
||||
|
||||
bool CNetServerSession::HandshakeHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::HandshakeHandler(): %s.", pMsg->GetString().c_str());
|
||||
switch (pMsg->GetType())
|
||||
{
|
||||
case NMT_ClientHandshake:
|
||||
{
|
||||
CClientHandshake *msg=(CClientHandshake *)pMsg;
|
||||
|
||||
if (msg->m_ProtocolVersion != PS_PROTOCOL_VERSION)
|
||||
{
|
||||
pSession->Push(new CCloseRequestMessage());
|
||||
BaseHandler(new CNetErrorMessage(PS_OK, SS_UNCONNECTED), pSession);
|
||||
}
|
||||
|
||||
CServerHandshakeResponse *retmsg=new CServerHandshakeResponse();
|
||||
retmsg->m_UseProtocolVersion=PS_PROTOCOL_VERSION;
|
||||
retmsg->m_Flags=0;
|
||||
retmsg->m_Message=pSession->m_pServer->m_WelcomeMessage;
|
||||
pSession->Push(retmsg);
|
||||
|
||||
pSession->m_pMessageHandler=AuthenticateHandler;
|
||||
|
||||
HANDLED(pMsg);
|
||||
}
|
||||
}
|
||||
return BaseHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::AuthenticateHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
CNetServer *pServer=pSession->m_pServer;
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): %s.", pMsg->GetString().c_str());
|
||||
if (pMsg->GetType() == NMT_Authenticate)
|
||||
{
|
||||
CAuthenticate *msg=(CAuthenticate *)pMsg;
|
||||
|
||||
if (msg->m_Password == pSession->m_pServer->m_Password)
|
||||
{
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): Login Successful");
|
||||
pSession->m_Name=msg->m_Name;
|
||||
|
||||
pSession->m_pServer->m_Sessions.push_back(pSession);
|
||||
|
||||
CResult *msg=new CResult();
|
||||
msg->m_Code=NRC_OK;
|
||||
msg->m_Message=L"Logged in";
|
||||
pSession->Push(msg);
|
||||
if (pServer->GetServerState() == NSS_PreGame)
|
||||
{
|
||||
// We're in pre-game. The connected client becomes a Player.
|
||||
// Find the first free player slot and assign the player to the
|
||||
// client.
|
||||
// If no free player slots could be found - demote to chatter/
|
||||
// observer.
|
||||
pSession->m_pMessageHandler=PreGameHandler;
|
||||
if (!pServer->AddNewPlayer(pSession))
|
||||
pSession->m_pMessageHandler=ObserverHandler;
|
||||
}
|
||||
else // We're not in pre-game. The session becomes a chatter/observer here.
|
||||
{
|
||||
pSession->m_pMessageHandler=ObserverHandler;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(WARNING, LOG_CAT_NET, "CNetServerSession::AuthenticateHandler(): Login Failed");
|
||||
CResult *msg=new CResult();
|
||||
msg->m_Code=NRC_PasswordInvalid;
|
||||
msg->m_Message=L"Invalid Password";
|
||||
pSession->Push(msg);
|
||||
}
|
||||
|
||||
HANDLED(pMsg);
|
||||
}
|
||||
return BaseHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::PreGameHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::PreGameHandler(): %s.", pMsg->GetString().c_str());
|
||||
|
||||
return ChatHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::ObserverHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
printf("CNetServerSession::ObserverHandler(): %s.\n", pMsg->GetString().c_str());
|
||||
|
||||
// TODO Implement observers and chatter => observer promotion
|
||||
/*
|
||||
if (pMsg->GetType() == NMT_RequestObserve)
|
||||
*/
|
||||
|
||||
return ChatHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::ChatHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
if (pMsg->GetType() == NMT_ChatMessage)
|
||||
{
|
||||
CChatMessage *msg=(CChatMessage *)pMsg;
|
||||
msg->m_Sender=pSession->m_Name;
|
||||
CStrW wstr=msg->m_Message;
|
||||
g_Console->ReceivedChatMessage(pSession->GetName().c_str(), wstr.c_str());
|
||||
pSession->m_pServer->Broadcast(msg);
|
||||
|
||||
TAKEN(pMsg);
|
||||
}
|
||||
|
||||
return BaseHandler(pMsg, pNetSession);
|
||||
}
|
||||
|
||||
bool CNetServerSession::InGameHandler(CNetMessage *pMsg, CNetSession *pNetSession)
|
||||
{
|
||||
CNetServerSession *pSession=(CNetServerSession *)pNetSession;
|
||||
if (pMsg->GetType() != NMT_EndCommandBatch)
|
||||
LOG(NORMAL, LOG_CAT_NET, "CNetServerSession::InGameHandler(): %s.", pMsg->GetString().c_str());
|
||||
|
||||
if (BaseHandler(pMsg, pNetSession))
|
||||
return true;
|
||||
|
||||
if (ChatHandler(pMsg, pNetSession))
|
||||
return true;
|
||||
|
||||
if (pMsg->GetType() >= NMT_COMMAND_FIRST && pMsg->GetType() <= NMT_COMMAND_LAST)
|
||||
{
|
||||
// All Command Messages (i.e. simulation turn synchronized messages)
|
||||
//pSession->m_pPlayer->ValidateCommand(pMsg);
|
||||
pSession->m_pServer->QueueIncomingCommand(pMsg);
|
||||
TAKEN(pMsg);
|
||||
}
|
||||
|
||||
switch (pMsg->GetType())
|
||||
{
|
||||
case NMT_EndCommandBatch:
|
||||
// TODO Update client timing information and recalculate turn length
|
||||
HANDLED(pMsg);
|
||||
|
||||
default:
|
||||
UNHANDLED(pMsg);
|
||||
}
|
||||
}
|
56
source/ps/Network/ServerSession.h
Normal file
56
source/ps/Network/ServerSession.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
CNetServerSession - the server's representation of a connected client
|
||||
|
||||
AUTHOR: Simon Brenner <simon.brenner@home.se>
|
||||
|
||||
DESCRIPTION:
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _Network_ServerSession_H
|
||||
#define _Network_ServerSession_H
|
||||
|
||||
#include "Network/Session.h"
|
||||
|
||||
class CNetServer;
|
||||
class CPlayer;
|
||||
|
||||
class CNetServerSession: public CNetSession
|
||||
{
|
||||
CNetServer *m_pServer;
|
||||
CPlayer *m_pPlayer;
|
||||
bool m_IsObserver;
|
||||
|
||||
protected:
|
||||
friend class CNetServer;
|
||||
|
||||
inline void SetPlayer(CPlayer *pPlayer)
|
||||
{ m_pPlayer=pPlayer; }
|
||||
|
||||
public:
|
||||
virtual ~CNetServerSession();
|
||||
|
||||
inline CNetServerSession(CNetServer *pServer, CSocketInternal *pInt, MessageHandler *pMsgHandler=HandshakeHandler):
|
||||
CNetSession(pInt, pMsgHandler),
|
||||
m_pServer(pServer),
|
||||
m_pPlayer(NULL),
|
||||
m_IsObserver(false)
|
||||
{}
|
||||
|
||||
inline bool IsObserver()
|
||||
{ return m_IsObserver; }
|
||||
|
||||
// Called by server when starting the game, after sending NMT_StartGame to
|
||||
// all connected clients.
|
||||
void StartGame();
|
||||
|
||||
static MessageHandler BaseHandler;
|
||||
static MessageHandler HandshakeHandler;
|
||||
static MessageHandler AuthenticateHandler;
|
||||
static MessageHandler PreGameHandler;
|
||||
static MessageHandler ObserverHandler;
|
||||
static MessageHandler ChatHandler;
|
||||
static MessageHandler InGameHandler;
|
||||
};
|
||||
|
||||
#endif
|
@ -583,6 +583,7 @@ void CSocketBase::RunWaitLoop()
|
||||
if (res == -1)
|
||||
{
|
||||
perror("CSocketSet::RunWaitLoop(), select");
|
||||
pthread_mutex_lock(&g_SocketSetInternal.m_Mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -597,6 +598,7 @@ void CSocketBase::RunWaitLoop()
|
||||
return;
|
||||
else if (bt=='r')
|
||||
{
|
||||
pthread_mutex_lock(&g_SocketSetInternal.m_Mutex);
|
||||
//printf("Op mask reload after select\n");
|
||||
continue;
|
||||
}
|
||||
|
@ -7,12 +7,21 @@
|
||||
#include "scripting/JSCollection.h"
|
||||
|
||||
CPlayer::CPlayer(uint playerID):
|
||||
m_PlayerID(playerID)
|
||||
m_PlayerID(playerID),
|
||||
m_Name(CStrW(L"Player #")+CStrW(playerID)),
|
||||
m_Colour(0.7f, 0.7f, 0.7f),
|
||||
m_UpdateCB(0)
|
||||
{
|
||||
AddReadOnlyProperty( L"id", &m_PlayerID );
|
||||
AddProperty( L"controlled", (IJSObject::GetFn)GetControlledEntities_JS );
|
||||
AddProperty( L"name", &m_Name );
|
||||
AddProperty( L"colour", &m_Colour );
|
||||
AddProperty( L"controlled", (IJSObject::GetFn)&CPlayer::GetControlledEntities_JS );
|
||||
AddSynchedProperty( L"name", &m_Name );
|
||||
AddSynchedProperty( L"colour", &m_Colour );
|
||||
}
|
||||
|
||||
void CPlayer::Update(CStrW name, ISynchedJSProperty *prop)
|
||||
{
|
||||
if (m_UpdateCB)
|
||||
m_UpdateCB(name, prop->ToString(), this, m_UpdateCBData);
|
||||
}
|
||||
|
||||
bool CPlayer::ValidateCommand(CNetMessage *pMsg)
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define _Player_H
|
||||
|
||||
#include "CStr.h"
|
||||
#include "scripting/SynchedJSObject.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "scripting/ScriptCustomTypes.h"
|
||||
#include "EntityHandles.h"
|
||||
@ -10,22 +11,44 @@ class CNetMessage;
|
||||
|
||||
typedef SColour SPlayerColour;
|
||||
|
||||
class CPlayer : public CJSObject<CPlayer>
|
||||
class CPlayer : public CSynchedJSObject<CPlayer>
|
||||
{
|
||||
// FIXME: These shouldn't be public (need load-from-attributes method in game.cpp)
|
||||
public:
|
||||
typedef void (UpdateCallback)(CStrW name, CStrW value, CPlayer *player, void *userdata);
|
||||
|
||||
private:
|
||||
CStrW m_Name;
|
||||
uint m_PlayerID;
|
||||
SPlayerColour m_Colour;
|
||||
|
||||
UpdateCallback *m_UpdateCB;
|
||||
void *m_UpdateCBData;
|
||||
|
||||
virtual void Update(CStrW name, ISynchedJSProperty *prop);
|
||||
|
||||
public:
|
||||
CPlayer( uint playerID );
|
||||
|
||||
bool ValidateCommand(CNetMessage *pMsg);
|
||||
|
||||
inline uint GetPlayerID() const
|
||||
{ return m_PlayerID; }
|
||||
|
||||
inline const CStrW &GetName() const
|
||||
{ return m_Name; }
|
||||
inline void SetName(const CStrW &name)
|
||||
{ m_Name = name; }
|
||||
|
||||
inline const SPlayerColour &GetColour() const
|
||||
{ return m_Colour; }
|
||||
inline void SetColour(const SPlayerColour &colour)
|
||||
{ m_Colour = colour; }
|
||||
|
||||
inline void SetUpdateCallback(UpdateCallback *cb, void *userdata)
|
||||
{
|
||||
m_UpdateCB=cb;
|
||||
m_UpdateCBData=userdata;
|
||||
}
|
||||
|
||||
// Caller frees...
|
||||
std::vector<HEntity>* GetControlledEntities();
|
||||
|
@ -22,7 +22,7 @@ void CWorld::Initialize(CGameAttributes *pAttribs)
|
||||
|
||||
CStr mapfilename("maps/scenarios/");
|
||||
|
||||
mapfilename += (CStr)pAttribs->GetValue( "mapFile" );
|
||||
mapfilename += (CStr)pAttribs->m_MapFile;
|
||||
|
||||
try {
|
||||
CMapReader reader;
|
||||
|
@ -127,7 +127,7 @@ void XMLWriter_Element::Text(const char* text)
|
||||
|
||||
|
||||
|
||||
template <> void XMLWriter_File::ElementAttribute<CStr>(const char* name, CStr& value, bool newelement)
|
||||
template <> void XMLWriter_File::ElementAttribute<CStr>(const char* name, const CStr& value, bool newelement)
|
||||
{
|
||||
if (newelement)
|
||||
{
|
||||
@ -145,35 +145,30 @@ template <> void XMLWriter_File::ElementAttribute<CStr>(const char* name, CStr&
|
||||
}
|
||||
}
|
||||
|
||||
// (Simon) Since GCC refuses to pass temporaries through non-const reference,
|
||||
// define this wrapper function to convert [ugly] a const CStr to a non-const
|
||||
template <>
|
||||
inline void XMLWriter_File::ElementAttribute<const CStr>(const char *name, const CStr& value, bool newelement)
|
||||
{
|
||||
ElementAttribute(name, (CStr &)value, newelement);
|
||||
}
|
||||
|
||||
// Attribute/setting value-to-string template specialisations:
|
||||
|
||||
// Use CStr's conversion for most types:
|
||||
#define TYPE(T) \
|
||||
template <> void XMLWriter_File::ElementAttribute<T>(const char* name, T& value, bool newelement) \
|
||||
#define TYPE2(ID_T, ARG_T) \
|
||||
template <> void XMLWriter_File::ElementAttribute<ID_T>(const char* name, ARG_T value, bool newelement) \
|
||||
{ \
|
||||
ElementAttribute<const CStr>(name, CStr(value), newelement); \
|
||||
ElementAttribute(name, CStr(value), newelement); \
|
||||
}
|
||||
#define TYPE(T) TYPE2(T, const T &)
|
||||
|
||||
TYPE(int)
|
||||
TYPE(unsigned int)
|
||||
TYPE(float)
|
||||
TYPE(double)
|
||||
TYPE(const char*)
|
||||
// This is the effect of doing const T& with T=const char* - char const* const&
|
||||
// Weird - I know ;-)
|
||||
TYPE2(const char *, char const* const&)
|
||||
|
||||
template <> void XMLWriter_File::ElementAttribute<CStrW>(const char* name, CStrW& value, bool newelement)
|
||||
template <> void XMLWriter_File::ElementAttribute<CStrW>(const char* name, const CStrW& value, bool newelement)
|
||||
{
|
||||
ElementAttribute<const CStr>(name, value.utf8(), newelement);
|
||||
ElementAttribute(name, value.utf8(), newelement);
|
||||
}
|
||||
|
||||
template <> void XMLWriter_File::ElementAttribute<CPlayer*>(const char* name, CPlayer*& value, bool newelement)
|
||||
template <> void XMLWriter_File::ElementAttribute<CPlayer*>(const char* name, CPlayer*const & value, bool newelement)
|
||||
{
|
||||
ElementAttribute(name, value->m_PlayerID, newelement);
|
||||
ElementAttribute(name, value->GetPlayerID(), newelement);
|
||||
}
|
@ -100,7 +100,7 @@ private:
|
||||
|
||||
void ElementStart(XMLWriter_Element* element, const char* name);
|
||||
void ElementText(const char* text);
|
||||
template <typename T> void ElementAttribute(const char* name, T& value, bool newelement);
|
||||
template <typename T> void ElementAttribute(const char* name, const T& value, bool newelement);
|
||||
void ElementClose();
|
||||
void ElementEnd(const char* name, int type);
|
||||
|
||||
|
@ -64,8 +64,7 @@ jsval SColour::ToString( JSContext* cx, uintN argc, jsval* argv )
|
||||
buffer[255] = 0;
|
||||
|
||||
utf16string str16(buffer, buffer+wcslen(buffer));
|
||||
|
||||
return( STRING_TO_JSVAL( JS_NewUCStringCopyZ( cx, buffer ) ) );
|
||||
return( STRING_TO_JSVAL( JS_NewUCStringCopyZ( cx, str16.c_str() ) ) );
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,9 +59,8 @@ JSFunctionSpec ScriptFunctionTable[] =
|
||||
{"setCursor", setCursor, 1, 0, 0 },
|
||||
{"startGame", startGame, 0, 0, 0 },
|
||||
{"endGame", endGame, 0, 0, 0 },
|
||||
{"joinGame", joinGame, 0, 0, 0 },
|
||||
{"createClient", createClient, 0, 0, 0 },
|
||||
{"startServer", startServer, 0, 0, 0 },
|
||||
{"createServer", createServer, 0, 0, 0 },
|
||||
{"loadLanguage", loadLanguage, 1, 0, 0 },
|
||||
{"getLanguageID", getLanguageID, 0, 0, 0 },
|
||||
{"getFPS", getFPS, 0, 0, 0 },
|
||||
@ -325,38 +324,16 @@ JSBool setCursor(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalOb
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// Some globals from main.cpp
|
||||
extern void CreateGame();
|
||||
extern CGameAttributes g_GameAttributes;
|
||||
|
||||
JSBool startServer(JSContext* cx, JSObject* UNUSEDPARAM(globalObject), unsigned int argc, jsval* argv, jsval* rval)
|
||||
JSBool createServer(JSContext* cx, JSObject* UNUSEDPARAM(globalObject), unsigned int argc, jsval* argv, jsval* rval)
|
||||
{
|
||||
CSocketAddress listenAddress;
|
||||
if (argc == 0)
|
||||
{
|
||||
CNetServer::GetDefaultListenAddress(listenAddress);
|
||||
}
|
||||
if (argc == 1)
|
||||
{
|
||||
LOG(MESSAGE, LOG_CAT_NET, "startServer: Server port is %d\n", g_ScriptingHost.ValueToInt(argv[0]));
|
||||
listenAddress=CSocketAddress(g_ScriptingHost.ValueToInt(argv[0]), IPv4);
|
||||
}
|
||||
|
||||
g_Game=new CGame();
|
||||
g_NetServer=new CNetServer(&g_NetServerAttributes, g_Game, &g_GameAttributes);
|
||||
PS_RESULT res=g_NetServer->Bind(listenAddress);
|
||||
if (res != PS_OK)
|
||||
{
|
||||
LOG(ERROR, LOG_CAT_NET, "startServer: Bind error: %s", res);
|
||||
*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
|
||||
return JS_TRUE;
|
||||
}
|
||||
g_NetServer=new CNetServer(g_Game, &g_GameAttributes);
|
||||
|
||||
*rval = BOOLEAN_TO_JSVAL(JS_TRUE);
|
||||
*rval=OBJECT_TO_JSVAL(g_NetServer->GetScript());
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// More from main.cpp
|
||||
// from main.cpp
|
||||
extern void StartGame();
|
||||
|
||||
JSBool createClient(JSContext* cx, JSObject* UNUSEDPARAM(globalObject), unsigned int argc, jsval* argv, jsval* rval)
|
||||
@ -373,45 +350,8 @@ JSBool createClient(JSContext* cx, JSObject* UNUSEDPARAM(globalObject), unsigned
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool joinGame(JSContext* cx, JSObject* UNUSEDPARAM(globalObject), unsigned int argc, jsval* argv, jsval* rval)
|
||||
{
|
||||
CStrW username;
|
||||
CStr password;
|
||||
CStr connectHostName;
|
||||
int connectPort=PS_DEFAULT_PORT;
|
||||
if (argc >= 2) // Two args; name, hostname and default port
|
||||
{
|
||||
username=g_ScriptingHost.ValueToUCString(argv[0]);
|
||||
password="";
|
||||
connectHostName=g_ScriptingHost.ValueToString(argv[1]);
|
||||
}
|
||||
else
|
||||
return JS_FALSE;
|
||||
|
||||
if (argc == 3)
|
||||
{
|
||||
connectPort=g_ScriptingHost.ValueToInt(argv[2]);
|
||||
}
|
||||
|
||||
if (g_Game)
|
||||
{
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
g_Game=new CGame();
|
||||
g_NetClient=new CNetClient(g_Game, &g_GameAttributes);
|
||||
g_NetClient->SetLoginInfo(username, password);
|
||||
PS_RESULT res=g_NetClient->BeginConnect(connectHostName.c_str(), connectPort);
|
||||
if (res != PS_OK)
|
||||
{
|
||||
LOG(ERROR, LOG_CAT_NET, "joinGame: BeginConnect error: %s", res);
|
||||
*rval=BOOLEAN_TO_JSVAL(JS_FALSE);
|
||||
}
|
||||
else
|
||||
*rval=BOOLEAN_TO_JSVAL(JS_TRUE);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// TODO Replace startGame with create(Game|Server|Client)/game.start() - after
|
||||
// merging CGame and CGameAttributes
|
||||
JSBool startGame(JSContext* cx, JSObject* UNUSEDPARAM(globalObject), unsigned int argc, jsval* argv, jsval* rval)
|
||||
{
|
||||
if (argc != 0)
|
||||
|
@ -35,9 +35,8 @@ JSFunc getGlobal;
|
||||
JSFunc setCursor;
|
||||
|
||||
JSFunc GetGameObject;
|
||||
JSFunc startServer;
|
||||
JSFunc createServer;
|
||||
JSFunc createClient;
|
||||
JSFunc joinGame;
|
||||
JSFunc startGame;
|
||||
JSFunc endGame;
|
||||
|
||||
|
@ -127,7 +127,7 @@ jsval ScriptingHost::ExecuteScript(const CStrW& script, const CStrW& calledFrom,
|
||||
|
||||
JSBool ok = JS_EvaluateUCScript(m_Context, contextObject ? contextObject : m_GlobalObject, script.utf16().c_str(), (int)script.Length(), asciiName, 0, &rval);
|
||||
|
||||
delete( asciiName );
|
||||
delete[]( asciiName );
|
||||
|
||||
if (!ok) return JSVAL_NULL;
|
||||
|
||||
|
78
source/scripting/SynchedJSObject.cpp
Normal file
78
source/scripting/SynchedJSObject.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "SynchedJSObject.h"
|
||||
#include "Parser.h"
|
||||
#include "ScriptCustomTypes.h"
|
||||
|
||||
template <>
|
||||
CStrW ToNetString(const uint &val)
|
||||
{
|
||||
return CStrW(val);
|
||||
}
|
||||
|
||||
template <>
|
||||
void SetFromNetString(uint &val, CStrW string)
|
||||
{
|
||||
val=string.ToUInt();
|
||||
}
|
||||
|
||||
template <>
|
||||
CStrW ToNetString(const CStrW &data)
|
||||
{ return data; }
|
||||
|
||||
template <> void SetFromNetString(CStrW &data, CStrW string)
|
||||
{ data=string; }
|
||||
|
||||
template <>
|
||||
CStrW ToNetString(const SColour &data)
|
||||
{
|
||||
wchar_t buf[256];
|
||||
swprintf(buf, 256, L"%f %f %f %f", data.r, data.g, data.b, data.a);
|
||||
buf[255]=0;
|
||||
|
||||
return CStrW(buf);
|
||||
}
|
||||
|
||||
template <>
|
||||
void SetFromNetString(SColour &data, CStrW wstring)
|
||||
{
|
||||
CParser &parser(CParserCache::Get("$value_$value_$value_$value"));
|
||||
CParserLine line;
|
||||
|
||||
line.ParseString(parser, CStr(wstring));
|
||||
|
||||
float values[4];
|
||||
if (line.GetArgCount() != 4) return;
|
||||
for (uint i=0; i<4; ++i)
|
||||
{
|
||||
if (!line.GetArgFloat(i, values[i]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
data.r = values[0];
|
||||
data.g = values[1];
|
||||
data.b = values[2];
|
||||
data.a = values[3];
|
||||
}
|
||||
|
||||
void CSynchedJSObjectBase::IterateSynchedProperties(IterateCB *cb, void *userdata)
|
||||
{
|
||||
SynchedPropertyIterator it=m_SynchedProperties.begin();
|
||||
while (it != m_SynchedProperties.end())
|
||||
{
|
||||
cb(it->first, it->second, userdata);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
ISynchedJSProperty *CSynchedJSObjectBase::GetSynchedProperty(CStrW name)
|
||||
{
|
||||
SynchedPropertyIterator prop=m_SynchedProperties.find(name);
|
||||
if (prop != m_SynchedProperties.end())
|
||||
return prop->second;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
138
source/scripting/SynchedJSObject.h
Normal file
138
source/scripting/SynchedJSObject.h
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
CSynchedJSObject
|
||||
|
||||
AUTHOR: Simon Brenner <simon.brenner@home.se>
|
||||
|
||||
DESCRIPTION:
|
||||
A helper class for CJSObject that enables a callback to be called
|
||||
whenever an attribute of the class changes and enables all (synched)
|
||||
properties to be set and retrieved as strings for network sync.
|
||||
|
||||
All string conversions are performed by specific functions that use a
|
||||
strictly (hrm) defined format - or at least a format that is specific
|
||||
for the type in question (which is why JSParseString can't be used -
|
||||
the JS interface's ToString function is also not usable since it often
|
||||
produces a human-readable format that doesn't parse well and might
|
||||
change outside the control of the network protocol).
|
||||
|
||||
This replaces CAttributeMap for both player and game attributes.
|
||||
|
||||
USAGE:
|
||||
First you must create your subclass, make it inherit from
|
||||
CSynchedJSObject and implement the pure virtual method Update (see
|
||||
prototype below)
|
||||
|
||||
Then you may use it just like CJSObject (see ScriptableObject.h) - with
|
||||
one exception: Any property you want to be synchronized (i.e. have the
|
||||
new property functionality including the update callback) is added using
|
||||
the AddSynchedProperty method instead: AddSynchedProperty(name, &m_Property)
|
||||
|
||||
The extra arguments that exist in the AddProperty method haven't been
|
||||
implemented (if you by any chance would need to, just do it ;-)
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _SynchedJSObject_H
|
||||
#define _SynchedJSObject_H
|
||||
|
||||
#include "CStr.h"
|
||||
#include "ScriptableObject.h"
|
||||
|
||||
template <typename T>
|
||||
void SetFromNetString(T &data, CStrW string);
|
||||
|
||||
template <typename T>
|
||||
CStrW ToNetString(const T &data);
|
||||
|
||||
#define TYPE(type) \
|
||||
template <> CStrW ToNetString(const type &data); \
|
||||
template <> void SetFromNetString(type &data, CStrW string);
|
||||
|
||||
TYPE(uint)
|
||||
TYPE(CStrW)
|
||||
|
||||
#undef TYPE
|
||||
|
||||
class ISynchedJSProperty: public IJSProperty
|
||||
{
|
||||
public:
|
||||
virtual void FromString(CStrW value)=0;
|
||||
virtual CStrW ToString()=0;
|
||||
};
|
||||
|
||||
// non-templated base class
|
||||
struct CSynchedJSObjectBase
|
||||
{
|
||||
template <typename PropType, bool ReadOnly = false>
|
||||
class CSynchedJSProperty: public ISynchedJSProperty
|
||||
{
|
||||
PropType *m_Data;
|
||||
CStrW m_Name;
|
||||
CSynchedJSObjectBase *m_Owner;
|
||||
|
||||
virtual void Set(JSContext *cx, jsval value)
|
||||
{
|
||||
if (!ReadOnly)
|
||||
{
|
||||
if (ToPrimitive(cx, value, *m_Data))
|
||||
m_Owner->Update(m_Name, this);
|
||||
}
|
||||
}
|
||||
virtual jsval Get(JSContext *cx)
|
||||
{
|
||||
return ToJSVal(*m_Data);
|
||||
}
|
||||
|
||||
virtual void ImmediateCopy(IJSProperty *other)
|
||||
{
|
||||
*m_Data = *( ((CSynchedJSProperty<PropType, ReadOnly>*)other)->m_Data );
|
||||
}
|
||||
|
||||
virtual void FromString(CStrW value)
|
||||
{
|
||||
SetFromNetString(*m_Data, value);
|
||||
}
|
||||
|
||||
virtual CStrW ToString()
|
||||
{
|
||||
return ToNetString(*m_Data);
|
||||
}
|
||||
|
||||
public:
|
||||
inline CSynchedJSProperty(CStrW name, PropType* native, CSynchedJSObjectBase *owner):
|
||||
m_Data(native),
|
||||
m_Name(name),
|
||||
m_Owner(owner)
|
||||
{
|
||||
m_AllowsInheritance = false;
|
||||
m_Intrinsic = true;
|
||||
}
|
||||
};
|
||||
|
||||
typedef STL_HASH_MAP<CStrW, ISynchedJSProperty *, CStrW_hash_compare> SynchedPropertyTable;
|
||||
typedef SynchedPropertyTable::iterator SynchedPropertyIterator;
|
||||
SynchedPropertyTable m_SynchedProperties;
|
||||
|
||||
protected:
|
||||
virtual void Update(CStrW name, ISynchedJSProperty *prop)=0;
|
||||
|
||||
public:
|
||||
ISynchedJSProperty *GetSynchedProperty(CStrW name);
|
||||
|
||||
typedef void (IterateCB)(CStrW name, ISynchedJSProperty *prop, void *userdata);
|
||||
void IterateSynchedProperties(IterateCB *cb, void *userdata);
|
||||
};
|
||||
|
||||
template <typename Class>
|
||||
class CSynchedJSObject: public CJSObject<Class>, public CSynchedJSObjectBase
|
||||
{
|
||||
protected:
|
||||
template <typename T> void AddSynchedProperty(CStrW name, T *native)
|
||||
{
|
||||
ISynchedJSProperty *prop=new CSynchedJSProperty<T>(name, native, this);
|
||||
m_Properties[name]=prop;
|
||||
m_SynchedProperties[name]=prop;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -521,7 +521,7 @@ void CEntity::renderSelectionOutline( float alpha )
|
||||
#ifdef SCED // HACK: ScEd doesn't have a g_Game, so we can't use its CPlayers
|
||||
glColor3fv(PlayerColours[ (intptr_t)m_player ]);
|
||||
#else
|
||||
SColour& col = m_player->m_Colour;
|
||||
const SPlayerColour& col = m_player->GetColour();
|
||||
glColor3f( col.r, col.g, col.b );
|
||||
#endif
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user