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.
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
}
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
}
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
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;
}
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
//<...>
}