[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Scripting

Steve Baker wrote:

> Gregor Mueckl wrote:
>>1. Python is slow. I didn't do performance tests on Python before
>>deciding to make this my scripting language of choice. I expected script
>>code to be *way* slower than compiled code (esp. considering the
>>complexity of the Python interpreter). But I had no idea how slow Python
>>actually is.
> Python is slow because it's a very full-featured language.

That makes it a powerful language.

> Most game's scripting requirements don't need anything like such a
> sophisticated language.  Also (as I explained before), I think you
> actually need to use a 'byte-code' approach so that you can interpret
> a little out of each of dozens of programs each frame.

The question is whether you actually want this approach. It could be 
useful if you have to deal with some very complex AI code (containing 
things like recursion and other "nasty" stuff and/or can't guarantee a 
constant runtime in case you take an event handler approach).

> I've written a real simple parser to translate a C subset into a very
> simple bytecode - it's not especially fast - but it seems good enough
> for my needs.

That's the drawback: I don't know of any open implmentation of a 
bytecode interpreter that allows you to specify how many instructions it 
should execute from which script. So you would have to take it into your 
own hands - with all the problems involved in reinventing the wheel once 
again (I don't think that I have to go into detail here).

>>2. I've planned to have much more Python script code (i.e. a much lower
>>level interface to the engine) than realistically possible, given that
>>your figures are not too far off from what I use Python for, Steve.
>>Currently I start to think about making compiled plugins, which in turn
>>could act as stubs to script interpreters. So the core engine can become
>>extraordinarily generic while not suffering too much speed penalty by
>>loading additional compiled code instead of slower scripts for those
>>parts that are too slow to remain interpreted.
> Yes.  If you allow '.so' plugins, they can either be pure C/C++ programs
> or they can be stubs that call an interpreter to interpret Python or
> whatever you need.

So IMHO it's best to write the interpreter plugin first and start using 
that and only revert to compiled code if the scripts begin to become too 
slow to run. This approach is good for AI development, because during 
testing you can skip compilation entirely and when it's finished you 
rewrite it in C/C++ to give you the time savings you need for the extra 
graphics/effects/higher framerate or whatever.

>>Btw: Could it be that hard to write a JIT for a interpreted language
>>when the script is already compiled into some bytecode (with preferably
>>very few instructions)? It wouldn't have to really optimise things, but
>>could avoid at least some of the overhead a bytecode interpreter needs.
>>So what thit comes down to is to generate asm code junks for each of the
>>instructions, which are then patched and concatenated together. I just
>>don't understand why this could be so hard to do.
> The main problem (as far as I'm concerned) is that you kill any hope of
> portability if you generate machine-code.  That's why I use a simple
> byte-code.  It's slower - but not unacceptably so for the relatively
> simple things I expect interpreted code to do in my games.

Well... how many platforms do you want to support with your code? My 
guess is that at least 80-90% of the players will have Intel-compatible 
hardware, maybe even more. The rest is far from being an uniform 
environment: UNIX Workstations, Macs, PS(/2), etc.

> I don't anticipate interpreting thousands of lines of code per
> frame for each script - I rather expect to execute just a few
> dozen bytecodes for each one.

Wouldn't it be better to revert to an event handler system then? If the 
scripts are that simple (I'm refering to your emamples below now) it 
could be far better to do it this way than letting your scripts loop 
within a virtual thread and thereby wasting time *every* frame instead 
of wasting time only when really needed. The overhead required to 
determine whether and / or which event handler should be called can be 
neglected in these examples.

But again, your approach makes perfectly sense if at least some of your 
scripts are a *lot* more complex than in your examples.

> But it all depends on what you expect your scripts to be used for.

See above.

> I just want behaviours such as:
>  * If you hit the "use" command while pointing at the light
>    switch, the light turns on or off.
>  * If you hit the "use" command while pointing at a door, the
>    door will only open if you own the appropriate key.
> ...that kind of thing.  Hence, most scripts would be less than a
> dozen lines of code - but there would hundreds or perhaps thousands
> of them in a typical game.
> There is also the idea of using scripts for AI - but in that case,
> I envision lots of high level functionality being placed into C/C++
> code with the scripts just linking them together in some way that's
> different for each critter in the game.  These scripts would also
> be just a few dozen lines long - with only a handful of those lines
> being executed every frame of the game.

OK. Two notes here:

1. It's OK to call compiled helper code in scripts (although that might 
depend on one's taste). I believe, however, that the design of such 
helper code could become very difficult to get right. It should be 
lightweight but at the same time flexible enough to support different 
behaviour models, at least different enough so that scripting isn't lead 
ad absurdum, because if it's too specific to the creature you're 
designing the AI for you could easily have left out the interpreter 
entirely. It's difficult to ge this right.

2. If you execute only a certain segment of the code in each game cycle 
you end up having yet another problem (it would be one for me if I ever 
took that approach in my current project): It's hard to write frame-rate 
independent scripting code, i.e. code that does calculations based on 
the elapsed time. Consider the following (pseudo-)code that could be in 
the AI of a racing simulation:

int time, lasttime;
Pos lastpos, pos; // the car's position

while(some_condition) {
   if(distance(lastpos,pos)/(time-lasttime)<desired_speed) {
   } else {

Admittedly it would not be a good driver, but it shows up a problem when 
it is interrupted for too long. Asume it's execution is paused for some 
  10 msecs just after having fetched the time and the physics simulation 
of the game calculates another cycle. The result is that the calculated 
velocity of the car is out of date and it's likely that the script takes 
the wrong action when the car's speed is near desired_speed. This leads 
to a somewhat unstable driving behaviour with at worst somewhat random 
behaviour from time to time.

This example is somehow constructed. But I bet that similar passages can 
be found for real AI code even in shooters and other types of games. You 
can't switch to non-time-based code if it's a networked game, but 
otherwise a simple step-by-step solution could bring the same results 
without those side effects.

Duh, it got late while I wrote this! I think it's better I stop here for 
now and go to bed unless I might fall asleep and crash my head into the 
keyboard :-)

Good night,

* Gregor Mueckl                 GregorMueckl@gmx.de *
*                                                   *
* The ChallengeOS project:                          *
* http://challengeos.sourceforge.net                *
* Math problems?                                    *
* Call 1-800-[(10x)(13i)^2]-[sin(xy)/2.362x].       *