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.).