From 0a8902f748854fa84be720d51f5a8f4b0fc79a2d Mon Sep 17 00:00:00 2001 From: PintTheDragon Date: Fri, 30 Dec 2022 18:40:08 -0800 Subject: [PATCH] Fix errors --- SCPStats/API/API.cs | 32 ++- .../GeneratingWarningMessageEventArgs.cs | 3 +- .../SendingWarningMessageEventArgs.cs | 3 +- SCPStats/API/EventArgs/UserInfoEventArgs.cs | 3 +- SCPStats/API/Events.cs | 8 +- SCPStats/Commands/HatCommand.cs | 9 +- SCPStats/EventHandler.cs | 196 +++++++++++------- SCPStats/Hats/HatPlayerComponent.cs | 71 +++---- SCPStats/Hats/Hats.cs | 22 +- SCPStats/Helper.cs | 26 +-- SCPStats/IDs.cs | 3 +- SCPStats/PlayerInfo.cs | 6 +- SCPStats/SCPStats.cs | 12 +- SCPStats/Verification.cs | 4 +- SCPStats/Websocket/WebsocketHandler.cs | 2 +- SCPStats/Websocket/WebsocketRequests.cs | 143 ++++++++++--- SCPStats/Websocket/WebsocketThread.cs | 4 +- 17 files changed, 336 insertions(+), 211 deletions(-) diff --git a/SCPStats/API/API.cs b/SCPStats/API/API.cs index 2543734..6ca4ff8 100644 --- a/SCPStats/API/API.cs +++ b/SCPStats/API/API.cs @@ -11,6 +11,9 @@ using System.Threading.Tasks; using Exiled.API.Features; using Exiled.API.Features.Items; +using InventorySystem; +using InventorySystem.Items; +using InventorySystem.Items.Pickups; using JetBrains.Annotations; using Mirror; using SCPStats.Hats; @@ -83,9 +86,22 @@ public static void SpawnHat(Player player, HatInfo hat, bool showHat = false) if(!hat.Rotation.IsZero()) rot = hat.Rotation; if(hat.Scale != Vector3.one || hat.Position != Vector3.zero || !hat.Rotation.IsZero()) item = hat.Item; - var itemObj = new Item(Server.Host.Inventory.CreateItemInstance(item, false)) {Scale = scale}; + var itemModel = InventoryItemLoader.AvailableItems[item]; - var pickup = itemObj.Spawn(Vector3.zero, Quaternion.identity); + var psi = new PickupSyncInfo() + { + ItemId = item, + Serial = ItemSerialGenerator.GenerateNext(), + Weight = itemModel.Weight + }; + + var pickup = Object.Instantiate(itemModel.PickupDropModel, Vector3.zero, Quaternion.identity); + pickup.transform.localScale = scale; + pickup.NetworkInfo = psi; + + NetworkServer.Spawn(pickup.gameObject); + pickup.InfoReceived(new PickupSyncInfo(), psi); + pickup.RefreshPositionAndRotation(); SpawnHat(player, pickup, itemOffset, rot, showHat); } @@ -94,11 +110,11 @@ public static void SpawnHat(Player player, HatInfo hat, bool showHat = false) /// Turn a pickup into a 's hat. It is recommended to use over this method. /// /// The who should wear the hat. - /// The that should be worn. + /// The that should be worn. /// A that will be added to the hat's position each time it is updated. /// A that will be added to the hat's rotation each time it is updated. /// A indicating if the hat should be displayed on its owner's screen. - public static void SpawnHat(Player player, Pickup pickup, Vector3 posOffset, Quaternion rotOffset, bool showHat = false) + public static void SpawnHat(Player player, ItemPickupBase pickup, Vector3 posOffset, Quaternion rotOffset, bool showHat = false) { HatPlayerComponent playerComponent; @@ -113,12 +129,12 @@ public static void SpawnHat(Player player, Pickup pickup, Vector3 posOffset, Qua playerComponent.item = null; } - var rigidbody = pickup.Base.gameObject.GetComponent(); + var rigidbody = pickup.gameObject.GetComponent(); rigidbody.useGravity = false; rigidbody.isKinematic = true; - playerComponent.item = pickup.Base.gameObject.AddComponent(); - playerComponent.item.item = pickup.Base; + playerComponent.item = pickup.gameObject.AddComponent(); + playerComponent.item.item = pickup; playerComponent.item.player = playerComponent; playerComponent.item.pos = Hats.Hats.GetHatPosForRole(player.Role); playerComponent.item.itemOffset = posOffset; @@ -133,7 +149,7 @@ public static void SpawnHat(Player player, Pickup pickup, Vector3 posOffset, Qua /// A list of all the warnings that the specified player has. public static async Task> GetWarnings(Player player) { - return await GetWarnings(player.RawUserId); + return await GetWarnings(Helper.HandleId(player)); } /// diff --git a/SCPStats/API/EventArgs/GeneratingWarningMessageEventArgs.cs b/SCPStats/API/EventArgs/GeneratingWarningMessageEventArgs.cs index 924f2f7..ef4425c 100644 --- a/SCPStats/API/EventArgs/GeneratingWarningMessageEventArgs.cs +++ b/SCPStats/API/EventArgs/GeneratingWarningMessageEventArgs.cs @@ -6,6 +6,7 @@ // ----------------------------------------------------------------------- using System.Collections.Generic; +using Exiled.Events.EventArgs.Interfaces; using SCPStats.Websocket.Data; namespace SCPStats.API.EventArgs @@ -13,7 +14,7 @@ namespace SCPStats.API.EventArgs /// /// Includes all of the information before a warning message is generated. /// - public class GeneratingWarningMessageEventArgs : System.EventArgs + public class GeneratingWarningMessageEventArgs : IExiledEvent { internal GeneratingWarningMessageEventArgs(List warnings, string initialMessage) { diff --git a/SCPStats/API/EventArgs/SendingWarningMessageEventArgs.cs b/SCPStats/API/EventArgs/SendingWarningMessageEventArgs.cs index a4c9ed5..b83108b 100644 --- a/SCPStats/API/EventArgs/SendingWarningMessageEventArgs.cs +++ b/SCPStats/API/EventArgs/SendingWarningMessageEventArgs.cs @@ -6,6 +6,7 @@ // ----------------------------------------------------------------------- using System.Collections.Generic; +using Exiled.Events.EventArgs.Interfaces; using SCPStats.Websocket.Data; namespace SCPStats.API.EventArgs @@ -13,7 +14,7 @@ namespace SCPStats.API.EventArgs /// /// Includes all of the information before a warning message is sent. /// - public class SendingWarningMessageEventArgs : System.EventArgs + public class SendingWarningMessageEventArgs : IExiledEvent { internal SendingWarningMessageEventArgs(List warnings, string message) { diff --git a/SCPStats/API/EventArgs/UserInfoEventArgs.cs b/SCPStats/API/EventArgs/UserInfoEventArgs.cs index 7c35775..d3ee201 100644 --- a/SCPStats/API/EventArgs/UserInfoEventArgs.cs +++ b/SCPStats/API/EventArgs/UserInfoEventArgs.cs @@ -6,6 +6,7 @@ // ----------------------------------------------------------------------- using Exiled.API.Features; +using Exiled.Events.EventArgs.Interfaces; using SCPStats.Websocket.Data; namespace SCPStats.API.EventArgs @@ -13,7 +14,7 @@ namespace SCPStats.API.EventArgs /// /// Includes all of the information for user info events. /// - public class UserInfoEventArgs : System.EventArgs + public class UserInfoEventArgs : IExiledEvent { internal UserInfoEventArgs(Player player, UserInfoData userInfo, CentralAuthPreauthFlags? flags) { diff --git a/SCPStats/API/Events.cs b/SCPStats/API/Events.cs index 8302b4b..fa436e9 100644 --- a/SCPStats/API/Events.cs +++ b/SCPStats/API/Events.cs @@ -35,12 +35,12 @@ public static class Events /// public static Exiled.Events.Events.CustomEventHandler SendingWarningMessage; - internal static void OnUserInfoReceived(UserInfoEventArgs ev) => UserInfoReceived.InvokeSafely(ev); + internal static void OnUserInfoReceived(UserInfoEventArgs ev) => UserInfoReceived?.InvokeSafely(ev); - internal static void OnUserInfoHandled(UserInfoEventArgs ev) => UserInfoHandled.InvokeSafely(ev); + internal static void OnUserInfoHandled(UserInfoEventArgs ev) => UserInfoHandled?.InvokeSafely(ev); - internal static void OnGeneratingWarningMessage(GeneratingWarningMessageEventArgs ev) => GeneratingWarningMessage.InvokeSafely(ev); + internal static void OnGeneratingWarningMessage(GeneratingWarningMessageEventArgs ev) => GeneratingWarningMessage?.InvokeSafely(ev); - internal static void OnSendingWarningMessage(SendingWarningMessageEventArgs ev) => SendingWarningMessage.InvokeSafely(ev); + internal static void OnSendingWarningMessage(SendingWarningMessageEventArgs ev) => SendingWarningMessage?.InvokeSafely(ev); } } \ No newline at end of file diff --git a/SCPStats/Commands/HatCommand.cs b/SCPStats/Commands/HatCommand.cs index 5199a89..4abfcdc 100644 --- a/SCPStats/Commands/HatCommand.cs +++ b/SCPStats/Commands/HatCommand.cs @@ -11,6 +11,7 @@ using CommandSystem; using Exiled.API.Features; using Exiled.Permissions.Extensions; +using PlayerRoles; using RemoteAdmin; using SCPStats.Hats; using UnityEngine; @@ -139,7 +140,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s case "on": if (playerComponent.item == null) { - if(p.Role != RoleType.None && p.Role != RoleType.Spectator) p.SpawnHat(HatPlayers[p.UserId].Item1, hasInfo && info.Item2.ShowHat); + if(p.Role != RoleTypeId.None && p.Role != RoleTypeId.Spectator) p.SpawnHat(HatPlayers[p.UserId].Item1, hasInfo && info.Item2.ShowHat); response = SCPStats.Singleton?.Translation?.HatEnabled ?? "You put on your hat."; return true; } @@ -158,7 +159,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s case "toggle": if (playerComponent.item == null) { - if(p.Role != RoleType.None && p.Role != RoleType.Spectator) p.SpawnHat(HatPlayers[p.UserId].Item1, hasInfo && info.Item2.ShowHat); + if(p.Role != RoleTypeId.None && p.Role != RoleTypeId.Spectator) p.SpawnHat(HatPlayers[p.UserId].Item1, hasInfo && info.Item2.ShowHat); response = SCPStats.Singleton?.Translation?.HatEnabled ?? "You put on your hat."; return true; } @@ -170,7 +171,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } case "default": HatPlayers[p.UserId] = new Tuple(HatPlayers[p.UserId].Item2, HatPlayers[p.UserId].Item2, HatPlayers[p.UserId].Item3, HatPlayers[p.UserId].Item4); - if(p.Role != RoleType.None && p.Role != RoleType.Spectator) p.SpawnHat(HatPlayers[p.UserId].Item1, hasInfo && info.Item2.ShowHat); + if(p.Role != RoleTypeId.None && p.Role != RoleTypeId.Spectator) p.SpawnHat(HatPlayers[p.UserId].Item1, hasInfo && info.Item2.ShowHat); response = SCPStats.Singleton?.Translation?.HatDefault ?? "Your hat has been changed back to your default hat."; return true; @@ -221,7 +222,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s } HatPlayers[p.UserId] = new Tuple(item, HatPlayers[p.UserId].Item2, HatPlayers[p.UserId].Item3, HatPlayers[p.UserId].Item4); - if(p.Role != RoleType.None && p.Role != RoleType.Spectator) p.SpawnHat(HatPlayers[p.UserId].Item1, hasInfo && info.Item2.ShowHat); + if(p.Role != RoleTypeId.None && p.Role != RoleTypeId.Spectator) p.SpawnHat(HatPlayers[p.UserId].Item1, hasInfo && info.Item2.ShowHat); response = SCPStats.Singleton?.Translation?.HatChanged ?? "Your hat has been changed."; return true; diff --git a/SCPStats/EventHandler.cs b/SCPStats/EventHandler.cs index 7cb63a7..2527348 100644 --- a/SCPStats/EventHandler.cs +++ b/SCPStats/EventHandler.cs @@ -12,10 +12,15 @@ using System.Linq; using Exiled.API.Enums; using Exiled.API.Features; +using Exiled.CustomItems.API.EventArgs; using Exiled.CustomItems.API.Features; -using Exiled.Events.EventArgs; +using Exiled.Events.EventArgs.Player; +using Exiled.Events.EventArgs.Scp049; +using Exiled.Events.EventArgs.Scp914; +using Exiled.Events.EventArgs.Server; using Exiled.Loader; using MEC; +using PlayerRoles; using PlayerStatsSystem; using SCPStats.Commands; using SCPStats.Hats; @@ -48,7 +53,8 @@ public class EventHandler //Tuple. internal static Dictionary> UserInfo = new Dictionary>(); private static List PreRequestedIDs = new List(); - internal static List DelayedIDs = new List(); + // ID: seconds delayed + internal static Dictionary DelayedIDs = new Dictionary(); internal static Dictionary LocalBanCache = new Dictionary(); @@ -171,7 +177,7 @@ private static IEnumerator SendStart() foreach (var player in Player.List) { var playerInfo = Helper.GetPlayerInfo(player, false, false); - if (player?.UserId == null || !playerInfo.IsAllowed || playerInfo.PlayerID == null || player.DoNotTrack || player.Role == RoleType.None || player.Role == RoleType.Spectator) continue; + if (player?.UserId == null || !playerInfo.IsAllowed || playerInfo.PlayerID == null || player.DoNotTrack || player.Role == RoleTypeId.None || player.Role == RoleTypeId.Spectator) continue; ids.Add(playerInfo); } @@ -239,7 +245,7 @@ private static IEnumerator GetRoundEndUsers() private static IEnumerator SendWinsLose(string leadingTeam) { - var winLose = new Dictionary>(); + var winLose = new Dictionary>(); foreach (var player in Player.List) { @@ -248,15 +254,15 @@ private static IEnumerator SendWinsLose(string leadingTeam) if (PauseRound || Helper.IsPlayerTutorial(player) || player.IsOverwatchEnabled) { - winLose[playerInfo.PlayerID] = new Tuple(false, true, playerInfo.PlayerRole); + winLose[playerInfo.PlayerID] = new Tuple(false, true, playerInfo.PlayerRole); } - else if (playerInfo.PlayerRole != RoleType.None && playerInfo.PlayerRole != RoleType.Spectator && !Helper.IsPlayerGhost(player)) + else if (playerInfo.PlayerRole != RoleTypeId.None && playerInfo.PlayerRole != RoleTypeId.Spectator && !Helper.IsPlayerGhost(player)) { - winLose[playerInfo.PlayerID] = new Tuple(true, false, playerInfo.PlayerRole); + winLose[playerInfo.PlayerID] = new Tuple(true, false, playerInfo.PlayerRole); } else { - winLose[playerInfo.PlayerID] = new Tuple(false, false, playerInfo.PlayerRole); + winLose[playerInfo.PlayerID] = new Tuple(false, false, playerInfo.PlayerRole); } } @@ -292,19 +298,19 @@ internal static void OnKill(DyingEventArgs ev) { if (!ev.IsAllowed || !Helper.IsRoundRunning()) return; - var killerInfo = Helper.GetFootprintInfo(ev.Handler.Base is AttackerDamageHandler attack ? attack.Attacker : default); - var targetInfo = Helper.GetPlayerInfo(ev.Target); + var killerInfo = Helper.GetFootprintInfo(ev.DamageHandler.Base is AttackerDamageHandler attack ? attack.Attacker : default); + var targetInfo = Helper.GetPlayerInfo(ev.Player); - if (!killerInfo.IsAllowed || !targetInfo.IsAllowed || (killerInfo.PlayerID == null && targetInfo.PlayerID == null) || targetInfo.PlayerRole == RoleType.None || targetInfo.PlayerRole == RoleType.Spectator) return; + if (!killerInfo.IsAllowed || !targetInfo.IsAllowed || (killerInfo.PlayerID == null && targetInfo.PlayerID == null) || targetInfo.PlayerRole == RoleTypeId.None || targetInfo.PlayerRole == RoleTypeId.Spectator) return; - var damageID = ev.Handler.Base.ToID(); + var damageID = ev.DamageHandler.Base.ToID(); if (damageID == 10 /* Pocket ID */ && PocketPlayers.TryGetValue(targetInfo.PlayerID, out var killer)) { killerInfo.PlayerID = killer; - killerInfo.PlayerRole = RoleType.Scp106; + killerInfo.PlayerRole = RoleTypeId.Scp106; } - else if (killerInfo.PlayerID == null && killerInfo.PlayerRole == RoleType.None) + else if (killerInfo.PlayerID == null && killerInfo.PlayerRole == RoleTypeId.None) { killerInfo.PlayerID = targetInfo.PlayerID; killerInfo.PlayerRole = targetInfo.PlayerRole; @@ -317,7 +323,7 @@ internal static void OnRoleChanged(ChangingRoleEventArgs ev) { if (ev.Player?.UserId != null && ev.Player.GameObject != null && !ev.Player.IsHost) { - if (ev.NewRole != RoleType.None && ev.NewRole != RoleType.Spectator) + if (ev.NewRole != RoleTypeId.None && ev.NewRole != RoleTypeId.Spectator) { Timing.CallDelayed(.5f, () => ev.Player.SpawnCurrentHat()); } @@ -334,12 +340,12 @@ internal static void OnRoleChanged(ChangingRoleEventArgs ev) if (ev.Reason == SpawnReason.Escaped) { - var cuffer = (ev.Player?.IsCuffed ?? false) && ev.Player.Cuffer?.UserId != null ? Helper.GetPlayerInfo(ev.Player.Cuffer) : new PlayerInfo(null, RoleType.None, true); + var cuffer = (ev.Player?.IsCuffed ?? false) && ev.Player.Cuffer?.UserId != null ? Helper.GetPlayerInfo(ev.Player.Cuffer) : new PlayerInfo(null, RoleTypeId.None, true); if (!cuffer.IsAllowed || cuffer.PlayerID == playerInfo.PlayerID) { cuffer.PlayerID = null; - cuffer.PlayerRole = RoleType.None; + cuffer.PlayerRole = RoleTypeId.None; } if(playerInfo.PlayerID != null || cuffer.PlayerID != null) WebsocketHandler.SendRequest(RequestType.Escape, "{\"playerid\":\""+playerInfo.PlayerID+"\",\"role\":\""+playerInfo.PlayerRole.ToID()+"\",\"cufferid\":\""+cuffer.PlayerID+"\",\"cufferrole\":\""+cuffer.PlayerRole.ToID()+"\"}"); } @@ -380,18 +386,6 @@ internal static void OnDrop(DroppingItemEventArgs ev) WebsocketHandler.SendRequest(RequestType.Drop, "{\"playerid\":\""+playerInfo.PlayerID+"\",\"itemid\":\""+ev.Item.Base.ItemTypeId.ToID()+"\"}"); } - internal static void OnPickupAmmo(PickingUpAmmoEventArgs ev) - { - if (!ev.Pickup.Base || !ev.Pickup.Base.gameObject || !ev.IsAllowed || CustomItem.TryGet(ev.Pickup, out _) || !ev.Pickup.Base.gameObject.TryGetComponent(out var hat)) return; - - if (ev.Player?.UserId != null && !ev.Player.IsHost && ev.Player.IsVerified && ev.Player.IPAddress != "127.0.0.WAN" && ev.Player.IPAddress != "127.0.0.1" && (hat.player == null || hat.player.gameObject != ev.Player?.GameObject) && (SCPStats.Singleton?.Config.DisplayHatHint ?? true)) - { - ev.Player.ShowHint(SCPStats.Singleton?.Translation?.HatHint ?? "You can get a hat like this at patreon.com/SCPStats.", 2f); - } - - ev.IsAllowed = false; - } - internal static void OnJoin(VerifiedEventArgs ev) { if (ev.Player?.UserId == null || ev.Player.IsHost || !ev.Player.IsVerified || Helper.IsPlayerNPC(ev.Player)) return; @@ -451,20 +445,20 @@ internal static void OnUse(UsedItemEventArgs ev) WebsocketHandler.SendRequest(RequestType.Use, "{\"playerid\":\""+playerInfo.PlayerID+"\",\"itemid\":\""+ev.Item.Base.ItemTypeId.ToID()+"\"}"); } - internal static void OnThrow(ThrowingItemEventArgs ev) + internal static void OnThrow(ThrownItemEventArgs ev) { - if (!ev.IsAllowed || ev.Item?.Base == null || !Helper.IsRoundRunning()) return; + if (ev.Projectile?.Base == null || !Helper.IsRoundRunning()) return; var playerInfo = Helper.GetPlayerInfo(ev.Player); if (!playerInfo.IsAllowed || playerInfo.PlayerID == null) return; - WebsocketHandler.SendRequest(RequestType.Use, "{\"playerid\":\""+playerInfo.PlayerID+"\",\"itemid\":\""+ev.Item.Base.ItemTypeId.ToID()+"\"}"); + WebsocketHandler.SendRequest(RequestType.Use, "{\"playerid\":\""+playerInfo.PlayerID+"\",\"itemid\":\""+ev.Projectile.Base.NetworkInfo.ItemId.ToID()+"\"}"); } - internal static void OnUpgrade(UpgradingItemEventArgs ev) + internal static void OnUpgrade(UpgradingPickupEventArgs ev) { - if (!ev.IsAllowed || ev.Item?.Base == null || ev.Item.Base.gameObject == null) return; - if (ev.Item.Base.gameObject.TryGetComponent(out _)) ev.IsAllowed = false; + if (!ev.IsAllowed || ev.Pickup?.Base == null || ev.Pickup.Base.gameObject == null) return; + if (ev.Pickup.Base.gameObject.TryGetComponent(out _)) ev.IsAllowed = false; } internal static void OnEnterPocketDimension(EnteringPocketDimensionEventArgs ev) @@ -502,7 +496,7 @@ internal static void OnBan(BannedEventArgs ev) var name = ev.Target?.UserId != null ? ev.Target.Nickname : ev.Details.OriginalName; var ip = (SCPStats.Singleton?.Config?.LinkIpsToBans ?? false) ? Helper.HandleIP(ev.Target) : null; - WebsocketHandler.SendRequest(RequestType.AddWarning, "{\"type\":\"1\",\"playerId\":\""+Helper.HandleId(ev.Details.Id) + (ip != null ? "\",\"playerIP\":\"" + ip : "") + "\",\"message\":\""+ev.Details.Reason.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"length\":"+((long) TimeSpan.FromTicks(ev.Details.Expires-ev.Details.IssuanceTime).TotalSeconds)+",\"playerName\":\""+name.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"issuer\":\""+(!string.IsNullOrEmpty(ev.Issuer?.UserId) && !(ev.Issuer?.IsHost ?? false) ? Helper.HandleId(ev.Issuer) : "")+"\",\"issuerName\":\""+(!string.IsNullOrEmpty(ev.Issuer?.Nickname) && !(ev.Issuer?.IsHost ?? false) ? ev.Issuer.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"") : "")+"\"}"); + WebsocketHandler.SendRequest(RequestType.AddWarning, "{\"type\":\"1\",\"playerId\":\""+Helper.HandleId(ev.Details.Id) + (ip != null ? "\",\"playerIP\":\"" + ip : "") + "\",\"message\":\""+ev.Details.Reason.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"length\":"+((long) TimeSpan.FromTicks(ev.Details.Expires-ev.Details.IssuanceTime).TotalSeconds)+",\"playerName\":\""+name.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"issuer\":\""+(!string.IsNullOrEmpty(ev.Player?.UserId) && !(ev.Player?.IsHost ?? false) ? Helper.HandleId(ev.Player) : "")+"\",\"issuerName\":\""+(!string.IsNullOrEmpty(ev.Player?.Nickname) && !(ev.Player?.IsHost ?? false) ? ev.Player.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"") : "")+"\"}"); Timing.RunCoroutine(UpdateLocalBanCache()); } @@ -529,21 +523,21 @@ internal static void OnKick(KickingEventArgs ev) { if (!ev.IsAllowed || !(SCPStats.Singleton?.Config?.ModerationLogging ?? true) || ev.Target?.UserId == null || ev.Target.IsHost || !ev.Target.IsVerified || Helper.IsPlayerNPC(ev.Target) || JustJoined.Contains(ev.Target.UserId) || (SCPStats.Singleton?.Translation?.BannedMessage != null && ev.Reason.StartsWith(SCPStats.Singleton.Translation.BannedMessage.Split('{').First())) || (SCPStats.Singleton?.Translation?.WhitelistKickMessage != null && ev.Reason.StartsWith(SCPStats.Singleton.Translation.WhitelistKickMessage)) || (SCPStats.Singleton?.Config?.IgnoredMessages ?? IgnoredMessages).Any(val => ev.Reason.StartsWith(val)) || IgnoredMessagesFromIntegration.Any(val => ev.Reason.StartsWith(val))) return; - WebsocketHandler.SendRequest(RequestType.AddWarning, "{\"type\":\"2\",\"playerId\":\""+Helper.HandleId(ev.Target.UserId)+"\",\"message\":\""+ev.Reason.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"playerName\":\""+ev.Target.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"issuer\":\""+(!string.IsNullOrEmpty(ev.Issuer?.UserId) && !(ev.Issuer?.IsHost ?? false) ? Helper.HandleId(ev.Issuer) : "")+"\",\"issuerName\":\""+(!string.IsNullOrEmpty(ev.Issuer?.Nickname) && !(ev.Issuer?.IsHost ?? false) ? ev.Issuer.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"") : "")+"\"}"); + WebsocketHandler.SendRequest(RequestType.AddWarning, "{\"type\":\"2\",\"playerId\":\""+Helper.HandleId(ev.Target.UserId)+"\",\"message\":\""+ev.Reason.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"playerName\":\""+ev.Target.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"issuer\":\""+(!string.IsNullOrEmpty(ev.Player?.UserId) && !(ev.Player?.IsHost ?? false) ? Helper.HandleId(ev.Player) : "")+"\",\"issuerName\":\""+(!string.IsNullOrEmpty(ev.Player?.Nickname) && !(ev.Player?.IsHost ?? false) ? ev.Player.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"") : "")+"\"}"); } internal static void OnReportingCheater(ReportingCheaterEventArgs ev) { if (!ev.IsAllowed || !(SCPStats.Singleton?.Config?.ModerationLogging ?? true) || ev.Target?.UserId == null || ev.Target.IsHost || !ev.Target.IsVerified || Helper.IsPlayerNPC(ev.Target)) return; - WebsocketHandler.SendRequest(RequestType.AddWarning, "{\"type\":\"7\",\"playerId\":\""+Helper.HandleId(ev.Target.UserId)+"\",\"message\":\""+ev.Reason.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"playerName\":\""+ev.Target.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"issuer\":\""+(!string.IsNullOrEmpty(ev.Issuer?.UserId) && !(ev.Issuer?.IsHost ?? false) ? Helper.HandleId(ev.Issuer) : "")+"\",\"issuerName\":\""+(!string.IsNullOrEmpty(ev.Issuer?.Nickname) && !(ev.Issuer?.IsHost ?? false) ? ev.Issuer.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"") : "")+"\"}"); + WebsocketHandler.SendRequest(RequestType.AddWarning, "{\"type\":\"7\",\"playerId\":\""+Helper.HandleId(ev.Target.UserId)+"\",\"message\":\""+ev.Reason.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"playerName\":\""+ev.Target.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"issuer\":\""+(!string.IsNullOrEmpty(ev.Player?.UserId) && !(ev.Player?.IsHost ?? false) ? Helper.HandleId(ev.Player) : "")+"\",\"issuerName\":\""+(!string.IsNullOrEmpty(ev.Player?.Nickname) && !(ev.Player?.IsHost ?? false) ? ev.Player.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"") : "")+"\"}"); } internal static void OnReporting(LocalReportingEventArgs ev) { if (!ev.IsAllowed || !(SCPStats.Singleton?.Config?.ModerationLogging ?? true) || ev.Target?.UserId == null || ev.Target.IsHost || !ev.Target.IsVerified || Helper.IsPlayerNPC(ev.Target)) return; - WebsocketHandler.SendRequest(RequestType.AddWarning, "{\"type\":\"8\",\"playerId\":\""+Helper.HandleId(ev.Target.UserId)+"\",\"message\":\""+ev.Reason.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"playerName\":\""+ev.Target.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"issuer\":\""+(!string.IsNullOrEmpty(ev.Issuer?.UserId) && !(ev.Issuer?.IsHost ?? false) ? Helper.HandleId(ev.Issuer) : "")+"\",\"issuerName\":\""+(!string.IsNullOrEmpty(ev.Issuer?.Nickname) && !(ev.Issuer?.IsHost ?? false) ? ev.Issuer.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"") : "")+"\"}"); + WebsocketHandler.SendRequest(RequestType.AddWarning, "{\"type\":\"8\",\"playerId\":\""+Helper.HandleId(ev.Target.UserId)+"\",\"message\":\""+ev.Reason.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"playerName\":\""+ev.Target.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"")+"\",\"issuer\":\""+(!string.IsNullOrEmpty(ev.Player?.UserId) && !(ev.Player?.IsHost ?? false) ? Helper.HandleId(ev.Player) : "")+"\",\"issuerName\":\""+(!string.IsNullOrEmpty(ev.Player?.Nickname) && !(ev.Player?.IsHost ?? false) ? ev.Player.Nickname.Replace("\\", "\\\\").Replace("\"", "\\\"") : "")+"\"}"); } internal static void OnRecalling(FinishingRecallEventArgs ev) @@ -551,65 +545,107 @@ internal static void OnRecalling(FinishingRecallEventArgs ev) if (!ev.IsAllowed || !Helper.IsRoundRunning()) return; var playerInfo = Helper.GetPlayerInfo(ev.Target, true, false); - var scp049Info = Helper.GetPlayerInfo(ev.Scp049, true, false); + var scp049Info = Helper.GetPlayerInfo(ev.Player, true, false); if (playerInfo.PlayerID == scp049Info.PlayerID) scp049Info.PlayerID = null; if (playerInfo.PlayerID == null && scp049Info.PlayerID == null) return; WebsocketHandler.SendRequest(RequestType.Revive, "{\"playerid\":\""+playerInfo.PlayerID+"\",\"scp049\":\""+scp049Info.PlayerID+"\"}"); } - - internal static void OnPreauth(PreAuthenticatingEventArgs ev) + + internal static void OnReservedSlotCheck(ReservedSlotsCheckEventArgs ev) { - if (ev.UserId == null) return; - var id = Helper.HandleId(ev.UserId); - //If the server is full and they aren't in the reserved slots list. - if (ev.ServerFull && !ev.IsAllowed) + // Reserved slot checking is handled as follows: + // If the player has info, we'll check their reserved slot status. + // If they have a reserved slot, we'll let them in. Otherwise, we'll do nothing. + // If they don't have info yet, let them through. Then, preauth will delay them until they get data. + // Once they get info, they'll end up back here. + // + // The reason that hasReservedSlot isn't used is that it could lead to a situation where someone with a local + // and SCPStats reserved slot gets denied entry because of their local reserved slot. This is because SCPStats + // bypasses player limit checks, but local doesn't. Instead, we do SCPStats first, then fallback to local, so players + // with local reserved slots can still get in. + + // Check if they have info. + if (UserInfo.TryGetValue(id, out var userInfo) && userInfo.Item2 != null && + userInfo.Item1.HasValue) { - //If we've already gotten userinfo from them, use it to check if they have reserved slots. - //Otherwise, request their userinfo and delay them. - if (UserInfo.TryGetValue(id, out var userInfo) && userInfo.Item2 != null && userInfo.Item1.HasValue) - { - if (WebsocketRequests.HandleReservedSlots(userInfo.Item2, userInfo.Item1.Value)) - { - ev.IsAllowed = true; - } - } - else + // They have info. + + if (WebsocketRequests.HandleReservedSlots(userInfo.Item2, userInfo.Item1.Value)) { - //If they haven't been pre-requested (such as at round end), request their info. - if (!PreRequestedIDs.Contains(id)) - { - if (UserInfo.Count > 500) UserInfo.Remove(UserInfo.Keys.First()); - UserInfo[id] = new Tuple((CentralAuthPreauthFlags) ev.Flags, null); - WebsocketHandler.SendRequest(RequestType.UserInfo, Helper.UserInfoData(id, ev.Request.RemoteEndPoint.Address.ToString().Trim().ToLower())); - } - - //Delay them by 4 seconds. - ev.IsAllowed = true; - ev.Delay(4, true); + // They have an actual reserved slot. + ev.Result = ReservedSlotEventResult.AllowConnectionUnconditionally; } + + // They either have a reserved slot from us or they don't. + // If they do, we've let them through. Otherwise, we'll leave it up to other plugins. + return; } - //If the server isn't full and they aren't pre-requested (such as at round end). - else if(!PreRequestedIDs.Contains(id)) + + // They don't have info. Let them through temporarily. + ev.Result = ReservedSlotEventResult.AllowConnectionUnconditionally; + } + + internal static void OnPreauth(PreAuthenticatingEventArgs ev) + { + if (!ev.IsAllowed) return; + + var id = Helper.HandleId(ev.UserId); + var ip = Helper.HandleIP(ev.IpAddress); + + // We only *need* to do delays if a system like bans, reserved slots, or whitelist depends on it. + var delayNeeded = (SCPStats.Singleton?.Config?.SyncBans ?? false) || Config.WhitelistEnabled() || + (SCPStats.Singleton?.Config?.ReservedSlots?.Count(req => req != "DiscordRoleID") ?? 0) > 0; + + // If we have their info, no need to do anything. + if (UserInfo.TryGetValue(id, out var userInfo) && userInfo.Item2 != null && userInfo.Item1.HasValue) { - //If we haven't delayed them and it's the first round/empty round, and we need to confirm bans/handle a whitelist, and we have a positive first round preauth delay. - if (!DelayedIDs.Contains(id) && (firstRound || Server.PlayerCount < 1) && ((SCPStats.Singleton?.Config?.SyncBans ?? false) || Config.WhitelistEnabled()) && (int) SCPStats.Singleton?.Config?.FirstRoundPreauthDelay > 0) - { - //First delay their connection, then request their data after. - DelayedIDs.Add(id); - ev.Delay(SCPStats.Singleton?.Config?.FirstRoundPreauthDelay ?? 4, true); - } - //If they have been delayed, don't re-request. - else if (DelayedIDs.Contains(id)) return; + // We won't delay anymore, so no need to store this. + DelayedIDs.Remove(id); + + // We should make sure the user isn't banned/is whitelisted (if these options are enabled). + ev.RunUserInfoPreauth(id, ip, userInfo.Item2, ev.Flags); + return; + } - //Request their info. + // We'll figure out how many times we've already delayed them. + // If it's 0 (so we haven't delayed), we can request userinfo. If it's 4 (so 4 seconds delayed), we can + // request user info again and let them through like normal. + if (!DelayedIDs.TryGetValue(id, out var secondsDelayed)) + secondsDelayed = 0; + + // If they haven't been pre-requested (such as at round end), request their info. + // With the delay stuff, we can only do this on 0 or 4. + if (!PreRequestedIDs.Contains(id) && (secondsDelayed == 0 || secondsDelayed == 4)) + { if (UserInfo.Count > 500) UserInfo.Remove(UserInfo.Keys.First()); - UserInfo[id] = new Tuple((CentralAuthPreauthFlags) ev.Flags, null); + UserInfo[id] = new Tuple(ev.Flags, null); WebsocketHandler.SendRequest(RequestType.UserInfo, Helper.UserInfoData(id, ev.Request.RemoteEndPoint.Address.ToString().Trim().ToLower())); } + + // Now, we can delay if it's needed, and if we're less than 6. + if (delayNeeded && secondsDelayed < 4) + { + // Remove them from PreRequestedIDs to make sure their info is requested if something fails. + PreRequestedIDs.Remove(id); + + // This needs to be 4 in order to avoid the preauth ratelimit. + if (DelayedIDs.Count > 500) DelayedIDs.Remove(DelayedIDs.Keys.First()); + DelayedIDs[id] = secondsDelayed + 4; + + ev.Delay(4, true); + return; + } + + // No need to keep them in DelayedIDs, as we'll only Reject or Accept from this point forward. + DelayedIDs.Remove(id); + + // At this point we don't have data, and we aren't going to delay to get it. + // We should try to run the preauth user info in case this user has a ban. + ev.RunUserInfoPreauth(id, ip, userInfo.Item2, ev.Flags); } internal static IEnumerator UpdateLocalBanCache() diff --git a/SCPStats/Hats/HatPlayerComponent.cs b/SCPStats/Hats/HatPlayerComponent.cs index 08b0e9d..8e371e6 100644 --- a/SCPStats/Hats/HatPlayerComponent.cs +++ b/SCPStats/Hats/HatPlayerComponent.cs @@ -1,4 +1,5 @@ -// ----------------------------------------------------------------------- + +// ----------------------------------------------------------------------- // // Copyright (c) SCPStats.com. All rights reserved. // Licensed under the Apache v2 license. @@ -7,14 +8,15 @@ using System; using System.Collections.Generic; -using Exiled.API.Enums; +using CustomPlayerEffects; using Exiled.API.Extensions; -using Exiled.API.Features; -using InventorySystem.Items.Pickups; using MEC; -using Mirror; +using PlayerRoles; +using PlayerRoles.FirstPersonControl; +using PlayerRoles.PlayableScps.Scp096; +using PlayerRoles.PlayableScps.Scp939; +using PluginAPI.Core; using UnityEngine; -using Scp096 = PlayableScps.Scp096; namespace SCPStats.Hats { @@ -38,7 +40,7 @@ private IEnumerator MoveHat() try { if (item == null || item.gameObject == null) continue; - + var player = Player.Get(gameObject); var pickup = item.item; var pickupInfo = pickup.NetworkInfo; @@ -46,76 +48,57 @@ private IEnumerator MoveHat() pickupInfo.Locked = true; - if (player.Role == RoleType.None || player.Role == RoleType.Spectator || Helper.IsPlayerGhost(player) || (player.TryGetEffect(EffectType.Invisible, out var effect) && effect.Intensity != 0)) + if (player.Role == RoleTypeId.None || player.Role == RoleTypeId.Spectator || Helper.IsPlayerGhost(player) || (player.EffectsManager.TryGetEffect(out var effect) && effect.Intensity != 0)) { - pickupInfo.Position = Vector3.one * 6000f; pickup.transform.position = Vector3.one * 6000f; + pickupInfo.ServerSetPositionAndRotation(Vector3.one * 6000f, Quaternion.identity); pickup.NetworkInfo = pickupInfo; continue; } - var camera = player.CameraTransform; + var camera = player.Camera; var rotAngles = camera.rotation.eulerAngles; - if (player.Role.Team == Team.SCP) rotAngles.x = 0; + if (PlayerRolesUtils.GetTeam(player.Role) == Team.SCPs) rotAngles.x = 0; var rotation = Quaternion.Euler(rotAngles); var rot = rotation * item.rot; var transform1 = pickup.transform; - var pos = (player.Role != RoleType.Scp079 ? rotation * (item.pos+item.itemOffset) : (item.pos+item.itemOffset)) + camera.position; + var pos = (player.Role != RoleTypeId.Scp079 ? rotation * (item.pos+item.itemOffset) : (item.pos+item.itemOffset)) + camera.position; + transform1.position = pos; transform1.rotation = rot; - pickupInfo.Rotation = new LowPrecisionQuaternion(rot); - transform1.position = pos; - pickupInfo.Position = pos; + pickupInfo.ServerSetPositionAndRotation(pos, rot); var fakePickupInfo = pickup.NetworkInfo; - fakePickupInfo.Position = Vector3.zero; - fakePickupInfo.Rotation = new LowPrecisionQuaternion(Quaternion.identity); + fakePickupInfo.ServerSetPositionAndRotation(Vector3.zero, Quaternion.identity); fakePickupInfo.Locked = true; - var ownerPickupInfo = pickupInfo; - ownerPickupInfo.Locked = true; - if (!item.showHat) - { - ownerPickupInfo.Position = Vector3.zero; - ownerPickupInfo.Rotation = new LowPrecisionQuaternion(Quaternion.identity); - } + var ownerPickupInfo = item.showHat ? pickupInfo : fakePickupInfo; - foreach (var player1 in Player.List) + foreach (var player1 in Player.GetPlayers()) { - if (player1?.UserId == null || player1.IsHost || !player1.IsVerified || Helper.IsPlayerNPC(player1)) continue; + if (player1?.UserId == null || player1.IsServer || !player1.IsReady || Helper.IsPlayerNPC(player1)) continue; if (player1 == player) { MirrorExtensions.SendFakeSyncVar(player1, pickup.netIdentity, pickupType, "NetworkInfo", ownerPickupInfo); } - else if (player1.Role.Team == player.Role.Team) + else if (PlayerRolesUtils.GetTeam(player1.Role) == PlayerRolesUtils.GetTeam(player.Role)) { MirrorExtensions.SendFakeSyncVar(player1, pickup.netIdentity, pickupType, "NetworkInfo", pickupInfo); } - else - switch (player1.Role.Type) + else if(player1.ReferenceHub.roleManager.CurrentRole is FpcStandardRoleBase role) + switch (player1.Role) { - case RoleType.Scp93953: - case RoleType.Scp93989: - { - if (!player.ReferenceHub.scp939visionController.CanSee(player1.ReferenceHub.scp939visionController._myVisuals939)) - { - MirrorExtensions.SendFakeSyncVar(player1, pickup.netIdentity, pickupType, "NetworkInfo", fakePickupInfo); - } - else - { - MirrorExtensions.SendFakeSyncVar(player1, pickup.netIdentity, pickupType, "NetworkInfo", pickupInfo); - } - + case RoleTypeId.Scp939 when role.VisibilityController is Scp939VisibilityController vision && !vision.ValidateVisibility(player.ReferenceHub): + MirrorExtensions.SendFakeSyncVar(player1, pickup.netIdentity, pickupType, "NetworkInfo", fakePickupInfo); break; - } - case RoleType.Scp096 when player1.CurrentScp is Scp096 script && script.EnragedOrEnraging && !script.HasTarget(player.ReferenceHub): + case RoleTypeId.Scp096 when role.VisibilityController is Scp096VisibilityController vision && !vision.ValidateVisibility(player.ReferenceHub): MirrorExtensions.SendFakeSyncVar(player1, pickup.netIdentity, pickupType, "NetworkInfo", fakePickupInfo); break; default: @@ -128,7 +111,7 @@ private IEnumerator MoveHat() { if (!_threw) { - Log.Error(e); + Log.Error(e.ToString()); _threw = true; } } diff --git a/SCPStats/Hats/Hats.cs b/SCPStats/Hats/Hats.cs index 01b6443..cdccb16 100644 --- a/SCPStats/Hats/Hats.cs +++ b/SCPStats/Hats/Hats.cs @@ -6,6 +6,7 @@ // ----------------------------------------------------------------------- using Exiled.API.Features; +using PlayerRoles; using SCPStats.Commands; using UnityEngine; @@ -27,27 +28,26 @@ internal static void SpawnHat(this Player p, HatInfo hat, bool showHat) API.API.SpawnHat(p, hat, showHat); } - internal static Vector3 GetHatPosForRole(RoleType role) + internal static Vector3 GetHatPosForRole(RoleTypeId role) { switch (role) { - case RoleType.Scp173: + case RoleTypeId.Scp173: return new Vector3(0, .55f, -.05f); - case RoleType.Scp106: + case RoleTypeId.Scp106: return new Vector3(0, .45f, .18f); - case RoleType.Scp096: + case RoleTypeId.Scp096: return new Vector3(.15f, .425f, .325f); - case RoleType.Scp93953: + case RoleTypeId.Scp939: + // TODO: Fix. return new Vector3(0, -.5f, 1.125f); - case RoleType.Scp93989: - return new Vector3(0, -.3f, 1.1f); - case RoleType.Scp049: + case RoleTypeId.Scp049: return new Vector3(0, .125f, -.05f); - case RoleType.None: + case RoleTypeId.None: return new Vector3(-1000, -1000, -1000); - case RoleType.Spectator: + case RoleTypeId.Spectator: return new Vector3(-1000, -1000, -1000); - case RoleType.Scp0492: + case RoleTypeId.Scp0492: return new Vector3(0, .1f, -.16f); default: return new Vector3(0, .15f, -.07f); diff --git a/SCPStats/Helper.cs b/SCPStats/Helper.cs index 891138f..ecb86f7 100644 --- a/SCPStats/Helper.cs +++ b/SCPStats/Helper.cs @@ -16,6 +16,8 @@ using Exiled.API.Features; using Exiled.Loader; using Footprinting; +using PlayerRoles; +using PlayerStatsSystem; using SCPStats.Websocket.Data; using UnityEngine; @@ -148,22 +150,22 @@ internal static bool IsPlayerTutorial(Player p) { var playerIsSh = ((List) Integrations.GetSH?.Invoke(null, null))?.Any(pl => pl.Id == p.Id) ?? false; - return p.Role == RoleType.Tutorial && !playerIsSh && !IsPlayerGhost(p); + return p.Role == RoleTypeId.Tutorial && !playerIsSh && !IsPlayerGhost(p); } - internal static bool IsPlayerTutorial(Player p, RoleType role) + internal static bool IsPlayerTutorial(Player p, RoleTypeId role) { var playerIsSh = ((List) Integrations.GetSH?.Invoke(null, null))?.Any(pl => pl.Id == p.Id) ?? false; - return role == RoleType.Tutorial && !playerIsSh && !IsPlayerGhost(p); + return role == RoleTypeId.Tutorial && !playerIsSh && !IsPlayerGhost(p); } internal static PlayerInfo GetPlayerInfo(Player p, bool tutorial = true, bool spectator = true) { - return p != null && (p.NoClipEnabled || (p.IsGodModeEnabled && !p.IsSpawnProtected) || IsPlayerNPC(p) || (tutorial && IsPlayerTutorial(p))) - ? new PlayerInfo(null, RoleType.None, false) - : p?.UserId == null || p.IsHost || !p.IsVerified || (spectator && (p.Role == RoleType.None || p.Role == RoleType.Spectator)) - ? new PlayerInfo(null, RoleType.None, true) + return p != null && (p.ReferenceHub.playerStats.GetModule().HasFlag(AdminFlags.Noclip) || (p.IsGodModeEnabled && !p.IsSpawnProtected) || IsPlayerNPC(p) || (tutorial && IsPlayerTutorial(p))) + ? new PlayerInfo(null, RoleTypeId.None, false) + : p?.UserId == null || p.IsHost || !p.IsVerified || (spectator && (p.Role == RoleTypeId.None || p.Role == RoleTypeId.Spectator)) + ? new PlayerInfo(null, RoleTypeId.None, true) : p.DoNotTrack ? new PlayerInfo(null, p.Role, true) : new PlayerInfo(Helper.HandleId(p.UserId), p.Role, true); @@ -173,19 +175,19 @@ internal static PlayerInfo GetFootprintInfo(Footprint f, bool tutorial = true, b { if (!f.IsSet) { - return new PlayerInfo(null, RoleType.None, true); + return new PlayerInfo(null, RoleTypeId.None, true); } var p = Player.Get(f.Hub); - if(p != null && (p.NoClipEnabled || (p.IsGodModeEnabled && !p.IsSpawnProtected) || IsPlayerNPC(p) || (tutorial && IsPlayerTutorial(p, f.Role)))) + if(p != null && (p.ReferenceHub.playerStats.GetModule().HasFlag(AdminFlags.Noclip) || (p.IsGodModeEnabled && !p.IsSpawnProtected) || IsPlayerNPC(p) || (tutorial && IsPlayerTutorial(p, f.Role)))) { - return new PlayerInfo(null, RoleType.None, false); + return new PlayerInfo(null, RoleTypeId.None, false); } - if(p != null && (p.IsHost || !p.IsVerified || (spectator && (f.Role == RoleType.None || f.Role == RoleType.Spectator)))) + if(p != null && (p.IsHost || !p.IsVerified || (spectator && (f.Role == RoleTypeId.None || f.Role == RoleTypeId.Spectator)))) { - return new PlayerInfo(null, RoleType.None, true); + return new PlayerInfo(null, RoleTypeId.None, true); } if (p != null && !p.DoNotTrack) diff --git a/SCPStats/IDs.cs b/SCPStats/IDs.cs index 5084ed2..6b5ee1a 100644 --- a/SCPStats/IDs.cs +++ b/SCPStats/IDs.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using Exiled.API.Enums; +using PlayerRoles; using PlayerStatsSystem; namespace SCPStats @@ -266,7 +267,7 @@ internal static int ToID(this DamageHandlerBase damageHandler) } } - internal static int ToID(this RoleType roleType) + internal static int ToID(this RoleTypeId roleType) { if (RoleIDs.TryGetValue(roleType.ToString(), out var id)) return id; return -1; diff --git a/SCPStats/PlayerInfo.cs b/SCPStats/PlayerInfo.cs index 876b365..c25632b 100644 --- a/SCPStats/PlayerInfo.cs +++ b/SCPStats/PlayerInfo.cs @@ -5,15 +5,17 @@ // // ----------------------------------------------------------------------- +using PlayerRoles; + namespace SCPStats { internal struct PlayerInfo { internal string PlayerID; - internal RoleType PlayerRole; + internal RoleTypeId PlayerRole; internal bool IsAllowed; - public PlayerInfo(string playerID, RoleType playerRole, bool isAllowed) + public PlayerInfo(string playerID, RoleTypeId playerRole, bool isAllowed) { PlayerID = playerID; PlayerRole = playerRole; diff --git a/SCPStats/SCPStats.cs b/SCPStats/SCPStats.cs index e2cab79..1c583f2 100644 --- a/SCPStats/SCPStats.cs +++ b/SCPStats/SCPStats.cs @@ -109,19 +109,19 @@ private IEnumerator EnableEvents() Exiled.Events.Handlers.Player.ChangingRole += EventHandler.OnRoleChanged; Exiled.Events.Handlers.Player.PickingUpItem += EventHandler.OnPickup; Exiled.Events.Handlers.Player.DroppingItem += EventHandler.OnDrop; - Exiled.Events.Handlers.Player.PickingUpAmmo += EventHandler.OnPickupAmmo; Exiled.Events.Handlers.Player.Verified += EventHandler.OnJoin; Exiled.Events.Handlers.Player.Destroying += EventHandler.OnLeave; Exiled.Events.Handlers.Player.UsedItem += EventHandler.OnUse; - Exiled.Events.Handlers.Player.ThrowingItem += EventHandler.OnThrow; + Exiled.Events.Handlers.Player.ThrownItem += EventHandler.OnThrow; Exiled.Events.Handlers.Server.ReloadedRA += EventHandler.OnRAReload; Exiled.Events.Handlers.Server.ReloadedConfigs += LoadConfigs; - Exiled.Events.Handlers.Scp914.UpgradingItem += EventHandler.OnUpgrade; + Exiled.Events.Handlers.Scp914.UpgradingPickup += EventHandler.OnUpgrade; Exiled.Events.Handlers.Player.EnteringPocketDimension += EventHandler.OnEnterPocketDimension; Exiled.Events.Handlers.Player.EscapingPocketDimension += EventHandler.OnEscapingPocketDimension; Exiled.Events.Handlers.Player.Banned += EventHandler.OnBan; Exiled.Events.Handlers.Player.Kicking += EventHandler.OnKick; Exiled.Events.Handlers.Scp049.FinishingRecall += EventHandler.OnRecalling; + Exiled.Events.Handlers.Player.ReservedSlot += EventHandler.OnReservedSlotCheck; Exiled.Events.Handlers.Player.PreAuthenticating += EventHandler.OnPreauth; Exiled.Events.Handlers.Server.ReportingCheater += EventHandler.OnReportingCheater; Exiled.Events.Handlers.Server.LocalReporting += EventHandler.OnReporting; @@ -190,19 +190,19 @@ public override void OnDisabled() Exiled.Events.Handlers.Player.ChangingRole -= EventHandler.OnRoleChanged; Exiled.Events.Handlers.Player.PickingUpItem -= EventHandler.OnPickup; Exiled.Events.Handlers.Player.DroppingItem -= EventHandler.OnDrop; - Exiled.Events.Handlers.Player.PickingUpAmmo -= EventHandler.OnPickupAmmo; Exiled.Events.Handlers.Player.Verified -= EventHandler.OnJoin; Exiled.Events.Handlers.Player.Destroying -= EventHandler.OnLeave; Exiled.Events.Handlers.Player.UsedItem -= EventHandler.OnUse; - Exiled.Events.Handlers.Player.ThrowingItem -= EventHandler.OnThrow; + Exiled.Events.Handlers.Player.ThrownItem -= EventHandler.OnThrow; Exiled.Events.Handlers.Server.ReloadedRA -= EventHandler.OnRAReload; Exiled.Events.Handlers.Server.ReloadedConfigs -= LoadConfigs; - Exiled.Events.Handlers.Scp914.UpgradingItem -= EventHandler.OnUpgrade; + Exiled.Events.Handlers.Scp914.UpgradingPickup -= EventHandler.OnUpgrade; Exiled.Events.Handlers.Player.EnteringPocketDimension -= EventHandler.OnEnterPocketDimension; Exiled.Events.Handlers.Player.EscapingPocketDimension -= EventHandler.OnEscapingPocketDimension; Exiled.Events.Handlers.Player.Banned -= EventHandler.OnBan; Exiled.Events.Handlers.Player.Kicking -= EventHandler.OnKick; Exiled.Events.Handlers.Scp049.FinishingRecall -= EventHandler.OnRecalling; + Exiled.Events.Handlers.Player.ReservedSlot -= EventHandler.OnReservedSlotCheck; Exiled.Events.Handlers.Player.PreAuthenticating -= EventHandler.OnPreauth; Exiled.Events.Handlers.Server.ReportingCheater -= EventHandler.OnReportingCheater; Exiled.Events.Handlers.Server.LocalReporting -= EventHandler.OnReporting; diff --git a/SCPStats/Verification.cs b/SCPStats/Verification.cs index 9f70467..70d0cfb 100644 --- a/SCPStats/Verification.cs +++ b/SCPStats/Verification.cs @@ -25,7 +25,7 @@ internal static async Task UpdateID() using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, "https://scpstats.com/getid")) { - var str = "{\"ip\": \"" + ServerConsole.Ip + "\",\"port\": \"" + ServerConsole.Port + "\",\"id\": \"" + SCPStats.ServerID + "\"}"; + var str = "{\"ip\": \"" + ServerConsole.Ip + "\",\"port\": \"" + Server.Port + "\",\"id\": \"" + SCPStats.ServerID + "\"}"; requestMessage.Headers.Add("Signature", Helper.HmacSha256Digest(SCPStats.Secret, str)); requestMessage.Content = new StringContent(str, Encoding.UTF8, "application/json"); @@ -63,7 +63,7 @@ private static async Task Verify() using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, "https://scpstats.com/verify")) { - var str = "{\"ip\": \"" + ServerConsole.Ip + "\",\"port\": \"" + ServerConsole.Port + "\",\"id\": \"" + SCPStats.ServerID + "\"}"; + var str = "{\"ip\": \"" + ServerConsole.Ip + "\",\"port\": \"" + Server.Port + "\",\"id\": \"" + SCPStats.ServerID + "\"}"; requestMessage.Headers.Add("Signature", Helper.HmacSha256Digest(SCPStats.Secret, str)); requestMessage.Content = new StringContent(str, Encoding.UTF8, "application/json"); diff --git a/SCPStats/Websocket/WebsocketHandler.cs b/SCPStats/Websocket/WebsocketHandler.cs index 057f45d..3f16e3f 100644 --- a/SCPStats/Websocket/WebsocketHandler.cs +++ b/SCPStats/Websocket/WebsocketHandler.cs @@ -42,7 +42,7 @@ internal static void SendRequest(RequestType type, string data = "") var str = ((int) type).ToString().PadLeft(2, '0')+data; var message = "p" + SCPStats.ServerID + str.Length + " " + str + Helper.HmacSha256Digest(SCPStats.Secret, str + WebsocketThread.Nonce); - Log.Debug(">" + "p" + SCPStats.ServerID + str.Length + " " + str, SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug(">" + "p" + SCPStats.ServerID + str.Length + " " + str); WebsocketThread.Queue.Enqueue(message); WebsocketThread.Signal.Set(); diff --git a/SCPStats/Websocket/WebsocketRequests.cs b/SCPStats/Websocket/WebsocketRequests.cs index 65e2064..09e78b6 100644 --- a/SCPStats/Websocket/WebsocketRequests.cs +++ b/SCPStats/Websocket/WebsocketRequests.cs @@ -10,8 +10,12 @@ using System.Linq; using System.Text.RegularExpressions; using Exiled.API.Features; +using Exiled.Events.EventArgs.Player; using Exiled.Loader; +using JetBrains.Annotations; using MEC; +using PlayerRoles; +using PluginAPI.Events; using SCPStats.API; using SCPStats.API.EventArgs; using SCPStats.Commands; @@ -136,7 +140,7 @@ private static void HandleUserInfo(string info) var infoSplit = info.Split(' ').ToList(); var playerId = infoSplit[0]; - Log.Debug("Received user info for " + playerId, SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Received user info for " + playerId); infoSplit.RemoveAt(0); @@ -145,11 +149,11 @@ private static void HandleUserInfo(string info) var data = new UserInfoData(flags); - Log.Debug("Is discord member: " + data.IsDiscordMember, SCPStats.Singleton?.Config?.Debug ?? false); - Log.Debug("Is discord booster: " + data.IsBooster, SCPStats.Singleton?.Config?.Debug ?? false); - Log.Debug("Discord roles: " + string.Join(", ", data.DiscordRoles), SCPStats.Singleton?.Config?.Debug ?? false); - Log.Debug("Is banned: " + data.IsBanned, SCPStats.Singleton?.Config?.Debug ?? false); - Log.Debug("Has hat perms: " + data.HasHat, SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Is discord member: " + data.IsDiscordMember); + Log.Debug("Is discord booster: " + data.IsBooster); + Log.Debug("Discord roles: " + string.Join(", ", data.DiscordRoles)); + Log.Debug("Is banned: " + data.IsBanned); + Log.Debug("Has hat perms: " + data.HasHat); CentralAuthPreauthFlags? preauthFlags = null; if (EventHandler.UserInfo.TryGetValue(playerId, out var userinfo)) @@ -166,6 +170,74 @@ private static void HandleUserInfo(string info) RunUserInfo(player); } + + /// + /// Handles user info for a pre-authenticated player. + /// If a player should be kicked, a PreauthCancellationData will be returned. + /// + /// The handled ID of the player. + /// The handled IP of the player. + /// A bool indicating if an action was taken. + internal static bool RunUserInfoPreauth(this PreAuthenticatingEventArgs ev, string id, string ip, [CanBeNull] UserInfoData data, CentralAuthPreauthFlags flags) + { + // Data is removed when we reject so it has the potential to be refreshed. + // This is the same behavior that happens with Joins, where a player's info is removed on leave. + + if (data != null) + { + if (!IsWhitelisted(data, flags)) + { + Log.Debug("[Preauth] Player is not whitelisted. Disconnecting!"); + EventHandler.UserInfo.Remove(id); + + ev.Reject(SCPStats.Singleton?.Translation?.WhitelistKickMessage ?? "[SCPStats] You are not whitelisted on this server!", true); + return true; + } + + if ((SCPStats.Singleton?.Config?.SyncBans ?? false) && data.IsBanned && !flags.HasFlagFast(CentralAuthPreauthFlags.IgnoreBans)) + { + Log.Debug("[Preauth] Player is banned. Disconnecting!"); + EventHandler.UserInfo.Remove(id); + + ev.RejectBanned(data.BanText, DateTime.Now.AddSeconds(data.BanLength), true); + return true; + } + } + else + { + // With null UserInfo, we can only check bans offline. + + if (SCPStats.Singleton?.Config?.SyncBans ?? false) + { + // Try to get a ban for their ID. If there isn't one, try their IP. + if (EventHandler.LocalBanCache.TryGetValue(id, out var banExpiry) || + EventHandler.LocalBanCache.TryGetValue(ip, out banExpiry)) + { + + // Now, let's check if the ban expires after now. If it does, + // we'll send them a message, otherwise we can return. + if (banExpiry > DateTimeOffset.Now.ToUnixTimeSeconds()) + { + Log.Debug("[Preauth] Player is banned (by cache). Disconnecting!"); + EventHandler.UserInfo.Remove(id); + + + ev.RejectBanned( + SCPStats.Singleton?.Translation?.CacheBannedMessage ?? "[SCPStats] You have been banned from this server, but there was an error fetching the details of your ban.", + DateTimeOffset.FromUnixTimeSeconds(banExpiry).DateTime, + true + ); + return true; + } + + // They aren't banned, so let them pass. We'll re-query their user info though, just to be safe. + WebsocketHandler.SendRequest(RequestType.UserInfo, Helper.UserInfoData(id, ip)); + } + } + } + + return false; + } internal static bool RunUserInfo(Player player) { @@ -173,7 +245,7 @@ internal static bool RunUserInfo(Player player) if (player?.UserId == null || player.IsHost || !player.IsVerified || Helper.IsPlayerNPC(player)) return false; - if (EventHandler.DelayedIDs.Contains(playerId)) + if (EventHandler.DelayedIDs.ContainsKey(playerId)) { EventHandler.DelayedIDs.Remove(playerId); } @@ -181,15 +253,16 @@ internal static bool RunUserInfo(Player player) //If the user doesn't exist, or their data is null, handle them as unconfirmed. if (!EventHandler.UserInfo.TryGetValue(playerId, out var tupleData) || tupleData.Item2 == null) return HandleUnconfirmedUser(player); - Log.Debug("Found player. Invoking UserInfoReceived event.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Found player. Invoking UserInfoReceived event."); var ev = new UserInfoEventArgs(player, tupleData.Item2, tupleData.Item1); Events.OnUserInfoReceived(ev); - Log.Debug("Attempting whitelist and ban sync.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Attempting whitelist and ban sync."); - if(ev.Flags.HasValue && (HandleWhitelist(player, ev.UserInfo, ev.Flags.Value) || ((SCPStats.Singleton?.Config?.SyncBans ?? false) && HandleBans(player, ev.UserInfo)))) return true; - Log.Debug("Player whitelisted and not banned or ban sync failed, adding hat.", SCPStats.Singleton?.Config?.Debug ?? false); + // No need to handle bans here as it's done in preauth. + if(ev.Flags.HasValue && HandleWhitelist(player, ev.UserInfo, ev.Flags.Value)) return true; + Log.Debug("Player whitelisted and not banned or ban sync failed, adding hat."); Timing.RunCoroutine(DelayedUserInfo(player, ev, playerId)); @@ -222,7 +295,7 @@ private static bool HandleUnconfirmedUser(Player player) //we'll send them a message, otherwise we can return. if (banExpiry > DateTimeOffset.Now.ToUnixTimeSeconds()) { - Log.Debug("Player is banned (by cache). Disconnecting!", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Player is banned (by cache). Disconnecting!"); ServerConsole.Disconnect(player.GameObject, SCPStats.Singleton?.Translation?.CacheBannedMessage ?? "[SCPStats] You have been banned from this server, but there was an error fetching the details of your ban."); return true; } @@ -235,7 +308,7 @@ private static bool HandleUnconfirmedUser(Player player) //If we don't, we should kick them. if (Config.WhitelistEnabled()) { - Log.Debug("Player's UserInfo is not confirmed. Disconnecting!", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Player's UserInfo is not confirmed. Disconnecting!"); ServerConsole.Disconnect(player.GameObject, SCPStats.Singleton?.Translation?.NotConfirmedKickMessage ?? "[SCPStats] An authentication error occured between the server and SCPStats! Please try again."); return true; } @@ -251,16 +324,19 @@ private static IEnumerator DelayedUserInfo(Player player, UserInfoEventAr HandleHats(player, ev.UserInfo); - Log.Debug("Syncing roles.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Syncing roles."); HandleRolesync(player, ev.UserInfo); - Log.Debug("Finished handling user info. Invoking UserInfoHandled event.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Finished handling user info. Invoking UserInfoHandled event."); Events.OnUserInfoHandled(ev); } - private static bool HandleWhitelist(Player player, UserInfoData data, CentralAuthPreauthFlags flags) + /// + /// Returns true if the player is whitelisted (or if a whitelist is not enabled). + /// + private static bool IsWhitelisted(UserInfoData data, CentralAuthPreauthFlags flags) { - if (flags.HasFlagFast(CentralAuthPreauthFlags.IgnoreWhitelist) || !Config.WhitelistEnabled()) return false; + if (flags.HasFlagFast(CentralAuthPreauthFlags.IgnoreWhitelist) || !Config.WhitelistEnabled()) return true; var passed = false; @@ -278,9 +354,14 @@ private static bool HandleWhitelist(Player player, UserInfoData data, CentralAut if (!SCPStats.Singleton.Config.WhitelistRequireAll) break; } - if (passed) return false; - - Log.Debug("Player is not whitelisted. Disconnecting!", SCPStats.Singleton?.Config?.Debug ?? false); + return passed; + } + + private static bool HandleWhitelist(Player player, UserInfoData data, CentralAuthPreauthFlags flags) + { + if (IsWhitelisted(data, flags)) return false; + + Log.Debug("Player is not whitelisted. Disconnecting!"); ServerConsole.Disconnect(player.GameObject, SCPStats.Singleton?.Translation?.WhitelistKickMessage ?? "[SCPStats] You are not whitelisted on this server!"); return true; } @@ -312,7 +393,7 @@ internal static bool HandleReservedSlots(UserInfoData data, CentralAuthPreauthFl private static bool HandleBans(Player player, UserInfoData data) { if (!data.IsBanned || player.IsStaffBypassEnabled) return false; - Log.Debug("Player is banned. Disconnecting!", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Player is banned. Disconnecting!"); ServerConsole.Disconnect(player.GameObject, (SCPStats.Singleton?.Translation?.BannedMessage ?? "[SCPStats] You have been banned from this server:\nExpires in: {duration}.\nReason: {reason}.").Replace("{duration}", Helper.SecondsToString(data.BanLength)).Replace("{reason}", data.BanText)); return true; } @@ -321,14 +402,14 @@ private static void HandleHats(Player player, UserInfoData data) { if (!data.HasHat) return; - Log.Debug("User has hat. Giving permissions!", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("User has hat. Giving permissions!"); var item = IDs.ItemIDToType(data.HatID); if (Enum.IsDefined(typeof(ItemType), item)) HatCommand.HatPlayers[player.UserId] = new Tuple(new HatInfo(item, data.HatScale, data.HatOffset, data.HatRotation), new HatInfo(item, data.HatScale, data.HatOffset, data.HatRotation), true, data.CustomHatTier); else HatCommand.HatPlayers[player.UserId] = new Tuple(new HatInfo(ItemType.SCP268), new HatInfo(ItemType.SCP268), true, data.CustomHatTier); - if (player.Role != RoleType.None && player.Role != RoleType.Spectator) + if (player.Role != RoleTypeId.None && player.Role != RoleTypeId.Spectator) { player.SpawnCurrentHat(); } @@ -341,11 +422,11 @@ private static void HandleHats(Player player, UserInfoData data) private static void HandleRolesync(Player player, UserInfoData data) { - Log.Debug("Started rolesync", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Started rolesync"); if (SCPStats.Singleton == null || SCPStats.Singleton.Config == null || ServerStatic.PermissionsHandler == null || ServerStatic.PermissionsHandler._groups == null) return; - Log.Debug("Checking if player already has a role.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Checking if player already has a role."); if (player.Group != null && !PlayerHasGroup(player, SCPStats.Singleton.Config.BoosterRole) && !PlayerHasGroup(player, SCPStats.Singleton.Config.DiscordMemberRole) && !SCPStats.Singleton.Config.RoleSync.Any(role => { @@ -353,20 +434,20 @@ private static void HandleRolesync(Player player, UserInfoData data) return split.Length >= 2 && split[1] != "IngameRoleName" && PlayerHasGroup(player, split[1]); })) return; - Log.Debug("Player does not have a role. Attempting discord rolesync.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Player does not have a role. Attempting discord rolesync."); if ((data.DiscordRoles.Length > 0 || data.Ranks.Length > 0 || data.Stats.Length > 0) && SCPStats.Singleton.Config.RoleSync.Select(x => x.Split(':')).Any(s => GiveRoleSync(player, s, data))) return; - Log.Debug("Attempting booster/discord member rolesync.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Attempting booster/discord member rolesync."); if (data.IsBooster && !SCPStats.Singleton.Config.BoosterRole.Equals("fill this") && !SCPStats.Singleton.Config.BoosterRole.Equals("none")) { - Log.Debug("Giving booster role.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Giving booster role."); GiveRole(player, SCPStats.Singleton.Config.BoosterRole); } else if (data.IsDiscordMember && !SCPStats.Singleton.Config.DiscordMemberRole.Equals("fill this") && !SCPStats.Singleton.Config.DiscordMemberRole.Equals("none")) { - Log.Debug("Giving discord member role.", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Giving discord member role."); GiveRole(player, SCPStats.Singleton.Config.DiscordMemberRole); } } @@ -444,7 +525,7 @@ private static bool CheckSingle(string req, UserInfoData data) private static void GiveRole(Player player, string key) { - Log.Debug("Giving " + player.UserId + " the role " + key, SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Giving " + player.UserId + " the role " + key); if (!ServerStatic.PermissionsHandler._groups.ContainsKey(key)) { @@ -468,7 +549,7 @@ private static void GiveRole(Player player, string key) Rainbow(player); - Log.Debug("Successfully gave role!", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Successfully gave role!"); } private static bool PlayerHasGroup(Player p, string key) diff --git a/SCPStats/Websocket/WebsocketThread.cs b/SCPStats/Websocket/WebsocketThread.cs index 5f9c270..4050119 100644 --- a/SCPStats/Websocket/WebsocketThread.cs +++ b/SCPStats/Websocket/WebsocketThread.cs @@ -162,7 +162,7 @@ private static void OnClose(object sender, CloseEventArgs e) ws.OnClose -= OnClose; ws.OnError -= OnError; - Log.Debug("Restarting Websocket Client", SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("Restarting Websocket Client"); CreateConnection(10000); } @@ -214,7 +214,7 @@ private static void OnMessage(object sender, MessageEventArgs e) return; } - Log.Debug("<"+e.Data, SCPStats.Singleton?.Config?.Debug ?? false); + Log.Debug("<"+e.Data); WebsocketRequests.Enqueue(e.Data); }