Advertisement

Callback for changing script property's value

Started by March 25, 2021 01:01 AM
4 comments, last by WitchLord 3 years, 8 months ago

Hello,

I'm currently in need of some way of knowing that script member value changed. I know this can be done in scripts via virtual properties (just use setter and do whatever logic is needed there), but my use cases are more low level and actually need to be handled on C++ side - think of transparent implementation of dirty flag, or having a requirement that we need to call some callback whenever value changes - implementing this in all setters will be error prone and very verbose.

What I'm thinking is some ability to be able to register some callback whenever a property setter is invoked (so basically some sort of implicit virtual property with a setter that's pass-through and just calls C++ func?). This could be maybe turned off/on on per property basis even, to give fine grained control. This callback will be on C++ side, and containing information like: type, current value, new value. With this implementing script-wide dirty flag would be trivial - I could use meta-info like [dirty] on properties and then use this callback to figure out if the dirty flag for variable should be toggled. It would make network replication code writing a breeze.

I can't figure out if there is some way to hook into property execution for script declared variables. If I could wrap a specific property setter with another call that would be enough for me to control the setting of value, then calling original setter - but I have no idea that's even possible for script members.

Anyone had such use case and found some elegant solution maybe?


Where are we and when are we and who are we?
How many people in how many places at how many times?

In theory you might be able to do it using the debugger interface, e.g. set the line callback and check each time if any variable has changed. Of course this would kill performance, but without changes to the library this is the only thing I can think of (except for explicitly writing setters that would set the dirty flag).

To have something more performative the compiler would have to add explicit calls to the callback from the VM whenever it is setting a value on a member that has been flagged for this callback.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement

WitchLord said:

To have something more performative the compiler would have to add explicit calls to the callback from the VM whenever it is setting a value on a member that has been flagged for this callback.

Could you maybe point to some parts of the code that would have to be touched for this to happen? I can try experiment, but I tried to find how setters and getters are invoked when we for example do assignment to a virtual property, and could not locate it :/ I know on the basic level virtual properties are just two functions, but not sure how this wires to the actual property that is being read/written to. And how to extend it to normal properties too.

Some links to the GitHub with files/line numbers would be enough to get me started - I will try to look myself but I don't know much about AS internals, so this would set me up to speed ?

I also assume we have several options - like you mentioned we can insert something whenever assignment to such variable happens (that would happen on compiler level), or we can have it resolved on runtime (so it won't be compile time, but more flexible) - for this I guess it would have to hook into actual read/write of the variable?


Where are we and when are we and who are we?
How many people in how many places at how many times?

Ok think I found it:
https://github.com/IngwiePhoenix/AngelScript/blob/50229f5281b40dafa22b7c135a499798f39ddb3f/sdk/angelscript/source/as_compiler.cpp#L8261

Basically if I wanted to be super-intrusive I think I'd need to insert the callback into bytecode somewhere here? I'll try to experiment, though it may explode since it's my first contact with the bytecode directly ? The flow I think would be:

  • Save current value as temporary old_value before performing assignment
  • Perform assignment
  • Invoke function call with (old_value, current_value, type)

Sounds simple but I bet there is dozens of edge cases, various paths (whether it's primitive, handle, value object etc.) not to mention the lifetime of the values (though I assume value types will be copied so it should be safe, reference would be refcounted), but still sounds like major endeavour. I'll think whether manually handling this wouldn't just be easier hah.


Where are we and when are we and who are we?
How many people in how many places at how many times?

yes, that is basically it.

It may help to examine the bytecode generated from a script when you compile the library with AS_DEBUG defined.

Then you can see how the bytecode for a function call with arguments of varied types should be formed.

The debug output is generated in a folder AS_DEBUG under the working directory when compiling scripts. There will be one file per function.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement