Complete.Org: Mailing Lists: Archives: freeciv-dev: September 1999:
[Freeciv-Dev] Script implementation
Home

[Freeciv-Dev] Script implementation

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: Freeciv Dev <freeciv-dev@xxxxxxxxxxxx>
Subject: [Freeciv-Dev] Script implementation
From: Artur Biesiadowski <abies@xxxxxxxxx>
Date: Thu, 02 Sep 1999 12:29:39 +0200

Here are some random ideas about how to embed script in freeciv in
script independent manner and some example how to make funs in guile :)

I'll write all freeciv<->script engine interface functions in capital
case - they could be anything, macro, normal function or function
resolved to dynamic library.

We need to rename main to real_main and provide hook into SVM(Script
Virtual Machine) - at least guile needs that.

int main( int argc, char * argv[] )
{
   SVM_INIT(argc,argv);
}

and it will do neccessary initialization and then call
real_main(argc,argv)

Scripts will be mostly embedded into rulesets. 


[building_hydro_plant]
name        = "Hydro Plant"
tech_req    = "Electronics"
obsolete_by = "None"
is_wonder   = 0
build_cost  = 240
upkeep      = 4
can_build = SCRIPT guile
(
... script
)
ENDSCRIPT


Normal parser will real ruleset, when it will spot script keyword in
correct place, it passes stream/text to SVM.  As we may want to have few
dialects translating to same VM, type of script is next - but this is
resolved by SVM. Call will be something like


void * proc = SVM_REGISTER_SCRIPT( SCRIPT_CAN_BUILD ,stream);

Stream is something to decide - either FILE*, fd or just text buffer (in
this case it is needed to load it fully). SCRIPT_CAN_BUILD is some
id/pointer to description record to specify type of function - so
specific arguments to function can be added. proc is redurned magic word
than SVM later uses to run function (for example for guile this can be
just const char *.

Later when running script is needed, C code calls

if ( SVM_RUN_SCRIPT_BOOL_I( SCRIPT_CAN_BUILD, building->proc, city->id )
)
        .. can build improvement
else
        .. cannot build improvement

I at end of call function represents that one integer is passed to
script as argument. This is unfortunate, with single scripting language
it would not be needed as direct calls will be used, but...

For example for guile it can be implemented as 

SVM_RUN_SCRIPT_BOOL_I( somestruct * scripttype , void * proc , int i )
{
        char buf[MAX_L];
        sprintf(buf, "(%s %d)", (const char *)proc, i);
        return ( gh_scm2bool(gh_eval(buf)));
}


but some other languages/VMs could actively use scripttype, have some
complicated record in proc etc.

Proc going other way - calls from script to C are the resposibility of
script specific code, and can be done in SVM_INIT. 

-------------

There are at least some problems here. I used city id here - somebody
might want to use just struct city ptr. I'm against it - it would
require to keep strict synchronization between SVM and C malloc/free.
With just int ids, even if script saves id somewhere and later uses it
when data not longer exists, there is no problem, as no operations will
be performed/error detected and printed.

-----
Here I have used text gh_eval - of course it could be doen more
efficiently by 

return ( gh_scm2bool(gh_call1((SCM)proc,gh_long2SCM(i)) ));

(in this case proc is SCM, nor char*)

-----
To show simplicity of embedding guile, here is an example of unit hp get
function

SCM c_hook_unit_hp_get( SCM unitid )
{
   struct unit * unit = find_unit_by_id(gh_scm2int(unitid));
   if ( unit == 0 )
      ... report error or just return some vaue like -1 or 0
   return gh_long2scm(unit->hp);
}

If we would like to be very robust we could also add check if gh_scm2int
haven't returned SCM_UNDEFINED (somebody passed non-int from script).
I'm not sure about this because it would be most probably catched at
find unti step and then reported (and scripts should be supposed to be
flawless - I think that abort() on malformed script is reasonable
behaviour - in other case they can start to do weird things to game; it
is better to crash than to have subtle bugs in game logic).

Such functions have to be registered first - 
gh_new_procedure_1_0("unit-hp-get", c_hook_unit_hp_get);

Registering could be done by writing generic function which would use
some structure contianing, guile fun name, c fun pointer (as above),
plus help text for given fun + argument descriptions (so manual pages
could be generated automatically).

Do anybody with python/TCL experience can show similar code for them ? -
I'm interested how much hassle is needed for registering C hooks, or
extracting/creating values for it.

Artur



[Prev in Thread] Current Thread [Next in Thread]