CatStation/Content.Server/EUI/EuiManager.cs
Ewanderer d7f7a91183
Documentation of e api (#44395)
* documentation pass on content.server -> E!

* APi pass on content.server for the E folder.

* documentation pass on content.shared for e.

* Apply suggestions from code review

Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>

* fixing documentation according to review part 1.

* typo

* Very minor fixes

* And a short explanation about EUI

---------

Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Co-authored-by: SlamBamActionman <slambamactionman@gmail.com>
2026-07-01 08:38:04 +00:00

169 lines
5.4 KiB
C#

using Content.Shared.Eui;
using Robust.Server.Player;
using Robust.Shared.Enums;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Content.Server.EUI
{
/// <summary>
/// Manager for server-side EUI handling.
/// </summary>
/// <remarks>
/// An EUI is a system for making a relatively-easy connection between client and server
/// for the purposes of UIs.
/// </remarks>
public sealed partial class EuiManager : IPostInjectInit
{
[Dependency] private ILogManager _log = default!;
[Dependency] private IPlayerManager _players = default!;
[Dependency] private IServerNetManager _net = default!;
private ISawmill? _sawmill;
private readonly Dictionary<ICommonSession, PlayerEuiData> _playerData =
new();
private readonly Queue<(ICommonSession player, uint id)> _stateUpdateQueue =
new Queue<(ICommonSession, uint id)>();
private sealed class PlayerEuiData
{
public uint NextId = 1;
public readonly Dictionary<uint, BaseEui> OpenUIs = new();
}
void IPostInjectInit.PostInject()
{
_players.PlayerStatusChanged += PlayerStatusChanged;
}
/// <summary>
/// Initialisation of the EuIManager.
/// </summary>
public void Initialize()
{
_net.RegisterNetMessage<MsgEuiCtl>();
_net.RegisterNetMessage<MsgEuiState>();
_net.RegisterNetMessage<MsgEuiMessage>(RxMsgMessage);
_sawmill = _log.GetSawmill("eui");
}
/// <summary>
/// Dispatches all queued state updates to the respective clients.
/// </summary>
public void SendUpdates()
{
while (_stateUpdateQueue.TryDequeue(out var tuple))
{
var (player, id) = tuple;
// Check that UI and player still exist.
// COULD have been removed in the mean time.
if (!_playerData.TryGetValue(player, out var plyDat) || !plyDat.OpenUIs.TryGetValue(id, out var ui))
{
continue;
}
ui.DoStateUpdate();
}
}
/// <summary>
/// Sends an "open" message to a client.
/// </summary>
/// <param name="eui">The Eui to open.</param>
/// <param name="player">The player client to receive the message.</param>
/// <exception cref="ArgumentException">Throws if the Eui is somehow already open.</exception>
public void OpenEui(BaseEui eui, ICommonSession player)
{
if (eui.Id != 0)
{
throw new ArgumentException("That EUI is already open!");
}
var data = _playerData[player];
var newId = data.NextId++;
eui.Initialize(this, player, newId);
data.OpenUIs.Add(newId, eui);
var msg = new MsgEuiCtl();
msg.Id = newId;
msg.Type = MsgEuiCtl.CtlType.Open;
msg.OpenType = eui.GetType().Name;
_net.ServerSendMessage(msg, player.Channel);
}
/// <summary>
/// Sends a "close" message to whatever client holds the provded Eui.
/// </summary>
/// <param name="eui">Eui to close.</param>
public void CloseEui(BaseEui eui)
{
eui.Shutdown();
_playerData[eui.Player].OpenUIs.Remove(eui.Id);
var msg = new MsgEuiCtl();
msg.Id = eui.Id;
msg.Type = MsgEuiCtl.CtlType.Close;
_net.ServerSendMessage(msg, eui.Player.Channel);
}
private void RxMsgMessage(MsgEuiMessage message)
{
if (!_players.TryGetSessionByChannel(message.MsgChannel, out var ply))
{
return;
}
if (!_playerData.TryGetValue(ply, out var dat))
{
return;
}
if (!dat.OpenUIs.TryGetValue(message.Id, out var eui))
{
_sawmill?.Warning($"Got EUI message from player {ply} for non-existing UI {message.Id}");
return;
}
eui.HandleMessage(message.Message);
}
private void PlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus == SessionStatus.Connected)
{
_playerData.Add(e.Session, new PlayerEuiData());
}
else if (e.NewStatus == SessionStatus.Disconnected)
{
if (_playerData.TryGetValue(e.Session, out var plyDat))
{
// Gracefully close all open UIs.
foreach (var ui in plyDat.OpenUIs.Values)
{
ui.Closed();
}
_playerData.Remove(e.Session);
}
}
}
/// <summary>
/// Queues an update notification for a specific Eui.
/// </summary>
/// <param name="eui">The Eui to be updated.</param>
public void QueueStateUpdate(BaseEui eui)
{
DebugTools.Assert(eui.Id != 0, "EUI has not been opened yet.");
DebugTools.Assert(!eui.IsShutDown, "EUI has been closed.");
_stateUpdateQueue.Enqueue((eui.Player, eui.Id));
}
}
}