So, after some struggling around this I've decided to blog about it!
There is a guide that I assume is widely used, which leads you down the path of setting up your HttpService as an actor.
While it works, it doesn't work well.
To use it you need to either spawn in the actor in every level, update your references to the actor and hope that everything is ok. Often it is not, and what about the state?
Like your authentication token or when your session will timeout?
If you're only using level streaming, it makes sense to stick with the actor. However, when you start using OpenLevel(), you're going to run into issues without some elaborate hack.
Our HttpService starts off life in our shared plugin, and then it is subclassed per game for specific functionality related to that game. Our GameInstance also shares a common parent in the shared plugin, so we do not reinvent the wheel with each game.
The HttpService has a reference inside of the game instance, however, when you call OpenLevel actors are destroyed. Now you need to make sure that you're using a new HttpService actor because the old one will be killed.
Perhaps you will also need to save your HttpState in another place. That presents additional challenges because now your HttpService has to know about that data store, which would likely end up as the GameInstance.
It all gets messy pretty quick. Not worry, I've found a solution.
Instead of using an Actor, make it a UObject.
Doing this allows us to create a reference in GameInstance that will persist OpenLevel calls.
However, there are some caveats.
- UObjects will be garbage collected.
- There is no Tick()
So, we have these two problems to discuss the first is simple to solve. The reference in GameInstance needs to have a UPROPERTY(), and that will prevent garbage collection.
Second, our implementation knows that the HTTP authentication token is only valid for 5 minutes. We need to get a new token or extend the session lifetime by making periodic requests. Without Tick() and Timers, this presents a problem which I solved by creating an FTickerDelegate.
FTickerDelegate TickDelegate = FTickerDelegate::CreateUObject(this, &UForgedHttpService::Tick);
TickHandle = FTicker::GetCoreTicker().AddTicker(TickDelegate);
It is important to note that this Tick function returns a bool and needs a float. The float is your delta time, and the bool is whether or not to get called again.
That happens on the game thread so don't go overboard inside your Tick function, or you'll likely see a performance hit.
Our implementation tracks the time elapsed, checks if we need to make another request to keep the session active, and finally sends a request to do so.
As always I hope you've found this information to be useful and if you have a better suggestion or implementation, I would love to hear about it.