diff --git a/TShockAPI/ConfigFile.cs b/TShockAPI/ConfigFile.cs index f32099ab..c0227d05 100755 --- a/TShockAPI/ConfigFile.cs +++ b/TShockAPI/ConfigFile.cs @@ -295,6 +295,10 @@ namespace TShockAPI [Description("Disable a player if this number of projectiles is created within 1 second.")] public int ProjectileThreshold = 50; + /// HealOtherThreshold - Disables a player if this number of HealOtherPlayer packets is sent within 1 second. + [Description("Disables a player if this number of HealOtherPlayer packets is sent within 1 second.")] + public int HealOtherThreshold = 50; + /// ProjIgnoreShrapnel - Whether or not to ignore shrapnel from crystal bullets for the projectile threshold count. [Description("Ignore shrapnel from crystal bullets for projectile threshold.")] public bool ProjIgnoreShrapnel = true; diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 88b33fef..aae3839b 100755 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -1261,7 +1261,8 @@ namespace TShockAPI { PacketTypes.PlaceItemFrame, HandlePlaceItemFrame }, { PacketTypes.SyncExtraValue, HandleSyncExtraValue }, { PacketTypes.LoadNetModule, HandleLoadNetModule }, - { PacketTypes.ToggleParty, HandleToggleParty } + { PacketTypes.ToggleParty, HandleToggleParty }, + { PacketTypes.PlayerHealOther, HandleHealOther } }; } @@ -1283,6 +1284,37 @@ namespace TShockAPI return false; } + private static bool HandleHealOther(GetDataHandlerArgs args) + { + byte plr = args.Data.ReadInt8(); + short amount = args.Data.ReadInt16(); + + if (amount <= 0 || Main.player[plr] == null || !Main.player[plr].active) + { + return true; + } + + if (amount > TShock.Config.MaxDamage * 0.2) + { + args.Player.Disable("HealOtherPlayer cheat attempt!", DisableFlags.WriteToLogAndConsole); + return true; + } + + if (args.Player.HealOtherThreshold > TShock.Config.HealOtherThreshold) + { + args.Player.Disable("Reached HealOtherPlayer threshold.", DisableFlags.WriteToLogAndConsole); + return true; + } + + if (TShock.CheckIgnores(args.Player) || (DateTime.UtcNow - args.Player.LastThreat).TotalMilliseconds < 5000) + { + return true; + } + + args.Player.HealOtherThreshold++; + return false; + } + private static bool HandlePlayerSlot(GetDataHandlerArgs args) { byte plr = args.Data.ReadInt8(); diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index ee2348f6..5addd336 100755 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -93,7 +93,12 @@ namespace TShockAPI /// The number of projectiles created by the player in the last second. /// public int ProjectileThreshold { get; set; } - + + /// + /// The number of HealOtherPlayer packets sent by the player in the last second. + /// + public int HealOtherThreshold { get; set; } + /// /// A timer to keep track of whether or not the player has recently thrown an explosive /// diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index ae5f7889..be37a462 100755 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1030,6 +1030,15 @@ namespace TShockAPI player.PaintThreshold = 0; } + if (player.HealOtherThreshold >= TShock.Config.HealOtherThreshold) + { + player.Disable("Reached HealOtherPlayer threshold", flags); + } + if (player.HealOtherThreshold > 0) + { + player.HealOtherThreshold = 0; + } + if (player.RespawnTimer > 0 && --player.RespawnTimer == 0 && player.Difficulty != 2) { player.Spawn(); @@ -1458,7 +1467,7 @@ namespace TShockAPI /// The CommandEventArgs object private void ServerHooks_OnCommand(CommandEventArgs args) { - if (args.Handled) + if (args.Handled || string.IsNullOrWhiteSpace(args.Command)) return; // Damn you ThreadStatic and Redigit