1
0

QuickDialogSystem.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. using System.Diagnostics.CodeAnalysis;
  2. using Content.Shared.Administration;
  3. using Robust.Server.Player;
  4. using Robust.Shared.Enums;
  5. using Robust.Shared.Network;
  6. using Robust.Shared.Player;
  7. namespace Content.Server.Administration;
  8. /// <summary>
  9. /// This handles the server portion of quick dialogs, including opening them.
  10. /// </summary>
  11. public sealed partial class QuickDialogSystem : EntitySystem
  12. {
  13. [Dependency] private readonly IPlayerManager _playerManager = default!;
  14. /// <summary>
  15. /// Contains the success/cancel actions for a dialog.
  16. /// </summary>
  17. private readonly Dictionary<int, (Action<QuickDialogResponseEvent> okAction, Action cancelAction)> _openDialogs = new();
  18. private readonly Dictionary<NetUserId, List<int>> _openDialogsByUser = new();
  19. private int _nextDialogId = 1;
  20. /// <inheritdoc/>
  21. public override void Initialize()
  22. {
  23. _playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
  24. SubscribeNetworkEvent<QuickDialogResponseEvent>(Handler);
  25. }
  26. public override void Shutdown()
  27. {
  28. base.Shutdown();
  29. _playerManager.PlayerStatusChanged -= PlayerManagerOnPlayerStatusChanged;
  30. }
  31. private void Handler(QuickDialogResponseEvent msg, EntitySessionEventArgs args)
  32. {
  33. if (!_openDialogs.ContainsKey(msg.DialogId) || !_openDialogsByUser[args.SenderSession.UserId].Contains(msg.DialogId))
  34. {
  35. args.SenderSession.Channel.Disconnect($"Replied with invalid quick dialog data with id {msg.DialogId}.");
  36. return;
  37. }
  38. switch (msg.ButtonPressed)
  39. {
  40. case QuickDialogButtonFlag.OkButton:
  41. _openDialogs[msg.DialogId].okAction.Invoke(msg);
  42. break;
  43. case QuickDialogButtonFlag.CancelButton:
  44. _openDialogs[msg.DialogId].cancelAction.Invoke();
  45. break;
  46. default:
  47. throw new ArgumentOutOfRangeException();
  48. }
  49. _openDialogs.Remove(msg.DialogId);
  50. _openDialogsByUser[args.SenderSession.UserId].Remove(msg.DialogId);
  51. }
  52. private int GetDialogId()
  53. {
  54. return _nextDialogId++;
  55. }
  56. private void PlayerManagerOnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
  57. {
  58. if (e.NewStatus != SessionStatus.Disconnected && e.NewStatus != SessionStatus.Zombie)
  59. return;
  60. var user = e.Session.UserId;
  61. if (!_openDialogsByUser.ContainsKey(user))
  62. return;
  63. foreach (var dialogId in _openDialogsByUser[user])
  64. {
  65. _openDialogs[dialogId].cancelAction.Invoke();
  66. _openDialogs.Remove(dialogId);
  67. }
  68. _openDialogsByUser.Remove(user);
  69. }
  70. private void OpenDialogInternal(ICommonSession session, string title, List<QuickDialogEntry> entries, QuickDialogButtonFlag buttons, Action<QuickDialogResponseEvent> okAction, Action cancelAction)
  71. {
  72. var did = GetDialogId();
  73. RaiseNetworkEvent(
  74. new QuickDialogOpenEvent(
  75. title,
  76. entries,
  77. did,
  78. buttons),
  79. session
  80. );
  81. _openDialogs.Add(did, (okAction, cancelAction));
  82. if (!_openDialogsByUser.ContainsKey(session.UserId))
  83. _openDialogsByUser.Add(session.UserId, new List<int>());
  84. _openDialogsByUser[session.UserId].Add(did);
  85. }
  86. private bool TryParseQuickDialog<T>(QuickDialogEntryType entryType, string input, [NotNullWhen(true)] out T? output)
  87. {
  88. switch (entryType)
  89. {
  90. case QuickDialogEntryType.Integer:
  91. {
  92. var result = int.TryParse(input, out var val);
  93. output = (T?) (object?) val;
  94. return result;
  95. }
  96. case QuickDialogEntryType.Float:
  97. {
  98. var result = float.TryParse(input, out var val);
  99. output = (T?) (object?) val;
  100. return result;
  101. }
  102. case QuickDialogEntryType.ShortText:
  103. {
  104. if (input.Length > 100)
  105. {
  106. output = default;
  107. return false;
  108. }
  109. output = (T?) (object?) input;
  110. return output is not null;
  111. }
  112. case QuickDialogEntryType.LongText:
  113. {
  114. if (input.Length > 2000)
  115. {
  116. output = default;
  117. return false;
  118. }
  119. //It's verrrry likely that this will be longstring
  120. var longString = (LongString) input;
  121. output = (T?) (object?) longString;
  122. return output is not null;
  123. }
  124. default:
  125. throw new ArgumentOutOfRangeException(nameof(entryType), entryType, null);
  126. }
  127. }
  128. private QuickDialogEntryType TypeToEntryType(Type T)
  129. {
  130. if (T == typeof(int) || T == typeof(uint) || T == typeof(long) || T == typeof(ulong))
  131. return QuickDialogEntryType.Integer;
  132. if (T == typeof(float) || T == typeof(double))
  133. return QuickDialogEntryType.Float;
  134. if (T == typeof(string)) // People are more likely to notice the input box is too short than they are to notice it's too long.
  135. return QuickDialogEntryType.ShortText;
  136. if (T == typeof(LongString))
  137. return QuickDialogEntryType.LongText;
  138. throw new ArgumentException($"Tried to open a dialog with unsupported type {T}.");
  139. }
  140. }
  141. /// <summary>
  142. /// A type used with quick dialogs to indicate you want a large entry window for text and not a short one.
  143. /// </summary>
  144. /// <param name="String">The string retrieved.</param>
  145. public record struct LongString(string String)
  146. {
  147. public static implicit operator string(LongString longString)
  148. {
  149. return longString.String;
  150. }
  151. public static explicit operator LongString(string s)
  152. {
  153. return new(s);
  154. }
  155. }