C / Python integration and local variables

Emmanuel Astier emmanuel.astier at winwise.fr
Tue Sep 4 14:29:56 EDT 2001


On 03 Sep 2001 14:00:14 +0200, Alexandre Courbot
<alexandrecourbot at linuxgames.com> wrote:

>Hello everybody,
>
>In the project I'm working on, (written in C++) we use Python as a
>scripting language, to control the behavior of game elements
>(characters, etc...) from scripts. Here is a simplified representation
>or what we used to do:
>
>class character
>{
>    PyObject * locals;
>
>    PyCodeObject * script;
>
>    // Parse 'file' with PyParser_SimpleParseFile and create
>    // the 'script' code object with PyNode_Compile
>    void set_script (string file);
>
>    // Function called once per game cycle, so the character
>    // can do it's business
>    void update ()
>    {
>        // data::globals is the game's globals namespace
>	// which contains all wrapped methods and types
>        PyEval_EvalCode (script, data::globals, locals);
>    }
>
>
>    // Control methods
>    void go_north ();
>    void go_south ();
>    void go_east ();
>    void go_west ();
>  
>    void speak (string t);
>};
>
>locals is built at constructor time, and is supposed to contain the
>character's locales variables. You can assume it just contains a
>'myself' member, which is a reference to the instance of the character
>so it can be accessed from Python. That way, into the Python script set
>with set_script () I can do something like myself.go_north () to control
>the character from Python. So far, it works like a charm.
>
>The problem is, that sometimes the script could have it's own variables.
>For example, the following script would make the character utter some
>random speech:
>
>--------------------------------------------------------------------
>speech = ["Hello!", "What time is it?", "The knights who say 'ni!'"]
>
>myself.speak (speech[randint (0, 2)])
>--------------------------------------------------------------------
>
>That would make the character randomly say one of these 3 sentences each
>time it's updated.
>
>The problem is, that the speech list is created each time the script is
>run. That's a waste of CPU time, and we'd like to avoid this. So we
>tried a different approach: each script has 3 functions: init () which
>is called when the script is set, cleanup () which is called when the
>script is deleted (constructor/destructor behavior, actually) and run ()
>which is the script itself (that is, what is run by update). Ideally,
>our script would then be built this way:
>
>---------------------------------------------------------------------
>def init ():
>     speech = ["Hello!", "What time is it?", \
>               "The knights who say 'ni!'"]
>
>def cleanup ():
>     del speech
>
>def run ():
>     myself.speak (speech[randint (0, 2)])
>---------------------------------------------------------------------
>
>The PyCodeObject is built differently: First we import the script as a
>module (PyImport_ImportModule), then we get the run () function by doing
>a 'PyObject * function = PyObject_GetAttrString (module, "run")' and,
>finally, we get the function code object by 'script = (PyCodeObject *)
>PyObject_GetAttrString (function, "func_code");'. Idem for init () and
>cleanup (). Then we run init by 'PyEval_Evalcode (initcode,
>data::globals, locals) and we expect to have speech created into the
>locals PyObject * (at least, that's what was done with the old code,
>even though it wasn't necessary). And, when update () is called, the run
>() function is called... raising a NameError because it cannot find
>neither 'myself' nor 'speech'! And if I print the locals () inside the
>function, it displays an enpty dictionary, even though the locals are
>correctly passed from C++! That's where we are totally stuck. I've
>browsed the archive of this list, and found that several people had
>problems with locals () inside function calls, that's probably where the
>problem lies.
>
>What we would like to achieve is to keep the init () cleanup () run ()
>structure (or anything equivalent) for our scripts and being able to
>have persistant variables between scripts calls through the 'locals'
>PyObject * so Python can communicate with C++ and vice versa, and mainly
>so the script can have persistant variables. Speed is also a very
>important issue for us, as the compiled scripts are run very often (70
>times per second!). We'd be very thankfull to anyone who could help us
>solving this issue. Any comment or suggestion are welcome.
>
>Thanks for your time,
>
>Alex.
>
>
>

I tried to use Python for a script Game too.
And I don't have this problem (but I have other ones ...)

In my application, my Global vars in the Python modules are preserved
at each trame... (I thought you mean global to the python module when
you said local vars...)

In order to call the functions I used the 
PyObject_CallFunction( LogicalEntryPoint, NULL );
instruction.

LogicalEntryPoint being the entry point of the function object code.
I found it using, at load / reload time  :
module = PyImport_Import(modName);
To get the module.
mdict = PyModule_GetDict( Module );
To get the dictionnary of the module.
And :
LogicalEntryPoint = PyDict_GetItemString(mdict, "LogicalTrame");
to get the entry point.
I also check it is really callable...

And it works...

At the next step ( if I ever found some time for this ), I will try to
use StackLess python in order to stay within a script, with all locals
vars preserved...

Hope it helps, 

Emmanuel





More information about the Python-list mailing list