Prevent assigning non-refcounted references

Started by
4 comments, last by noizex 6 years ago

Hi,

I have a C++ class that may be retrieved inside script object, but I'd like to make it only allow usage when retrieved by method that returns it, and do not allow storing this reference in some variable. Example:


# Here I have script property that proxies the retrieval to C++ side object obj
Transform@ transform
{
   get { return obj.getTransform() }  
}

# I want these scenarios to be:
# allowed

obj.transform.position += vec3(1,2,3);
someMethod(obj.transform)

# disallowed

Transform@ t;
@t = obj.transform;

I tried using opAssign, some asOBJ_NOHANDLE but seems like these won't do what I need? Is it even possible? Thing is that I can ensure Transform@ comes valid from C++ side when it's retrieved through getTransform(), but if it gets stored for longer it may crash the application - there is slim chance for this but it's not guaranteed to exist for a whole lifetime of script object. I could make it refcounted, but making it for a purpose of sharing it inside script, where I don't actually want this to be stored anywhere, doesn't seem to make sense. Does anything in AS exists that would allow me prevent such behaviour?

Thanks,
noizex


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

asOBJ_NOHANDLE would prevent the script from being able to store a reference to the object. But it also prevents you from forwarding the reference further to another function call (as that implicitly requires storing a reference temporarily).

Currently there is no way of doing what you want exactly the way you want it.

Does it have to be a reference type? Can't the transform type be a value type instead? I assume it represents a 4x4 float matrix. 

 

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

Thanks for the answer, Andreas! I wasn't sure I understand the limitations of asOBJ_NOHANDLE correctly.

Unfortunately it would not make much sense to make it value type - as Transform is just one example of such object (and even it has more members: position / orientation / scale), there are more like Mesh, Rigidbody, Animator - always a single instance per game object. The idea was that C++ has low-level, non-logic C++ "components" for complex stuff (animations, physics), that may or may not be present on the object. Their presence depends on the purpose of the object: if it's a static object with no animations it won't have these available and they would be null, but if it's some monster it would have animator, transform, rigidbody and so on.  

I also thought it would be fancy to expose them as props so you can refer to them as obj.animator or obj.transform - but it's a single instance and its lifetime should not really depend on ref count but be explicitly controlled by the owning object (removing component is possible if it's no longer needed - say, game object reaches an animation after which it just stays inanimate and does not require a full animator component - for example upon death). But if I say "remove it" I want it to be removed, not kept alive because some script writer decided to store reference to that component somewhere in a script, which should not really happen. I could probably disconnect it from world & object, so even if the instance exists it no longer affects the object, but it may cause a lot of "dangling" components if people are not careful and store the reference somewhere for whole object's lifetime - it would defeat the purpose of removing the component if no longer in acive use.

If preventing storing reference or nullifying them all once such component is removed is not an option, I think I will go for less direct method of access to the components that should work - instead of exposing whole component type to script side, I will wrap it in functions - instead of obj.transform.position (where transform is Transform@ and position is vec3 member of it) I could have obj.getPosition(), obj.getRotation()obj.setPosition() and so on. Each such method will be a wrapper:


vec3 getPosition(Object* obj) 
{
    asIScriptContext *ctx = asGetActiveContext();
    Transform* transform = obj->getComponent<Transform>();
    if (transform) 
    {
       return transform->getPosition();
    }
    else
    {
       ctx->SetException("Calling getPosition() on an object without Transform!");
    }
}

This won't "scope" these properties nicely in a way that could be achieved with props - obj.transform.position - but I think I can still wrap some of them at least to obj.position where applicable. And maybe even, instead of throwing script exception where no component is found, add it automatically when it's not found, assuming that calling some method using this component means that the user actually needs it.

It also has a positive side, which is not needing to register types for all these components, just wrapper methods for their public interface. I was hoping that there may be some tricky way to achieve what I wanted initially, but if there is no such way right now it's not deal breaker.


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

asOBJ_NOHANDLE is mostly intended to be used with singletons, where you would register the singleton as a global property. That would still allow the script to access the singleton through the global property, but not store any further references to it.

It is a very complex subject to expose objects to the script to allow the script to work with it but still have the application keep full control of the lifetime. Basically two options exist, either the application keep track of all references (possibly by enumerating all potential locations where it can be stored, which you can imagine would become an extremely costly operation), or you create a proxy object, where the script would not receive a reference to the actual object, but instead only to the proxy object. This proxy object can then have a way to sever the link to the real object in an easy manner when the real object has to be destroyed.

The latter option is how I do it in my own prototype engine (see gameobjectlink.h in the source code).

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

Thanks for sharing, I'll have a look at the code and use it as a guide. Regarding the issue discussed, I'll go for exposing only methods and not the whole type - similar to how you do, so all the attributes will be on the game object, not accessed through specific classes like Transform, Rigidbody etc. This should simplify the situation and avoid problem with ownership. 


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

This topic is closed to new replies.

Advertisement