[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Scripting in games.
Gregor Mückl wrote:
>>> * OPCODE_LINE_NUMBER is not really necessary, is it?
<snip>
>> That opcode is *only* generated if you turn on the PSL_TRACE shell
>> variable. It's mostly there to help me debug the interpreter - but I
>> may someday wire it into a PSL debugger of some kind.
>
> Having line number information is quite useful for debugging scripts.
> However, such an instruction would slow down the script with debugging
> turned on and this may be quite counter-productive if you want to track
> down a problem that is actually related to incorrect timing inside your
> scripts (it'd be like those nasty cases where the bug just won't show up
> inside gdb...).
Yes - but it's hard to know what else to do. I think it's actually
rather unlikely that timing issues would show up in scripts. After all,
the execution path of the script is deterministic - and since the scheduler
is also deterministic (unless you go out of your way to make it otherwise),
I don't think you'll see race conditions between scripts that would run
any differently if the scripts run slightly more slowly.
Well - I guess it's not impossible - but right now, I'm thinking mostly
in terms of *me* debugging PSL rather than end users debugging their
scripts.
> But because the instructions you store are CISC-like and
> embedded in a bytestream it's quite hard to find a way to encode the
> line number without hitting performance during normal execution.
Yes - exactly.
>>> I like the ability to step the interpreter instruction by instruction.
>>
>> That's the single reason why I wrote PSL rather than picking up (say)
>> Python.
>>
>
> That's why I wanted my own interpreter as well ;). It's been a result of
> our last discussion of game scripting, I think.
Yes.
>> There is a special PSL reserved word 'pause' - which allows you to
>> implement
>> non-preemptive scheduling by calling the 'step' function in a tight loop
>> until it hits that pause instruction.
>
> Hm... could the interpreter be forced to pause state by callback
> functions as well?
Certainly, it *could* - but it's not strictly necessary because you
can say:
int pause_the_script = FALSE ;
pslValue *callback_function_that_pauses ( ... )
{
...
pause_the_script = TRUE ;
}
...then in your script-running-code:
while ( ! pause_the_script &&
pslprogram -> step () != PSL_PROGRAM_PAUSE )
...
...so the whole pause mechanism happens outside of the PSL interpreter and is
understood only between the callback function and the loop in which 'step' is
called.
Granted this is a little inelegant - and I've been thinking about doing something
better - but what there is now is technically enough to do what you want.
What is perhaps more useful STILL is some kind of "pause-until-some-condition-is-met"
so that scripts can block waiting for some event to happen. This will be useful in
reducing the amount of CPU time burned interpreting scripts that say:
while ( ! some_condition )
pause ;
...so then we need some kind of event mechanism - which comes back to (I'm beginning
to believe) a system in which messages are passed to scripts - and they can either
pause_for_one_frame or pause_until_a_message_arrives. That could also be implemented
as just simply: pause_until_one_of_a_specified_list_of_messages_arrives - with the
application sending a 'one frame tick passed' message to all scripts just in case they
want to run every frame.
>> However, you can easily implement a scheme that runs a set number of
>> opcodes
>> or for a set amount of time - or which runs important scripts faster than
>> non-important ones...that's *entirely* up to the application - which was
>> THE major reason for not just picking up a standard scripting language.
>>
> I feel quite uncomfortable with an interpreter that you can only call
> and wait until it returns - if it returns at all (greetings to all
> infinite loops out there!). This way stable frame rates and accurate
> timing go down the drain very fast.
Right - my feelings exactly. It's pretty easy with PSL to limit the
script to only consuming X amount of CPU time - or (more easily) just
counting the number of PSL byte-codes the script ran and pre-empting it
if it uses too many. That allows programs to get stuck in infinite loops
without unduly crippling the game.
You *could* start implementing really fancy priority schemes - penalising
programs that took too long to run last frame - so you cut them off
sooner this frame. If you did that then a script that was stuck in a
loop would eventually stop running at all.
However, I'm inclined to stick with something really simple until I
see a real need to do better.
Whatever you decide, having an interpreter that runs just one bytecode
at a time gives the game programmer the power.
> But with the ability to accurately schedule multiple VMs the situation
> improves vastly. Or at least you've got one appology less for jittery
> framerates ;).
:-)
>> PSL is 50 to 100 times slower than C++ so
>> it's desirable to take as many common operations out of PSL and into C++
>> as is reasonably possible.
>>
>
> Right. There are actually two concequences of this rule:
>
> 1. Provide as many C++ routines in your standard library as possible.
Yes...although there is a risk of making the standard library too
hard to learn.
> 2. Don't hesitate to add new VM opcodes to simplify and shorten the
> bytecode. The shorter the compiled code is the faster it will run.
Yes - although I rather wanted to stick with a single byte opcode - so
there *is* a limit there. Right now, there are only 64 instructions - so
we have a long way to go.
> The third concequence, providing user-defined callbacks, is already there.
Indeed.
> After looking at my game engine's hand-crafted makefiles I find that I
> don't need to care about yet another dependency ;). Already there is
> Python (to be kicked soon), SDL and OpenGL with it's own set of
> dependencies here and Ogg, Vorbis and lzo still to come when the
> corresponding code is integrated (prototyped components are already
> there). So one other dependency doesn't actually change much.
Well, in your case, perhaps that's acceptable. However, it's been
shown time after time that programs (especially games) with large
numbers of dependancies are a real turnoff for users. This may
not be an issue for commercial software - but still, minimising
dependancies has a HUGE impact on the number of 'customer service'
emails you'll have to field.
> I must admit that I would not have found a way to parse expressions.
> That has given me serious headaches and hasn't been that much fun to me.
The trick is to rely heavily on recursion and to structure the functions
in the parser according to the priority of each operator. The code in
pslExpression is actually very easy to understand.
I imagine there are more efficient ways to generate code - but this
works and is clean. One *major* deficiency is that if you say:
x = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 ;
...it'll actually generate 10 'push_constant' instructions and ten 'add'
instructions instead of just saying:
x = 55 ;
...I'll need to fix that one day - and I'm not looking forward to it!
Right now, I havn't decided whether to try to write a peephole optimiser
or whether to re-jigger the compiler to calculate constant expressions
correctly.
>> If you passionately needed to fork the code base, you could (it's
>> released under LGPL) - but I really don't see the need.
>
> Me neither at the moment. But I'll probably fork an own version off just
> before I release my game engine to make compilation of this beast a bit
> easier.
The beauty of (L)GPL is that you know you can always do that if you need to.
----------------------------- Steve Baker -------------------------------
Mail : <sjbaker1@airmail.net> WorkMail: <sjbaker@link.com>
URLs : http://www.sjbaker.org
http://plib.sf.net http://tuxaqfh.sf.net http://tuxkart.sf.net
http://prettypoly.sf.net http://freeglut.sf.net
http://toobular.sf.net http://lodestone.sf.net