Systems Abstraction
Namespaces: RisingV.Shared.Systems
, RisingV.Shared.Managers
Assembly: RisingV.Shared
The Systems abstraction is the lightest layer on top of the Manager
framework, aimed at orchestrating game‐loop tickers—logic that runs every
server tick, physics step, or fixed interval (think Update
in Unity’s ECS).
Plugin
└─ SystemManager
├─ HealthRegenSystem
├─ WeatherSystem
└─ SpawnCleanupSystem
Each system supplies:
Update(float dt)
– executed once per tick.- Optional stages (
PreUpdate
,PostUpdate
). IsEnabled
flag that can be toggled at runtime or via config.
1. Core Contracts
Contract | Purpose |
---|---|
ISystem |
Marker; ties a system to SystemManager . |
ITickSystem |
Adds the Update method with delta‐time parameter. |
IStageSystem |
Adds optional PreUpdate / PostUpdate hooks. |
SystemBase |
Convenience abstract base that wires logging, enable flag, and config binding. |
SystemManager |
TypeMapManager that registers systems and drives the tick loop. |
ISystem & ITickSystem
public interface ISystem : IManageable<SystemManager>
{
bool IsEnabled { get; }
}
public interface ITickSystem : ISystem
{
void Update(float deltaTime);
}
IStageSystem (Optional)
public interface IStageSystem : ITickSystem
{
void PreUpdate(float dt) { }
void PostUpdate(float dt) { }
}
SystemBase
Provides:
protected readonly Logger Log
protected ConfigEntry<bool>? EnabledEntry
(auto‐bound)- Virtual
OnEnable()
/OnDisable()
for side‐effects.
public abstract class SystemBase : IStageSystem
{
public virtual bool IsEnabled => EnabledEntry?.Value ?? true;
public virtual void PreUpdate(float dt) { }
public abstract void Update(float dt);
public virtual void PostUpdate(float dt) { }
}
2. SystemManager
public sealed class SystemManager
: TypeMapManager<ISystem, SystemManager>, IPluginComponent
{
public float TimeScale { get; set; } = 1f;
// Tick driver – call from plugin’s server loop
public void Tick(float deltaTime)
{
deltaTime *= TimeScale;
foreach (var sys in this.Enumerate<ITickSystem>())
{
if (!sys.IsEnabled) continue;
if (sys is IStageSystem stage) stage.PreUpdate(deltaTime);
sys.Update(deltaTime);
if (sys is IStageSystem stage2) stage2.PostUpdate(deltaTime);
}
}
}
Lifecycle mirrors other managers (Initialize → Load → Ready …
) so systems can
perform heavy allocation in Load
and start ticking only after Ready
.
3. Typical Usage
// Plugin field
public override void OnInitialize()
{
SystemManager.AddSystem<HealthRegenSystem>(this);
SystemManager.AddSystem<WeatherSystem>(this);
}
// If you want to handle the ticks manually, e.g. in a server loop, you can do so:
// Make sure the SystemManager.UseBuiltInTickRunner is set to false.
public void OnServerTick(float dt) => _systems.Update(dt);
Creating a system:
public sealed class HealthRegenSystem : SystemBase
{
public override void Update(float dt)
{
foreach (var player in PlayerDatabase.All)
player.AddHealth(0.5f * dt);
}
}
4. Advanced Features
Feature | Description |
---|---|
TimeScale | Speed up / slow down simulation (0 pauses all systems). |
Dynamic Add/Remove | Add or unload systems at runtime; manager updates enumeration cache safely. |
Priority Sorting (Optional) | Override SystemManager.OrderItems to sort by custom attribute [SystemPriority] . |
Hot Reload | Systems implementing IReloadable will receive config changes without restart. |
5. Best Practices
- Keep per‐tick work tiny—heavy jobs belong in asynchronous Tasks or Jobs.
- Use deltaTime rather than
Time.fixedDeltaTime
constants to remain framerate‐independent. - Toggle systems via config (
EnabledEntry
) for fast iteration. - Log at Trace in hot paths; strip in release builds.
6. Troubleshooting
Symptom | Possible Cause | Remedy |
---|---|---|
System never runs | Not registered / IsEnabled=false |
Add in Initialize ; check config. |
Stutter spikes | Heavy logic in Update |
Profile; move to job threads or cache expensive lookups. |
Tick order wrong | Dependencies between systems | Implement priority attribute or split into multiple managers. |
TL;DR
- System = ticked logic module with optional pre/post stages.
- SystemManager orchestrates updates and plugs into the Manager lifecycle.
- Use for timers, regen, world effects, cleanups—any looped behaviour that doesn’t justify a full Engine.