diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 909a84d68..ec674d1a6 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -1380,6 +1380,21 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA return; } + // TorchGod projectile is created only when a player is experiencing the torch god event. + // However, checking for happyFunTorchTime being true doesn't work, since due to a bug or oversight, + // clients don't sync it when it's set to true, so we check for unlockedBiomeTorches being false instead. + // The server will assume ownership of this projectile, despite it being hostile, so it is the only hostile projectile clients are allowed to create. + if (type == ProjectileID.TorchGod && !args.Player.TPlayer.unlockedBiomeTorches) + { + if (CheckProjectileThreshold()) + return; + + ProjectileThresholdIncrement(); + TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile super accepted from (torch god) {0}", args.Player.Name)); + args.Handled = false; + return; + } + /// If the projectile is a directional projectile, check if the player is holding their respected item to validate the projectile creation. if (directionalProjectiles.ContainsKey(type)) { @@ -1484,23 +1499,8 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA // return; } - if (args.Player.ProjectileThreshold >= TShock.Config.Settings.ProjectileThreshold) - { - if (TShock.Config.Settings.KickOnProjectileThresholdBroken) - { - args.Player.Kick(GetString("Projectile create threshold exceeded {0}.", TShock.Config.Settings.ProjectileThreshold)); - } - else - { - args.Player.Disable(GetString("Reached projectile create threshold."), DisableFlags.WriteToLogAndConsole); - args.Player.RemoveProjectile(ident, owner); - } - - TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from projectile create threshold from {0} {1}/{2}", args.Player.Name, args.Player.ProjectileThreshold, TShock.Config.Settings.ProjectileThreshold)); - TShock.Log.ConsoleDebug(GetString("If this player wasn't hacking, please report the projectile create threshold they were disabled for to TShock so we can improve this!")); - args.Handled = true; + if (CheckProjectileThreshold()) return; - } if ( (Projectile_MaxValuesAI.ContainsKey(type) && @@ -1531,17 +1531,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA return; } - if (!args.Player.HasPermission(Permissions.ignoreprojectiledetection)) - { - if (type == ProjectileID.CrystalShard && TShock.Config.Settings.ProjIgnoreShrapnel) // Ignore crystal shards - { - TShock.Log.Debug(GetString("Ignoring shrapnel per config..")); - } - else if (!Main.projectile[index].active) - { - args.Player.ProjectileThreshold++; // Creating new projectile - } - } + ProjectileThresholdIncrement(); if ((type == ProjectileID.Bomb || type == ProjectileID.Dynamite @@ -1554,6 +1544,45 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA // Denotes that the player has recently set a fuse - used for cheat detection. args.Player.RecentFuse = 10; } + + // Checks if the player has exceeded the projectile creation threshold. + bool CheckProjectileThreshold() + { + if (args.Player.ProjectileThreshold >= TShock.Config.Settings.ProjectileThreshold) + { + if (TShock.Config.Settings.KickOnProjectileThresholdBroken) + { + args.Player.Kick(GetString("Projectile create threshold exceeded {0}.", TShock.Config.Settings.ProjectileThreshold)); + } + else + { + args.Player.Disable(GetString("Reached projectile create threshold."), DisableFlags.WriteToLogAndConsole); + args.Player.RemoveProjectile(ident, owner); + } + + TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from projectile create threshold from {0} {1}/{2}", args.Player.Name, args.Player.ProjectileThreshold, TShock.Config.Settings.ProjectileThreshold)); + TShock.Log.ConsoleDebug(GetString("If this player wasn't hacking, please report the projectile create threshold they were disabled for to TShock so we can improve this!")); + args.Handled = true; + return true; + } + return false; + } + + // Incremenets the player's projectile creation threshold, if necessary. + void ProjectileThresholdIncrement() + { + if (!args.Player.HasPermission(Permissions.ignoreprojectiledetection)) + { + if (type == ProjectileID.CrystalShard && TShock.Config.Settings.ProjIgnoreShrapnel) // Ignore crystal shards + { + TShock.Log.Debug(GetString("Ignoring shrapnel per config..")); + } + else if (!Main.projectile[index].active) + { + args.Player.ProjectileThreshold++; // Creating new projectile + } + } + } } /// Handles the NPC Strike event for Bouncer. diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 96e75a8ee..c81e51e5d 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -591,6 +591,12 @@ private static bool IsRectPositionValid(TSPlayer player, TileRect rect) /// , if the rect at a valid distance, otherwise . private static bool IsRectDistanceValid(TSPlayer player, TileRect rect) { + int range = 32; + + // Torches can be modified from far away through a water gun, or if the player is experiencing the torch god event. + if (IsRectATorch(rect)) + range = 104; // Base value of 100 comes from Player.TorchAttack, bumped by 4 blocks to be safe. + for (int x = 0; x < rect.Width; x++) { for (int y = 0; y < rect.Height; y++) @@ -598,7 +604,7 @@ private static bool IsRectDistanceValid(TSPlayer player, TileRect rect) int realX = rect.X + x; int realY = rect.Y + y; - if (!player.IsInRange(realX, realY)) + if (!player.IsInRange(realX, realY, range)) { return false; } @@ -608,6 +614,20 @@ private static bool IsRectDistanceValid(TSPlayer player, TileRect rect) return true; } + /// + /// Checks whether the tile rect is modifying a torch. + /// + /// The tile rectangle of the operation. + /// , if the rect is modifying a torch, otherwise . + private static bool IsRectATorch(TileRect rect) + { + // Rect is definitely not a torch... + if (rect.Width != 1 || rect.Height != 1) + return false; + + // Is the rect actually modifying a torch? + return rect[0, 0].Type == TileID.Torches && Main.tile[rect.X, rect.Y].type == TileID.Torches; + } /// /// Checks whether the tile rect is a valid conversion spread (Clentaminator, Powders, etc.).