forked from 0ad/0ad
janwas
f715e6c226
scriptglue: additional refactoring scriptinghost: remove useless GC function SpiderMonkey.h: more dox This was SVN commit r2497.
921 lines
26 KiB
C++
Executable File
921 lines
26 KiB
C++
Executable File
// 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 "CLogger.h"
|
|
#include "CConsole.h"
|
|
#include "CStr.h"
|
|
#include "EntityHandles.h"
|
|
#include "Entity.h"
|
|
#include "EntityManager.h"
|
|
#include "BaseEntityCollection.h"
|
|
#include "Scheduler.h"
|
|
#include "timer.h"
|
|
#include "LightEnv.h"
|
|
#include "MapWriter.h"
|
|
#include "GameEvents.h"
|
|
#include "Interact.h"
|
|
#include "Renderer.h"
|
|
|
|
#include "Game.h"
|
|
#include "Network/Server.h"
|
|
#include "Network/Client.h"
|
|
#include "gui/CGUI.h"
|
|
#include "ps/i18n.h"
|
|
#include "scripting/JSInterface_Entity.h"
|
|
#include "scripting/JSCollection.h"
|
|
#include "scripting/JSInterface_BaseEntity.h"
|
|
#include "scripting/JSInterface_Vector3D.h"
|
|
#include "scripting/JSInterface_Selection.h"
|
|
#include "scripting/JSInterface_Camera.h"
|
|
#include "scripting/JSInterface_Console.h"
|
|
#include "scripting/JSInterface_VFS.h"
|
|
#include "scripting/JSConversions.h"
|
|
#ifndef NO_GUI
|
|
# include "gui/scripting/JSInterface_IGUIObject.h"
|
|
#endif
|
|
|
|
extern CConsole* g_Console;
|
|
|
|
|
|
// 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);
|
|
|
|
// consistent argc checking for normal function wrappers: reports an
|
|
// error via JS and returns if number of parameters is incorrect.
|
|
// .. require exact number (most common case)
|
|
#define REQUIRE_PARAMS(exact_number, func_name)\
|
|
if(argc != exact_number)\
|
|
{\
|
|
JS_ReportError(cx, #func_name ": number of parameters passed doesn't match expected count");\
|
|
return JS_FALSE;\
|
|
}
|
|
// .. require 0 params (avoids L4 warning "unused argv param")
|
|
#define REQUIRE_NO_PARAMS(func_name)\
|
|
UNUSED(argv);\
|
|
if(argc != 0)\
|
|
{\
|
|
JS_ReportError(cx, #func_name ": number of parameters passed doesn't match expected count");\
|
|
return JS_FALSE;\
|
|
}
|
|
// .. accept at most N params (e.g. buildTime)
|
|
#define REQUIRE_MAX_PARAMS(max_number, func_name)\
|
|
if(argc > max_number)\
|
|
{\
|
|
JS_ReportError(cx, #func_name ": too many parameters passed");\
|
|
return JS_FALSE;\
|
|
}
|
|
// .. accept at least N params (e.g. issueCommand)
|
|
#define REQUIRE_MIN_PARAMS(min_number, func_name)\
|
|
if(argc < min_number)\
|
|
{\
|
|
JS_ReportError(cx, #func_name ": too few parameters passed");\
|
|
return JS_FALSE;\
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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* UNUSEDPARAM(rval))
|
|
{
|
|
REQUIRE_MIN_PARAMS(1, WriteLog);
|
|
|
|
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 );
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Entity
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Retrieve the entity currently occupying the specified handle.
|
|
// params: handle [int]
|
|
// returns: entity
|
|
JSBool getEntityByHandle( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
|
|
{
|
|
REQUIRE_PARAMS(1, getEntityByHandle);
|
|
*rval = JSVAL_NULL;
|
|
|
|
i32 handle;
|
|
try
|
|
{
|
|
handle = g_ScriptingHost.ValueToInt( argv[0] );
|
|
}
|
|
catch( PSERROR_Scripting_ConversionFailed )
|
|
{
|
|
JS_ReportError( cx, "Invalid handle" );
|
|
return( JS_TRUE );
|
|
}
|
|
HEntity* v = g_EntityManager.getByHandle( (u16)handle );
|
|
if( !v )
|
|
{
|
|
JS_ReportError( cx, "No entity occupying handle: %d", handle );
|
|
return( JS_TRUE );
|
|
}
|
|
JSObject* entity = (*v)->GetScript();
|
|
|
|
*rval = OBJECT_TO_JSVAL( entity );
|
|
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 )
|
|
{
|
|
REQUIRE_PARAMS(1, getEntityTemplate);
|
|
*rval = JSVAL_NULL;
|
|
|
|
CStrW templateName;
|
|
try
|
|
{
|
|
templateName = g_ScriptingHost.ValueToUCString( argv[0] );
|
|
}
|
|
catch( PSERROR_Scripting_ConversionFailed )
|
|
{
|
|
JS_ReportError( cx, "Invalid template identifier" );
|
|
return( JS_TRUE );
|
|
}
|
|
CBaseEntity* v = g_EntityTemplateCollection.getTemplate( templateName );
|
|
if( !v )
|
|
{
|
|
JS_ReportError( cx, "No such template: %s", CStr8(templateName).c_str() );
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
*rval = OBJECT_TO_JSVAL( v->GetScript() );
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
|
|
// 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 )
|
|
{
|
|
REQUIRE_MIN_PARAMS(2, issueCommand);
|
|
debug_assert(JSVAL_IS_OBJECT(argv[0]));
|
|
*rval = JSVAL_NULL;
|
|
|
|
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]));
|
|
|
|
CNetMessage *msg = CNetMessage::CommandFromJSArgs(entities, cx, argc-1, argv+1);
|
|
if (msg)
|
|
{
|
|
g_Console->InsertMessage(L"issueCommand: %hs", msg->GetString().c_str());
|
|
g_Game->GetSimulation()->QueueLocalCommand(msg);
|
|
*rval = g_ScriptingHost.UCStringToValue(msg->GetString());
|
|
}
|
|
|
|
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* obj, uint argc, jsval* argv, jsval* rval )
|
|
{
|
|
REQUIRE_PARAMS(2, AddGlobalHandler);
|
|
|
|
*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* obj, uint argc, jsval* argv, jsval* rval )
|
|
{
|
|
REQUIRE_PARAMS(2, RemoveGlobalHandler);
|
|
|
|
*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* UNUSEDPARAM(rval) )
|
|
{
|
|
REQUIRE_PARAMS(2, setTimeout);
|
|
|
|
size_t delay;
|
|
try
|
|
{
|
|
delay = g_ScriptingHost.ValueToInt( argv[1] );
|
|
}
|
|
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] );
|
|
g_Scheduler.pushTime( delay, fragment, JS_GetScopeChain( cx ) );
|
|
return( JS_TRUE );
|
|
}
|
|
case JSTYPE_FUNCTION:
|
|
{
|
|
JSFunction* fn = JS_ValueToFunction( cx, argv[0] );
|
|
g_Scheduler.pushTime( delay, fn, JS_GetScopeChain( cx ) );
|
|
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* UNUSEDPARAM(rval) )
|
|
{
|
|
REQUIRE_MIN_PARAMS(2, setInterval);
|
|
REQUIRE_MAX_PARAMS(3, setInterval);
|
|
|
|
size_t first, interval;
|
|
try
|
|
{
|
|
first = g_ScriptingHost.ValueToInt( argv[1] );
|
|
if( argc == 3 )
|
|
{
|
|
// toDo, first, interval
|
|
interval = g_ScriptingHost.ValueToInt( 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] );
|
|
g_Scheduler.pushInterval( first, interval, fragment, JS_GetScopeChain( cx ) );
|
|
return( JS_TRUE );
|
|
}
|
|
case JSTYPE_FUNCTION:
|
|
{
|
|
JSFunction* fn = JS_ValueToFunction( cx, argv[0] );
|
|
g_Scheduler.pushInterval( first, interval, fn, JS_GetScopeChain( cx ) );
|
|
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* UNUSEDPARAM(rval) )
|
|
{
|
|
REQUIRE_NO_PARAMS(cancelInterval);
|
|
|
|
g_Scheduler.m_abortInterval = true;
|
|
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)
|
|
{
|
|
REQUIRE_NO_PARAMS(createServer);
|
|
|
|
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)
|
|
{
|
|
REQUIRE_NO_PARAMS(createClient);
|
|
|
|
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)
|
|
{
|
|
REQUIRE_NO_PARAMS(startGame);
|
|
|
|
*rval = BOOLEAN_TO_JSVAL(JS_TRUE);
|
|
|
|
// Hosted MP Game
|
|
if (g_NetServer)
|
|
*rval = BOOLEAN_TO_JSVAL(g_NetServer->StartGame() == 0);
|
|
// Joined MP Game: startGame is invalid - do nothing
|
|
else if (g_NetClient)
|
|
{
|
|
}
|
|
// 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 );
|
|
}
|
|
}
|
|
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
|
|
// Immediately ends the current game (if any).
|
|
// params:
|
|
// returns:
|
|
JSBool endGame(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval))
|
|
{
|
|
REQUIRE_NO_PARAMS(endGame);
|
|
|
|
EndGame();
|
|
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* UNUSEDPARAM(rval))
|
|
{
|
|
REQUIRE_PARAMS(1, loadLanguage);
|
|
|
|
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)
|
|
{
|
|
REQUIRE_NO_PARAMS(getLanguageID);
|
|
*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 crash(JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval))
|
|
{
|
|
REQUIRE_NO_PARAMS(crash);
|
|
|
|
MICROLOG(L"Crashing at user's request.");
|
|
return *(JSBool*)0;
|
|
}
|
|
|
|
|
|
// Force a JS GC (garbage collection) cycle to take place immediately.
|
|
// params:
|
|
// returns: true [bool]
|
|
// notes:
|
|
// - writes an indication of how long this took to the console.
|
|
JSBool forceGC( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval )
|
|
{
|
|
REQUIRE_NO_PARAMS(forceGC);
|
|
|
|
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 )
|
|
{
|
|
REQUIRE_NO_PARAMS(getGUIGlobal);
|
|
|
|
*rval = OBJECT_TO_JSVAL( g_GUI.GetScriptObject() );
|
|
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 )
|
|
{
|
|
REQUIRE_NO_PARAMS(getFPS);
|
|
|
|
*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* UNUSEDPARAM(rval) )
|
|
{
|
|
REQUIRE_NO_PARAMS(exitProgram);
|
|
|
|
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 vmem( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* UNUSEDPARAM(rval) )
|
|
{
|
|
REQUIRE_NO_PARAMS(vmem);
|
|
|
|
#ifdef _WIN32
|
|
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* UNUSEDPARAM(rval) )
|
|
{
|
|
REQUIRE_PARAMS(1, setCursor);
|
|
|
|
g_CursorName = g_ScriptingHost.ValueToString(argv[0]);
|
|
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* UNUSEDPARAM(rval) )
|
|
{
|
|
REQUIRE_NO_PARAMS(_rewriteMaps);
|
|
|
|
CMapWriter::RewriteAllMaps(g_Game->GetWorld()->GetTerrain(), g_Game->GetWorld()->GetUnitManager(), &g_LightEnv);
|
|
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* UNUSEDPARAM(rval) )
|
|
{
|
|
REQUIRE_PARAMS(1, _lodbias);
|
|
|
|
g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, (float)g_ScriptingHost.ValueToDouble(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* obj, uint argc, jsval* argv, jsval* rval )
|
|
{
|
|
REQUIRE_PARAMS(1, setCameraTarget);
|
|
*rval = JSVAL_NULL;
|
|
|
|
CVector3D* target;
|
|
if( !( target = ToNative<CVector3D>( argv[0] ) ) )
|
|
{
|
|
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 buildTime( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval )
|
|
{
|
|
REQUIRE_MAX_PARAMS(1, buildTime);
|
|
|
|
// buildTime( ) = "date time"
|
|
// buildTime(0) = "date"
|
|
// buildTime(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 v3dist( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval )
|
|
{
|
|
REQUIRE_PARAMS(2, v3dist);
|
|
|
|
CVector3D* a = ToNative<CVector3D>( argv[0] );
|
|
CVector3D* b = ToNative<CVector3D>( argv[1] );
|
|
float dist = ( *a - *b ).GetLength();
|
|
*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 )
|
|
{
|
|
REQUIRE_NO_PARAMS(getGlobal);
|
|
|
|
*rval = OBJECT_TO_JSVAL( globalObject );
|
|
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(getEntityByHandle, getEntityByHandle, 1)
|
|
JS_FUNC(getEntityTemplate, getEntityTemplate, 1)
|
|
JS_FUNC(issueCommand, issueCommand, 2)
|
|
|
|
// Camera
|
|
JS_FUNC(setCameraTarget, setCameraTarget, 1)
|
|
|
|
// GUI
|
|
#ifndef NO_GUI
|
|
JS_FUNC(getGUIObjectByName, JSI_IGUIObject::getByName, 1) // external
|
|
JS_FUNC(getGUIGlobal, getGUIGlobal, 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)
|
|
|
|
// Game Setup
|
|
JS_FUNC(startGame, startGame, 0)
|
|
JS_FUNC(endGame, endGame, 0)
|
|
JS_FUNC(createClient, createClient, 0)
|
|
JS_FUNC(createServer, createServer, 0)
|
|
|
|
// VFS (external)
|
|
JS_FUNC(buildFileList, JSI_VFS::BuildFileList, 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)
|
|
|
|
// 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, crash, 0)
|
|
JS_FUNC(forceGC, forceGC, 0)
|
|
|
|
// Misc. Engine Interface
|
|
JS_FUNC(writeLog, WriteLog, 1)
|
|
JS_FUNC(exit, exitProgram, 0)
|
|
JS_FUNC(vmem, vmem, 0)
|
|
JS_FUNC(_rewriteMaps, _rewriteMaps, 0)
|
|
JS_FUNC(_lodbias, _lodbias, 0)
|
|
JS_FUNC(setCursor, setCursor, 1)
|
|
JS_FUNC(getFPS, getFPS, 0)
|
|
|
|
// Miscellany
|
|
JS_FUNC(v3dist, v3dist, 2)
|
|
JS_FUNC(buildTime, buildTime, 0)
|
|
JS_FUNC(getGlobal, getGlobal, 0)
|
|
|
|
// end of table marker
|
|
{0, 0, 0, 0, 0}
|
|
};
|
|
#undef JS_FUNC
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// property accessors
|
|
//-----------------------------------------------------------------------------
|
|
|
|
JSBool GetEntitySet( JSContext* cx, JSObject*, jsval argv, jsval* vp )
|
|
{
|
|
std::vector<HEntity>* extant = g_EntityManager.getExtant();
|
|
|
|
*vp = OBJECT_TO_JSVAL( EntityCollection::Create( *extant ) );
|
|
delete( extant );
|
|
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
|
|
JSBool GetPlayerSet( JSContext* cx, JSObject*, jsval id, jsval* vp )
|
|
{
|
|
std::vector<CPlayer*>* players = g_Game->GetPlayers();
|
|
|
|
*vp = OBJECT_TO_JSVAL( PlayerCollection::Create( *players ) );
|
|
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
|
|
JSBool GetLocalPlayer( JSContext* cx, JSObject*, jsval id, jsval* vp )
|
|
{
|
|
*vp = OBJECT_TO_JSVAL( g_Game->GetLocalPlayer()->GetScript() );
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
|
|
JSBool GetGaiaPlayer( JSContext* cx, JSObject*, jsval id, jsval* vp )
|
|
{
|
|
*vp = OBJECT_TO_JSVAL( g_Game->GetPlayer( 0 )->GetScript() );
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
|
|
JSBool SetLocalPlayer( JSContext* cx, JSObject* obj, jsval 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* cx, JSObject*, jsval id, jsval* vp )
|
|
{
|
|
if (g_Game)
|
|
*vp = OBJECT_TO_JSVAL( g_Game->GetView()->GetScript() );
|
|
else
|
|
*vp = JSVAL_NULL;
|
|
return( JS_TRUE );
|
|
}
|
|
|
|
|
|
enum ScriptGlobalTinyIDs
|
|
{
|
|
GLOBAL_SELECTION,
|
|
GLOBAL_GROUPSARRAY,
|
|
GLOBAL_CAMERA,
|
|
GLOBAL_CONSOLE,
|
|
};
|
|
|
|
// 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 },
|
|
{ "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 },
|
|
|
|
// end of table marker
|
|
{ 0, 0, 0, 0, 0 },
|
|
};
|