diff --git a/CHANGELOG.md b/CHANGELOG.md index 43c4b274..7b25a146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin ## TShock 4.5.12 * Fixed the ability to spawn Zenith projectile with non-original items. (@AgaSpace) * Fixed item dupe via /logout & NPC. (@Terrarxxn) +* Added hook `GetDataHandlers.OnNpcTalk` for NpcTalk and a handler for it that stops unregistered and logged out players from interacting with NPCs, preventing them from smuggling or duplicating items via NPC item slots. (@tru321) +* Fixed the ability to create custom messages with your death (or the death of another player) (@AgaSpace) ## TShock 4.5.11 * Add the new allowed buff TentacleSpike to NPC buff cheat detection bouncer. (@sgkoishi) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 2145b373..1d1ccf26 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -2178,6 +2178,7 @@ namespace TShockAPI bool pvp = args.PVP; bool crit = args.Critical; byte direction = args.Direction; + PlayerDeathReason reason = args.PlayerDeathReason; if (id >= Main.maxPlayers || TShock.Players[id] == null) { @@ -2242,6 +2243,20 @@ namespace TShockAPI return; } + /* + * PlayerDeathReason does not initially contain any information, so all fields have values -1 or null. + * We can use this to determine the real cause of death. + * + * If the player was not specified, that is, the player index is -1, then it is definitely a custom cause, as you can only deal damage with a projectile or another player. + * This is how everything else works. If an NPC is specified, its value is not -1, which is a custom cause. + */ + if (TShock.Config.Settings.DisableCustomDeathMessages && + (reason._sourcePlayerIndex == -1 || reason._sourceNPCIndex != -1 || reason._sourceOtherIndex != -1 || reason._sourceCustomReason != null)) + { + TShock.Log.ConsoleDebug("Bouncer / OnPlayerDamage rejected custom death message from {0}", args.Player.Name); + args.Handled = true; + return; + } } /// Bouncer's KillMe hook stops crash exploits from out of bounds values. @@ -2279,6 +2294,12 @@ namespace TShockAPI args.Handled = true; return; } + if (TShock.Config.Settings.DisableCustomDeathMessages && playerDeathReason._sourceCustomReason != null) + { + TShock.Log.ConsoleDebug("Bouncer / OnKillMe rejected custom death message from {0}", args.Player.Name); + args.Handled = true; + return; + } } } diff --git a/TShockAPI/Configuration/TShockConfig.cs b/TShockAPI/Configuration/TShockConfig.cs index 2c643fab..d87dfa22 100644 --- a/TShockAPI/Configuration/TShockConfig.cs +++ b/TShockAPI/Configuration/TShockConfig.cs @@ -460,6 +460,10 @@ namespace TShockAPI.Configuration /// Prohibit the use of Zenith projectile with different objects instead of weapons. [Description("Prohibit the use of Zenith projectile with different objects instead of weapons.")] public bool DisableModifiedZenith = false; + + /// Allows you to disable or enable protection against creating custom messages with death. Created for developers who came up with a more original solution to this problem. + [Description("Allows you to disable or enable protection against creating custom messages with death. Created for developers who came up with a more original solution to this problem.")] + public bool DisableCustomDeathMessages = true; #endregion diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index c4366961..bf0fb54e 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -118,6 +118,7 @@ namespace TShockAPI { PacketTypes.PlaceChest, HandlePlaceChest }, { PacketTypes.Zones, HandlePlayerZone }, { PacketTypes.PasswordSend, HandlePassword }, + { PacketTypes.NpcTalk, HandleNpcTalk }, { PacketTypes.PlayerAnimation, HandlePlayerAnimation }, { PacketTypes.PlayerMana, HandlePlayerMana }, { PacketTypes.PlayerTeam, HandlePlayerTeam }, @@ -1066,6 +1067,40 @@ namespace TShockAPI return args.Handled; } + /// + /// Using when player trying to talk to a NPC + /// + public class NpcTalkEventArgs : GetDataHandledEventArgs + { + /// + /// The Terraria ID of the player talking to the NPC + /// + public byte PlayerId { get; set; } + + /// + /// The NPC ID of the NPC the player is talking to + /// + public short NPCTalkTarget { get; set; } + } + public static HandlerList NpcTalk = new HandlerList(); + private static bool OnNpcTalk(TSPlayer player, MemoryStream data, byte _plr, short _npctarget) + { + if (NpcTalk == null) + { + return false; + } + + var args = new NpcTalkEventArgs + { + Player = player, + Data = data, + PlayerId = _plr, + NPCTalkTarget = _npctarget, + }; + NpcTalk.Invoke(null, args); + return args.Handled; + } + /// /// For use with a PlayerAnimation event /// @@ -3104,6 +3139,30 @@ namespace TShockAPI return true; } + private static bool HandleNpcTalk(GetDataHandlerArgs args) + { + var plr = args.Data.ReadInt8(); + var npc = args.Data.ReadInt16(); + + if (OnNpcTalk(args.Player, args.Data, plr, npc)) + return true; + + //Rejecting player who trying to talk to a npc if player were disabled, mainly for unregistered and logged out players. Preventing smuggling or duplicating their items if player put it in a npc's item slot + if (args.Player.IsBeingDisabled()) + { + TShock.Log.ConsoleDebug("GetDataHandlers / HandleNpcTalk rejected npc talk {0}", args.Player.Name); + args.Player.SendData(PacketTypes.NpcTalk, "", plr, -1); + return true; + } + + if (args.Player.IsBouncerThrottled()) + { + TShock.Log.ConsoleDebug("Bouncer / HandleNpcTalk rejected from bouncer throttle from {0}", args.Player.Name); + return true; + } + return false; + } + private static bool HandlePlayerAnimation(GetDataHandlerArgs args) { if (OnPlayerAnimation(args.Player, args.Data))