Replicated UProperty gets garbage collected

I have a UProperty in my custom PlayerController declared as

UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnCommanderChanged, Category = "Gameplay")
ACommander* Commander = nullptr;

Everything works fine if I just run a single game instance but once I run 2 PIE instances after a few minutes the game crashes from a null reference. The code it crashes on is run every frame, so it’s definitely set at some point, it just mysteriously goes null after a while. I put a data breakpoint on the address of Commander and found it gets set to null at GarbageCollection.cpp:786.

Other possibly relevant information:

  • The ACommander is spawned in BeginPlay of my GameMode
  • The PlayerController has a setter that I call from PostLogin for remote clients
  • ACommander isn’t attached/a child of my PlayerController, I just want to keep a reference to it directly in the controller

I’m hesitant to call this a bug since UE4’s GC seems to be pretty robust but I’m not sure what else it could be, unless I’m just fundamentally misunderstanding what the UPROPERTY specifier is for or something like that.

Hey -

What type of class is ACommander? What is the code where the crash occurs? Can you post the callstack and log files from the crash for more information? Additionally, can you provide a sample project with this issue occurring or provide reproduction steps I can use to test this on my machine?

Cheers

ACommander is inherited from AActor, but the actual actor I spawn is a BP inherited from ACommander. The code it was crashing on was just me trying to call a function on it. I was able to create a small repro case for this, put it on my dropbox Dropbox - File Deleted
I’ve been running it under the DebugGameEditor configuration.From the Play dropdown I change the number of players to 2 then choose “New Editor Window (PIE)”. After it runs for a few minutes it should fail an assertion I put in AReplicationTestPlayerController::Tick.

I’ve also attached a text file with the Stack Traces and the Log from the repro case. There are two stack traces, the first one is the one I get when the data breakpoint is hit and the second is from where the assertion failed.

In the course of making the repro case I found that my issue didn’t occur until I had both created a component of some sort in the object and started spawning a BP class inherited from my C++ class.

Thanks.

Hey -

You’re correct that the issue lies with the component being created. If you take the “Component = CreateDefaultSubobject” line out of the constructor for ACommander, and create the component in BeginPlay instead, it stops crashing. Here is the syntax of what it would look like in BeginPlay:

void ACommander::BeginPlay()
{
     Super::BeginPlay();

     MyComponent = ConstructObject<UProjectileMovementComponent>(UProjectileMovementComponent::StaticClass(), this, FName(TEXT("Movement")));
}

Cheers

This worked in my repro case but I’m still getting the same behavior in the game I’m working on; it takes longer but still crashes. I spent most of yesterday modifying my repro case trying to get it to crash. I left it running over night but it was still up when I got in. At this point I’m out of ideas but maybe you can think of something else to try?

Is the callstack in the Crash Reporter window the same as before making these changes in your original project? Can you provide an updated callstack and log file from your project rather than the sample project?

It is the same callstack as before (first is when the data breakpoint is hit, second is the actual crash callstack). And here’s the log. In case it’s useful, the exception error I get is “Exception thrown at 0x00007FFD83AD5530 (UE4Editor-Engine.dll) in UE4Editor.exe: 0xC0000005: Access violation reading location 0x00000000000001FC.”

I also had the idea to upgrade to 4.11 Preview 8 to see if the new version fixed it but no luck there either.

Looking at this stacktrace file; while the breakpoint is the same, the callstack you’re receiving is different than the first stacktrace you sent. This leads me to believe that the crash you’re receiving now is not the same as the original. Especially since this callstack mentions the UMiniMapCanvas::DrawMiniMap() function, it sounds like there is a problem with how the mini map is being displayed. You can test this by removing the functionality where the mini map is being added/shown on the screen and if the crash still occurs.

Oh, sorry, guess I was a little unclear on this, the stack trace I uploaded last week was from the sample I sent you; the one I uploaded on Tuesday is from my actual game project. Just to make sure I reverted the project back to last Wednesday, ran it until the crash, then diffed that stack trace with the one from Tuesday, no differences found.

I also found today that EndPlay is being called on my ACommander with EEndPlayReason::Destroyed being passed in, despite me never destroying it, which eventually leads to the garbage collection that I’m seeing. Grabbed the stack trace for that too as well. It seems that the network layer is cleaning it up, but I’m not really sure why since it still exists on the server. But maybe this is normal behavior and I need to rethink how I’m doing things.

I could just check for null in the DrawMiniMap function, however the ACommander I store in the PlayerController should never be null and if it is then that means something has gone wrong, so I’d rather it crash as early as possible so I can hopefully figure out why than just fail silently.

After thinking about why the network layer might be destroying my actor, I decided to try setting bAlwaysRelevant to true in its constructor and that seems to have fixed it. It seems that our map is large enough that, as I was wandering around waiting for it to happen, I would always get far enough away from where it spawns that it would be marked as no longer relevant, which explains everything else that was happening.

The only thing I’m confused by is why none of my other classes seem to exhibit this behavior. I checked and they’re not set to always relevant but my commander was the only thing that was getting cleaned up by the network layer.

Thanks for helping as I attempted to figure this out.