diff --git a/CHANGELOG.md b/CHANGELOG.md index 94b9e1ba..c79a1ea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Fixed kick on hardcore death / kick on mediumcore death / ban on either from taking action against journey mode players. (@hakusaro) * Attempted to fix the problem with the magic mirror spawn problems. You should be able to remove your spawn point in SSC by right clicking on a bed now. (@hakusaro, @AxeelAnder) * Add HandleFoodPlatterTryPlacing event, which is called whenever a player places a food in a plate. Add antihack to bouncer, to prevent removing food from plates if the region is protected; To prevent placement if they are not in range; To prevent placement if the item is not placed from player hand. (@Patrikkk) +* 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) ## TShock 4.4.0 (Pre-release 8) * Update for OTAPI 2.0.0.36 and Terraria 1.4.0.4. (@hakusaro, @Patrikkk, @DeathCradle) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index abd5fe05..b0bc2e9b 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -67,6 +67,7 @@ namespace TShockAPI GetDataHandlers.MassWireOperation += OnMassWireOperation; GetDataHandlers.PlayerDamage += OnPlayerDamage; GetDataHandlers.KillMe += OnKillMe; + GetDataHandlers.FishOutNPC += OnFishOutNPC; GetDataHandlers.FoodPlatterTryPlacing += OnFoodPlatterTryPlacing; } @@ -2063,6 +2064,41 @@ namespace TShockAPI } } + /// + /// Called when the player fishes out an NPC. + /// + /// + /// + internal void OnFishOutNPC(object sender, GetDataHandlers.FishOutNPCEventArgs args) + { + Projectile projectile = null; + foreach (var recentProjectile in args.Player.RecentlyCreatedProjectiles) + { + if (Main.projectile[recentProjectile.Index] != null && Main.projectile[recentProjectile.Index].Name == "Bobber") + { + projectile = Main.projectile[recentProjectile.Index]; + break; + } + } + if (!FishingRodItemIDs.Contains(args.Player.SelectedItem.type) || projectile == null || !FishableNpcIDs.Contains(args.NpcID)) + { + TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing 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 / OnFoodPlatterTryPlacing 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 74dbff85..9d2b63ef 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 } }; } @@ -1864,6 +1865,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 (FoodPlatterTryPlacing == 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 { /// @@ -3606,6 +3646,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(); @@ -3679,6 +3732,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. ///