Apologies if I am missing something obvious, but I am trying to architect my game and in my experience I have had something like an OnlineManager to manage things like connections, notifications of server going away, players coming online and so forth. I am using an external system to handle much of the work that an online server would handle, but I have to pump/update it periodically to make sure data gets sent and consumed in a timely manner. The documentation suggests using an actor’s tick() method, which I think would work, but I don’t really see my OnlineManager to be an actor.
I have looked at the GameInstance and GameInstanceSubsystem, but these do not have a built in tick or update method, though they have the lifetime I am looking for (create on game start, destroy on game end). I could make it am FTickableObject, but then I am not sure who should own it. I could subclass GameInstance and then create it in there, but again this doesn’t quite feel right.
Ultimately, I would like a system that I can tick at a specific rate, and will have the lifetime similar to GameInstance and ideally be owned by that class as well. Perhaps a GameInstanceSubsystem that creates my OnlineManager, then schedules a timer to update it on the cadence I want.
Does anyone have a much better approach or is there some system or pattern in Unreal that I am not aware of that would make this trivial?
The UE Design Pattern that I personally would use is this
GameState BP
An Actor Class that has a BP, which has a pointer stored in GameState
If you really want to do something in GameInstance, you can now just get the GameState and get your reference very easily, and if the GameState itself is valid and alive, your Subsystem Actor will be valid and alive too.
Then
In GameState, create the Actor in BeginPlay. I do this in GameState because GameInstance is not a good place to be creating actors, you want to know the world is up and running, which is the job of GameState class.
Advantage of GameState over GameMode class is that GameState exists on Server and Clients (GameMode is Server only).
Each time a new level is brought up, or a lobby level, that has your Special GameState class, the actor that runs your Online logics will be created
Advantage of using an Actor is that you can then do Everything You Could Ever Want to Do with Blueprints, or with C++, and have a very happy Tick Function
I know you want the lifetime of GameInstance, but you won’t have stability there, whereas GameState + Actor BP will always run nice and stable. No player will be experiencing your game without a GameState anyway, in a connected multiplayer game.
My answer assumes you have a Lobbly level at the very least, or you are talking about managing your connections in a living game, and knowing when players are joining and leaving, or in a lobby level, coming online like you mentioned.
So the one word answer to your question from my perspective is GameState
You could run all the logic I am talking about directly in your GameState subclass
But I tend to favor, just like you, designing my own systems and connecting to UE architecture only to get things up and running.
So I would launch my own Actor SMROnlineManagerSubsystem from GameState and let GameState do its own thing.
In my multiplayer games, GameState is the place where I create every Actor Subsystem, of which I have many, and then each Actor BP divides up the work and manages the load of my entire Game world, and all the people in it.
I use GameInstance only for detecting network connection issues.
My model does mean that all subsystems get re-initialized each time a level change occurs, but that has to happen since the actors are actually being destroyed, and I tend to feel that:
if you can’t frequently create /destroy things,
You shouldn’t be using them in the first place
That’s my first test of system stability, especially when clients can come and go at any time, bringing up and tearing down all my systems each time they join/leave,
“Can I rip this thing apart and put it back together as often as I want and it still runs with no crashes?”
Thanks a lot for the thorough explanation. Upon looking at the GameState class, I believe using it would cause issues for my specific use case. I am looking at a solution to handle things outside a specific level of the game, like connections to external services, telemetry, friends presence, notifications, etc. To create all these connections each time the level changes would lead to a lot of problems as well as gaps in coverage (what happens if a friend comes online while the level is loading and I don’t receive the notification).
With this specific use case in mind, handling things not related to actual gameplay but to the human player’s connections to services, would you change your implementation choice or still use a GameState BP?
#pragma once
#include "CoreMinimal.h"
//#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
//#include "UObject/UnrealType.h"
//#include "UObject/ScriptMacros.h"
#include "JoyFlow.generated.h"
//<~~~~ "Blueprintable" This is what allows you to do
// all the Async Logic Flow with Easy-To-Use Delegates and Events in a BP of UObject,
// while running all the C++ Core via BP-Callables as you see below
// ♥ Rama
UCLASS(Blueprintable,BlueprintType)
class UJoyFlow : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Joy Flow")
float YourVarsYouWantAccessToFromBP = 3.333f;
UFUNCTION(BlueprintCallable, Category = "Joy Flow")
void StartSMRVictory()
{
// Functions that can be launched to Your SMR API as soon as GameInstance
// runs Init() and creates this UObject (loooong before any world is even loaded!)
}
UFUNCTION(BlueprintCallable, Category = "Joy Flow")
void FancySMRAPILogics();
};
.cpp
#include "JoyFlow.h"
//other classes you want to use
//#include "JoyBPLibrary.h"
void UJoyFlow::FancySMRAPILogics()
{
}
The above code allows you to create a BP of the UObject base C++ class.
So now you can do all your Flow Logic (including delegates, which are so easy in BP and so necessary with any kind of async response system like Online Systems)
All of your C++ Core can be in the C++ class I showed, the template code with BP-Callable functions, so you can do the Flow of Logic in BP , while the C++ does what it does best, connecting to external systems that you wrote that use C++.
You can then create this BP in your GameInstanceBP, again, with Speed and Ease in the Init() of your GameInstance.
GameInstance can ensure stability of your reference, as your UObject won’t go anywhere, because GameInstance will NEVER go anywhere
As You Know (writing for benefit of other readers):
GameInstance is first and last, the Beginning and the End of Every UE Game
~
The issue is ticking during a level change in my mind…
Rama Update: The Joyous News
The Truly Joyous News (Made My Day Anyway…) is that GameInstance CAN act as
a World Context Object
that is stable during Level Changes
Meaning, GameInstance is having its UWorld updated as Levels change, so I can print out the float without concern of what level’s UWorld GameInstance is using, it is auto-updated for me by
The Awesome Unreal Engine Devs who made GameInstance So Robust!
I changed my current level multiple times, and the Counter kept steadily increasing of Total GameInstance Time Alive (as opposed to GameState and GameTimeSeconds, which would only report the time for the current level, resetting to 0 with each level change)
So that means, @SMR, That you CAN track the total time your entire Game setup has been alive, even between level loads,
The Total Lifespan of GameInstance Object Can Be Known With One Timer!
And of course, if you know the Total Time Alive of Your Game Instance, you already have everything else,
as Tracking Time Alive Accurately is Everything in a Multi-Part Online System…
Because as long as that float keeps increasing, you know your Systems Are Live!
Of course, I could only see the print out of the current time while a level was loaded, because it prints to the viewport
is that means you only have to connect to your external architecture once, inside the BP or the C++ Code of your UObject (My template code for you above)
Which was your main focus!
My point is, your SMR Online System UObject created by GameInstance Init,
will last as long as GameInstance,
because Your Custom Object, with all the Ease and Speed of Blueprints, is not dependent for its lifespan on anything other than the UObject reference in the GameInstance itself.
Real Time In Between Levels?
Please note, if you find there’s a gap in the timer during level changes (It looks accurate in my pic, but just in case)
You can use this instead:
FApp:GetCurrentTime()
How Do I Access My OnlineSubsystem Statically, From Anywhere?
I don’t know if this is obvious to You @SMR but for other people’s sakes:
Now that your UObject is live, created and ref stored in Your GameInstance, you can create a One Node Static Accessor , anywhere in BP, by creating a BP Function Library
Now, with 1 node, you have access to Your UObject Online Subsystem that:
In C++ has all sorts of fancy code for your External Online System API
In BP, is handling the calling of the fancy C++ code with the Joyful Flow of BP Events / Delegates, timers, everything that is so easy and fast to do in BP as compared with C++
Lives as long as GameInstance does (the Supreme Life Span for any UObject)
Is being ticked by GameInstance on a Timer and given a valid UWorldContext by GameInstance itself (as per a pic above)
Is Statically Accessible Anywhere in BP: Level BP, Random Actor Class, PlayerController, etc!
Wow Rama, now that is a reply! Thanks for the work you did on this, you definitely went above and beyond. I am glad I was on the right track and seeing your analysis gives me that confirmation this path will not lead to any obvious errors out of the gate. If I do find anything crazy that could help others, I will try to update this thread!