![]() TGE Version 1.5.2 | |
| |
ConsoleOverviewThe console module, rooted in engine/console/console.h, is a combined compiler and interpreter runtime that serves as the foundation for Torque applications. All GUIs, game objects, interfaces, and game logic are handled through the console. The language itself is syntactically similar to a typeless C++, with some additional features that allow for easier mod development. Console scripts can be loaded via the exec() console command from the console window (brought up using the ~ key) or they can be loaded automatically from a mod via that mod's main.cs.Console Language ReferenceThe Torque console language scanner and parser were built using the tools lex and yacc. The scan and grammar files are engine/console/scan.l and engine/console/gram.y respectively. The grammar is shown in a somewhat more understandable way in the attached document.FunctionsConsole functions can be declared in either console scripts or in the C++ game/engine code.
// A simple script function declaration. function helloWorld () { echo("Hello World!"); } // Let's call it! helloWorld(); // Or, for a method on a class: function SimObject::helloWorld(%this) { echo("Hello World!"); echo("Called on object: " @ %this); } // And call it... $object = new SimObject(); $object.helloWorld();
ConsoleFunction(helloWorld, void, 1, 1, "A simple test function.") { Con::printf("Hello World!"); } A rundown of the arguments to the ConsoleFunction macro:
Methods for classes can also be declared in C++, using the ConsoleMethod macro:
ConsoleMethod(SimObject, helloWorld, void, 2, 2, "A somewhat more complex test method.") { Con::printf("Hello World!"); Con::printf("Called on object: %s", argv[1]); Con::printf("Also called on object: %s", object->getName()); } This defines a method callable on any instance of a SimObject the console has access to.
Classes, Objects and NamespacesDeclaring Console ClassesObjects in the scripting language are simply instances of C++ classes deriving from SimObject, declared in the game engine and processed with a special set of macros (declared in engine/console/consoleObject.h). The DECLARE_CONOBJECT(class_name) macro is placed inside the class definition and the IMPLEMENT_CONOBJECT (class_name) macro is placed in a linked source file. IMPLEMENT_CONOBJECT has several versions, depending on the type of class:
// A very simple SimObject sample. class SampleObject : public SimObject { typedef SimObject Parent; public: DECLARE_CONOBJECT(SampleObject); S32 someVariable; // signed integer variable }; IMPLEMENT_CONOBJECT(SampleObject); You can instantiate and manipulate this object in script like this:
function foo()
{
%obj = new SampleObject(MySampleObject);
echo(%obj.getName());
}
Adding Class Member FieldsData members of C++ classes can be accessed from within the scripting language.When the game starts, the console calls AbstractClassRep::initialize(), which assigns network IDs to classes, links class hierarchies, and initializes field data for each class. To do this, each class with accessible data members declares a static member function called initPersistFields(). This function calls the addField static member function for each data member of the class:
void SampleObject::initPersistFields() { Parent::initPersistFields(); // adds the parent class's fields as well. addField("someVariable", TypeS32, Offset(someVariable, SampleObject)); } The type must be properly specified, of course. The Offset macro is used to determine the relative address of the data member in the class. Once this is defined, scripts can use the member field of the object directly:
function bar()
{
%obj = new SampleObject(MySampleObject);
%obj.someVariable = 100;
echo(%obj.someVariable);
}
Field types are declared in engine/console/consoleTypes.h. New console data types can be added by adding a type to consoleTypes.h and calling Con::registerType(typeId, typeSize, getDataFunc, setDataFunc). See engine/console/consoleTypes.cc for examples of how types are defined. Dynamically Defined FieldsMember fields of objects can also be defined from within the script itself. For example:
function foo() { %object = new SampleObject(); %object.scriptVariable = "Hello World!"; echo(%object.scriptVariable); }
NamespacesNamespaces (engine/console/consoleInternal.h) are collections of class member functions. Every SimObject belongs to exactly one namespace. By default, an object belongs to the namespace that corresponds to its class - so an instance of GameBase will have the GameBase namespace. Each namespace has a parent, so methods can invoke parent class methods by calling Parent::function().New namespaces (not class-based) can be added in the engine via the Con::linkNamespaces() function. Assigning the mNameSpace field of SimObject then assigns the new namespace to an object. For example, in GuiControl::onAdd(), if a control has a name its name is used as its namespace. This allows a named GUI control to have special behaviors. The ScriptObject class (defined in engine/console/scriptObject.cc) allows for the creation of "classes" within the scripting language:
new ScriptObject(MyObject) { class = Bar; superClass = Foo; }; function Bar::doSomething(%this) { echo("Hi!"); } MyObject.doSomething(); > Hi! function Foo::doSomething(%this) { echo("Hi! Foo"); } function Bar::go(%this) { %this.doSomething(); Parent::doSomething(%this); } MyObject.go(); > Hi! > Hi! Foo
PackagesPackages are collections of functions that can be enabled and disabled at runtime. Package functions can override (redefine) the behavior of existing functions in the global state or in packages that have been activated earlier. Prior versions of a function can then be accessed using "Parent". For example:
function foo() { echo("foo!"); } package SamplePackage { function foo() { echo("Haha!"); Parent::foo(); } } % foo(); foo! % ActivatePackage(SamplePackage); % foo(); Haha! foo! Packages are useful for creating mods to games or for implementing specific game modes. VariablesThe console language supports global variables and local (function scoped) variables. Global variables are specified by a preceding $, and local variables by a % sign. Example:
$someGlobal = "This is some global."; function foo(%local1, %local2) { %local3 = $someGlobal; } function bar(%local) { // You can also create a new global from within a function! $alottaGlobal = %local @ " is."; } ArraysThe console language supports associative single- and multi-dimensional arrays. Arrays actually construct new variables with the names concatenated - so for example $array[10] is the same as $array10, while $array[3, 4] is the same as $array3_4. Strings can be used as array indexes as well: $array["foo"] = 100;. Array dimensions are separated with commas inside the brackets - $array[1, 0] = 10;Special String OperatorsThere are several special operators for strings in the scripting language:
CompilerScripts are executed in a two step process: First the script is compiled into a tokenized instruction stream, then the instruction stream is processed using the compiled evaluator.DebuggerThe console supports remote debugging via another instance of Torque. In the game instance to be debugged, debugger port and password must be set using the dbgSetParameters(port, password); Then, in the instance to be used as the debugger, the GUIs and scripts in common/debugger/ must be loaded.
Interfacing with C++ CodeThe C++ game and engine code can be called from the scripts as described above, and the game code can also call into script using the console execute and evaluate functions:
// simple execute of a console function using argv array: const char *execute(S32 argc, const char* argv[]); // simple execute of a console function, without stuffing an array const char *executef(S32 argc, ...); // execution of a method on a SimObject using argv array: // first param is func name, second param MUST be empty // also, MUST have at least those two params const char *execute(SimObject *, S32 argc, const char *argv[]); // execution of a method on a SimObject without stuffing an array // first param is funcName, remaining params are args const char *executef(SimObject *, S32 argc, ...); // evaluation of an arbitrary console command script: const char *evaluate(const char* string, bool echo, const char *fileName); // evaluation of a formatted (ala printf) command string: const char *evaluatef(const char* string, ...); Examples:
SimObject *mySimObject = new SimObject; Con::executef(mySimObject, 4, "doSomething", Con::getIntArg(20), "Bye", "Hi"); Con::evaluatef("mySimObject.doSomething(%d,\"%s\",\"%s\");", 20, "Bye", "Hi"); const char *argv[5]; argv[0] = "doSomething"; argv[1] = NULL; argv[2] = Con::getIntArg(20); argv[3] = "Bye"; argv[4] = "Hi"; Con::execute(mySimObject, 5, argv); The functions Con::getIntArg, Con::getFloatArg and Con::getArgBuffer(size) are used to allocate on the console stack string variables that will be passed into the next console function called. This allows the console to avoid copying some data. |