diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f36e88..e97338de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,6 +90,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Added filtering and validation on packet 96 (Teleport player through portal) (@QuiCM) * Update tracker now uses TLS (@pandabear41) * When deleting an user account, any player logged in to that account is now logged out properly (@Enerdy) +* Add NPCAddBuff data handler and bouncer (@AxeelAnder) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 223de05b..7287924f 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -36,6 +36,31 @@ namespace TShockAPI /// Bouncer is the TShock anti-hack and anti-cheat system. internal sealed class Bouncer { + static Dictionary NPCAddBuffTimeMax = new Dictionary() + { + { BuffID.Poisoned, 3600 }, + { BuffID.OnFire, 1200 }, + { BuffID.CursedInferno, 420 }, + { BuffID.Frostburn, 900 }, + { BuffID.Ichor, 1200 }, + { BuffID.Venom, 1260 }, + { BuffID.Midas, 120 }, + { BuffID.Wet, 1500 }, + { BuffID.Slimed, 1500 }, + { BuffID.Lovestruck, 1800 }, + { BuffID.Stinky, 1800 }, + { BuffID.SoulDrain, 30 }, + { BuffID.ShadowFlame, 660 }, + { BuffID.DryadsWard, 120 }, + { BuffID.BoneJavelin, 900 }, + { BuffID.StardustMinionBleed, 900 }, + { BuffID.DryadsWardDebuff, 120 }, + { BuffID.Daybreak, 300 }, + { BuffID.BetsysCurse, 600 }, + { BuffID.Oiled, 540 } + }; + + /// Constructor call initializes Bouncer and related functionality. /// A new Bouncer. internal Bouncer() @@ -49,6 +74,7 @@ namespace TShockAPI GetDataHandlers.PlayerAnimation += OnPlayerAnimation; GetDataHandlers.NPCStrike += OnNPCStrike; GetDataHandlers.ItemDrop += OnItemDrop; + GetDataHandlers.NPCAddBuff += OnNPCAddBuff; GetDataHandlers.PlayerBuff += OnPlayerBuff; GetDataHandlers.ChestItemChange += OnChestItemChange; GetDataHandlers.NPCHome += OnUpdateNPCHome; @@ -454,6 +480,65 @@ namespace TShockAPI } } + /// Handles NPCAddBuff events. + /// The object that triggered the event. + /// The packet arguments that the event has. + internal void OnNPCAddBuff(object sender, GetDataHandlers.NPCAddBuffEventArgs args) + { + short id = args.ID; + byte type = args.Type; + short time = args.Time; + + if (id >= Main.npc.Length) + { + args.Handled = true; + return; + } + + NPC npc = Main.npc[id]; + + if (npc == null) + { + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + args.Handled = true; + return; + } + + bool detectedNPCBuffTimeCheat = false; + + if (NPCAddBuffTimeMax.ContainsKey(type)) + { + if (time > NPCAddBuffTimeMax[type]) + { + detectedNPCBuffTimeCheat = true; + } + + if (npc.townNPC && npc.netID != NPCID.Guide && npc.netID != NPCID.Clothier) + { + if (type != BuffID.Lovestruck && type != BuffID.Stinky && type != BuffID.DryadsWard && + type != BuffID.Wet && type != BuffID.Slimed) + { + detectedNPCBuffTimeCheat = true; + } + } + } + else + { + detectedNPCBuffTimeCheat = true; + } + + if (detectedNPCBuffTimeCheat) + { + args.Player.Kick("Added buff to NPC abnormally.", true); + args.Handled = true; + } + } + /// Handles Buff events. /// The object that triggered the event. /// The packet arguments that the event has. diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index c59ce1a7..a7792569 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -1003,6 +1003,46 @@ namespace TShockAPI NPCHome.Invoke(null, args); return args.Handled; } + + /// + /// For use in a NPCAddBuff event + /// + public class NPCAddBuffEventArgs : GetDataHandledEventArgs + { + /// + /// The ID of the npc + /// + public short ID { get; set; } + /// + /// Buff Type + /// + public byte Type { get; set; } + /// + /// Time the buff lasts + /// + public short Time { get; set; } + } + /// + /// NPCAddBuff - Called when a npc is buffed + /// + public static HandlerList NPCAddBuff = new HandlerList(); + + private static bool OnNPCAddBuff(TSPlayer player, MemoryStream data, short id, byte type, short time) + { + if (NPCAddBuff == null) + return false; + + var args = new NPCAddBuffEventArgs + { + Player = player, + Data = data, + ID = id, + Type = type, + Time = time + }; + NPCAddBuff.Invoke(null, args); + return args.Handled; + } /// /// For use in a PlayerBuff event @@ -1455,6 +1495,7 @@ namespace TShockAPI { PacketTypes.PlayerSlot, HandlePlayerSlot }, { PacketTypes.TileGetSection, HandleGetSection }, { PacketTypes.UpdateNPCHome, UpdateNPCHome }, + { PacketTypes.NpcAddBuff, HandleNPCAddBuff }, { PacketTypes.PlayerAddBuff, HandlePlayerAddBuff }, { PacketTypes.ItemDrop, HandleItemDrop }, { PacketTypes.UpdateItemDrop, HandleItemDrop }, @@ -2627,6 +2668,18 @@ namespace TShockAPI } return false; } + + private static bool HandleNPCAddBuff(GetDataHandlerArgs args) + { + var id = args.Data.ReadInt16(); + var type = args.Data.ReadInt8(); + var time = args.Data.ReadInt16(); + + if (OnNPCAddBuff(args.Player, args.Data, id, type, time)) + return true; + + return false; + } private static bool HandlePlayerAddBuff(GetDataHandlerArgs args) {