Safe GetTransform() calls and general null checking.

Hey! I’ve been doing a lot of work trying to make an AI system where a bunch of AI props are spawned and moved around via Routines, and can be destroyed by the player. I’ve been able to get this functionality working somewhat, but issues eventually happen when scaled up/testing (with ~100 objects).

Consistently, GetTransform() throws issues during gameplay. I call it once every half tick of the AI (0.05 vs 0.1 Sleep in loop) to get the current position/rotation of objects for various functions that need them. I have been doing my best to make this as failsafe as possible.

This is my current setup to try and make it foolproof, though it’s still causing me issues. TriggerCancelEvent() is just a function that cancels the current “AI” routine that is happening concurrently (moving the objects around with MoveTo).

    IsValidCheck()<suspends>:void=
        loop:
            Sleep(0.05)
            if(Data.Prop.IsValid[]):
                Print("Valid Letter Check Transform")
                if(Position := FailableGetPosition[Data.Prop]):
                    set CurPosition = Position
                else:
                    Print("FAILED POSITION")
                if(Rotation := FailableGetRotation[Data.Prop]):
                    set CurRotation = Rotation
                else:
                    Print("FAILED ROTATION")
            else:
                TriggerCancelEvent()      
                return

    FailableGetPosition(Prop : creative_prop)<transacts><decides>:vector3=
        var ReturnedVector :?vector3 = false
        Transform := Data.Prop.GetTransform()
        set ReturnedVector = option{Transform.Translation}
        ReturnedVector?

    FailableGetRotation(Prop : creative_prop)<transacts><decides>:rotation=
        var ReturnedRotation :?rotation = false
        Transform := Data.Prop.GetTransform()
        set ReturnedRotation = option{Transform.Rotation}
        ReturnedRotation?

Looking at this post (Link) it seems like the issue is with GetTransform() being called on a prop that’s set to “None” but is still considered valid? Is there a simple way to check this in addition to the IsValid check? Been losing my mind over this issue because the exception seems to get triggered so arbitrarily (will test the game and it works fine for days, then testing with others it suddenly happens immediately).

Hey, so your code looks okay to me, the IsValid[] check should work, are you sure you don’t call any GetTransform() anywhere else ?

Also maybe replace this, just in case :person_shrugging:

Transform := Data.Prop.GetTransform()
Transform := Prop.GetTransform() # by this

Good catch on that typo lol. But yeah, that part of the code is the only one where I check the Transform for the moving props? But there are other instances where I get transforms of other objects in the scene, and maybe those could be what’s causing issues (though those don’t ever get destroyed).

In between posts I set up that failable wrapper function for every time I grab transforms in the code. Some were not as safely set up with Props, and after making those changes I haven’t encountered any issues so far? In the past when I’ve implemented fixes that seem to work, pretty soon the issue happens again so I’ll keep an eye out, but if the base wrapper seems safe/there aren’t underlying issues with it fingers crossed it should work.

My main paranoia is none of the error checks I’ve set up have been fired (print statements in an else statement for if the failable check fails) so I’m sort of at the mercy of whatever unknown repro steps happen that have been causing the errors to begin with. Will update this post if anything comes up I guess?

EDIT: Right after posting this I tested it and immediately got the “Unknown Error in GetTransform()” I’m in agony - debugging is really tough, because all of these checks happen on 100s of objects like, every frame or so, and the debug prints are throttling any ability to see the error when it triggers, but without print statements I can’t narrow down which section of code the error is in. Are there any better ways to debug in Verse?

Hey, no I’m not sure you can debug otherwise, your print statements (in this example) won’t fire because your failable cannot fail

    FailableGetPosition(Prop : creative_prop)<transacts><decides>:vector3=
        var ReturnedVector :?vector3 = false
        Transform := Data.Prop.GetTransform() # This uses (), not [], even if it can crash, it cannot fail :D I'm not sure but I think it works like this
        set ReturnedVector = option{Transform.Translation}
        ReturnedVector? # This returns (option{(smthThatCannotFail)})?, it's like returning smthThatCannotFail directly

Your function acts like this one

    FailableGetPosition(Prop : creative_prop):vector3= Data.Prop.GetTransform().Translation

The only fail check you’ve done is the

if(Data.Prop.IsValid[]):

But you don’t print anything in the else statement :person_shrugging:
What I usually do when I want to debug is to go from an healthy state and then uncomment/comment things chunks by chunks, you just need to Push Verse Changes each time so it’s pretty fast

Oh - I was assuming that if the GetTransform() call triggered the error I’ve been getting (Error: VerseRuntimeErrors: Unknown error in GetTransform()) then the function call would fail, taking advantage of it being failable. If that isn’t how it’s working (which seems to be the case) then I guess it’s probably a bit redundant.

From more of the debugging I’ve been doing since the initial errors I’ve since added more Print statements, including one in an else statement post “is Prop Valid”. What I found is really interesting - the error isn’t happening in the function? This is real odd because this was happening even when these lines were the only pieces of code that were actively calling “GetTransform()” at all at the time (I went through and commented out/made unreachable all other lines using that function).

The Code of the check:

    IsValidCheck()<suspends>:void=
        loop:
            Sleep(1.0)
            Print(ToString(Data.Char) + ToString(Data.Index) + " is starting!")
            if(Data.Prop.IsValid[]):
                if(Position := FailableGetPosition[Data.Prop]):
                    set CurPosition = Position
                if(Rotation := FailableGetRotation[Data.Prop]):
                    set CurRotation = Rotation
            else:
                Print(ToString(Data.Char) + ToString(Data.Index) + " Invalid?")
                TriggerCancelEvent()
                set IsDestroyed = true
                break
            Print(ToString(Data.Char) + ToString(Data.Index) + " is done!")

Output of the Error Log after a break:

LogVerse: : A1 is starting!
LogVerse: : A1 Invalid?
LogVerse: : A1 Destroyed
LogVerse: : F22 is starting!
LogVerse: : F22 is done!
LogVerse: : A4 is starting!
LogVerse: : A4 is done!
...
More starting/done spam while objects update, cuz a bunch of them are all firing at once.
...
LogVerse: : D14 is starting!
LogVerse: : D14 is done!
LogVerse: : E20 is starting!
LogVerse: : E20 is done!
LogVerse: Error: VerseRuntimeErrors: Unknown error in GetTransform()

I’ve been confused as to what’s going on, but as I’ve worked I’ve managed to get the code to be much more stable, by making changes not directly related to calls of “GetTransform()”. The IsValidCheck() line is a routine that runs alongside the AI - initializing them both via sync seems to have stabilized the code significantly, if not entirely fixed the issue?

Before, with just like 20 props moving around, it was almost 100% that the issue would trigger while breaking them, After, with 100 props, I’ve been able to destroy all of them in multiple separate tests and nothing has broken.

I don’t know why this is the case, and a part of me is wondering if GetTransform() is being utilized in the “MoveTo function”, and that’s where the break is happening? If the prop is destroyed and MoveTo is still acting on it/not cancelled in time, then it throws this error. Not sure of there’s any merit to that thought though, I’m grasping at straws lol.

Uhm, yeah really strange, I’m not sure if your solution is a good solution, maybe try to let the thing run for 1hour and see how it goes ?

My best guess would be that your IsValid[] check is made some instructions before calling the GetTransform() methods.

Because you see, Verse is sequential but a frame is a long duration for a computer.

And as you said, you seem to have multiple routines, which can happen all together (need to check this?). So what would happen is that on the same frame your IsValid[] gets called, but then your other routine destroys the prop before the call to GetTransform() is actually done.

I’m not sure about all of this, this is just a guess, also if I’m right, even calling IsValid[] just before calling GetTransform() could not always work.

Gotcha - yeah, my “solution” def seems more incidental. I can see where you’re coming from and have been worried this might be the case? I guess the thing that confuses me though is I’ve had print statements both before the IsValid, and in between the IsValid and GetTransform(), and when the error is thrown, and those aren’t triggered when the error happens. It seems like it’s happening somewhere else?

Will be doing some more checks into where issues might be coming up later, and hopefully I’m just making a silly mistake somewhere else. It’s tough to debug because to test I need to destroy the props manually (they get destroyed by players using weapons) and especially if the issue is with the timing of “destroying props in the space between lines of code” it’s hard to consistently repro. That’s more of a skill issue on my end tho - I really appreciate all the help with sorting through things!

2 Likes