diff --git a/CHANGELOG.md b/CHANGELOG.md index b36b4a62..2078fd4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * The default group that gets this permission is `Guest` for the time being. * To add this command to your guest group, give them `tshock.synclocalarea`, with `/group addperm guest tshock.synclocalarea`. * This command may be removed at any time in the future (and will likely be removed when send tile square handling is fixed). +* Add FishOutNPC event handler, which is called whenever a player fishes out an NPC using a fishing rod. Added antihack to Bouncer, to prevent unathorized and invalid mob spawning, by checking player action, NPC IDs and range. (@Patrikkk, @moisterrific) * Fixed smart door automatic door desync and deletion issue. (@hakusaro) ## TShock 4.4.0 (Pre-release 8) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 098dd468..395bfddd 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -71,6 +71,7 @@ namespace TShockAPI GetDataHandlers.MassWireOperation += OnMassWireOperation; GetDataHandlers.PlayerDamage += OnPlayerDamage; GetDataHandlers.KillMe += OnKillMe; + GetDataHandlers.FishOutNPC += OnFishOutNPC; GetDataHandlers.FoodPlatterTryPlacing += OnFoodPlatterTryPlacing; } @@ -1882,6 +1883,34 @@ namespace TShockAPI } } + /// + /// Called when the player fishes out an NPC. + /// + /// + /// + internal void OnFishOutNPC(object sender, GetDataHandlers.FishOutNPCEventArgs args) + { + var projectile = args.Player.RecentlyCreatedProjectiles.FirstOrDefault(p => Main.projectile[p.Index] != null && Main.projectile[p.Index].Name == "Bobber"); + + if (!FishingRodItemIDs.Contains(args.Player.SelectedItem.type) || Main.projectile[projectile.Index] == null || !FishableNpcIDs.Contains(args.NpcID)) + { + TShock.Log.ConsoleDebug("Bouncer / OnFishOutNPC rejected invalid NPC spawning from {0}", args.Player.Name); + args.Handled = true; + return; + } + if (args.NpcID == NPCID.DukeFishron && !args.Player.HasPermission(Permissions.summonboss)) + { + TShock.Log.ConsoleDebug("Bouncer / OnFishOutNPC rejected summon boss permissions from {0}", args.Player.Name); + args.Handled = true; + return; + } + if (args.Player.IsInRange(args.TileX, args.TileY, 55)) + { + TShock.Log.ConsoleDebug("Bouncer / OnFishOutNPC rejected range checks from {0}", args.Player.Name); + args.Handled = true; + } + } + /// /// Called when a player is trying to place an item into a food plate. /// diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 9e249d30..304de4e2 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -151,6 +151,7 @@ namespace TShockAPI { PacketTypes.CrystalInvasionStart, HandleOldOnesArmy }, { PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 }, { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 }, + { PacketTypes.FishOutNPC, HandleFishOutNPC }, { PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing }, { PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker } }; @@ -1865,6 +1866,45 @@ namespace TShockAPI return args.Handled; } + /// + /// For use in a FishOutNPC event. + /// + public class FishOutNPCEventArgs : GetDataHandledEventArgs + { + /// + /// The X world position of the spawning NPC. + /// + public ushort TileX { get; set; } + /// + /// The Y world position of the spawning NPC. + /// + public ushort TileY { get; set; } + /// + /// The NPC type that is being spawned. + /// + public short NpcID { get; set; } + } + /// + /// Called when a player fishes out an NPC. + /// + public static HandlerList FishOutNPC = new HandlerList(); + private static bool OnFishOutNPC(TSPlayer player, MemoryStream data, ushort tileX, ushort tileY, short npcID) + { + if (FishOutNPC == null) + return false; + + var args = new FishOutNPCEventArgs + { + Player = player, + Data = data, + TileX = tileX, + TileY = tileY, + NpcID = npcID + }; + FishOutNPC.Invoke(null, args); + return args.Handled; + } + public class FoodPlatterTryPlacingEventArgs : GetDataHandledEventArgs { /// @@ -3629,6 +3669,19 @@ namespace TShockAPI return false; } + + private static bool HandleFishOutNPC(GetDataHandlerArgs args) + { + ushort tileX = args.Data.ReadUInt16(); + ushort tileY = args.Data.ReadUInt16(); + short npcType = args.Data.ReadInt16(); + + if (OnFishOutNPC(args.Player, args.Data, tileX, tileY, npcType)) + return true; + + return false; + } + private static bool HandleFoodPlatterTryPlacing(GetDataHandlerArgs args) { short tileX = args.Data.ReadInt16(); @@ -3717,6 +3770,39 @@ namespace TShockAPI TileID.Womannequin, }; + /// + /// List of Fishing rod item IDs. + /// + internal static readonly List FishingRodItemIDs = new List() + { + ItemID.WoodFishingPole, + ItemID.ReinforcedFishingPole, + ItemID.FiberglassFishingPole, + ItemID.FisherofSouls, + ItemID.GoldenFishingRod, + ItemID.MechanicsRod, + ItemID.SittingDucksFishingRod, + ItemID.Fleshcatcher, + ItemID.HotlineFishingHook, + ItemID.BloodFishingRod, + ItemID.ScarabFishingRod + }; + + /// + /// List of NPC IDs that can be fished out by the player. + /// + internal static readonly List FishableNpcIDs = new List() + { + NPCID.EyeballFlyingFish, + NPCID.ZombieMerman, + NPCID.GoblinShark, + NPCID.BloodEelHead, + NPCID.BloodEelBody, + NPCID.BloodEelTail, + NPCID.BloodNautilus, + NPCID.DukeFishron + }; + /// /// These projectiles create tiles on death. ///