diff --git a/CHANGELOG.md b/CHANGELOG.md index 7be60f9a..c24fd45d 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 * 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. * 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. +* 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 10) * Fix all rope coils. (@Olink) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index f4fa4053..f6de4c80 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -39,6 +39,7 @@ namespace TShockAPI internal Handlers.SendTileSquareHandler STSHandler { get; set; } internal Handlers.NetModules.NetModulePacketHandler NetModuleHandler { get; set; } internal Handlers.EmojiHandler EmojiHandler { get; set; } + internal Handlers.LandGolfBallInCupHandler LandGolfBallInCupHandler { get; set; } /// Constructor call initializes Bouncer and related functionality. /// A new Bouncer. @@ -52,6 +53,9 @@ namespace TShockAPI EmojiHandler = new Handlers.EmojiHandler(); GetDataHandlers.Emoji += EmojiHandler.OnReceive; + + LandGolfBallInCupHandler = new Handlers.LandGolfBallInCupHandler(); + GetDataHandlers.LandGolfBallInCup += LandGolfBallInCupHandler.OnLandGolfBallInCup; // Setup hooks GetDataHandlers.GetSection += OnGetSection; diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index a73c7aef..cf13e396 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -153,6 +153,7 @@ namespace TShockAPI { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 }, { PacketTypes.Emoji, HandleEmoji }, { PacketTypes.SyncRevengeMarker, HandleSyncRevengeMarker }, + { PacketTypes.LandGolfBallInCup, HandleLandGolfBallInCup }, { PacketTypes.FishOutNPC, HandleFishOutNPC }, { PacketTypes.FoodPlatterTryPlacing, HandleFoodPlatterTryPlacing }, { PacketTypes.SyncCavernMonsterType, HandleSyncCavernMonsterType } @@ -1901,6 +1902,56 @@ namespace TShockAPI Emoji.Invoke(null, args); return args.Handled; } + + /// + /// For use in a LandBallInCup 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. @@ -3618,6 +3669,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(); diff --git a/TShockAPI/Handlers/LandGolfBallInCupHandler.cs b/TShockAPI/Handlers/LandGolfBallInCupHandler.cs new file mode 100644 index 00000000..7d246976 --- /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.ConsoleDebug($"LandGolfBallInCupHandler: Packet rejected for ID spoofing. Expected {args.PlayerIndex} , received {args.PlayerIndex} from {args.Player.Name}."); + args.Handled = true; + return; + } + + if (args.TileX > Main.maxTilesX || args.TileX < 0 + || args.TileY > Main.maxTilesY || args.TileY < 0) + { + TShock.Log.ConsoleDebug($"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.ConsoleDebug($"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.ConsoleDebug($"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.ConsoleDebug($"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.ConsoleDebug($"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 fef6f380..495127c8 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -98,6 +98,7 @@ +