SubSystem only on server

I’ve made a UGameInstanceSubsystem subsystem. The question is the same for all subsystem. How can I check that I’m on the dedicated server in UGameInstanceSubsystem::ShouldCreateSubsystem(UObject* Outer) to create stuff only on the server?

Quoted from here:

When these subsystems are initialized, it is also possible that certain values may not be available as expected for server/client instances. For example, a common problem is that the instance’s NetMode may not be correct when certain subsystems are initialized. This can cause issues if a subsystem has code that should only be run on a server or on a client during initialization. Furthermore, this makes it very difficult to control whether a subsystem is only spawned on a client or server, as during initialization there is not a good way to tell which one the instance will be.

Thus, I would destroy the subsystem on server at a later point in time when there is a valid world and I can check IsNetMode(NM_DedicatedServer).

Good idea but as far I understand lifecycle of my subsystem is deeply linked to its inheritance. Is it simply possible to destroy a subsystem?

Correct, but technically you can destroy it (see FSubsystemCollectionBase::Deinitialize for reference). A neat approach was posted by siliex#1214 in Slackers:

bool UMyServerWorldSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
    if (!Super::ShouldCreateSubsystem(Outer))
    {
        return false;
    }

    UWorld* World = Cast<UWorld>(Outer);
    return World->GetNetMode() < NM_Client; // will be created in standalone, dedicated servers, and listen servers
}

So he doesn’t destroy anything, but simply create wherever needed. This 100% works great for a WorldSubsystem, though as stated in the link I sent before, this could be a problem for other types of subsystems.

1 Like

If you are a Game subsystem (you derive from UGameInstanceSubsystem), then ShouldCreateSubsystem will get invoked before the World exists.

Instead, you can query the GameInstance’s WorldContext to see if it is running on a dedicated server via RunAsDedicated. Then use that to decide if you should create your subsystem or not.

bool UMyGameSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
	// Call super
	if (!Super::ShouldCreateSubsystem(Outer))
	{
		return false;
	}

	// Downcast Outer to a game instance
	const auto Game = static_cast<UGameInstance*>(Outer);

	// If our world context is running as a dedicated server, dont create our instance
	bool ShouldCreate = !Game->GetWorldContext()->RunAsDedicated;

	// Return if we should create this subsystem instance
	return ShouldCreate
}
1 Like

Dedicated server is built with its own target and has a dedicated macro so you can switch on that

bool UMyGameSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
#if UE_SERVER
    return Super::ShouldCreateSubsystem(Outer);
#else
    return false;
#endif
}
1 Like

Hey all, I figured I’d add my solution to this thread. This is for a UWorldSubsystem. It runs only on Dedicated, Standalone, and ListenServer.

Be sure to compile outside the Editor and not use Live Coding for this :slight_smile:

Works in 5.3.1

bool UMyWorldSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
    // Call super
    if (!Super::ShouldCreateSubsystem(Outer))
    {
        return false;
    }
    
    UWorld* World = Cast<UWorld>(Outer);
    TEnumAsByte<EWorldType::Type> type = World->WorldType;
    bool IsValidType = type != EWorldType::Editor && type != EWorldType::EditorPreview && type != EWorldType::None;    
    if (IsValidType) return World->GetNetMode() < NM_Client;
    return false;
}
2 Likes

I’ll also add on this: on 5.3 the proposed solution for UWorldSubsystem isn’t working for me in case of pie-“seamless” traveling. First level loaded fine, but on travel client return the server’s netmode.

Hence

is a way to go for me:

bool UMyWorldSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
	return true;
}

void UMyWorldSubsystem::OnWorldBeginPlay(UWorld& InWorld)
{
	//doing server-only subsystem
	if (InWorld.GetNetMode() == NM_Client)
		return;

	//Subsystem is still exist, but isn't initialized on clients. May also wish to disable Tick() if your ss is tickable

	//==========================
	//Actual initialization goes below

	//<...>
}