From 9a05678933e2b1105940775b24b9a1d49c738bf7 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Sun, 17 May 2020 21:41:31 +0200 Subject: [PATCH 1/6] Updating HandleProjectileNew Packet structure order is different in 1.4 --- TShockAPI/GetDataHandlers.cs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 31a316b1..599133d6 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2296,23 +2296,21 @@ namespace TShockAPI private static bool HandleProjectileNew(GetDataHandlerArgs args) { short ident = args.Data.ReadInt16(); - var pos = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); - var vel = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); - float knockback = args.Data.ReadSingle(); - short dmg = args.Data.ReadInt16(); + Vector2 pos = args.Data.ReadVector2(); + Vector2 vel = args.Data.ReadVector2(); byte owner = args.Data.ReadInt8(); short type = args.Data.ReadInt16(); - BitsByte bits = args.Data.ReadInt8(); - //owner = (byte)args.Player.Index; + BitsByte bits = (BitsByte)args.Data.ReadByte(); float[] ai = new float[Projectile.maxAI]; + for (int i = 0; i < Projectile.maxAI; ++i) + ai[i] = !bits[i] ? 0.0f : args.Data.ReadSingle(); + short dmg = bits[4] ? args.Data.ReadInt16() : (short)0; + float knockback = bits[5] ? args.Data.ReadSingle() : 0.0f; + short origDmg = bits[6] ? args.Data.ReadInt16() : (short)0; + short projUUID = bits[7] ? args.Data.ReadInt16() : (short)-1; + if (projUUID >= 1000) + projUUID = -1; - for (int i = 0; i < Projectile.maxAI; i++) - { - if (bits[i]) - ai[i] = args.Data.ReadSingle(); - else - ai[i] = 0f; - } var index = TShock.Utils.SearchProjectile(ident, owner); From 2d5166cfa4aa54923eff12c514dbefaf0f8906c7 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 18 May 2020 00:04:49 +0200 Subject: [PATCH 2/6] Updating SpawnPlayer and PlayerUpdate packet Fixing the HasHackedItemStacks method by adding missing Void Vault check. --- TShockAPI/Bouncer.cs | 2 +- TShockAPI/GetDataHandlers.cs | 112 +++++++++++++++++++++++++---------- TShockAPI/ItemBans.cs | 4 +- TShockAPI/TSPlayer.cs | 31 ++++++++-- 4 files changed, 111 insertions(+), 38 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 5258e7dd..56c77839 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -99,7 +99,7 @@ namespace TShockAPI byte plr = args.PlayerId; BitsByte control = args.Control; BitsByte pulley = args.Pulley; - byte item = args.Item; + byte item = args.SelectedItem ; var pos = args.Position; var vel = args.Velocity; diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 599133d6..360fd6e8 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -307,25 +307,44 @@ namespace TShockAPI /// public byte Control { get; set; } /// - /// Selected item + /// Pulley update (BitFlags) /// - public byte Item { get; set; } + public byte Pulley { get; set; } /// - /// Position of the player + /// Misc (BitFlags) Check tshock.readme.io + /// + public byte Misc { get; set; } + /// + /// (BitFlags) Wether or not the player is sleeping. + /// + public byte Sleeping { get; set; } + /// + /// The selected item in player's hand. + /// + public byte SelectedItem { get; set; } + /// + /// Position of the player. /// public Vector2 Position { get; set; } /// - /// Velocity of the player + /// Velocity of the player. /// public Vector2 Velocity { get; set; } - /// Pulley update (BitFlags) - public byte Pulley { get; set; } + /// + /// Original poisition of the player when using Potion of Return. + /// + public Vector2? OriginalPos { get; set; } + /// + /// Home Position of the player for Potion of Return. + /// + public Vector2? HomePos { get; set; } + } /// /// PlayerUpdate - When the player sends it's updated information to the server /// public static HandlerList PlayerUpdate = new HandlerList(); - private static bool OnPlayerUpdate(TSPlayer player, MemoryStream data, byte plr, byte control, byte item, Vector2 position, Vector2 velocity, byte pulley) + private static bool OnPlayerUpdate(TSPlayer player, MemoryStream data, byte plr, byte control, byte pulley, byte misc, byte sleeping, byte selectedItem, Vector2 position, Vector2 velocity, Vector2? originalPos, Vector2? homePos) { if (PlayerUpdate == null) return false; @@ -336,10 +355,14 @@ namespace TShockAPI Data = data, PlayerId = plr, Control = control, - Item = item, + Pulley = pulley, + Misc = misc, + Sleeping = sleeping, + SelectedItem = selectedItem, Position = position, Velocity = velocity, - Pulley = pulley + OriginalPos = originalPos, + HomePos = homePos }; PlayerUpdate.Invoke(null, args); return args.Handled; @@ -746,12 +769,21 @@ namespace TShockAPI /// Y location of the player's spawn /// public int SpawnY { get; set; } + /// + /// Value of the timer countdown before the player can respawn alive. + /// If > 0, then player is still dead. + /// + public int RespawnTimer { get; set; } + /// + /// Context of where the player is spawning from. + /// + public PlayerSpawnContext SpawnContext { get; set; } } /// /// PlayerSpawn - When a player spawns /// public static HandlerList PlayerSpawn = new HandlerList(); - private static bool OnPlayerSpawn(TSPlayer player, MemoryStream data, byte pid, int spawnX, int spawnY) + private static bool OnPlayerSpawn(TSPlayer player, MemoryStream data, byte pid, int spawnX, int spawnY, int respawnTimer, PlayerSpawnContext spawnContext) { if (PlayerSpawn == null) return false; @@ -763,6 +795,8 @@ namespace TShockAPI PlayerId = pid, SpawnX = spawnX, SpawnY = spawnY, + RespawnTimer = respawnTimer, + SpawnContext = spawnContext }; PlayerSpawn.Invoke(null, args); return args.Handled; @@ -2006,11 +2040,13 @@ namespace TShockAPI private static bool HandleSpawn(GetDataHandlerArgs args) { - var player = args.Data.ReadInt8(); - var spawnx = args.Data.ReadInt16(); - var spawny = args.Data.ReadInt16(); + byte player = args.Data.ReadInt8(); + short spawnx = args.Data.ReadInt16(); + short spawny = args.Data.ReadInt16(); + int respawnTimer = args.Data.ReadInt32(); + PlayerSpawnContext context = (PlayerSpawnContext)args.Data.ReadByte(); - if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny)) + if (OnPlayerSpawn(args.Player, args.Data, player, spawnx, spawny, respawnTimer, context)) return true; if ((Main.ServerSideCharacter) && (args.Player.sX > 0) && (args.Player.sY > 0) && (args.TPlayer.SpawnX > 0) && ((args.TPlayer.SpawnX != args.Player.sX) && (args.TPlayer.SpawnY != args.Player.sY))) @@ -2029,7 +2065,10 @@ namespace TShockAPI args.Player.Teleport(args.Player.sX * 16, (args.Player.sY * 16) - 48); } - args.Player.Dead = false; + if (respawnTimer > 0) + args.Player.Dead = true; + else + args.Player.Dead = false; return false; } @@ -2040,34 +2079,45 @@ namespace TShockAPI return true; } - byte plr = args.Data.ReadInt8(); - BitsByte control = args.Data.ReadInt8(); - BitsByte pulley = args.Data.ReadInt8(); - byte item = args.Data.ReadInt8(); - var pos = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); - var vel = Vector2.Zero; - if (pulley[2]) - vel = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle()); + byte playerID = args.Data.ReadInt8(); + BitsByte control = (BitsByte)args.Data.ReadByte(); + BitsByte pulley = (BitsByte)args.Data.ReadByte(); + BitsByte misc = (BitsByte)args.Data.ReadByte(); + BitsByte sleeping = (BitsByte)args.Data.ReadByte(); + byte selectedItem = args.Data.ReadInt8(); + Vector2 position = args.Data.ReadVector2(); - if (OnPlayerUpdate(args.Player, args.Data, plr, control, item, pos, vel, pulley)) + Vector2 velocity = Vector2.Zero; + if (pulley[2]) // if UpdateVelocity + velocity = args.Data.ReadVector2(); + + Vector2? originalPosition = new Vector2?(); + Vector2? homePosition = Vector2.Zero; + if (misc[6]) // if UsedPotionofReturn + { + originalPosition = new Vector2?(args.Data.ReadVector2()); + homePosition = new Vector2?(args.Data.ReadVector2()); + } + + if (OnPlayerUpdate(args.Player, args.Data, playerID, control, pulley, misc, sleeping, selectedItem, position, velocity, originalPosition, homePosition)) return true; if (control[5]) { // Reimplementation of normal Terraria stuff? - if (args.TPlayer.inventory[item].Name == "Mana Crystal" && args.Player.TPlayer.statManaMax <= 180) + if (args.TPlayer.inventory[selectedItem].Name == "Mana Crystal" && args.Player.TPlayer.statManaMax <= 180) { args.Player.TPlayer.statMana += 20; args.Player.TPlayer.statManaMax += 20; args.Player.PlayerData.maxMana += 20; } - else if (args.TPlayer.inventory[item].Name == "Life Crystal" && args.Player.TPlayer.statLifeMax <= 380) + else if (args.TPlayer.inventory[selectedItem].Name == "Life Crystal" && args.Player.TPlayer.statLifeMax <= 380) { args.TPlayer.statLife += 20; args.TPlayer.statLifeMax += 20; args.Player.PlayerData.maxHealth += 20; } - else if (args.TPlayer.inventory[item].Name == "Life Fruit" && args.Player.TPlayer.statLifeMax >= 400 && args.Player.TPlayer.statLifeMax <= 495) + else if (args.TPlayer.inventory[selectedItem].Name == "Life Fruit" && args.Player.TPlayer.statLifeMax >= 400 && args.Player.TPlayer.statLifeMax <= 495) { args.TPlayer.statLife += 5; args.TPlayer.statLifeMax += 5; @@ -2076,11 +2126,11 @@ namespace TShockAPI } // Where we rebuild sync data for Terraria? - args.TPlayer.selectedItem = item; - args.TPlayer.position = pos; + args.TPlayer.selectedItem = selectedItem; + args.TPlayer.position = position; args.TPlayer.oldVelocity = args.TPlayer.velocity; - args.TPlayer.velocity = vel; - args.TPlayer.fallStart = (int)(pos.Y / 16f); + args.TPlayer.velocity = velocity; + args.TPlayer.fallStart = (int)(position.Y / 16f); args.TPlayer.controlUp = false; args.TPlayer.controlDown = false; args.TPlayer.controlLeft = false; diff --git a/TShockAPI/ItemBans.cs b/TShockAPI/ItemBans.cs index a85f462c..0ebd7ee6 100644 --- a/TShockAPI/ItemBans.cs +++ b/TShockAPI/ItemBans.cs @@ -156,9 +156,9 @@ namespace TShockAPI DisableFlags disableFlags = TShock.Config.DisableSecondUpdateLogs ? DisableFlags.WriteToConsole : DisableFlags.WriteToLogAndConsole; bool useItem = ((BitsByte) args.Control)[5]; TSPlayer player = args.Player; - string itemName = player.TPlayer.inventory[args.Item].Name; + string itemName = player.TPlayer.inventory[args.SelectedItem].Name; - if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(player.TPlayer.inventory[args.Item].netID), args.Player)) + if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(player.TPlayer.inventory[args.SelectedItem].netID), args.Player)) { player.TPlayer.controlUseItem = false; player.Disable($"holding banned item: {itemName}", disableFlags); diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 5829f877..9a772227 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -360,6 +360,8 @@ namespace TShockAPI Item[] piggy = TPlayer.bank.item; Item[] safe = TPlayer.bank2.item; Item[] forge = TPlayer.bank3.item; + Item[] voidVault = TPlayer.bank4.item; + Item trash = TPlayer.trashItem; for (int i = 0; i < NetItem.MaxInventory; i++) { @@ -507,7 +509,7 @@ namespace TShockAPI } else if (i < NetItem.TrashIndex.Item2) { - // 179-219 + // 178-179 Item item = new Item(); if (trash != null && trash.netID != 0) { @@ -525,9 +527,9 @@ namespace TShockAPI } } } - else + else if (i < NetItem.ForgeIndex.Item2) { - // 220 + // 179-220 var index = i - NetItem.ForgeIndex.Item1; Item item = new Item(); if (forge[index] != null && forge[index].netID != 0) @@ -545,8 +547,29 @@ namespace TShockAPI } } } - } + else if (i < NetItem.VoidIndex.Item2) + { + // 220-260 + var index = i - NetItem.VoidIndex.Item1; + Item item = new Item(); + if (voidVault[index] != null && voidVault[index].netID != 0) + { + item.netDefaults(voidVault[index].netID); + item.Prefix(voidVault[index].prefix); + item.AffixName(); + + if (voidVault[index].stack > item.maxStack || voidVault[index].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove Void Vault item {0} ({1}) and then rejoin.", item.Name, voidVault[index].stack); + } + } + } + } + } return check; From 3b3fa9fc7218c10fa126337b111fe2b4fd40a403 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 18 May 2020 00:41:12 +0200 Subject: [PATCH 3/6] Update Modify Tile packet. Only updating. Did not patch any possible exploits. Requesting someone to tend to this when possible. --- TShockAPI/Bouncer.cs | 1 + TShockAPI/GetDataHandlers.cs | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 56c77839..a5f132ac 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -183,6 +183,7 @@ namespace TShockAPI /// The packet arguments that the event has. internal void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs args) { + // TODO: Add checks on the new edit actions. ReplaceTile, ReplaceWall, TryKillTile, Acutate, PokeLogicGate, SlopePoundTile EditAction action = args.Action; int tileX = args.X; int tileY = args.Y; diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 360fd6e8..570c020c 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2249,17 +2249,17 @@ namespace TShockAPI private static bool HandleTile(GetDataHandlerArgs args) { EditAction action = (EditAction)args.Data.ReadInt8(); - var tileX = args.Data.ReadInt16(); - var tileY = args.Data.ReadInt16(); - var editData = args.Data.ReadInt16(); + short tileX = args.Data.ReadInt16(); + short tileY = args.Data.ReadInt16(); + short editData = args.Data.ReadInt16(); EditType type = (action == EditAction.KillTile || action == EditAction.KillWall || - action == EditAction.KillTileNoItem) + action == EditAction.KillTileNoItem || action == EditAction.TryKillTile) ? EditType.Fail - : (action == EditAction.PlaceTile || action == EditAction.PlaceWall) + : (action == EditAction.PlaceTile || action == EditAction.PlaceWall || action == EditAction.ReplaceTile || action == EditAction.ReplaceWall) ? EditType.Type : EditType.Slope; - var style = args.Data.ReadInt8(); + byte style = args.Data.ReadInt8(); if (OnTileEdit(args.Player, args.Data, tileX, tileY, action, type, editData, style)) return true; @@ -3406,7 +3406,13 @@ namespace TShockAPI SlopeTile, FrameTrack, PlaceWire4, - KillWire4 + KillWire4, + PokeLogicGate, + Acutate, + TryKillTile, + ReplaceTile, + ReplaceWall, + SlopePoundTile } public enum EditType { @@ -3414,7 +3420,6 @@ namespace TShockAPI Type, Slope, } - /// /// The maximum place styles for each tile. /// From a02f959f5841a3376ec18df2b494cf2fe2e0fb40 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 18 May 2020 02:02:02 +0200 Subject: [PATCH 4/6] Update Buff related packets Buff IDs no longer fit in a byte. Vanilla reads/writes ushorts but they get converted to int. - Added helptip for Zone4 in playerzones eventargs. --- TShockAPI/Bouncer.cs | 6 +++--- TShockAPI/GetDataHandlers.cs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index a5f132ac..17c1a92e 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -1285,7 +1285,7 @@ namespace TShockAPI internal void OnPlayerBuff(object sender, GetDataHandlers.PlayerBuffEventArgs args) { byte id = args.ID; - byte type = args.Type; + int type = args.Type; int time = args.Time; if (TShock.Players[id] == null) @@ -1342,7 +1342,7 @@ namespace TShockAPI internal void OnNPCAddBuff(object sender, GetDataHandlers.NPCAddBuffEventArgs args) { short id = args.ID; - byte type = args.Type; + int type = args.Type; short time = args.Time; if (id >= Main.npc.Length) @@ -1857,7 +1857,7 @@ namespace TShockAPI } - private static Dictionary NPCAddBuffTimeMax = new Dictionary() + private static Dictionary NPCAddBuffTimeMax = new Dictionary() { { BuffID.Poisoned, 3600 }, { BuffID.OnFire, 1200 }, diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 570c020c..a1fc937d 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -944,7 +944,7 @@ namespace TShockAPI /// public BitsByte Zone3 { get; set; } /// - /// 0 = Old One's Army + /// 0 = Old One's Army, 1 = Granite, 2 = Marble, 3 = Hive, 4 = Gem Cave, 5 = Lihzhard Temple, 6 = Graveyard /// public BitsByte Zone4 { get; set; } } @@ -1215,7 +1215,7 @@ namespace TShockAPI /// /// Buff Type /// - public byte Type { get; set; } + public int Type { get; set; } /// /// Time the buff lasts /// @@ -1225,7 +1225,7 @@ namespace TShockAPI /// 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) + private static bool OnNPCAddBuff(TSPlayer player, MemoryStream data, short id, int type, short time) { if (NPCAddBuff == null) return false; @@ -1254,7 +1254,7 @@ namespace TShockAPI /// /// Buff Type /// - public byte Type { get; set; } + public int Type { get; set; } /// /// Time the buff lasts /// @@ -1264,7 +1264,7 @@ namespace TShockAPI /// PlayerBuff - Called when a player is buffed /// public static HandlerList PlayerBuff = new HandlerList(); - private static bool OnPlayerBuff(TSPlayer player, MemoryStream data, byte id, byte type, int time) + private static bool OnPlayerBuff(TSPlayer player, MemoryStream data, byte id, int type, int time) { if (PlayerBuff == null) return false; @@ -2714,7 +2714,7 @@ namespace TShockAPI for (int i = 0; i < Terraria.Player.maxBuffs; i++) { - var buff = args.Data.ReadInt8(); + var buff = args.Data.ReadUInt16(); if (buff == 10 && TShock.Config.DisableInvisPvP && args.TPlayer.hostile) buff = 0; @@ -2765,7 +2765,7 @@ namespace TShockAPI private static bool HandleNPCAddBuff(GetDataHandlerArgs args) { var id = args.Data.ReadInt16(); - var type = args.Data.ReadInt8(); + var type = args.Data.ReadUInt16(); var time = args.Data.ReadInt16(); if (OnNPCAddBuff(args.Player, args.Data, id, type, time)) @@ -2777,7 +2777,7 @@ namespace TShockAPI private static bool HandlePlayerAddBuff(GetDataHandlerArgs args) { var id = args.Data.ReadInt8(); - var type = args.Data.ReadInt8(); + var type = args.Data.ReadUInt16(); var time = args.Data.ReadInt32(); if (OnPlayerBuff(args.Player, args.Data, id, type, time)) From 91e3ebf4f07008929dacfadd25fd49494eb4cbe5 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 18 May 2020 02:37:39 +0200 Subject: [PATCH 5/6] Fix false cheat catch on stab items. Stab items (swords) now send projectiles to imitate directional stabbing. Bouncer catched since there was no handling of these new projectiles. --- TShockAPI/Bouncer.cs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 17c1a92e..18b0cbd3 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -796,6 +796,17 @@ namespace TShockAPI return; } + + if (stabProjectile.ContainsKey(type)) + { + if (stabProjectile.Values.Any(t => t == args.Player.TPlayer.HeldItem.type)) + { + args.Handled = false; + return; + } + } + + // Main.projHostile contains projectiles that can harm players // without PvP enabled and belong to enemy mobs, so they shouldn't be // possible for players to create. (Source: Ijwu, QuiCM) @@ -1922,5 +1933,18 @@ namespace TShockAPI TileID.Campfire }; + private static Dictionary stabProjectile = new Dictionary() + { + { ProjectileID.GladiusStab, ItemID.Gladius }, + { ProjectileID.RulerStab, ItemID.Ruler }, + { ProjectileID.CopperShortswordStab, ItemID.CopperShortsword }, + { ProjectileID.TinShortswordStab, ItemID.TinShortsword }, + { ProjectileID.IronShortswordStab, ItemID.IronShortsword }, + { ProjectileID.LeadShortswordStab, ItemID.LeadShortsword }, + { ProjectileID.SilverShortswordStab, ItemID.SilverShortsword }, + { ProjectileID.TungstenShortswordStab, ItemID.TungstenShortsword }, + { ProjectileID.GoldShortswordStab, ItemID.GoldShortsword }, + { ProjectileID.PlatinumShortswordStab, ItemID.PlatinumShortsword } + }; } } From f48a18aa7aee09ca553569201e35834623d8ad69 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 18 May 2020 02:44:39 +0200 Subject: [PATCH 6/6] Fixing my random if logic. Really.. time for bed. The Any wasn't terrible, but itwould have allow cross sending projectiles within that dictionary. (send gold stab with copper in hand, or vice versa etc) --- TShockAPI/Bouncer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 18b0cbd3..511c5820 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -799,7 +799,7 @@ namespace TShockAPI if (stabProjectile.ContainsKey(type)) { - if (stabProjectile.Values.Any(t => t == args.Player.TPlayer.HeldItem.type)) + if (stabProjectile[type] == args.Player.TPlayer.HeldItem.type) { args.Handled = false; return;