1
0

MultiServerKickManager.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. using System.Text.Json;
  2. using System.Text.Json.Serialization;
  3. using Content.Server.Database;
  4. using Content.Shared.CCVar;
  5. using Robust.Server.Player;
  6. using Robust.Shared.Asynchronous;
  7. using Robust.Shared.Configuration;
  8. using Robust.Shared.Enums;
  9. using Robust.Shared.Network;
  10. using Robust.Shared.Player;
  11. namespace Content.Server.Administration.Managers;
  12. /// <summary>
  13. /// Handles kicking people that connect to multiple servers on the same DB at once.
  14. /// </summary>
  15. /// <seealso cref="CCVars.AdminAllowMultiServerPlay"/>
  16. public sealed class MultiServerKickManager
  17. {
  18. public const string NotificationChannel = "multi_server_kick";
  19. [Dependency] private readonly IPlayerManager _playerManager = null!;
  20. [Dependency] private readonly IServerDbManager _dbManager = null!;
  21. [Dependency] private readonly ILogManager _logManager = null!;
  22. [Dependency] private readonly IConfigurationManager _cfg = null!;
  23. [Dependency] private readonly IAdminManager _adminManager = null!;
  24. [Dependency] private readonly ITaskManager _taskManager = null!;
  25. [Dependency] private readonly IServerNetManager _netManager = null!;
  26. [Dependency] private readonly ILocalizationManager _loc = null!;
  27. [Dependency] private readonly ServerDbEntryManager _serverDbEntry = null!;
  28. private ISawmill _sawmill = null!;
  29. private bool _allowed;
  30. public void Initialize()
  31. {
  32. _sawmill = _logManager.GetSawmill("multi_server_kick");
  33. _playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
  34. _cfg.OnValueChanged(CCVars.AdminAllowMultiServerPlay, b => _allowed = b, true);
  35. _dbManager.SubscribeToJsonNotification<NotificationData>(
  36. _taskManager,
  37. _sawmill,
  38. NotificationChannel,
  39. OnNotification,
  40. OnNotificationEarlyFilter
  41. );
  42. }
  43. // ReSharper disable once AsyncVoidMethod
  44. private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
  45. {
  46. if (_allowed)
  47. return;
  48. if (e.NewStatus != SessionStatus.InGame)
  49. return;
  50. // Send notification to other servers so they can kick this player that just connected.
  51. try
  52. {
  53. await _dbManager.SendNotification(new DatabaseNotification
  54. {
  55. Channel = NotificationChannel,
  56. Payload = JsonSerializer.Serialize(new NotificationData
  57. {
  58. PlayerId = e.Session.UserId,
  59. ServerId = (await _serverDbEntry.ServerEntity).Id,
  60. }),
  61. });
  62. }
  63. catch (Exception ex)
  64. {
  65. _sawmill.Error($"Failed to send notification for multi server kick: {ex}");
  66. }
  67. }
  68. private bool OnNotificationEarlyFilter()
  69. {
  70. if (_allowed)
  71. {
  72. _sawmill.Verbose("Received notification for player join, but multi server play is allowed on this server. Ignoring");
  73. return false;
  74. }
  75. return true;
  76. }
  77. // ReSharper disable once AsyncVoidMethod
  78. private async void OnNotification(NotificationData notification)
  79. {
  80. if (!_playerManager.TryGetSessionById(new NetUserId(notification.PlayerId), out var player))
  81. return;
  82. if (notification.ServerId == (await _serverDbEntry.ServerEntity).Id)
  83. return;
  84. if (_adminManager.IsAdmin(player, includeDeAdmin: true))
  85. return;
  86. _sawmill.Info($"Kicking {player} for connecting to another server. Multi-server play is not allowed.");
  87. _netManager.DisconnectChannel(player.Channel, _loc.GetString("multi-server-kick-reason"));
  88. }
  89. private sealed class NotificationData
  90. {
  91. [JsonPropertyName("player_id")]
  92. public Guid PlayerId { get; set; }
  93. [JsonPropertyName("server_id")]
  94. public int ServerId { get; set; }
  95. }
  96. }