[UE5, Networking] Should this go into the GameMode?

So far I’ve been working on a lot of things unrelated to networking and level building, but I want to change that.

Simplified, this is what I learnt about these classes:

gamemode (Server)

  • Holds private game rules.
  • Pulls and verifies replicated data from the GameState.
  • When a rule is met to switch to the next game “stage” it will instruct the GameState to do so.

gamestate (Server + Client, replicated)

  • Tracks and replicates data relevant to the GameMode’s rules such as team scores, match timer.
  • Holds stages (Lobby, start of match, end of match, score board).

I find myself questioning where the level related logic has to go. Think of Left 4 Dead which has a “Director” class which manages when and where zombies will spawn in a level. This Director would be private, server side only (GameMode?) but it’s spawns are replicated (Spawn handling in GameState stage?).

Makes sense. But what about zombie spawn positions? Zombie classes? They differ per level. Does this make sense?:

  • GameMode provides GameState with data assets (zombie classes, spawn positions)
  • GameMode instructs GameState to start “match” and timer.
  • GameState keeps track of timer and replicates it to others.
  • GameMode pulls timer data from GameState, after 2 minutes (private rule) it instructs GameState to spawn zombies of class A to position X, which automatically replicates them to all players.

What code do we do in the level? So far I have written absolutely 0 code in any of my levels, I just use them to place actors, meshes and other visuals, not logic. If for ease of editing you would add “spawn points” of zombies to a level and pass that data to the GameMode or GameState you create a circle between classes, this can’t be right.

Also I saw some awkward implementations of two way client server communication with RPC I don’t currently see the value of. It seems wrong if you at any point have to check your system’s authority within an Actor to switch between code, or have to validate client requests where the server should be leading in the first place?

References:

Networking Overview for Unreal Engine | Unreal Engine 5.1 Documentation

Multiplayer Network Compendium | An Unreal Engine Blog by Cedric Neukirchen

What should I do in GameMode, GameState, and PlayerState? - #2 by Raildex

Network | Unreal Engine Community Wiki

Reddit - Dive into anything

I see level blueprints as a beginner tool for the most part. Higher & more complex logic should probably be stored in manager classes.

In theory you could have one use triggers setup to specific elements placed in the world, but babysitting the settings from level to level would be tedious. I’d probably just build an editor widget toll for authoring level elements in-editor.

Just remember that GameMode will be wiped on switching levels.
In theory you could keep rules specific for that game mode. Like a capture the flag, what conditions are needed to win a match. Then replicated info could be passed on to agamestate which would replicate it to other players.

From the docs

The Game Mode is not replicated to any remote clients that join in a multiplayer game; it exists only on the server

1 Like

So does GameState, and it’s perfect that both are wiped. It seems that the GameMode must be as small as possible then, containing only the rules and controlling the GameState by the rules. I think I should be using the GameState to perform the actual game logic then by injecting manager classes into it. This would make it easy for clients to retrieve data from the GameState or its registered managers. A manager could be the zombie spawner, to which every spawned zombie is registered. A client could then do GameState->GetSpawnManager()->GetPawns() and filter by zombie type.

Same for actors placed in a level, for example a AZombieSpawner. OnBeginPlay it would do GameState->GetSpawnManager->RegisterSpawnPoint(Self) after which point it communicates with the Gamestate’s spawn manager.

This seems to be right to me and it keeps the GameMode and GameState to perform their intended functions and nothing else. Am I missing something else?

If you need persistence between levels then remember to use Game Instance for that. It is up and running as long as the game instance lasts, so between level / map loads / menus etc.

1 Like

Yes I know about the GameInstance :slight_smile: I use the game instance subsystems all the time. They are not as useful for gameplay as the other classes we mentioned earlier because they persist. I use them as managers mostly for settings or plugin / editor functionality.

Well it looks like you’ve got a grasp on where to go next with this :slight_smile: Hope it pans out well.

1 Like

Going to make a prototype with this :slight_smile: . I’ll also need to test the best way to inject manager classes into the GameState. First thought is that it’s either AActorComponent or UObject, AActorComponent seems the most logical solution.

How do you replicate a uobject? - #3 by Asusralis

Component Replication | Unreal Engine 4.27 Documentation

Yeah they should have less overhead not having transform nodes. UObject might be too low level.

1 Like

This is getting interesting :slight_smile: Components do not support the construction script, even if you’d make one of your own and call them at the time of their parent actor’s construction script. This means you can’t collect actors placed in the editor before BeginPlay runs. If you happen to require them on BeginPlay of the manager class you’d need to scan all actors in the level on BeginPlay, before the actors themselves attempt their own registration (as I mentioned). That was not exactly what I was hoping for. Maybe I’ll be forced to dump the idea of components and just use the GameState’s construction script and dump all code in there. Not much of a choice.

I think it has more to do with the fact that game mode is very specific. It doesn’t have a standard begin play from within c++ if my memory serves me well :stuck_out_tongue:

In game mode base

	/** Transitions to calls BeginPlay on actors. */
	UFUNCTION(BlueprintCallable, Category=Game)
	virtual void StartPlay();

There is no begin play and if you add it to your derived class it will most likely not fire.

You can try calling

AYourClass::AYourClass(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

not sure if ObjectInitializer.DoNotCreateDefaultSubobject(TEXT("Sprite")) is needed in the super for derived, doubt it.

You could have your components implement an interface with a startup phase and just call that.

Edit: Though creating a new c++ actor component I got

// Sets default values for this component's properties
UMyActorComponentNew::UMyActorComponentNew()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}

1 Like

I’m starting to think this as well. Every mode (both GameMode and GameState) is so specific that components are easily too generic to work with for gameplay purposes. Such as scripted events in Resident Evil “Open the door, a panel moves, 2 guys with rocket launchers spawn and a cutscene plays” if that is going to be managed in the GameState then components are of little use.

This is all a learning experience, a work in progress, so I’ll just post what I find here if it’s useful info for others.
I found myself removing the components and writing the logic in the GameState. I’m fine with it :slightly_smiling_face: . My initial idea of running the construction script on the GameState / GameState manager components to pull data from the level was flawed. I realized that if you delete / place an actor in a level manually it will not re run the construction script of the GameState. Nothing like that seems to exist in Blueprint which makes it a less powerful tool than I wanted.

That is a bit confusing, GameModeBase does have BeginPlay in blueprint that works just fine.

ConstructionScript is not the c++ constructor :slight_smile: .

Luckily I don’t have to deal much with manually placed things in the current project, I’m not a fan of configuring all kinds of stuff manually in a level. If I have to depend on BeginPlay for all manually placed actors to register themselves to a manager class then the manager will never know when all registrations are “done”, so it will have to start regardless of any registrations. This could be fragile when you have to make sure that registrations are done before you move from stage A to stage B which depends on them. Optimally the manager would spawn them in the first place, no issue there besides that it will make a designer or two furious :slight_smile: . Worst case is that you need to scan the entire level per manager for those actors on BeginPlay AND have those actors register themselves. That’s what I’m trying to avoid now, all else looks beautiful. Currently it looks like this if I’d implement a soccer game:

  • GameMode holds soccer rules.
  • GameInstance holds stages (match, break time, replay, scoreboard).
  • GameInstance OnBeginPlay scans for manually placed soccer characters, finds it got all its dependencies required to start a match, starts the match.
  • SoccerCharacter registers (unique) itself on BeginPlay at any point to the GameInstance.

In that situation, the GameInstance knows if it’s OK or invalid at the moment of its own BeginPlay, any later registrations are either extra or procedural.

It does require the “GetAllActorsOf…” wherever you need to set up a manager, no way to avoid it I guess. So be it :slight_smile:

You could expose the managers with TSubclassOf properties. Set their class there and spawn an instance in the startup process.

1 Like

About 20 youtube videos and 50 websites further I found that many people contradict eachother on what should go where. I’m a perfectionist, trying to get a clear understanding and I think what I said in my initial post, based on the references listed there, was wrong.

This makes a lot of sense, quoting a reddit user:

Reddit - Dive into anything

The Game logic, as far as I'm aware, should be stored in Game Mode. Game Mode is used for that because it is server-only and cannot be edited by the client, making it more resistant to cheating. Game State should hold variables like the score, but Game Mode should tell Game State what those variables should be and when they should change. 

If you are not doing any two way server - client instructions and clients simply can’t access the GameMode, this sounds a lot better than putting logic in the GameState.

Somewhat similar to MVC in web development, the GameState is the public Model, containing game related data shared to all players. It contains mostly / only data. The GameMode is the Controller, acting on a changing score in the GameState, spawning actors, executing game events. It contains the private logic and no / almost no stored data.

My initial posts looks like nonsense now :slight_smile:

Also in no situation should a player be allowed to do something like GameState->GetSomeManager()->DoStuff() because this should be server side only, it should be on the GameMode.

The idea is that the GameMode, being the server, performs all logic (such as scoring in soccer) and sets a score property on the GameState, which the GameState replicates down to players.


 3.6.1  GameMode

- Exists only on the server.
- Does not replicate.
- Acts similar to a Controller in MVC.
- Holds private game rules and all game logic, little to no data.
- Instructs a GameState when a rule is met.
- Writes game related public data to the GameState if relevant for all clients.

 3.6.2  GameState

- Exists on the server and all clients.
- Replicates down from server to clients.
- Acts similar to a Model in MVC.
- Holds public game related data relevant to all clients, little to no logic.
- Held data examples are match timer, team score, winning team, current cards in deck.
- Can be instructed by the GameMode to switch stage types such as lobby, start of match, end of match, score board.

Oh and I found this

It did successfully output a string with GEngine->AddOnScreenDebugMessage from game mode at the start of the game :wink:

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.