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)
{