mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
252 lines
8.5 KiB
C#
252 lines
8.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using Robust.Shared.Enums;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Shared.GameObjects;
|
|
|
|
public abstract class SharedUserInterfaceSystem : EntitySystem
|
|
{
|
|
protected readonly Dictionary<ICommonSession, List<PlayerBoundUserInterface>> OpenInterfaces = new();
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
SubscribeAllEvent<PredictedBoundUIWrapMessage>(OnMessageReceived);
|
|
SubscribeLocalEvent<UserInterfaceComponent, ComponentInit>(OnUserInterfaceInit);
|
|
SubscribeLocalEvent<UserInterfaceComponent, ComponentShutdown>(OnUserInterfaceShutdown);
|
|
}
|
|
|
|
private void OnUserInterfaceInit(EntityUid uid, UserInterfaceComponent component, ComponentInit args)
|
|
{
|
|
component.Interfaces.Clear();
|
|
|
|
foreach (var prototypeData in component.InterfaceData)
|
|
{
|
|
AddUi((uid, component), prototypeData);
|
|
}
|
|
}
|
|
|
|
private void OnUserInterfaceShutdown(EntityUid uid, UserInterfaceComponent component, ComponentShutdown args)
|
|
{
|
|
if (!TryComp(uid, out ActiveUserInterfaceComponent? activeUis))
|
|
return;
|
|
|
|
foreach (var bui in activeUis.Interfaces)
|
|
{
|
|
DeactivateInterface(uid, bui, activeUis);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates the received message, and then pass it onto systems/components
|
|
/// </summary>
|
|
internal void OnMessageReceived(BaseBoundUIWrapMessage msg, EntitySessionEventArgs args)
|
|
{
|
|
var uid = GetEntity(msg.Entity);
|
|
|
|
if (!TryComp(uid, out UserInterfaceComponent? uiComp) || args.SenderSession is not { } session)
|
|
return;
|
|
|
|
if (!uiComp.Interfaces.TryGetValue(msg.UiKey, out var ui))
|
|
{
|
|
Log.Debug($"Got BoundInterfaceMessageWrapMessage for unknown UI key: {msg.UiKey}");
|
|
return;
|
|
}
|
|
|
|
if (!ui.SubscribedSessions.Contains(session))
|
|
{
|
|
Log.Debug($"UI {msg.UiKey} got BoundInterfaceMessageWrapMessage from a client who was not subscribed: {session}");
|
|
return;
|
|
}
|
|
|
|
// if they want to close the UI, we can go home early.
|
|
if (msg.Message is CloseBoundInterfaceMessage)
|
|
{
|
|
CloseShared(ui, session);
|
|
return;
|
|
}
|
|
|
|
// verify that the user is allowed to press buttons on this UI:
|
|
if (ui.RequireInputValidation)
|
|
{
|
|
var attempt = new BoundUserInterfaceMessageAttempt(args.SenderSession, uid, msg.UiKey);
|
|
RaiseLocalEvent(attempt);
|
|
if (attempt.Cancelled)
|
|
return;
|
|
}
|
|
|
|
// get the wrapped message and populate it with the sender & UI key information.
|
|
var message = msg.Message;
|
|
message.Session = args.SenderSession;
|
|
message.Entity = msg.Entity;
|
|
message.UiKey = msg.UiKey;
|
|
|
|
// Raise as object so the correct type is used.
|
|
RaiseLocalEvent(uid, (object)message, true);
|
|
}
|
|
|
|
protected void DeactivateInterface(EntityUid entityUid, PlayerBoundUserInterface ui,
|
|
ActiveUserInterfaceComponent? activeUis = null)
|
|
{
|
|
if (!Resolve(entityUid, ref activeUis, false))
|
|
return;
|
|
|
|
activeUis.Interfaces.Remove(ui);
|
|
if (activeUis.Interfaces.Count == 0)
|
|
RemCompDeferred(entityUid, activeUis);
|
|
}
|
|
|
|
protected virtual void CloseShared(PlayerBoundUserInterface bui, ICommonSession session,
|
|
ActiveUserInterfaceComponent? activeUis = null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a UI after an entity has been created.
|
|
/// It cannot be added already.
|
|
/// </summary>
|
|
public void AddUi(Entity<UserInterfaceComponent?> ent, PrototypeData data)
|
|
{
|
|
if (!Resolve(ent, ref ent.Comp))
|
|
return;
|
|
|
|
ent.Comp.Interfaces[data.UiKey] = new PlayerBoundUserInterface(data, ent);
|
|
ent.Comp.MappedInterfaceData[data.UiKey] = data;
|
|
}
|
|
|
|
public bool TryGetUi(EntityUid uid, Enum uiKey, [NotNullWhen(true)] out PlayerBoundUserInterface? bui, UserInterfaceComponent? ui = null)
|
|
{
|
|
bui = null;
|
|
|
|
return Resolve(uid, ref ui, false) && ui.Interfaces.TryGetValue(uiKey, out bui);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Switches between closed and open for a specific client.
|
|
/// </summary>
|
|
public virtual bool TryToggleUi(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null)
|
|
{
|
|
if (!TryGetUi(uid, uiKey, out var bui, ui))
|
|
return false;
|
|
|
|
ToggleUi(bui, session);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Switches between closed and open for a specific client.
|
|
/// </summary>
|
|
public void ToggleUi(PlayerBoundUserInterface bui, ICommonSession session)
|
|
{
|
|
if (bui._subscribedSessions.Contains(session))
|
|
CloseUi(bui, session);
|
|
else
|
|
OpenUi(bui, session);
|
|
}
|
|
|
|
public bool TryOpen(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null)
|
|
{
|
|
if (!TryGetUi(uid, uiKey, out var bui, ui))
|
|
return false;
|
|
|
|
return OpenUi(bui, session);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens this interface for a specific client.
|
|
/// </summary>
|
|
public bool OpenUi(PlayerBoundUserInterface bui, ICommonSession session)
|
|
{
|
|
if (session.Status == SessionStatus.Connecting || session.Status == SessionStatus.Disconnected)
|
|
return false;
|
|
|
|
if (!bui._subscribedSessions.Add(session))
|
|
return false;
|
|
|
|
OpenInterfaces.GetOrNew(session).Add(bui);
|
|
RaiseLocalEvent(bui.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Owner, session));
|
|
if (!bui._subscribedSessions.Contains(session))
|
|
{
|
|
// This can happen if Content closed a BUI from inside the event handler.
|
|
// This will already have caused a redundant close event to be sent to the client, but whatever.
|
|
// Just avoid doing the rest to avoid any state corruption shit.
|
|
return false;
|
|
}
|
|
|
|
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new OpenBoundInterfaceMessage(), bui.UiKey), session.Channel);
|
|
|
|
// Fun fact, clients needs to have BUIs open before they can receive the state.....
|
|
if (bui.LastStateMsg != null)
|
|
RaiseNetworkEvent(bui.LastStateMsg, session.Channel);
|
|
|
|
ActivateInterface(bui);
|
|
return true;
|
|
}
|
|
|
|
private void ActivateInterface(PlayerBoundUserInterface ui)
|
|
{
|
|
EnsureComp<ActiveUserInterfaceComponent>(ui.Owner).Interfaces.Add(ui);
|
|
}
|
|
|
|
internal bool TryCloseUi(ICommonSession? session, EntityUid uid, Enum uiKey, bool remoteCall = false, UserInterfaceComponent? uiComp = null)
|
|
{
|
|
if (!Resolve(uid, ref uiComp))
|
|
return false;
|
|
|
|
if (!uiComp.OpenInterfaces.TryGetValue(uiKey, out var boundUserInterface))
|
|
return false;
|
|
|
|
if (!remoteCall)
|
|
SendUiMessage(boundUserInterface, new CloseBoundInterfaceMessage());
|
|
|
|
uiComp.OpenInterfaces.Remove(uiKey);
|
|
boundUserInterface.Dispose();
|
|
|
|
if (session != null)
|
|
RaiseLocalEvent(uid, new BoundUIClosedEvent(uiKey, uid, session), true);
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool TryClose(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null)
|
|
{
|
|
if (!TryGetUi(uid, uiKey, out var bui, ui))
|
|
return false;
|
|
|
|
return CloseUi(bui, session);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close this interface for a specific client.
|
|
/// </summary>
|
|
public bool CloseUi(PlayerBoundUserInterface bui, ICommonSession session, ActiveUserInterfaceComponent? activeUis = null)
|
|
{
|
|
if (!bui._subscribedSessions.Remove(session))
|
|
return false;
|
|
|
|
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new CloseBoundInterfaceMessage(), bui.UiKey), session.Channel);
|
|
CloseShared(bui, session, activeUis);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raised by client-side UIs to send to server.
|
|
/// </summary>
|
|
internal void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage msg)
|
|
{
|
|
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), msg, bui.UiKey));
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Raised by client-side UIs to send predicted messages to server.
|
|
/// </summary>
|
|
internal void SendPredictedUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage msg)
|
|
{
|
|
RaisePredictiveEvent(new PredictedBoundUIWrapMessage(GetNetEntity(bui.Owner), msg, bui.UiKey));
|
|
}
|
|
}
|