Is slightly changing the jscript API possible ?

For AI and campaign script related discussions and questions

Is slightly changing the jscript API possible ?

Postby Vincent » 10 Oct 2017, 18:35

Hi,

I'm trying to make wz API export to jscript more straightforward using some Template trickery ; basically I'm providing a "wrap_(f, engine, context);" function that takes a C function whose argument are DROID*, STRUCTURE* (actually object holding id and player since it's what is used internally), int ... and calls it using argument found in QtScript engine and context, wrapping the result value in a QScriptValue.

For instance :

Code: Select all
static QScriptValue js_activateStructure(QScriptContext *context, QScriptEngine *)
{
   QScriptValue structVal = context->argument(0);
   int id = structVal.property("id").toInt32();
   int player = structVal.property("player").toInt32();
   STRUCTURE *psStruct = IdToStruct(id, player);
   SCRIPT_ASSERT(context, psStruct, "No such structure id %d belonging to player %d", id, player);
   // ... and then do nothing with psStruct yet
   QScriptValue objVal = context->argument(1);
   int oid = objVal.property("id").toInt32();
   int oplayer = objVal.property("player").toInt32();
   OBJECT_TYPE otype = (OBJECT_TYPE)objVal.property("type").toInt32();
   BASE_OBJECT *psObj = IdToObject(otype, oid, oplayer);
   SCRIPT_ASSERT(context, psObj, "No such object id %d belonging to player %d", oid, oplayer);
   orderStructureObj(player, psObj);
   return QScriptValue(true);
}


becomes :
Code: Select all
static bool activateStructure(structure_id_player structVal, object_id_player_type objVal)
{
   STRUCTURE *psStruct = IdToStruct(structVal.id, structVal.player);
   BASE_OBJECT *psObj = IdToObject(objVal.type, objVal.id, objVal.player);
   orderStructureObj(structVal.player, psObj);
   return true;
}

static QScriptValue js_activateStructure(QScriptContext *context, QScriptEngine *engine)
{
   return wrap_(activateStructure, context, engine);
}


The goal is to separate the API function and the boilerplate code needed to make the function callable from qtscript side. This way it should be easier to add a new function to the API (just add a wrapper that calls wrap_ and register it), and since there is no qt code in the API function it should be possible to switch javascript engine without breaking anything and even provide bindings to other langages (python ? ruby ?) as long as the template mechanism is properly updated.

There are however some drawbacks : for the wrapper to work the API function have some restriction : they must not be overloaded (the compiler has no idea which overloads to pick), they must not use jscript global variable (the mechanism is based on the function signature) and they must not rely on jscript engine to display message.
This means some jscript function must be modified to comply with these requirements : overloads must be renamed, global variable must be passed as argument to the functions and error message won't be jscript specific (ie they will be printed in the wz log not in jscript console). On the other hand the API should be easier to maintain and expand.

Are such modifications acceptable ? I think API breakage may require changing version number. On the other hand it may be incremental : compatible functions can be ported to this system and new can be introduced while the non compatible one may be marked as deprecated.
Vincent
Trained
Trained
 
Posts: 103
Joined: 06 Aug 2016, 17:24

Re: Is slightly changing the jscript API possible ?

Postby NoQ » 10 Oct 2017, 20:17

The idea looks super cool.

I recall that for QJSEngine you don't even get Engine or Context - the engine already does accept API functions in form of DROID*, STRUCTURE*, int, etc.

for the wrapper to work the API function have some restriction : they must not be overloaded (the compiler has no idea which overloads to pick)
I think we have a lot of these when it comes to labels. The easy way to enumerate them is to Ctrl+F "|" through http://buildbot.wz2100.net/files/javasc ... cript.html - shows a lot of very widespread API functions. So that'd be a lot of fixes to scripts.

Can this be solved by providing "variant" or "union" wrapper types, eg. "structure_id_player" -> "structure_id_player_or_int_or_string" (potentially "_or_unspecified")?

they must not use jscript global variable (the mechanism is based on the function signature)
Hmm, you mean like functions that lookup the variable by name in the engine and then return or modify its value? Did you see many of those? Random thoughts: i guess in this case the value should rather live on the game side. If it gets updated, we can probably update it in the script context accordingly. Not sure what to do if it's writable from scripts - can the engine catch the write and update the game variable immediately? Random thoughts, my main question is whether it should live on the game side.

error message won't be jscript specific (ie they will be printed in the wz log not in jscript console)
Also looking for examples here, i'm a bit confused about what this means. Scripts print through debug(), and APIs print into wzlog (or even asserts) already, at least that's what i imagine.
User avatar
NoQ
Special
Special
 
Posts: 6098
Joined: 24 Dec 2009, 11:35
Location: /var/zone

Re: Is slightly changing the jscript API possible ?

Postby Vincent » 10 Oct 2017, 22:41

]Hmm, you mean like functions that lookup the variable by name in the engine and then return or modify its value? Did you see many of those? Random thoughts: i guess in this case the value should rather live on the game side. If it gets updated, we can probably update it in the script context accordingly. Not sure what to do if it's writable from scripts - can the engine catch the write and update the game variable immediately? Random thoughts, my main question is whether it should live on the game side.


Actually I spotted several "engine->globalObject().property("me").toInt32();" ; I didn't see another property of globalObject used in C++ code so far. I guess "me" is used to determine if the player (represented by a number) is the human one, or at least the one that has interface in a multplay game.

]Also looking for examples here, i'm a bit confused about what this means. Scripts print through debug(), and APIs print into wzlog (or even asserts) already, at least that's what i imagine.

There are several calls to "SCRIPT_ASSERT" macro which throw an error in javascript side if the assert is not verified. I can add some check on the argument but script assert happening in c++ code won't be able to throw error in jscript anymore with the new system.
Vincent
Trained
Trained
 
Posts: 103
Joined: 06 Aug 2016, 17:24

Re: Is slightly changing the jscript API possible ?

Postby NoQ » 11 Oct 2017, 10:19

Aha, right, "me" is tricky. Its value is different for different engines. It allows API to work for "current player" (i.e. the AI works with his own objects, not human's or other AI's objects, and he doesn't need to explain that every time he uses the API). This is being relied upon actively, so i guess this cannot be easily changed without rewriting a lot of scripts.

And SCRIPT_ASSERT, as far as i remember, not only prints stuff, but also throws a javascript exception, interrupting execution of the current callback on script side to avoid further senseless behavior. Not sure this is relied on, but it may be; aubergine has actively promoted the use of try-catch over such API exceptions [1]. And arguably it also improves experience in case there are unexpected errors in the script.

Can both problems be fixed by providing a universal engine wrapper object as the first mandatory argument to every API function definition? That would be able to, i.e., ".getCurrentPlayer()" or ".throwException()"? (again, exceptions don't sound *super* important, but better keep).

Also if you need heavy API changes, you may want to introduce API levels, i.e. maintain multiple APIs with versioning and allowing scripts to pick whatever they want, probably in their accompanying .json (for AIs, we don't have that for rules or map scripts yet). But for now i don't see an immediate heavy need for that (it would have already allowed us to avoid some weird quirks in the existing APIs, but it doesn't sound much, and it can be delayed arbitrarily).
User avatar
NoQ
Special
Special
 
Posts: 6098
Joined: 24 Dec 2009, 11:35
Location: /var/zone

Re: Is slightly changing the jscript API possible ?

Postby Per » 21 Oct 2017, 18:36

I think having no global state for JS functions would make them unreasonably argument verbose. Not sure how that could even possibly work for upgrades and other larger information blocks, which are stored in the global js state.
Per
Warzone 2100 Team Member
Warzone 2100 Team Member
 
Posts: 3649
Joined: 03 Aug 2006, 19:39


Return to Scripting