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 @@
+