Effect System & How to restore Effects upon reconnecting to Server

Hey everyone,

Before I elaborate on my question, I would first like to establish a clear understanding on my current set-up/systems and my requirements:

Requirements

  • Implementation in C++
  • Online Multiplayer (Listen-Server Only)

Example Scenarios - “Poison Effect”

  1. Player walks into a “Toxic Gas Hazard” which applies Damage over time while inside it.
  2. Player is hit by a Monster which applies Damage over a specific amount of time.

In case the Player is disconnected while the “Poison Effect” is active, when reconnecting, it should continue from where it left off.

Current Setup
I have created an UEffect Class which holds various details such as Duration, Frequency, TargetActor etc. and functionality such as OnRegister(), OnApply(), OnRemove().

As per the above examples, I could have a UPoisonEffect which applies 5 damage every 1 second to the TargetActor by accessing its UStatComponent and modifying its Stat.Health value. For example, if this was applied to a Client, the Effect itself would call a ServerRPC to modify the Stat, which will then be replicated where needed.

I have also created a AHazard Class which essentially holds a UBoxComponent and a UEffect. Upon Begin/EndOverlap it Applies/Removes the Effect respectively.

Lastly, I have created an UEffectSubsystem which derives from UTickableWorldSubsystem. This is responsible for managing all Effects across the entire game and is used to Register/Apply/Remove Effects from Actors. Minor Clarification: Effects might have a Delay, which is why we first Register the Effect and then Apply it (potentially) at a later time.

Note: This approach is working as expected. Player walks into a Hazard, an Effect is applied via the Subsystem (Locally) and its Effects take place as intended. It works for both local (e.g., Apply Post-Process) and replicated cases (e.g., Modify Health).

Issue
Now that I have provided some insight into my architecture, I can elaborate on what’s troubling me: Up to this point I hadn’t considered what would happen if a Player (with an Effect applied) disconnected and then reconnected back to the game. As it stands, since the UEffectSubsystem cannot be replicated, this means that all applied Effects will be removed upon disconnecting.

I am aware of how to make data persistent when reconnecting and in short I plan on using the PlayerState (here’s a great article for anyone interested), but please do let me know if there’s a better solution. Having said that, I don’t think my current approach can work, so I am looking for alternatives.

Potential Solutions
To my understanding, since the Effects will have to be copied upon disconnection, this means the Server should be aware of all Effects applied. Based on this we have:

A) Replace UEffectSubsystem with UEffectComponent. This will be a replicated Component where all Effects will be replicated. I might have to convert the UEffect Class into an FEffect Struct and potentially use Fast Arrays, but that’s fine by me. For local-only Effects, they would still need to be replicated as we would still have to copy them on disconnect, but they simply wouldn’t run on the Server, only Locally.

B) Keep the UEffectSubsystem, but only for the Server (meaning it won’t be initialized for other connections). Then, the one responsible for applying the Effect (e.g., AHazard) would call a ServerRPC which would handle registering/applying the Effect to the Server Subsystem. I do see an issue with local-only Effects though since the Client won’t be aware of them, because they will only live on the Server; I could skip the Server-only approach and leave as is, but then I would need to separate them into replicated and non replicated which would confuse things.

I don’t like any solution in particular which is why I am creating this post. As it stands, I am leaning towards Solution A, but I would appreciate some more info/guidance before locking in my decision. I am even open to reworking my entire approach if needed, so no need to hold anything back :slight_smile: