From 20620f2b473e1e959e6d9f8b9033607c1a478519 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 1 Jun 2020 11:54:59 +0200 Subject: [PATCH 01/32] PacketType handler delegate organization. We have the elements in GetDataHandlerDelegates dict in order by its packet type. --- TShockAPI/GetDataHandlers.cs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index b5c9bbb0..a5adc6d4 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -151,9 +151,9 @@ namespace TShockAPI { PacketTypes.CrystalInvasionStart, HandleOldOnesArmy }, { PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 }, { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 }, + { PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker }, { PacketTypes.FishOutNPC, HandleFishOutNPC }, - { PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing }, - { PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker } + { PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing } }; } @@ -3671,6 +3671,21 @@ namespace TShockAPI return false; } + private static bool HandleSyncRevengeMarker(GetDataHandlerArgs args) + { + int uniqueID = args.Data.ReadInt32(); + Vector2 location = args.Data.ReadVector2(); + int netId = args.Data.ReadInt32(); + float npcHpPercent = args.Data.ReadSingle(); + int npcTypeAgainstDiscouragement = args.Data.ReadInt32(); //tfw the argument is Type Against Discouragement + int npcAiStyleAgainstDiscouragement = args.Data.ReadInt32(); //see ^ + int coinsValue = args.Data.ReadInt32(); + float baseValue = args.Data.ReadSingle(); + bool spawnedFromStatus = args.Data.ReadBoolean(); + + return false; + } + private static bool HandleFishOutNPC(GetDataHandlerArgs args) { ushort tileX = args.Data.ReadUInt16(); @@ -3697,21 +3712,6 @@ namespace TShockAPI return false; } - private static bool HandleSyncRevengeMarker(GetDataHandlerArgs args) - { - int uniqueID = args.Data.ReadInt32(); - Vector2 location = args.Data.ReadVector2(); - int netId = args.Data.ReadInt32(); - float npcHpPercent = args.Data.ReadSingle(); - int npcTypeAgainstDiscouragement = args.Data.ReadInt32(); //tfw the argument is Type Against Discouragement - int npcAiStyleAgainstDiscouragement = args.Data.ReadInt32(); //see ^ - int coinsValue = args.Data.ReadInt32(); - float baseValue = args.Data.ReadSingle(); - bool spawnedFromStatus = args.Data.ReadBoolean(); - - return false; - } - public enum EditAction { KillTile = 0, From 26773f61a267f4b4f0f2d27cbcd94a7d4c13065b Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 1 Jun 2020 12:07:57 +0200 Subject: [PATCH 02/32] Add LandGolfBallInCup event handler. --- TShockAPI/GetDataHandlers.cs | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index a5adc6d4..fcb05489 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -152,6 +152,7 @@ namespace TShockAPI { PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 }, { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 }, { PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker }, + { PacketTypes.LandGolfBallInCup, HandleLandGolfBallInCup }, { PacketTypes.FishOutNPC, HandleFishOutNPC }, { PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing } }; @@ -1866,6 +1867,56 @@ namespace TShockAPI return args.Handled; } + /// + /// For use in a LandGolfBallInCup event. + /// + public class LandGolfBallInCupEventArgs : GetDataHandledEventArgs + { + /// + /// The player index in the packet, who puts the ball in the cup. + /// + public byte PlayerIndex { get; set; } + /// + /// The X tile position of where the ball lands in a cup. + /// + public ushort TileX { get; set; } + /// + /// The Y tile position of where the ball lands in a cup. + /// + public ushort TileY { get; set; } + /// + /// The amount of hits it took for the player to land the ball in the cup. + /// + public ushort Hits { get; set; } + /// + /// The type of the projectile that was landed in the cup. A golfball in legit cases. + /// + public ushort ProjectileType { get; set; } + } + + /// + /// Called when a player lands a golf ball in a cup. + /// + public static HandlerList LandGolfBallInCup = new HandlerList(); + private static bool OnLandGolfBallInCup(TSPlayer player, MemoryStream data, byte playerIndex, ushort tileX, ushort tileY, ushort hits, ushort projectileType ) + { + if (LandGolfBallInCup == null) + return false; + + var args = new LandGolfBallInCupEventArgs + { + Player = player, + Data = data, + PlayerIndex = playerIndex, + TileX = tileX, + TileY = tileY, + Hits = hits, + ProjectileType = projectileType + }; + LandGolfBallInCup.Invoke(null, args); + return args.Handled; + } + /// /// For use in a FishOutNPC event. /// @@ -3686,6 +3737,20 @@ namespace TShockAPI return false; } + private static bool HandleLandGolfBallInCup(GetDataHandlerArgs args) + { + byte playerIndex = args.Data.ReadInt8(); + ushort tileX = args.Data.ReadUInt16(); + ushort tileY = args.Data.ReadUInt16(); + ushort hits = args.Data.ReadUInt16(); + ushort projectileType = args.Data.ReadUInt16(); + + if (OnLandGolfBallInCup(args.Player, args.Data, playerIndex, tileX, tileY, hits, projectileType)) + return true; + + return false; + } + private static bool HandleFishOutNPC(GetDataHandlerArgs args) { ushort tileX = args.Data.ReadUInt16(); From 4944ee3144f14f92dc1b25527a62ab259c71c2bb Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 1 Jun 2020 12:59:40 +0200 Subject: [PATCH 03/32] Implement Handler.LandGolfBalInCupHandler and handle packet exploits. Added multiple checks to prevent clients from sending the golfball packet directly, without having golf play actions. --- TShockAPI/Bouncer.cs | 4 + .../Handlers/LandGolfBallInCupHandler.cs | 115 ++++++++++++++++++ TShockAPI/TShockAPI.csproj | 1 + 3 files changed, 120 insertions(+) create mode 100644 TShockAPI/Handlers/LandGolfBallInCupHandler.cs diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 395bfddd..7cf7b87f 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -37,6 +37,7 @@ namespace TShockAPI internal sealed class Bouncer { internal Handlers.SendTileSquareHandler STSHandler { get; set; } + internal Handlers.LandGolfBallInCupHandler LandGolfBallInCupHandler { get; set; } /// Constructor call initializes Bouncer and related functionality. /// A new Bouncer. @@ -45,6 +46,9 @@ namespace TShockAPI STSHandler = new Handlers.SendTileSquareHandler(); GetDataHandlers.SendTileSquare += STSHandler.OnReceiveSendTileSquare; + LandGolfBallInCupHandler = new Handlers.LandGolfBallInCupHandler(); + GetDataHandlers.LandGolfBallInCup += LandGolfBallInCupHandler.OnLandGolfBallInCup; + // Setup hooks GetDataHandlers.GetSection += OnGetSection; GetDataHandlers.PlayerUpdate += OnPlayerUpdate; diff --git a/TShockAPI/Handlers/LandGolfBallInCupHandler.cs b/TShockAPI/Handlers/LandGolfBallInCupHandler.cs new file mode 100644 index 00000000..4ab7cbc9 --- /dev/null +++ b/TShockAPI/Handlers/LandGolfBallInCupHandler.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using Terraria.ID; + +namespace TShockAPI.Handlers +{ + /// + /// Handles client side exploits of LandGolfBallInCup packet. + /// + public class LandGolfBallInCupHandler + { + /// + /// List of golf ball projectile IDs. + /// + public static readonly List GolfBallProjectileIDs = new List() + { + ProjectileID.DirtGolfBall, + ProjectileID.GolfBallDyedBlack, + ProjectileID.GolfBallDyedBlue, + ProjectileID.GolfBallDyedBrown, + ProjectileID.GolfBallDyedCyan, + ProjectileID.GolfBallDyedGreen, + ProjectileID.GolfBallDyedLimeGreen, + ProjectileID.GolfBallDyedOrange, + ProjectileID.GolfBallDyedPink, + ProjectileID.GolfBallDyedPurple, + ProjectileID.GolfBallDyedRed, + ProjectileID.GolfBallDyedSkyBlue, + ProjectileID.GolfBallDyedTeal, + ProjectileID.GolfBallDyedViolet, + ProjectileID.GolfBallDyedYellow + }; + + /// + /// List of golf club item IDs + /// + public static readonly List GolfClubItemIDs = new List() + { + ItemID.GolfClubChlorophyteDriver, + ItemID.GolfClubDiamondWedge, + ItemID.GolfClubShroomitePutter, + ItemID.Fake_BambooChest, + ItemID.GolfClubTitaniumIron, + ItemID.GolfClubGoldWedge, + ItemID.GolfClubLeadPutter, + ItemID.GolfClubMythrilIron, + ItemID.GolfClubWoodDriver, + ItemID.GolfClubBronzeWedge, + ItemID.GolfClubRustyPutter, + ItemID.GolfClubStoneIron, + ItemID.GolfClubPearlwoodDriver, + ItemID.GolfClubIron, + ItemID.GolfClubDriver, + ItemID.GolfClubWedge, + ItemID.GolfClubPutter + }; + + /// + /// Invoked when a player lands a golf ball in a cup. + /// + /// + /// + public void OnLandGolfBallInCup(object sender, GetDataHandlers.LandGolfBallInCupEventArgs args) + { + if (args.PlayerIndex != args.Player.Index) + { + TShock.Log.ConsoleError($"LandGolfBallInCupHandler: Packet is spoofing to be player ID {args.PlayerIndex}! - From [{args.Player.Index}]{args.Player.Name}"); + args.Handled = true; + return; + } + + if (args.TileX > Main.maxTilesX || args.TileX < 0 + || args.TileY > Main.maxTilesY || args.TileY < 0) + { + TShock.Log.ConsoleError($"LandGolfBallInCupHandler: X and Y position is out of world bounds! - From {args.Player.Name}"); + args.Handled = true; + return; + } + + if (!Main.tile[args.TileX, args.TileY].active() && Main.tile[args.TileX, args.TileY].type != TileID.GolfHole) + { + TShock.Log.ConsoleError($"LandGolfBallInCupHandler: Tile at packet position X:{args.TileX} Y:{args.TileY} is not a golf hole! - From {args.Player.Name}"); + args.Handled = true; + return; + } + + if (!GolfBallProjectileIDs.Contains(args.ProjectileType)) + { + TShock.Log.ConsoleError($"LandGolfBallInCupHandler: Invalid golf ball projectile ID {args.ProjectileType}! - From {args.Player.Name}"); + args.Handled = true; + return; + } + + var usedGolfBall = args.Player.RecentlyCreatedProjectiles.Any(e => GolfBallProjectileIDs.Contains(e.Type)); + var usedGolfClub = args.Player.RecentlyCreatedProjectiles.Any(e => e.Type == ProjectileID.GolfClubHelper); + if (!usedGolfClub && !usedGolfBall) + { + TShock.Log.ConsoleError($"GolfPacketHandler: Player did not have create a golf club projectile the last 5 seconds! - From {args.Player.Name}"); + args.Handled = true; + return; + } + + if (!GolfClubItemIDs.Contains(args.Player.SelectedItem.type)) + { + TShock.Log.ConsoleError($"LandGolfBallInCupHandler: Item selected is not a golf club! - From {args.Player.Name}"); + args.Handled = true; + return; + } + } + } +} diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index ea6f4f48..fedc3f0b 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -88,6 +88,7 @@ + From 22abd3a2eb94201430dbe1111077db4986315927 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 1 Jun 2020 13:01:15 +0200 Subject: [PATCH 04/32] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21ffffaa..92662b0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Fixed placement issues with Item Frames, Teleportation Pylons, etc. (@QuiCM) * Doors are good now for real probably (@QuiCM, @Hakusaro, @Olink) * Bump default max damage received cap to 42,000 to accommodate the Empress of Light's instant kill death amount. (@hakusaro, @moisterrific, @Irethia, @Ayrawei) +* Added LandGolfBallInCup event which is accessible for developers to work with, as well as LandGolfBallInCup handler to handle exploits where players could send direct packets to trigger and imitate golf ball cup landing anywhere in the game world. Added two public lists in Handlers.LandGolfBallInCupHandler: GolfBallProjectileIDs and GolfClubItemIDs. (@Patrikkk) ## TShock 4.4.0 (Pre-release 9) * Fixed pet licenses. (@Olink) From 2254df21fda2f7404efdff0c938c10b1a3a4ec45 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 1 Jun 2020 14:38:29 +0200 Subject: [PATCH 05/32] Add Emoji event This is received from the client when they are trying to display an emoji, this comes in to the server, gets processed, and the server sends back the Emote Bubble packet to the clients. --- CHANGELOG.md | 2 +- TShockAPI/GetDataHandlers.cs | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 121cb25a..9860f9d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when adding new entries; sign your name off when you add or change something. This should primarily be things like user changes, not necessarily codebase changes unless it's really relevant or large. ## Upcoming Changes -* Your change goes here! +* Add Emoji event to GetDataHandler. This packet is received when a player tries to display an emote. ## TShock 4.4.0 (Pre-release 10) * Fix all rope coils. (@Olink) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index b5c9bbb0..c9e9608e 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.Emoji, HandleEmoji }, { PacketTypes.FishOutNPC, HandleFishOutNPC }, { PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing }, { PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker } @@ -1866,6 +1867,40 @@ namespace TShockAPI return args.Handled; } + /// + /// For use in an Emoji event. + /// + public class EmojiEventArgs : GetDataHandledEventArgs + { + /// + /// The player index in the packet, who sends the emoji. + /// + public byte PlayerIndex { get; set; } + /// + /// The ID of the emoji, that is being received. + /// + public byte EmojiID { get; set; } + } + /// + /// Called when a player sends an emoji. + /// + public static HandlerList Emoji = new HandlerList(); + private static bool OnEmoji(TSPlayer player, MemoryStream data, byte playerIndex, byte emojiID) + { + if (Emoji == null) + return false; + + var args = new EmojiEventArgs + { + Player = player, + Data = data, + PlayerIndex = playerIndex, + EmojiID = emojiID + }; + Emoji.Invoke(null, args); + return args.Handled; + } + /// /// For use in a FishOutNPC event. /// @@ -3671,6 +3706,17 @@ namespace TShockAPI return false; } + private static bool HandleEmoji(GetDataHandlerArgs args) + { + byte playerIndex = args.Data.ReadInt8(); + byte emojiID = args.Data.ReadInt8(); + + if (OnEmoji(args.Player, args.Data, playerIndex, emojiID)) + return true; + + return false; + } + private static bool HandleFishOutNPC(GetDataHandlerArgs args) { ushort tileX = args.Data.ReadUInt16(); From f538ceb79371776afa386e9bc7648366f16b897c Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 1 Jun 2020 15:24:02 +0200 Subject: [PATCH 06/32] Adding EmojiHandler to handle an exploit. Adding sendemoji permission and checks. I know, this is not something important, but I'm going through the new packets one by one and adding events developers can work with, patching exploits, and thought this could be a core permission. --- CHANGELOG.md | 3 ++- TShockAPI/DB/GroupManager.cs | 3 ++- TShockAPI/Handlers/EmojiHandler.cs | 31 ++++++++++++++++++++++++++++++ TShockAPI/Permissions.cs | 3 +++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 TShockAPI/Handlers/EmojiHandler.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9860f9d5..67a650ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ This is the rolling changelog for TShock for Terraria. Use past tense when adding new entries; sign your name off when you add or change something. This should primarily be things like user changes, not necessarily codebase changes unless it's really relevant or large. ## Upcoming Changes -* Add Emoji event to GetDataHandler. This packet is received when a player tries to display an emote. +* Add Emoji event to GetDataHandler. This packet is received when a player tries to display an emote. + * Adding EmojiHandler to handle an exploit. Adding `tshock.sendemoji` permission and checks. Added this permission to guest group by default. ## TShock 4.4.0 (Pre-release 10) * Fix all rope coils. (@Olink) diff --git a/TShockAPI/DB/GroupManager.cs b/TShockAPI/DB/GroupManager.cs index 584ad374..3cff37a1 100644 --- a/TShockAPI/DB/GroupManager.cs +++ b/TShockAPI/DB/GroupManager.cs @@ -65,7 +65,8 @@ namespace TShockAPI.DB Permissions.canpartychat, Permissions.cantalkinthird, Permissions.canchat, - Permissions.synclocalarea)); + Permissions.synclocalarea, + Permissions.sendemoji)); AddDefaultGroup("default", "guest", string.Join(",", diff --git a/TShockAPI/Handlers/EmojiHandler.cs b/TShockAPI/Handlers/EmojiHandler.cs new file mode 100644 index 00000000..39570d80 --- /dev/null +++ b/TShockAPI/Handlers/EmojiHandler.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TShockAPI.Handlers +{ + /// + /// Handles an exploit and checks for permissions. + /// + public class EmojiHandler + { + public void OnEmoji(object sender, GetDataHandlers.EmojiEventArgs args) + { + if (args.PlayerIndex != args.Player.Index) + { + TShock.Log.ConsoleError($"EmojiHandler: Packet is spoofing to be player ID {args.PlayerIndex}! - From [{args.Player.Index}]{args.Player.Name}"); + args.Handled = true; + return; + } + + if (!args.Player.HasPermission(Permissions.sendemoji)) + { + args.Player.SendErrorMessage("You have no permission to send emotes!"); + args.Handled = true; + return; + } + } + } +} diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index baf21a73..4b196380 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -468,6 +468,9 @@ namespace TShockAPI [Description("Player can resync themselves with server state.")] public static readonly string synclocalarea = "tshock.synclocalarea"; + + [Description("Player can send emotes.")] + public static readonly string sendemoji = "tshock.sendemoji"; #endregion /// /// Lists all commands associated with a given permission From c94d15e63451bac4da16ba8d34a55bf6a603a45b Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 1 Jun 2020 15:31:45 +0200 Subject: [PATCH 07/32] Commit EmojiHandler.cs and register OnEmoji hook. --- TShockAPI/Bouncer.cs | 4 ++++ TShockAPI/Handlers/EmojiHandler.cs | 2 +- TShockAPI/TShockAPI.csproj | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 395bfddd..5bd42ffd 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -37,6 +37,7 @@ namespace TShockAPI internal sealed class Bouncer { internal Handlers.SendTileSquareHandler STSHandler { get; set; } + internal Handlers.EmojiHandler EmojiHandler { get; set; } /// Constructor call initializes Bouncer and related functionality. /// A new Bouncer. @@ -45,6 +46,9 @@ namespace TShockAPI STSHandler = new Handlers.SendTileSquareHandler(); GetDataHandlers.SendTileSquare += STSHandler.OnReceiveSendTileSquare; + EmojiHandler = new Handlers.EmojiHandler(); + GetDataHandlers.Emoji += EmojiHandler.OnEmoji; + // Setup hooks GetDataHandlers.GetSection += OnGetSection; GetDataHandlers.PlayerUpdate += OnPlayerUpdate; diff --git a/TShockAPI/Handlers/EmojiHandler.cs b/TShockAPI/Handlers/EmojiHandler.cs index 39570d80..8606884d 100644 --- a/TShockAPI/Handlers/EmojiHandler.cs +++ b/TShockAPI/Handlers/EmojiHandler.cs @@ -15,7 +15,7 @@ namespace TShockAPI.Handlers { if (args.PlayerIndex != args.Player.Index) { - TShock.Log.ConsoleError($"EmojiHandler: Packet is spoofing to be player ID {args.PlayerIndex}! - From [{args.Player.Index}]{args.Player.Name}"); + TShock.Log.ConsoleError($"EmojiHandler: Packet is spoofing to be player ID {args.PlayerIndex}! - From [{args.Player.Index}] {args.Player.Name}"); args.Handled = true; return; } diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index ea6f4f48..3647b476 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -88,6 +88,7 @@ + From ce5ee0d6230fa96954d317bf9ef3f74e5d746525 Mon Sep 17 00:00:00 2001 From: Patrikkk Date: Mon, 1 Jun 2020 17:02:27 +0200 Subject: [PATCH 08/32] Add HandleSyncCavernMonsterType This packet is never sent from the client to the server in a normal scenario. Although with modded clients, a packet can be sent to modify the cavernMonsterType of the server world and have the world spawn defined NPC types. Can be used to have the server randomly spawn bosses on players in caverns. Is it okay to have this simple handling in GetDataHandlers? A seperate class felt like an overkill. Moved the HandleSyncRevengeMarker packet handler to it's "correct" position, so I won't have merge issues between my last PR. As I've mentioned there, we have the Packets in their numerical order. --- CHANGELOG.md | 2 +- TShockAPI/GetDataHandlers.cs | 33 ++++++++++++++++++++------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 121cb25a..49ffe302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when adding new entries; sign your name off when you add or change something. This should primarily be things like user changes, not necessarily codebase changes unless it's really relevant or large. ## Upcoming Changes -* Your change goes here! +* Handling SyncCavernMonsterType packet to prevent an exploit where players could modify the server's cavern monster types and make the server spawn any NPCs - including bosses - onto other players. ## TShock 4.4.0 (Pre-release 10) * Fix all rope coils. (@Olink) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index b5c9bbb0..f2083f90 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -151,9 +151,10 @@ namespace TShockAPI { PacketTypes.CrystalInvasionStart, HandleOldOnesArmy }, { PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 }, { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 }, + { PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker }, { PacketTypes.FishOutNPC, HandleFishOutNPC }, { PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing }, - { PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker } + { PacketTypes.SyncCavernMonsterType, HandleSyncCavernMonsterType } }; } @@ -3671,6 +3672,21 @@ namespace TShockAPI return false; } + private static bool HandleSyncRevengeMarker(GetDataHandlerArgs args) + { + int uniqueID = args.Data.ReadInt32(); + Vector2 location = args.Data.ReadVector2(); + int netId = args.Data.ReadInt32(); + float npcHpPercent = args.Data.ReadSingle(); + int npcTypeAgainstDiscouragement = args.Data.ReadInt32(); //tfw the argument is Type Against Discouragement + int npcAiStyleAgainstDiscouragement = args.Data.ReadInt32(); //see ^ + int coinsValue = args.Data.ReadInt32(); + float baseValue = args.Data.ReadSingle(); + bool spawnedFromStatus = args.Data.ReadBoolean(); + + return false; + } + private static bool HandleFishOutNPC(GetDataHandlerArgs args) { ushort tileX = args.Data.ReadUInt16(); @@ -3697,19 +3713,10 @@ namespace TShockAPI return false; } - private static bool HandleSyncRevengeMarker(GetDataHandlerArgs args) + private static bool HandleSyncCavernMonsterType(GetDataHandlerArgs args) { - int uniqueID = args.Data.ReadInt32(); - Vector2 location = args.Data.ReadVector2(); - int netId = args.Data.ReadInt32(); - float npcHpPercent = args.Data.ReadSingle(); - int npcTypeAgainstDiscouragement = args.Data.ReadInt32(); //tfw the argument is Type Against Discouragement - int npcAiStyleAgainstDiscouragement = args.Data.ReadInt32(); //see ^ - int coinsValue = args.Data.ReadInt32(); - float baseValue = args.Data.ReadSingle(); - bool spawnedFromStatus = args.Data.ReadBoolean(); - - return false; + TShock.Log.ConsoleDebug($"HandleSyncCavernMonsterType: Player is trying to modify NPC cavernMonsterType; this is a crafted packet! - From {args.Player.Name}"); + return true; } public enum EditAction From d46f534533a72f1eedfbef8f542c6e266ddd04bf Mon Sep 17 00:00:00 2001 From: Chris <2648373+QuiCM@users.noreply.github.com> Date: Tue, 2 Jun 2020 10:18:40 +0930 Subject: [PATCH 09/32] Standardize permission rejection messages --- TShockAPI/Commands.cs | 8 ++++---- TShockAPI/GetDataHandlers.cs | 32 ++++++++++++++++---------------- TShockAPI/TSPlayer.cs | 6 +++--- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index 10d6f0da..4fc949b7 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -1987,7 +1987,7 @@ namespace TShockAPI void FailedPermissionCheck() { - args.Player.SendErrorMessage("You do not have sufficient permissions to start the {0} event.", eventType); + args.Player.SendErrorMessage("You do not have permission to start the {0} event.", eventType); return; } @@ -4873,7 +4873,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.tp)) { - args.Player.SendErrorMessage("You don't have the necessary permission to do that."); + args.Player.SendErrorMessage("You do not have permission to teleport."); break; } if (args.Parameters.Count <= 1) @@ -5051,7 +5051,7 @@ namespace TShockAPI } if (displayIdsRequested && !args.Player.HasPermission(Permissions.seeids)) { - args.Player.SendErrorMessage("You don't have the required permission to list player ids."); + args.Player.SendErrorMessage("You do not have permission to list player ids."); return; } @@ -6025,7 +6025,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.godmodeother)) { - args.Player.SendErrorMessage("You do not have permission to god mode another player!"); + args.Player.SendErrorMessage("You do not have permission to god mode another player."); return; } string plStr = String.Join(" ", args.Parameters); diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index b5c9bbb0..60d16b35 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2928,21 +2928,21 @@ namespace TShockAPI if (bosses.Contains(thingType) && !args.Player.HasPermission(Permissions.summonboss)) { TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawnBoss rejected boss {0} {1}", args.Player.Name, thingType); - args.Player.SendErrorMessage("You don't have permission to summon a boss."); + args.Player.SendErrorMessage("You do not have permission to summon a boss."); return true; } if (invasions.Contains(thingType) && !args.Player.HasPermission(Permissions.startinvasion)) { TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawnBoss rejected invasion {0} {1}", args.Player.Name, thingType); - args.Player.SendErrorMessage("You don't have permission to start an invasion."); + args.Player.SendErrorMessage("You do not have permission to start an invasion."); return true; } if (pets.Contains(thingType) && !args.Player.HasPermission(Permissions.spawnpets)) { TShock.Log.ConsoleDebug("GetDataHandlers / HandleSpawnBoss rejected pet {0} {1}", args.Player.Name, thingType); - args.Player.SendErrorMessage("You don't have permission to spawn pets."); + args.Player.SendErrorMessage("You do not have permission to spawn pets."); return true; } @@ -3234,7 +3234,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_timefreeze)) { - args.Player.SendErrorMessage("You don't have permission to freeze the time of the server!"); + args.Player.SendErrorMessage("You do not have permission to freeze the time of the server."); return true; } break; @@ -3246,7 +3246,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_timeset)) { - args.Player.SendErrorMessage("You don't have permission to modify the time of the server!"); + args.Player.SendErrorMessage("You don't have permission to modify the time of the server."); return true; } break; @@ -3255,7 +3255,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_godmode)) { - args.Player.SendErrorMessage("You don't have permission to toggle godmode!"); + args.Player.SendErrorMessage("You do not have permission to toggle godmode."); return true; } break; @@ -3264,7 +3264,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_windstrength)) { - args.Player.SendErrorMessage("You don't have permission to modify the wind strength of the server!"); + args.Player.SendErrorMessage("You do not have permission to modify the wind strength of the server."); return true; } break; @@ -3273,7 +3273,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_rainstrength)) { - args.Player.SendErrorMessage("You don't have permission to modify the rain strength of the server!"); + args.Player.SendErrorMessage("You do not have permission to modify the rain strength of the server."); return true; } break; @@ -3282,7 +3282,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_timespeed)) { - args.Player.SendErrorMessage("You don't have permission to modify the time speed of the server!"); + args.Player.SendErrorMessage("You do not have permission to modify the time speed of the server."); return true; } break; @@ -3291,7 +3291,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_rainfreeze)) { - args.Player.SendErrorMessage("You don't have permission to freeze the rain strength of the server!"); + args.Player.SendErrorMessage("You do not have permission to freeze the rain strength of the server."); return true; } break; @@ -3300,7 +3300,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_windfreeze)) { - args.Player.SendErrorMessage("You don't have permission to freeze the wind strength of the server!"); + args.Player.SendErrorMessage("You do not have permission to freeze the wind strength of the server."); return true; } break; @@ -3309,7 +3309,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_placementrange)) { - args.Player.SendErrorMessage("You don't have permission to modify the tile placement range of your character!"); + args.Player.SendErrorMessage("You do not have permission to modify the tile placement range of your character."); return true; } break; @@ -3318,7 +3318,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_setdifficulty)) { - args.Player.SendErrorMessage("You don't have permission to modify the world difficulty of the server!"); + args.Player.SendErrorMessage("You do not have permission to modify the world difficulty of the server."); return true; } break; @@ -3327,7 +3327,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_biomespreadfreeze)) { - args.Player.SendErrorMessage("You don't have permission to freeze the biome spread of the server!"); + args.Player.SendErrorMessage("You do not have permission to freeze the biome spread of the server."); return true; } break; @@ -3342,7 +3342,7 @@ namespace TShockAPI } if (!args.Player.HasPermission(Permissions.journey_setspawnrate)) { - args.Player.SendErrorMessage("You don't have permission to modify the NPC spawn rate of the server!"); + args.Player.SendErrorMessage("You do not have permission to modify the NPC spawn rate of the server."); return true; } break; @@ -3576,7 +3576,7 @@ namespace TShockAPI if (!args.Player.HasPermission(Permissions.startdd2)) { TShock.Log.ConsoleDebug("GetDataHandlers / HandleOldOnesArmy rejected permissions {0}", args.Player.Name); - args.Player.SendErrorMessage("You don't have permission to start the Old One's Army event."); + args.Player.SendErrorMessage("You do not have permission to start the Old One's Army event."); return true; } diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 54be5041..ade16234 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -664,13 +664,13 @@ namespace TShockAPI switch (failure) { case BuildPermissionFailPoint.GeneralBuild: - SendErrorMessage("You lack permission to build on this server."); + SendErrorMessage("You do not have permission to build on this server."); break; case BuildPermissionFailPoint.SpawnProtect: - SendErrorMessage("You lack permission to build in the spawn point."); + SendErrorMessage("You do not have permission to build in the spawn point."); break; case BuildPermissionFailPoint.Regions: - SendErrorMessage("You lack permission to build in this region."); + SendErrorMessage("You do not have permission to build in this region."); break; } From b5c3b430fa31c1a6c96fd0d8a772adf9b07d4752 Mon Sep 17 00:00:00 2001 From: Chris <2648373+QuiCM@users.noreply.github.com> Date: Tue, 2 Jun 2020 10:20:13 +0930 Subject: [PATCH 10/32] Standardize permission rejection message again --- TShockAPI/GetDataHandlers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 60d16b35..65fe622f 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -3246,7 +3246,7 @@ namespace TShockAPI { if (!args.Player.HasPermission(Permissions.journey_timeset)) { - args.Player.SendErrorMessage("You don't have permission to modify the time of the server."); + args.Player.SendErrorMessage("You do not have permission to modify the time of the server."); return true; } break; From 5b72ff6c0a0e5e23ce706c82d19925baa70ff931 Mon Sep 17 00:00:00 2001 From: Chris <2648373+QuiCM@users.noreply.github.com> Date: Tue, 2 Jun 2020 10:44:10 +0930 Subject: [PATCH 11/32] Add IPacketHandler --- TShockAPI/Bouncer.cs | 2 +- TShockAPI/Handlers/IPacketHandler.cs | 16 ++++++++++++++++ TShockAPI/Handlers/SendTileSquareHandler.cs | 18 +++++++++--------- TShockAPI/TShockAPI.csproj | 3 ++- 4 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 TShockAPI/Handlers/IPacketHandler.cs diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 395bfddd..fda85267 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -43,7 +43,7 @@ namespace TShockAPI internal Bouncer() { STSHandler = new Handlers.SendTileSquareHandler(); - GetDataHandlers.SendTileSquare += STSHandler.OnReceiveSendTileSquare; + GetDataHandlers.SendTileSquare += STSHandler.OnReceive; // Setup hooks GetDataHandlers.GetSection += OnGetSection; diff --git a/TShockAPI/Handlers/IPacketHandler.cs b/TShockAPI/Handlers/IPacketHandler.cs new file mode 100644 index 00000000..9b2e0444 --- /dev/null +++ b/TShockAPI/Handlers/IPacketHandler.cs @@ -0,0 +1,16 @@ +namespace TShockAPI.Handlers +{ + /// + /// Describes a packet handler that receives a packet from a GetDataHandler + /// + /// + public interface IPacketHandler where TEventArgs : GetDataHandledEventArgs + { + /// + /// Invoked when the packet is received + /// + /// + /// + void OnReceive(object sender, TEventArgs args); + } +} diff --git a/TShockAPI/Handlers/SendTileSquareHandler.cs b/TShockAPI/Handlers/SendTileSquareHandler.cs index 48462a03..fc252256 100644 --- a/TShockAPI/Handlers/SendTileSquareHandler.cs +++ b/TShockAPI/Handlers/SendTileSquareHandler.cs @@ -14,12 +14,12 @@ namespace TShockAPI.Handlers /// /// Provides processors for handling Tile Square packets /// - public class SendTileSquareHandler + public class SendTileSquareHandler : IPacketHandler { /// /// Maps grass-type blocks to flowers that can be grown on them with flower boots /// - Dictionary> _grassToPlantMap = new Dictionary> + public static Dictionary> GrassToPlantMap = new Dictionary> { { TileID.Grass, new List { TileID.Plants, TileID.Plants2 } }, { TileID.HallowedGrass, new List { TileID.HallowedPlants, TileID.HallowedPlants2 } }, @@ -29,7 +29,7 @@ namespace TShockAPI.Handlers /// /// Item IDs that can spawn flowers while you walk /// - List _flowerBootItems = new List + public static List FlowerBootItems = new List { ItemID.FlowerBoots, ItemID.FairyBoots @@ -40,7 +40,7 @@ namespace TShockAPI.Handlers /// Note: is empty at the time of writing, but entities are dynamically assigned their ID at initialize time /// which is why we can use the _myEntityId field on each entity type /// - Dictionary _tileEntityIdToTileIdMap = new Dictionary + public static Dictionary TileEntityIdToTileIdMap = new Dictionary { { TileID.TargetDummy, TETrainingDummy._myEntityID }, { TileID.ItemFrame, TEItemFrame._myEntityID }, @@ -57,7 +57,7 @@ namespace TShockAPI.Handlers /// /// /// - public void OnReceiveSendTileSquare(object sender, GetDataHandlers.SendTileSquareEventArgs args) + public void OnReceive(object sender, GetDataHandlers.SendTileSquareEventArgs args) { // By default, we'll handle everything args.Handled = true; @@ -196,9 +196,9 @@ namespace TShockAPI.Handlers UpdateMultipleServerTileStates(realX, realY, width, height, newTiles); // Tile entities have special placements that we should let the game deal with - if (_tileEntityIdToTileIdMap.ContainsKey(tileType)) + if (TileEntityIdToTileIdMap.ContainsKey(tileType)) { - TileEntity.PlaceEntityNet(realX, realY, _tileEntityIdToTileIdMap[tileType]); + TileEntity.PlaceEntityNet(realX, realY, TileEntityIdToTileIdMap[tileType]); } } @@ -214,7 +214,7 @@ namespace TShockAPI.Handlers { // Some boots allow growing flowers on grass. This process sends a 1x1 tile square to grow the flowers // The square size must be 1 and the player must have an accessory that allows growing flowers in order for this square to be valid - if (squareSize == 1 && args.Player.Accessories.Any(a => a != null && _flowerBootItems.Contains(a.type))) + if (squareSize == 1 && args.Player.Accessories.Any(a => a != null && FlowerBootItems.Contains(a.type))) { ProcessFlowerBoots(realX, realY, newTile, args); return; @@ -254,7 +254,7 @@ namespace TShockAPI.Handlers } ITile tile = Main.tile[realX, realY + 1]; - if (!_grassToPlantMap.TryGetValue(tile.type, out List plantTiles) && !plantTiles.Contains(newTile.Type)) + if (!GrassToPlantMap.TryGetValue(tile.type, out List plantTiles) && !plantTiles.Contains(newTile.Type)) { // If the tile below the tile square isn't a valid plant tile (eg grass) then we don't update the server tile state return; diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index ea6f4f48..af383252 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -88,6 +88,7 @@ + @@ -212,7 +213,7 @@ - +