UE4, C++ : PlayerControllerList in GWorld is empty

Hello
I am trying to understand, when, and why PlayerControllerList in GWorld is Empty. I’ve got scenario, when it should have minimum 1 PlayerController, but for few seconds this array is null ( by calling GWorld->GetFirstPlayerController() ).

In short:

  • I’ve created dedicated server with 1 Player from PIE
  • On PlayerController Tick, when two conditions are met ( !GWorld->IsServer() & GWorld->GetFirstPlayerController() I am performing HttpRequest - so it should be called only on Clients, that have attached PlayerController to GWorld
  • When I get HttpResponse, for 2-3 seconds GWorld->GetFirstPlayerController() returns NULL
  • I checked: AddController was caled only once for server, and for client, RemoveController was not called at all, so in theory PlayerControllerList should have 1 element on Client and on Server whole time

I ran into the same issue and came across this thread.

I see this warning at the declaration of GWorld:

/** Global UWorld pointer. Use of this pointer should be avoided whenever possible. */
extern ENGINE_API class UWorldProxy GWorld;

Looks like GWorld is getting assigned all over the place, and might be unreliable in the context of multiple worlds existing. I noticed this method within NUTUtil.h

	static inline UWorld* GetPrimaryWorld()
	{
		UWorld* ReturnVal = NULL;

		if (GEngine != NULL)
		{
			for (auto It=GEngine->GetWorldContexts().CreateConstIterator(); It; ++It)
			{
				const FWorldContext& Context = *It;

				if ((Context.WorldType == EWorldType::Game || Context.WorldType == EWorldType::PIE) && Context.World())
				{
					ReturnVal = Context.World();
					break;
				}
			}
		}

		return ReturnVal;
	}

I copied that as a static utility function on our gameside, also checking for ‘OwningGameInstance’ being nullptr, as well, since I noticed that it was in this empty PlayerControllerList situation.

UPDATE: I realized that this is a dangerous thing to do within PIE with multiplayer because this static function can still return the wrong world in a situation where there are multiple game instances.

The safest thing to do for your static functions is to make sure you get a UObject* selfReference passed in so that you can call GetWorld() from it, making sure that any class that might be passed in has a valid return value for ::GetWorld(). static BlueprintCallable functions will need the Blueprint scripter to pass in a self reference to fulfill this requirement.

Note that ‘GetWorldFromContextObject’ is not safe to use as a catch-all, as it is expensive and may result in stack overflow.