0ad/source/scripting/ScriptGlue.cpp
Matei 92578ae553 # Some initial work on networking, fixing session setup, game startup, and command queueing.
- Fixed outdated / buggy networking code in the GUI scripts.
- Finished the API to CNetClient so that it's possible to start a CGame
from it.
- Some enhancements for debugging networking: Enabled updates while the
game is minimized/out-of-focus if it's in a network session. Also
reduced the turn length to something slightly more manageable but still
unplayable (1 sec versus 3 sec).
- Fixed a bug where IssueCommand used to access the order it creates
after queueing it, which is bad if the order gets deleted while being
queued (e.g. in CNetClient).

This was SVN commit r5139.
2007-06-04 07:41:05 +00:00

1619 lines
46 KiB
C++

// This module defines the table of all functions callable from JS.
// it's required by the interpreter; we make use of the opportunity to
// document them all in one spot. we thus obviate having to dig through
// all the other headers. most of the functions are implemented here;
// as for the rest, we only link to their docs (duplication is bad).
#include "precompiled.h"
#include "ScriptGlue.h"
#include "JSConversions.h"
#include "GameEvents.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/MapWriter.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "graphics/scripting/JSInterface_Camera.h"
#include "graphics/scripting/JSInterface_LightEnv.h"
#include "gui/CGUI.h"
#include "lib/timer.h"
#include "maths/scripting/JSInterface_Vector3D.h"
#include "network/Client.h"
#include "network/Server.h"
#include "ps/CConsole.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "ps/Game.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/Hotkey.h"
#include "ps/Interact.h"
#include "ps/ProfileViewer.h"
#include "ps/i18n.h"
#include "ps/scripting/JSCollection.h"
#include "ps/scripting/JSInterface_Console.h"
#include "ps/scripting/JSInterface_Selection.h"
#include "ps/scripting/JSInterface_VFS.h"
#include "renderer/Renderer.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "simulation/Entity.h"
#include "simulation/EntityFormation.h"
#include "simulation/EntityHandles.h"
#include "simulation/EntityManager.h"
#include "simulation/EntityTemplate.h"
#include "simulation/EntityTemplateCollection.h"
#include "simulation/FormationManager.h"
#include "simulation/LOSManager.h"
#include "simulation/Scheduler.h"
#include "simulation/Simulation.h"
#include "simulation/TechnologyCollection.h"
#include "simulation/TriggerManager.h"
#ifndef NO_GUI
# include "gui/scripting/JSInterface_IGUIObject.h"
#endif
extern bool g_TerrainModified;
// rationale: the function table is now at the end of the source file to
// avoid the need for forward declarations for every function.
// all normal function wrappers have the following signature:
// JSBool func(JSContext* cx, JSObject* globalObject, uint argc, jsval* argv, jsval* rval);
// all property accessors have the following signature:
// JSBool accessor(JSContext* cx, JSObject* globalObject, jsval id, jsval* vp);
//-----------------------------------------------------------------------------
// Output
//-----------------------------------------------------------------------------
// Write values to the log file.
// params: any number of any type.
// returns:
// notes:
// - Each argument is converted to a string and then written to the log.
// - Output is in NORMAL style (see LOG).
JSBool WriteLog(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_PARAMS(1);
CStr logMessage;
for (int i = 0; i < (int)argc; i++)
{
try
{
CStr arg = g_ScriptingHost.ValueToString( argv[i] );
logMessage += arg;
}
catch( PSERROR_Scripting_ConversionFailed )
{
// Do nothing.
}
}
// We should perhaps unicodify (?) the logger at some point.
LOG( NORMAL, "script", logMessage );
*rval = JSVAL_TRUE;
return JS_TRUE;
}
//-----------------------------------------------------------------------------
// Entity
//-----------------------------------------------------------------------------
// Retrieve the entity currently occupying the specified handle.
// params: handle [int]
// returns: entity
JSBool GetEntityByUnitID( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(1);
*rval = JSVAL_NULL;
int uid;
try
{
uid = ToPrimitive<int>( argv[0] );
}
catch( PSERROR_Scripting_ConversionFailed )
{
JS_ReportError( cx, "Invalid parameter" );
return( JS_TRUE );
}
CUnit* unit = g_Game->GetWorld()->GetUnitManager().FindByID( uid );
if( !unit || !unit->GetEntity() )
{
*rval = JSVAL_NULL;
return( JS_TRUE );
}
*rval = OBJECT_TO_JSVAL( unit->GetEntity()->GetScript() );
return( JS_TRUE );
}
// Look up an EntityTemplate by name.
// params: template name [wstring]
// returns: entity template object
JSBool GetEntityTemplate( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAM_RANGE(1, 2);
*rval = JSVAL_NULL;
CStrW templateName;
CPlayer* player = 0;
try
{
templateName = g_ScriptingHost.ValueToUCString( argv[0] );
if( argc == 2 )
{
player = ToNative<CPlayer>( argv[1] );
}
}
catch( PSERROR_Scripting_ConversionFailed )
{
JS_ReportError( cx, "Invalid template identifier" );
return( JS_TRUE );
}
CEntityTemplate* v = g_EntityTemplateCollection.GetTemplate( templateName, player );
if( !v )
{
JS_ReportError( cx, "No such template: %s", CStr(templateName).c_str() );
return( JS_TRUE );
}
*rval = OBJECT_TO_JSVAL( v->GetScript() );
return( JS_TRUE );
}
JSBool GetPlayerUnitCount( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(2);
int unitCount, playerNum = ToPrimitive<int>( argv[0] );
CStrW unitName = ToPrimitive<CStrW>( argv[1] );
unitCount = g_EntityManager.GetPlayerUnitCount((size_t)playerNum, unitName);
*rval = ToJSVal( unitCount );
return JS_TRUE;
}
//Used to create net messages for formations--msgList.front() is the original message. see IssueCommand
void CreateFormationMessage( std::vector<CNetMessage*>& msgList, CNetMessage* msg, CEntityList& formation )
{
CNetMessage* retMsg;
const int type = msg->GetType();
if ( type == NMT_Goto )
{
//formationEnt->GetFormation()->BaseToMovement();
CGoto* tmp = static_cast<CGoto*>(msg);
retMsg = CNetMessage::CreatePositionMessage( formation, NMT_FormationGoto,
CVector2D(tmp->m_TargetX, tmp->m_TargetY) );
}
else if( type == NMT_Run )
{
CGoto* tmp = static_cast<CGoto*>(msg);
retMsg = CNetMessage::CreatePositionMessage( formation, NMT_FormationGoto,
CVector2D(tmp->m_TargetX, tmp->m_TargetY) );
}
else if ( type == NMT_Generic )
{
CGeneric* tmp = static_cast<CGeneric*>(msg);
retMsg = CNetMessage::CreateEntityIntMessage(formation, NMT_FormationGeneric,
tmp->m_Target, tmp->m_Action);
}
else
return;
msgList.push_back(retMsg);
}
// Issue a command (network message) to an entity or collection.
// params: either an entity- or entity collection object, message ID [int],
// any further params needed by CNetMessage::CommandFromJSArgs
// returns: command in serialized form [string]
JSBool IssueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
// at least one for target object, one for isQueued, and then 1 or more for the CommandFromJSArgs
JSU_REQUIRE_MIN_PARAMS(3);
JSU_ASSERT(JSVAL_IS_OBJECT(argv[0]), "Argument 0 must be an entity collection.");
*rval = JSVAL_NULL;
CEntityList entities, msgEntities;
if (JS_GetClass(JSVAL_TO_OBJECT(argv[0])) == &CEntity::JSI_class)
entities.push_back( (ToNative<CEntity>(argv[0])) ->me);
else
entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0]));
std::map<int, CEntityList> entityStore;
bool isQueued = ToPrimitive<bool>(argv[1]);
//Destroy old notifiers if we're explicitly being reassigned
for ( size_t i=0; i < entities.size(); i++)
{
if ( entities[i]->entf_get(ENTF_DESTROY_NOTIFIERS))
entities[i]->DestroyAllNotifiers();
}
std::vector<CNetMessage*> messages;
//Generate messages for formations
for (size_t i=0; i < entities.size(); i++ )
{
if ( entities[i]->m_formation >= 0)
{
CEntityFormation* formation = entities[i]->GetFormation();
bool duplicate = formation->IsDuplication();
if ( formation->IsLocked() && !duplicate)
{
formation->SelectAllUnits();
entityStore[entities[i]->m_formation] = formation->GetEntityList();
formation->SetDuplication(true);
}
}
else
msgEntities.push_back( entities[i] );
}
CNetMessage* msg = CNetMessage::CommandFromJSArgs(msgEntities, cx, argc-2, argv+2, isQueued);
if (!msg)
{
delete msg;
return JS_TRUE;
}
messages.push_back(msg);
for ( std::map<int, CEntityList>::iterator it=entityStore.begin(); it!=entityStore.end(); it++)
CreateFormationMessage(messages, msg, it->second);
for ( std::vector<CNetMessage*>::iterator it=messages.begin(); it != messages.end(); it++ )
{
g_Console->InsertMessage(L"IssueCommand: %hs", (*it)->GetString().c_str());
*rval = g_ScriptingHost.UCStringToValue((*it)->GetString());
g_Game->GetSimulation()->QueueLocalCommand(*it);
}
return JS_TRUE;
}
// Get the state of a given hotkey (from the hotkeys file)
JSBool isOrderQueued( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
*rval = ToJSVal(hotkeys[HOTKEY_ORDER_QUEUE]);
return JS_TRUE;
}
//-----------------------------------------------------------------------------
// formations
//-----------------------------------------------------------------------------
JSBool CreateEntityFormation( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(2);
CEntityList entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0]));
CStrW name = ToPrimitive<CStrW>( argv[1] );
g_FormationManager.CreateFormation( entities, name );
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool RemoveFromFormation( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(1);
CEntityList entities;
if (JS_GetClass(JSVAL_TO_OBJECT(argv[0])) == &CEntity::JSI_class)
entities.push_back( (ToNative<CEntity>(argv[0])) ->me);
else
entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0]));
*rval = g_FormationManager.RemoveUnitList(entities) ? JS_TRUE : JS_FALSE;
return JS_TRUE;
}
JSBool LockEntityFormation( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(1);
CEntity* entity = ToNative<CEntity>( argv[0] );
entity->GetFormation()->SetLock( ToPrimitive<bool>( argv[1] ) );
*rval = JSVAL_VOID;
return JS_TRUE;
}
JSBool IsFormationLocked( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(1);
CEntity* entity = ToNative<CEntity>( argv[0] );
*rval = entity->GetFormation()->IsLocked() ? JS_TRUE : JS_FALSE;
return JS_TRUE;
}
//-----------------------------------------------------------------------------
// Techs
//-----------------------------------------------------------------------------
JSBool GetTechnology( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(2);
CStrW name;
CPlayer* player;
try
{
name = g_ScriptingHost.ValueToUCString( argv[0] );
player = ToNative<CPlayer>( argv[1] );
}
catch( PSERROR_Scripting_ConversionFailed )
{
JS_ReportError( cx, "Invalid parameters for GetTechnology (expected name and player)" );
return( JS_TRUE );
}
*rval = JSVAL_NULL;
CTechnology* tech = g_TechnologyCollection.GetTechnology( name, player );
if ( tech )
*rval = ToJSVal( tech );
else
g_Console->InsertMessage( L"Warning: Invalid tech template name \"%ls\" passed for GetTechnology()", name.c_str() );
return JS_TRUE;
}
//-----------------------------------------------------------------------------
// Events
//-----------------------------------------------------------------------------
// Register a global handler for the specified DOM event.
// params: event type name [wstring], handler [fragment or function]
// returns: whether it was actually newly registered [bool]
JSBool AddGlobalHandler( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(2);
*rval = BOOLEAN_TO_JSVAL( g_JSGameEvents.AddHandlerJS( cx, argc, argv ) );
return( JS_TRUE );
}
// Remove a previously registered global handler for the specified DOM event.
// params: event type name [wstring], handler [fragment or function]
// returns: whether it was successfully removed [bool]
JSBool RemoveGlobalHandler( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(2);
*rval = BOOLEAN_TO_JSVAL( g_JSGameEvents.RemoveHandlerJS( cx, argc, argv ) );
return( JS_TRUE );
}
//-----------------------------------------------------------------------------
// Timer
//-----------------------------------------------------------------------------
// Request a callback be executed after the specified delay.
// params: callback [fragment or function], delay in milliseconds [int]
// returns:
// notes:
// - Scripts and functions registered this way are called on the first
// simulation frame after the specified period has elapsed. If this causes
// multiple segments of code to be executed in the same frame,
// relative timing is maintained. Delays of 0 milliseconds cause code to be
// executed on the following simulation frame. If more than one script or
// function is scheduled to execute in the same millisecond, the order of
// execution is undefined. Code is scheduled in simulation time, and is
// therefore suspended while the game is paused or frozen. Granularity of
// timing is also limited to 1/(Simulation frame rate); currently 100ms.
// The called function or script executes in the same scope as the
// code that called SetTimeout (amongst other things, the
// 'this' reference is usually maintained)
JSBool SetTimeout( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAM_RANGE(2, 3);
size_t delay;
try
{
delay = ToPrimitive<int>( argv[1] );
}
catch( PSERROR_Scripting_ConversionFailed )
{
JS_ReportError( cx, "Invalid timer parameters" );
return( JS_TRUE );
}
JSObject* scope;
if( argc == 3 )
{
if( !JSVAL_IS_OBJECT( argv[2] ) )
{
JS_ReportError( cx, "Invalid timer parameters" );
return( JS_TRUE );
}
scope = JSVAL_TO_OBJECT( argv[2] );
}
else
{
scope = JS_GetScopeChain( cx );
}
switch( JS_TypeOfValue( cx, argv[0] ) )
{
case JSTYPE_STRING:
{
CStrW fragment = g_ScriptingHost.ValueToUCString( argv[0] );
int id = g_Scheduler.PushTime( delay, fragment, scope );
*rval = INT_TO_JSVAL( id );
return( JS_TRUE );
}
case JSTYPE_FUNCTION:
{
JSFunction* fn = JS_ValueToFunction( cx, argv[0] );
int id = g_Scheduler.PushTime( delay, fn, scope );
*rval = INT_TO_JSVAL( id );
return( JS_TRUE );
}
default:
JS_ReportError( cx, "Invalid timer script" );
return( JS_TRUE );
}
}
// Request a callback be executed periodically.
// params: callback [fragment or function], initial delay in ms [int], period in ms [int]
// OR callback [fragment or function], period in ms [int] (initial delay = period)
// returns:
// notes:
// - SetTimeout's notes apply here as well.
JSBool SetInterval( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAM_RANGE(2, 3);
size_t first, interval;
try
{
first = ToPrimitive<int>( argv[1] );
if( argc == 3 )
{
// toDo, first, interval
interval = ToPrimitive<int>( argv[2] );
}
else
{
// toDo, interval (first = interval)
interval = first;
}
}
catch( PSERROR_Scripting_ConversionFailed )
{
JS_ReportError( cx, "Invalid timer parameters" );
return( JS_TRUE );
}
switch( JS_TypeOfValue( cx, argv[0] ) )
{
case JSTYPE_STRING:
{
CStrW fragment = g_ScriptingHost.ValueToUCString( argv[0] );
int id = g_Scheduler.PushInterval( first, interval, fragment, JS_GetScopeChain( cx ) );
*rval = INT_TO_JSVAL( id );
return( JS_TRUE );
}
case JSTYPE_FUNCTION:
{
JSFunction* fn = JS_ValueToFunction( cx, argv[0] );
int id = g_Scheduler.PushInterval( first, interval, fn, JS_GetScopeChain( cx ) );
*rval = INT_TO_JSVAL( id );
return( JS_TRUE );
}
default:
JS_ReportError( cx, "Invalid timer script" );
return( JS_TRUE );
}
}
// Cause all periodic functions registered via SetInterval to
// no longer be called.
// params:
// returns:
// notes:
// - Execution continues until the end of the triggered function or
// script fragment, but is not triggered again.
JSBool CancelInterval( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
g_Scheduler.m_abortInterval = true;
return( JS_TRUE );
}
// Cause the scheduled task (timeout or interval) with the given ID to
// no longer be called.
// params:
// returns:
// notes:
// - Execution continues until the end of the triggered function or
// script fragment, but is not triggered again.
JSBool CancelTimer( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(1);
try
{
int id = ToPrimitive<int>( argv[0] );
g_Scheduler.CancelTask( id );
}
catch( PSERROR_Scripting_ConversionFailed )
{
JS_ReportError( cx, "Invalid ID parameter" );
return( JS_TRUE );
}
return( JS_TRUE );
}
//Set the simulation rate scalar-time becomes time * SimRate.
//Params: rate [float] : sets SimRate
JSBool SetSimRate(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_PARAMS(1);
g_Game->SetSimRate( ToPrimitive<float>(argv[0]) );
return JS_TRUE;
}
//Generate a random float in [0, 1) using the simulation's random generator
JSBool SimRand(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
*rval = ToJSVal( g_Game->GetSimulation()->RandFloat() );
return JS_TRUE;
}
//Generate a random float int between 0 and the given number - 1 using the simulation's RNG
JSBool SimRandInt(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_PARAMS(1);
JSU_ASSERT(JSVAL_IS_INT(argv[0]), "SimRandInt(): first parameter must be an int");
*rval = ToJSVal( g_Game->GetSimulation()->RandInt(ToPrimitive<int>(argv[0])) );
return JS_TRUE;
}
// Script profiling functions: Begin timing a piece of code with StartJsTimer(num)
// and stop timing with StopJsTimer(num). The results will be printed to stdout
// when the game exits.
static const uint MAX_JS_TIMERS = 20;
static Timer js_timer;
static TimerUnit js_start_times[MAX_JS_TIMERS];
static TimerUnit js_timer_overhead;
static TimerClient js_timer_clients[MAX_JS_TIMERS];
static char js_timer_descriptions_buf[MAX_JS_TIMERS * 12]; // depends on MAX_JS_TIMERS and format string below
static void InitJsTimers()
{
char* pos = js_timer_descriptions_buf;
for(uint i = 0; i < MAX_JS_TIMERS; i++)
{
const char* description = pos;
pos += sprintf(pos, "js_timer %d", i)+1;
timer_add_client(&js_timer_clients[i], description);
}
// call several times to get a good approximation of 'hot' performance.
// note: don't use a separate timer slot to warm up and then judge
// overhead from another: that causes worse results (probably some
// caching effects inside JS, but I don't entirely understand why).
static const char* calibration_script =
"startXTimer(0);\n"
"stopXTimer (0);\n"
"startXTimer(0);\n"
"stopXTimer (0);\n"
"startXTimer(0);\n"
"stopXTimer (0);\n"
"startXTimer(0);\n"
"stopXTimer (0);\n"
"\n";
g_ScriptingHost.RunMemScript(calibration_script, strlen(calibration_script));
js_timer_overhead = js_timer_clients[0].sum/4;
}
JSBool StartJsTimer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
ONCE(InitJsTimers());
JSU_REQUIRE_PARAMS(1);
uint slot = ToPrimitive<uint>(argv[0]);
if (slot >= MAX_JS_TIMERS)
return JS_FALSE;
debug_assert(js_start_times[slot] == 0);
js_start_times[slot] = js_timer.get_timestamp();
return JS_TRUE;
}
JSBool StopJsTimer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_PARAMS(1);
uint slot = ToPrimitive<uint>(argv[0]);
if (slot >= MAX_JS_TIMERS)
return JS_FALSE;
debug_assert(js_start_times[slot] != 0);
TimerUnit dt = js_timer.get_timestamp() - js_start_times[slot] - js_timer_overhead;
js_start_times[slot] = 0;
timer_bill_client(&js_timer_clients[slot], dt);
return JS_TRUE;
}
//-----------------------------------------------------------------------------
// Game Setup
//-----------------------------------------------------------------------------
// Create a new network server object.
// params:
// returns: net server object
JSBool CreateServer(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
if( !g_Game )
g_Game = new CGame();
if( !g_NetServer )
g_NetServer = new CNetServer(g_Game, &g_GameAttributes);
*rval = OBJECT_TO_JSVAL(g_NetServer->GetScript());
return( JS_TRUE );
}
// Create a new network client object.
// params:
// returns: net client object
JSBool CreateClient(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
if( !g_Game )
g_Game = new CGame();
if( !g_NetClient )
g_NetClient = new CNetClient(g_Game, &g_GameAttributes);
*rval = OBJECT_TO_JSVAL(g_NetClient->GetScript());
return( JS_TRUE );
}
// Begin the process of starting a game.
// params:
// returns: success [bool]
// notes:
// - Performs necessary initialization while calling back into the
// main loop, so the game remains responsive to display+user input.
// - When complete, the engine calls the reallyStartGame JS function.
// TODO: Replace StartGame with Create(Game|Server|Client)/game.start() -
// after merging CGame and CGameAttributes
JSBool StartGame(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
*rval = BOOLEAN_TO_JSVAL(JS_TRUE);
// Hosted MP Game
if (g_NetServer)
{
*rval = BOOLEAN_TO_JSVAL(g_NetServer->StartGame() == 0);
}
// Joined MP Game
else if (g_NetClient)
{
*rval = BOOLEAN_TO_JSVAL(g_NetClient->StartGame() == 0);
}
// Start an SP Game Session
else if (!g_Game)
{
g_Game = new CGame();
PSRETURN ret = g_Game->StartGame(&g_GameAttributes);
if (ret != PSRETURN_OK)
{
// Failed to start the game - destroy it, and return false
delete g_Game;
g_Game = NULL;
*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
return( JS_TRUE );
}
}
else
{
*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
}
return( JS_TRUE );
}
// Immediately ends the current game (if any).
// params:
// returns:
JSBool EndGame(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
EndGame();
return JS_TRUE;
}
JSBool GetGameMode(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
*rval = ToJSVal( g_GameAttributes.GetGameMode() );
return JS_TRUE;
}
//-----------------------------------------------------------------------------
// Internationalization
//-----------------------------------------------------------------------------
// these remain here instead of in the i18n tree because they are
// really related to the engine's use of them, as opposed to i18n itself.
// contrariwise, translate() cannot be moved here because that would
// make i18n dependent on this code and therefore harder to reuse.
// Replaces the current language (locale) with a new one.
// params: language id [string] as in I18n::LoadLanguage
// returns:
JSBool LoadLanguage(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_PARAMS(1);
CStr lang = g_ScriptingHost.ValueToString(argv[0]);
I18n::LoadLanguage(lang);
return JS_TRUE;
}
// Return identifier of the current language (locale) in use.
// params:
// returns: language id [string] as in I18n::LoadLanguage
JSBool GetLanguageID(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
*rval = JSVAL_NULL;
JSString* s = JS_NewStringCopyZ(cx, I18n::CurrentLanguageName());
if (!s)
{
JS_ReportError(cx, "Error creating string");
return JS_FALSE;
}
*rval = STRING_TO_JSVAL(s);
return JS_TRUE;
}
//-----------------------------------------------------------------------------
// Debug
//-----------------------------------------------------------------------------
// Deliberately cause the game to crash.
// params:
// returns:
// notes:
// - currently implemented via access violation (read of address 0)
// - useful for testing the crashlog/stack trace code.
JSBool ProvokeCrash(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
MICROLOG(L"Crashing at user's request.");
return *(JSBool*)0;
}
// Force a JS garbage collection cycle to take place immediately.
// params:
// returns: true [bool]
// notes:
// - writes an indication of how long this took to the console.
JSBool ForceGarbageCollection(JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
double time = get_time();
JS_GC(cx);
time = get_time() - time;
g_Console->InsertMessage(L"Garbage collection completed in: %f", time);
*rval = JSVAL_TRUE;
return JS_TRUE ;
}
//-----------------------------------------------------------------------------
// GUI
//-----------------------------------------------------------------------------
// Returns the sort-of-global object associated with the current GUI.
// params:
// returns: global object
// notes:
// - Useful for accessing an object from another scope.
JSBool GetGuiGlobal(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
*rval = OBJECT_TO_JSVAL(g_GUI.GetScriptObject());
return JS_TRUE;
}
// Resets the entire GUI state and reloads the XML files.
// params:
// returns:
JSBool ResetGui(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval)
{
JSU_REQUIRE_NO_PARAMS();
// Slightly unpleasant code, because CGUI is a Singleton but we don't really
// want it to be
g_GUI.Destroy();
delete &g_GUI;
new CGUI;
GUI_Init();
g_GUI.SendEventToAll("load");
return JS_TRUE;
}
//-----------------------------------------------------------------------------
// Misc. Engine Interface
//-----------------------------------------------------------------------------
// Return the global frames-per-second value.
// params:
// returns: FPS [int]
// notes:
// - This value is recalculated once a frame. We take special care to
// filter it, so it is both accurate and free of jitter.
JSBool GetFps( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
*rval = INT_TO_JSVAL(fps);
return JS_TRUE;
}
// Cause the game to exit gracefully.
// params:
// returns:
// notes:
// - Exit happens after the current main loop iteration ends
// (since this only sets a flag telling it to end)
JSBool ExitProgram( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
kill_mainloop();
return JS_TRUE;
}
// Write an indication of total/available video RAM to console.
// params:
// returns:
// notes:
// - Not supported on all platforms.
// - Only a rough approximation; do not base low-level decisions
// ("should I allocate one more texture?") on this.
JSBool WriteVideoMemToConsole( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
#if OS_WIN
int left, total;
if (GetVRAMInfo(left, total))
g_Console->InsertMessage(L"VRAM: used %d, total %d, free %d", total-left, total, left);
else
g_Console->InsertMessage(L"VRAM: failed to detect");
#else
g_Console->InsertMessage(L"VRAM: [not available on non-Windows]");
#endif
return JS_TRUE;
}
// Change the mouse cursor.
// params: cursor name [string] (i.e. basename of definition file and texture)
// returns:
// notes:
// - Cursors are stored in "art\textures\cursors"
JSBool SetCursor( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(1);
g_CursorName = g_ScriptingHost.ValueToString(argv[0]);
return JS_TRUE;
}
JSBool GetCursorName( JSContext* UNUSED(cx), JSObject*, uint UNUSED(argc), jsval* UNUSED(argv), jsval* rval )
{
*rval = ToJSVal(g_CursorName);
return JS_TRUE;
}
// Trigger a rewrite of all maps.
// params:
// returns:
// notes:
// - Usefulness is unclear. If you need it, consider renaming this and updating the docs.
JSBool _RewriteMaps( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
g_Game->GetWorld()->RewriteMap();
return JS_TRUE;
}
// Change the LOD bias.
// params: LOD bias [float]
// returns:
// notes:
// - value is as required by GL_TEXTURE_LOD_BIAS.
// - useful for adjusting image "sharpness" (since it affects which mipmap level is chosen)
JSBool _LodBias( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(1);
g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, ToPrimitive<float>(argv[0]));
return JS_TRUE;
}
// Focus the game camera on a given position.
// params: target position vector [CVector3D]
// returns: success [bool]
JSBool SetCameraTarget( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(1);
*rval = JSVAL_NULL;
CVector3D* target = ToNative<CVector3D>( argv[0] );
if(!target)
{
JS_ReportError( cx, "Invalid camera target" );
return( JS_TRUE );
}
g_Game->GetView()->SetCameraTarget( *target );
*rval = JSVAL_TRUE;
return( JS_TRUE );
}
//-----------------------------------------------------------------------------
// Miscellany
//-----------------------------------------------------------------------------
// Return the date/time at which the current executable was compiled.
// params: none (-> "date time") OR
// what to display [int]: 0 (-> "date"); 1 (-> "time")
// returns: date and/or time [string]
// notes:
// - Displayed on main menu screen; tells non-programmers which auto-build
// they are running. Could also be determined via .EXE file properties,
// but that's a bit more trouble.
// - To be exact, the date/time returned is when scriptglue.cpp was
// last compiled; since the auto-build does full rebuilds, that is moot.
JSBool GetBuildTimestamp( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAM_RANGE(0, 1);
// no param => "date time"
// param = 0 => "date"
// param = 1 => "time"
JSString* s = JS_NewStringCopyZ(cx,
argc && argv[0]==JSVAL_ONE ? __TIME__
: argc ? __DATE__
: __DATE__" "__TIME__
);
*rval = STRING_TO_JSVAL(s);
return JS_TRUE;
}
// Return distance between 2 points.
// params: 2 position vectors [CVector3D]
// returns: Euclidean distance [float]
JSBool ComputeDistanceBetweenTwoPoints( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS(2);
CVector3D* a = ToNative<CVector3D>( argv[0] );
CVector3D* b = ToNative<CVector3D>( argv[1] );
float dist = ( *a - *b ).Length();
*rval = ToJSVal( dist );
return( JS_TRUE );
}
// Returns the global object.
// params:
// returns: global object
// notes:
// - Useful for accessing an object from another scope.
JSBool GetGlobal( JSContext* cx, JSObject* globalObject, uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
*rval = OBJECT_TO_JSVAL( globalObject );
return( JS_TRUE );
}
// Saves the current profiling data to the logs/profile.txt file
JSBool SaveProfileData( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
g_ProfileViewer.SaveToFile();
return( JS_TRUE );
}
// Activates the building placement cursor for placing a building. The currently selected units
// are then ordered to construct the building if it is placed.
// params: templateName - the name of the entity to place.
// returns: true if cursor was activated, false if cursor was already active.
JSBool StartPlacing( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
CStrW name;
if(argc == 0) {
name = L"hele_ho"; // save some typing during testing
}
else {
if(!ToPrimitive<CStrW>( g_ScriptingHost.GetContext(), argv[0], name ))
{
JS_ReportError( cx, "Invalid template name argument" );
*rval = JSVAL_NULL;
return( JS_FALSE );
}
}
*rval = g_BuildingPlacer.Activate(name) ? JS_TRUE : JS_FALSE;
return( JS_TRUE );
}
// Toggles drawing the sky
JSBool ToggleSky( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
g_Renderer.GetSkyManager()->m_RenderSky = !g_Renderer.GetSkyManager()->m_RenderSky;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
// Toggles drawing territory outlines
JSBool ToggleTerritoryRendering( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
g_Renderer.m_RenderTerritories = !g_Renderer.m_RenderTerritories;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
//-----------------------------------------------------------------------------
// water
// Toggles drawing the water plane
JSBool ToggleWater( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
g_Renderer.GetWaterManager()->m_RenderWater = !g_Renderer.GetWaterManager()->m_RenderWater;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
// Sets the water plane height
JSBool SetWaterHeight( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS( 1 );
float newHeight;
if(!ToPrimitive( g_ScriptingHost.GetContext(), argv[0], newHeight ))
{
JS_ReportError( cx, "Invalid water height argument" );
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.GetWaterManager()->m_WaterHeight = newHeight;
g_TerrainModified = true;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
// Gets the water plane height
JSBool GetWaterHeight( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
*rval = ToJSVal(g_Renderer.GetWaterManager()->m_WaterHeight);
return( JS_TRUE );
}
// Sets the water color
JSBool SetWaterColor( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS( 3 );
float r,g,b;
if(!ToPrimitive( g_ScriptingHost.GetContext(), argv[0], r )
|| !ToPrimitive( g_ScriptingHost.GetContext(), argv[1], g )
|| !ToPrimitive( g_ScriptingHost.GetContext(), argv[2], b ))
{
JS_ReportError( cx, "Invalid arguments" );
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.GetWaterManager()->m_WaterColor = CColor(r, g, b, 1.0f);
*rval = JSVAL_VOID;
return( JS_TRUE );
}
// Sets the water tint (used to tint reflections in fancy water)
JSBool SetWaterTint( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS( 3 );
float r,g,b;
if(!ToPrimitive( g_ScriptingHost.GetContext(), argv[0], r )
|| !ToPrimitive( g_ScriptingHost.GetContext(), argv[1], g )
|| !ToPrimitive( g_ScriptingHost.GetContext(), argv[2], b ))
{
JS_ReportError( cx, "Invalid arguments" );
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.GetWaterManager()->m_WaterTint = CColor(r, g, b, 1.0f);
*rval = JSVAL_VOID;
return( JS_TRUE );
}
// Sets the water tint (used to tint reflections in fancy water)
JSBool SetReflectionTint( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS( 3 );
float r,g,b;
if(!ToPrimitive( g_ScriptingHost.GetContext(), argv[0], r )
|| !ToPrimitive( g_ScriptingHost.GetContext(), argv[1], g )
|| !ToPrimitive( g_ScriptingHost.GetContext(), argv[2], b ))
{
JS_ReportError( cx, "Invalid arguments" );
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.GetWaterManager()->m_ReflectionTint = CColor(r, g, b, 1.0f);
*rval = JSVAL_VOID;
return( JS_TRUE );
}
// Sets the max water alpha (achieved when it is at WaterFullDepth or deeper)
JSBool SetWaterMaxAlpha( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS( 1 );
float val;
if(!ToPrimitive( g_ScriptingHost.GetContext(), argv[0], val ))
{
JS_ReportError( cx, "Invalid argument" );
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.GetWaterManager()->m_WaterMaxAlpha = val;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
// Sets the water full depth (when it is colored WaterMaxAlpha)
JSBool SetWaterFullDepth( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS( 1 );
float val;
if(!ToPrimitive( g_ScriptingHost.GetContext(), argv[0], val ))
{
JS_ReportError( cx, "Invalid argument" );
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.GetWaterManager()->m_WaterFullDepth = val;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
// Sets the water alpha offset (added to tweak water alpha near the shore)
JSBool SetWaterAlphaOffset( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS( 1 );
float val;
if(!ToPrimitive( g_ScriptingHost.GetContext(), argv[0], val ))
{
JS_ReportError( cx, "Invalid argument" );
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Renderer.GetWaterManager()->m_WaterAlphaOffset = val;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
//-----------------------------------------------------------------------------
// Is the game paused?
JSBool IsPaused( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
if( !g_Game )
{
JS_ReportError( cx, "Game is not started" );
return JS_FALSE;
}
*rval = g_Game->m_Paused ? JSVAL_TRUE : JSVAL_FALSE;
return JS_TRUE ;
}
// Pause/unpause the game
JSBool SetPaused( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS( 1 );
if( !g_Game )
{
JS_ReportError( cx, "Game is not started" );
return JS_FALSE;
}
try
{
g_Game->m_Paused = ToPrimitive<bool>( argv[0] );
}
catch( PSERROR_Scripting_ConversionFailed )
{
JS_ReportError( cx, "Invalid parameter to SetPaused" );
}
return JS_TRUE;
}
// Get game time
JSBool GetGameTime( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_NO_PARAMS();
if( !g_Game )
{
JS_ReportError( cx, "Game is not started" );
return JS_FALSE;
}
*rval = ToJSVal(g_Game->GetTime());
return JS_TRUE;
}
JSBool RegisterTrigger( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS_CPP(1);
CTrigger* trigger = ToNative<CTrigger>( argv[0] );
debug_assert( trigger );
g_TriggerManager.AddTrigger( trigger );
*rval = JSVAL_NULL;
return JS_TRUE;
}
JSBool GetTrigger( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS_CPP(1);
CStrW name = ToPrimitive<CStrW>( argv[0] );
if ( g_TriggerManager.m_TriggerMap.find(name) != g_TriggerManager.m_TriggerMap.end() )
*rval = ToJSVal( g_TriggerManager.m_TriggerMap[name] );
else
{
debug_printf("Invalid trigger name %ws", name.c_str());
*rval = JSVAL_NULL;
}
return JS_TRUE;
}
// Reveal map
JSBool RevealMap( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAM_RANGE(0, 1);
uint newValue;
if(argc == 0)
{
newValue = 2;
}
else if(!ToPrimitive( g_ScriptingHost.GetContext(), argv[0], newValue ) || newValue > 2)
{
JS_ReportError( cx, "Invalid argument (should be 0, 1 or 2)" );
*rval = JSVAL_VOID;
return( JS_FALSE );
}
g_Game->GetWorld()->GetLOSManager()->m_LOSSetting = newValue;
*rval = JSVAL_VOID;
return( JS_TRUE );
}
//-----------------------------------------------------------------------------
// function table
//-----------------------------------------------------------------------------
// the JS interpreter expects the table to contain 5-tuples as follows:
// - name the function will be called as from script;
// - function which will be called;
// - number of arguments this function expects
// - Flags (deprecated, always zero)
// - Extra (reserved for future use, always zero)
//
// we simplify this a bit with a macro:
#define JS_FUNC(script_name, cpp_function, min_params) { script_name, cpp_function, min_params, 0, 0 },
JSFunctionSpec ScriptFunctionTable[] =
{
// Console
JS_FUNC("writeConsole", JSI_Console::writeConsole, 1) // external
// Entity
JS_FUNC("getEntityByUnitID", GetEntityByUnitID, 1)
JS_FUNC("GetPlayerUnitCount", GetPlayerUnitCount, 1)
JS_FUNC("getEntityTemplate", GetEntityTemplate, 1)
JS_FUNC("issueCommand", IssueCommand, 2)
JS_FUNC("startPlacing", StartPlacing, 1)
// Formation
JS_FUNC("createEntityFormation", CreateEntityFormation, 2)
JS_FUNC("removeFromFormation", RemoveFromFormation, 1)
JS_FUNC("lockEntityFormation", LockEntityFormation, 1)
JS_FUNC("isFormationLocked", IsFormationLocked, 1)
// Trigger
JS_FUNC("registerTrigger", RegisterTrigger, 1)
// Tech
JS_FUNC("getTechnology", GetTechnology, 2)
// Camera
JS_FUNC("setCameraTarget", SetCameraTarget, 1)
// Sky
JS_FUNC("toggleSky", ToggleSky, 0)
// Water
JS_FUNC("toggleWater", ToggleWater, 0)
JS_FUNC("setWaterHeight", SetWaterHeight, 1)
JS_FUNC("getWaterHeight", GetWaterHeight, 0)
JS_FUNC("setWaterColor", SetWaterColor, 3)
JS_FUNC("setWaterTint", SetWaterTint, 3)
JS_FUNC("setReflectionTint", SetReflectionTint, 3)
JS_FUNC("setWaterMaxAlpha", SetWaterMaxAlpha, 0)
JS_FUNC("setWaterFullDepth", SetWaterFullDepth, 0)
JS_FUNC("setWaterAlphaOffset", SetWaterAlphaOffset, 0)
// Territory rendering
JS_FUNC("toggleTerritoryRendering", ToggleTerritoryRendering, 0)
// GUI
#ifndef NO_GUI
JS_FUNC("getGUIObjectByName", JSI_IGUIObject::getByName, 1) // external
JS_FUNC("getGUIGlobal", GetGuiGlobal, 0)
JS_FUNC("resetGUI", ResetGui, 0)
#endif
// Events
JS_FUNC("addGlobalHandler", AddGlobalHandler, 2)
JS_FUNC("removeGlobalHandler", RemoveGlobalHandler, 2)
// Timer
JS_FUNC("setTimeout", SetTimeout, 2)
JS_FUNC("setInterval", SetInterval, 2)
JS_FUNC("cancelInterval", CancelInterval, 0)
JS_FUNC("cancelTimer", CancelTimer, 0)
JS_FUNC("setSimRate", SetSimRate, 1)
// Random number generator
JS_FUNC("simRand", SimRand, 0)
JS_FUNC("simRandInt", SimRandInt, 1)
// Profiling
JS_FUNC("startXTimer", StartJsTimer, 1)
JS_FUNC("stopXTimer", StopJsTimer, 1)
// Game Setup
JS_FUNC("startGame", StartGame, 0)
JS_FUNC("endGame", EndGame, 0)
JS_FUNC("getGameMode", GetGameMode, 0)
JS_FUNC("createClient", CreateClient, 0)
JS_FUNC("createServer", CreateServer, 0)
// VFS (external)
JS_FUNC("buildDirEntList", JSI_VFS::BuildDirEntList, 1)
JS_FUNC("getFileMTime", JSI_VFS::GetFileMTime, 1)
JS_FUNC("getFileSize", JSI_VFS::GetFileSize, 1)
JS_FUNC("readFile", JSI_VFS::ReadFile, 1)
JS_FUNC("readFileLines", JSI_VFS::ReadFileLines, 1)
JS_FUNC("archiveBuilderCancel", JSI_VFS::ArchiveBuilderCancel, 1)
// Internationalization
JS_FUNC("loadLanguage", LoadLanguage, 1)
JS_FUNC("getLanguageID", GetLanguageID, 0)
// note: i18n/ScriptInterface.cpp registers translate() itself.
// rationale: see implementation section above.
// Debug
JS_FUNC("crash", ProvokeCrash, 0)
JS_FUNC("forceGC", ForceGarbageCollection, 0)
JS_FUNC("revealMap", RevealMap, 1)
// Misc. Engine Interface
JS_FUNC("writeLog", WriteLog, 1)
JS_FUNC("exit", ExitProgram, 0)
JS_FUNC("isPaused", IsPaused, 0)
JS_FUNC("setPaused", SetPaused, 1)
JS_FUNC("getGameTime", GetGameTime, 0)
JS_FUNC("vmem", WriteVideoMemToConsole, 0)
JS_FUNC("_rewriteMaps", _RewriteMaps, 0)
JS_FUNC("_lodBias", _LodBias, 0)
JS_FUNC("setCursor", SetCursor, 1)
JS_FUNC("getCursorName", GetCursorName, 0)
JS_FUNC("getFPS", GetFps, 0)
JS_FUNC("isOrderQueued", isOrderQueued, 1)
// Miscellany
JS_FUNC("v3dist", ComputeDistanceBetweenTwoPoints, 2)
JS_FUNC("buildTime", GetBuildTimestamp, 0)
JS_FUNC("getGlobal", GetGlobal, 0)
JS_FUNC("saveProfileData", SaveProfileData, 0)
// end of table marker
{0, 0, 0, 0, 0}
};
#undef JS_FUNC
//-----------------------------------------------------------------------------
// property accessors
//-----------------------------------------------------------------------------
JSBool GetEntitySet( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(argv), jsval* vp )
{
std::vector<HEntity> extant;
g_EntityManager.GetExtantAsHandles(extant);
*vp = OBJECT_TO_JSVAL(EntityCollection::Create(extant));
return JS_TRUE;
}
JSBool GetPlayerSet( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
{
std::vector<CPlayer*>* players = g_Game->GetPlayers();
*vp = OBJECT_TO_JSVAL( PlayerCollection::Create( *players ) );
return( JS_TRUE );
}
JSBool GetLocalPlayer( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
{
*vp = OBJECT_TO_JSVAL( g_Game->GetLocalPlayer()->GetScript() );
return( JS_TRUE );
}
JSBool GetGaiaPlayer( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
{
*vp = OBJECT_TO_JSVAL( g_Game->GetPlayer( 0 )->GetScript() );
return( JS_TRUE );
}
JSBool SetLocalPlayer( JSContext* cx, JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
{
CPlayer* newLocalPlayer = ToNative<CPlayer>( *vp );
if( !newLocalPlayer )
{
JS_ReportError( cx, "Not a valid Player." );
return( JS_TRUE );
}
g_Game->SetLocalPlayer( newLocalPlayer );
return( JS_TRUE );
}
JSBool GetGameView( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
{
if (g_Game)
*vp = OBJECT_TO_JSVAL( g_Game->GetView()->GetScript() );
else
*vp = JSVAL_NULL;
return( JS_TRUE );
}
JSBool GetRenderer( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* vp )
{
if (CRenderer::IsInitialised())
*vp = OBJECT_TO_JSVAL( g_Renderer.GetScript() );
else
*vp = JSVAL_NULL;
return( JS_TRUE );
}
enum ScriptGlobalTinyIDs
{
GLOBAL_SELECTION,
GLOBAL_GROUPSARRAY,
GLOBAL_CAMERA,
GLOBAL_CONSOLE,
GLOBAL_LIGHTENV
};
// shorthand
#define PERM JSPROP_PERMANENT
#define CONST JSPROP_READONLY
JSPropertySpec ScriptGlobalTable[] =
{
{ "selection" , GLOBAL_SELECTION, PERM, JSI_Selection::getSelection, JSI_Selection::SetSelection },
{ "groups" , GLOBAL_GROUPSARRAY, PERM, JSI_Selection::getGroups, JSI_Selection::setGroups },
{ "camera" , GLOBAL_CAMERA, PERM, JSI_Camera::getCamera, JSI_Camera::setCamera },
{ "console" , GLOBAL_CONSOLE, PERM | CONST, JSI_Console::getConsole, 0 },
{ "lightenv" , GLOBAL_LIGHTENV, PERM, JSI_LightEnv::getLightEnv, JSI_LightEnv::setLightEnv },
{ "entities" , 0, PERM | CONST, GetEntitySet, 0 },
{ "players" , 0, PERM | CONST, GetPlayerSet, 0 },
{ "localPlayer", 0, PERM , GetLocalPlayer, SetLocalPlayer },
{ "gaiaPlayer" , 0, PERM | CONST, GetGaiaPlayer, 0 },
{ "gameView" , 0, PERM | CONST, GetGameView, 0 },
{ "renderer" , 0, PERM | CONST, GetRenderer, 0 },
// end of table marker
{ 0, 0, 0, 0, 0 },
};