GetOwningLevelInstance() method doesn't work in PIE

We are hoping to use the ULevelInstanceSubsystem::GetOwningLevelInstance() method at run-time to determine which level instance an actor is coming from. This works great in editor, but stops working in PIE. I haven’t yet tested the game outside of PIE.

If it matters, we are using world partition.

After stepping through the code, the problem appears to be that when I call the ULevelInstanceSubsystem::GetOwningLevelInstance function, it calls itself recursively on the StreamingWorld->PersistentLevel, and then that just has no streaming level. The “Persistent” level for the streaming world is the persistent level, so nothing was streamed…?

I tried to check the “Enable Streaming” checkbox in the “World Partition Setup” section of “World Settings”. This didn’t fix the problem. I thought that maybe it would cause level instances to stream in rather than maybe some sort of short-cut that the editor is taking to throw everything into the persistent level.

I’m at a loss at this point.

What we’re trying to do is tag the level instance actors that we have. When we then examine an actor that was instanced as a result of the level instance getting instanced, we want to know which level instance the actor came from by examining the tags of the owning level instance. Is this a viable thing to do?

[Attachment Removed]

Steps to Reproduce
Just throw a world together with two level instances, each instancing a level with one actor in it. When PIE starts up, can you, from the context of the actor, determine which level instance it came from? Make sure world-partition is enabled.

[Attachment Removed]

Hi!

That would be normal when using Level Instances set to the ‘Embedded’ Level Behavior. The instances that are part of those LIs are merged into the main cell grid and the LI actor is not preserved during the cell generation.

What is the intended usage of the information?

Martin

[Attachment Removed]

The difference between both modes is that the Standalone LIs will be streamed separately from the main level. We are currently discussing ways so the tag from the ALevelInstance could be passed down to the internal actors during the cell generation phase. I’ll pass more information when it becomes available. I’m attending GDC next week so I apologize in advance for the delayed answers.

[Attachment Removed]

Hello,

One thing that didn’t came up yet in the discussion is the LI Property Overrides system. It allows to change properties on a per instance basis. You could add AActor::Tags (T1\T2) that can be queried at runtime on the actors in the LI instances. You can create an Editor Utility Widget or a new menu option so that assets in the current level are automatically assigned the tag.

I was originally looking at way to extend the PerInstanceData but they have been refactored as FCombinedData in 5.8. I’m not sure that it would be a good idea to customize code that is going away…

Sorry for the delay on this one.

Martin

[Attachment Removed]

Hi Spencer,

There is something weird going on here. The steps you shared are the right ones and I had the same result when I tried. I started debugging and it now works. I’m not sure what changed when I debugged. I tried to reset my overridden LI and restart the editor and it still works.

I did some searches and found a bug that might explain. The problem would be around Blueprints that do touch the Tags property. You should be able to work around this problem by using a custom C++ actor that can reference the other actors in the level.

Regards,

Martin

[Attachment Removed]

Your change is bypassing the ParentContainerInstance weak pointer that might not be valid. We haven’t seen that problem happening before but it could explain why I was able to reproduce once (not set). I dug around to find reasons why that could happen and wasn’t successful. It does appear safe as the proper parent is known at the LI Subsystem level. I’ll keep on digging to try the source of the problem, ie why the weak ptr is not valid.

The ULevelInstancePropertyOverridePolicy export issues were fixed in version 5.8. The CL is 49174780. Exporting the entire class is a proper workaround.

You should not override CanOverrideProperty. The design of the class is that CanOverridePropertyImpl is the overridden method.

[Attachment Removed]

Hi, Martin.

Thank you for responding. I found the “Level Behavior” property, and it is indeed set to “Embedded”. The other option seems to be “Standalone”. I don’t know what these mean.

Let me try to explain our use-case of level instances…

We have a symmetric world. Rather than manually place objects so that they reflect one another across the world origin, what we do is place all our objects in a level, and then we make 2 level instances of that level, each anchored at origin, but one is rotated 180 degrees. This way, when we edit the level through one of the two level instances, the change is automatically reflected on both sides of the world, and the world is kept perfectly symmetric.

That being said, we don’t want both sides of the world to look identical to one another in every possible way. We want some stylistic differences between them. So what we’ve done is tag the level instances. One is tagged “T1”, the other “T2”. Near the end of level load, we have some code that processes all the actors that were loaded into the level. For each actor, we need to know which level instance it came from so that we can stylize it appropriately. This means figuring out which level instance it came from. If we can figure that out, then we look at the tag to know which one it is and then stylize accordingly.

Does that make sense? Are we going about all this the wrong way? We are SO close to having this working. When we are _not_ using world partition, everything works fine, because the call to GetOwningLevelInstance() works. This call, however, just doesn’t work when world partition is enabled. I asked if we could do without world partition, but my leads told me that that was out of the question.

At one point I tried to use the coordinates of the actors to determine which side of the world they came from, but this has a lot of issues. Suffice it to say, this can’t or shouldn’t be solved as a geometry problem.

At this point I’m trying my best to scour the engine code to see if there’s some sort of engine modification I can make to preserve or signify the association between an actor and what level instance it came from. So far, my efforts have not yielded anything. It is extremely non-obvious.

Is it just not possible to do this when world partition is active?

[Attachment Removed]

Martin, I just switched both our LIs to use “Standalone” instead of “Embedded” and now our stuff is working. (I am crying tears of joy…Okay, I’m not crying…but I’m getting misty.) Is this the right fix? Do you foresee any potential downsides to this? Are there negative implications of this that we perhaps might run into?

[Attachment Removed]

Thanks, Martin. No worries. I’ve been delayed myself due to ISP problems. As long as we have a solution for now, I think we’re good to go. I just hope a lead doesn’t say, “we must use embedded LIs,” and then I’ll say, “but then our stuff stops working.” We’ll see what happens. Thanks again.

[Attachment Removed]

Martin,

How was GDC? I hope you had a good time.

Say, any chance that you were able to find a way to preserve the tag information on the ALevelInstance actor so that instantiated actors can find it? If nothing else, can you point me in the right direction?

I just found out that we cannot use “streamed” behavior of level instances. We have to use the “embedded” behavior, because we’re using a third-party plugin called “Snapnet”, and it doesn’t support streamed LIs. (sigh)

Anyhow, thank you for all your help. I hope you are doing well.

Sincerely,

--Spencer

[Attachment Removed]

One idea I have at the moment is to create some meta-data that goes out with our build. I then use that meta-data instead of relying on GetOwningLevelInstance() at run-time.

[Attachment Removed]

Martin, thank you for your help. I have done some work to implement what you suggest. However, my changes in editor do not appear to be sticking whether they are made manually or procedurally. To be clear…

1) I select the level instance,

2) Right click to get the context menu,

3) Select “Level” -> “Override”,

4) Add a tag to an actor,

5) Push the save button embedded in the viewport,

6) Go back into property override mode for the same level instance,

7) Check what tags exist on the same actor,

8) Notice that the tag I had earlier edited/overridden is not there.

Do I have the wrong expectation here? Is it still overridden, but I’m just not seeing it? The only thing that makes me thing that something _did_ get overridden is that the “Reset Overrides” option appears afterward, but I can’t see the override properties in the details panel for any loaded actor.

Note that I also made my own override policy class so that I narrow what properties can be overridden. I’m only allowing tag properties for now. Maybe I’ll try just opening the flood gates to see if that fixes anything.

Thanks for any thoughts you may be able to provide.

--Spencer

[Attachment Removed]

Martin,

I made the changes you can find in the attached diff. (This is a zip file with two PNG files in it.) This gets the property overrides feature working in editor. I haven’t yet tested in game.

Admittedly, I must label this change a hack, because I’m not really sure what I’m doing, but this appears to fix the problem I was seeing. Maybe you can run with this and find the programmer/owner of the code to get a real fix?

Thanks again for all your help. Does this change look reasonable or is it a hack?

Thanks,

--Spencer

[Attachment Removed]

Diff 1.…

[Attachment Removed]

Diff 2...

[Attachment Removed]

I should add that I also needed to add “ENGINE_API” to the “ULevelInstancePropertyOverridePolicy” class as well as remove the “final” keyword on the “CanOverrideProperty” virtual method. Otherwise, I would get linker errors when I tried to derive from this class.

[Attachment Removed]