Class SceneServerSystem
- Namespace
- FishMMO.Server.Implementation.World.SceneServer
- Assembly
- FishMMO.Server.dll
Manages scene server node services, scene loading/unloading, and heartbeat updates to the world server. Tracks scene instances, handles connection events, and synchronizes scene state with the database. Game logic and Broadcasts run synchronously on the main thread. Database operations are async to avoid blocking the main thread. Results from async DB queries that require main-thread state changes are marshalled via ISceneServerSystemMainThreadQueueData.
[CreateAssetMenu(fileName = "SceneServerSystem", menuName = "FishMMO/Server/SceneServer/Scene Server System", order = 1)]
[RequiresDataContainer(typeof(SceneInstanceMappingData))]
[RequiresDataContainer(typeof(SceneServerRuntimeData))]
[RequiresDataContainer(typeof(SceneServerSystemMainThreadQueueData))]
[RequiresDataContainer(typeof(AsyncWorkerData))]
public class SceneServerSystem : ServerBehaviour, IServerBehaviour<INetworkManagerWrapper, ServerManager, NetworkConnection, IServerBehaviour>, IServerComponent<INetworkManagerWrapper, ServerManager, NetworkConnection, IServerBehaviour>, ISceneServerSystem<NetworkConnection>, IServerBehaviour, IServerComponent
- Inheritance
-
ObjectScriptableObjectSceneServerSystem
- Implements
-
ISceneServerSystem<NetworkConnection>
- Inherited Members
-
ScriptableObject.SetDirty()ScriptableObject.CreateInstance<T>()Object.GetEntityId()Object.GetInstanceID()Object.GetHashCode()Object.InstantiateAsync<T>(T)Object.InstantiateAsync<T>(T, Transform)Object.InstantiateAsync<T>(T, Vector3, Quaternion)Object.InstantiateAsync<T>(T, Transform, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion)Object.Instantiate(Object, Vector3, Quaternion, Transform)Object.Instantiate(Object)Object.Instantiate(Object, Scene)Object.Instantiate<T>(T, InstantiateParameters)Object.Instantiate<T>(T, Vector3, Quaternion, InstantiateParameters)Object.Instantiate(Object, Transform)Object.Instantiate<T>(T)Object.Instantiate<T>(T, Vector3, Quaternion)Object.Instantiate<T>(T, Vector3, Quaternion, Transform)Object.Instantiate<T>(T, Transform)Object.Destroy(Object)Object.DestroyImmediate(Object)Object.DontDestroyOnLoad(Object)Object.DestroyObject(Object)Object.FindObjectsOfType<T>()Object.FindObjectsByType<T>(FindObjectsSortMode)Object.FindObjectsByType<T>(FindObjectsInactive, FindObjectsSortMode)Object.FindObjectOfType<T>()Object.FindFirstObjectByType<T>()Object.FindAnyObjectByType<T>()Object.FindFirstObjectByType<T>(FindObjectsInactive)Object.FindAnyObjectByType<T>(FindObjectsInactive)Object.FindObjectsByType<T>()Object.FindObjectsByType<T>(FindObjectsInactive)Object.ToString()Object.nameObject.hideFlags
Remarks
The database row is the authority for a server's lock and shutdown state, never the
process. Commands here write the row; every world and scene server reads its own row back
on each pulse and adopts what it finds. That is what makes a single in-game command reach
processes the player is not connected to — and it means anything else that can write those
rows (the Discord bot, a CMS, psql) controls the servers identically, exactly as
kick_requests already works for accounts.
This lives on the scene server because that is where players — and therefore admins — are. It is a partial of SceneServerSystem rather than a new behaviour so it needs no scene wiring: the scene server already loads this system.
Properties
PulseRate
Gets or sets the pulse rate for heartbeat updates.
public float PulseRate { get; set; }
Property Value
WorldSceneDetailsCache
Cache of world scene details, including max clients per scene.
public WorldSceneDetailsCache WorldSceneDetailsCache { get; }
Property Value
Methods
AdjustSceneCharacterCount(long, string, long, int)
Adjusts the character count for a specific scene instance.
public void AdjustSceneCharacterCount(long worldServerID, string sceneName, long sceneID, int amount)
Parameters
worldServerIDlongWorld server ID.
sceneNamestringScene name.
sceneIDlongScene row ID identifying the instance.
amountintAmount to adjust by (+1 or -1).
CloseInstance(long, string)
Returns an instance's occupants to the open world and unloads it.
public void CloseInstance(long sceneID, string reason)
Parameters
Remarks
The only way an instance may be unloaded. UnloadScene(long) destroys the scene outright, which was safe while the sole caller was the stale sweep — that only ever picks empty scenes — but is not once an instance can be closed with people standing in it. Evicting first sends each of them through the ordinary leave-instance path, so their state is saved, their session claim released and their client re-routed, rather than having the ground removed from under them.
InitializeOnce()
Called once to initialize the scene server system. Registers the server in the database and subscribes to connection and scene events.
public override ServerComponentInitializationStatus InitializeOnce()
Returns
InitializeOnceAsync(CancellationToken)
Initializes the system and registers it in the database without blocking the Unity main thread.
public override Task<ServerComponentInitializationStatus> InitializeOnceAsync(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationToken
Returns
Remarks
Awaits here deliberately capture Unity's SynchronizationContext (no
ConfigureAwait(false)), so execution resumes on the main thread and the Unity
and FishNet APIs used below stay legal.
NoteDeliberateInstanceExit(long, string, long)
Records that an instance's occupant left by choice rather than by losing its connection.
public void NoteDeliberateInstanceExit(long worldServerID, string sceneName, long sceneID)
Parameters
worldServerIDlongWorld server ID.
sceneNamestringInstance scene name.
sceneIDlongScene row ID identifying the instance.
Remarks
Called after the departure has been debited from the instance's population, so if this was the last occupant the instance is already empty and the next pulse reclaims it immediately instead of holding it for the idle timeout. See VacatedDeliberately for why the two cases differ.
A missing instance is not an error here, unlike in AdjustSceneCharacterCount(long, string, long, int): the instance may already have been unloaded by the very departure being recorded, and the only consequence of not marking a scene that no longer exists is nothing at all.
OnDeinitialize()
Called when the system is being destroyed. Unsubscribes from events and deletes scene data from the database.
public override void OnDeinitialize()
OnUpdate(float)
Drains the main-thread queue each frame.
protected override void OnUpdate(float deltaTime)
Parameters
deltaTimefloat
SceneManager_OnUnloadEnd(SceneUnloadEndEventArgs)
Handles scene unload completion events, removing scene mappings and cleaning up. Uses the flat SceneInstanceByHandle map for O(1) lookup per unloaded scene, then removes from both the flat map and the nested WorldScenes hierarchy.
public void SceneManager_OnUnloadEnd(SceneUnloadEndEventArgs args)
Parameters
argsSceneUnloadEndEventArgsScene unload end event arguments.
TryGetInstanceExpiry(long, out DateTime)
When an instance will close on its own, if it is time-bounded.
public bool TryGetInstanceExpiry(long sceneID, out DateTime expiresUtc)
Parameters
Returns
- bool
falsefor a scene this server does not host, an open-world scene, or one whose creation time was never recorded — none of which have an expiry to report.
Remarks
The lifetime cap is configuration this system owns, so the answer is derived here rather than by making every caller read the key and repeat the arithmetic — which is how two places end up disagreeing about when a dungeon ends.
TryGetSceneInstanceDetails(long, string, long, out ISceneInstanceDetails)
Attempts to get scene instance details for a given world server, scene name, and handle. Uses the flat SceneInstanceByHandle map for O(1) lookup, then validates the world server and scene name match as a correctness check.
public bool TryGetSceneInstanceDetails(long worldServerID, string sceneName, long sceneID, out ISceneInstanceDetails instanceDetails)
Parameters
worldServerIDlongWorld server ID.
sceneNamestringScene name.
sceneIDlonginstanceDetailsISceneInstanceDetailsOutput instance details.
Returns
- bool
True if found, false otherwise.
TryLoadSceneForConnection(NetworkConnection, ISceneInstanceDetails)
Attempts to load a scene for a connection if it is valid and loaded.
public bool TryLoadSceneForConnection(NetworkConnection connection, ISceneInstanceDetails instance)
Parameters
connectionNetworkConnectionNetwork connection.
instanceISceneInstanceDetailsScene instance details.
Returns
- bool
True if scene was loaded for the connection, false otherwise.
UnloadScene(long)
Unloads a scene by handle and removes its details from the database and server.
public void UnloadScene(long sceneID)
Parameters
sceneIDlong
UnloadSceneForConnection(NetworkConnection, string)
Unloads a scene for a connection by scene name.
public void UnloadSceneForConnection(NetworkConnection connection, string sceneName)
Parameters
connectionNetworkConnectionNetwork connection.
sceneNamestringName of the scene to unload.