From 09121368e402b4cd0f1f92b7f91945e5bbd259d3 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Thu, 21 Dec 2017 20:32:07 -0700 Subject: [PATCH 1/9] Move TShock.CheckRangePermission to TSPlayer TShock.CheckRangePermission is now TSPlayer.IsInRange, but the most important thing is that this method returns the opposite of what the original did, so all of the calls that would go to it are now inverted. --- CHANGELOG.md | 1 + TShockAPI/Bouncer.cs | 30 +++++++++++++++--------------- TShockAPI/GetDataHandlers.cs | 8 ++++---- TShockAPI/TSPlayer.cs | 14 ++++++++++++++ TShockAPI/TShock.cs | 20 +------------------- 5 files changed, 35 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7286ea27..88ba267a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Added `TSPlayer.CheckIgnores()` and removed `TShock.CheckIgnores(TSPlayer)`. (@hakusaro) * Hooks inside TShock can now be registered with their `Register` method and can be prioritized according to the TShock HandlerList system. (@hakusaro) * Fix message requiring login not using the command specifier set in the config file. (@hakusaro) +* Move `TShock.CheckRangePermission()` to `TSPlayer.IsInRange` which **returns the opposite** of what the previous method did (see updated docs). (@hakusaro) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 8e81d802..d4727962 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -83,7 +83,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, args.X, args.Y)) + if (!args.Player.IsInRange(args.X, args.Y)) { NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1); args.Handled = true; @@ -138,7 +138,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, args.X, args.Y)) + if (!args.Player.IsInRange(args.X, args.Y)) { args.Handled = true; return; @@ -253,7 +253,7 @@ namespace TShockAPI } if (TShock.Config.RangeChecks && - TShock.CheckRangePermission(args.Player, (int)(Main.npc[id].position.X / 16f), (int)(Main.npc[id].position.Y / 16f), 128)) + !args.Player.IsInRange((int)(Main.npc[id].position.X / 16f), (int)(Main.npc[id].position.Y / 16f), 128)) { args.Player.SendData(PacketTypes.NpcUpdate, "", id); args.Handled = true; @@ -319,7 +319,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, TShock.Players[id].TileX, TShock.Players[id].TileY, 100)) + if (!args.Player.IsInRange(TShock.Players[id].TileX, TShock.Players[id].TileY, 100)) { args.Player.SendData(PacketTypes.PlayerHp, "", id); args.Player.SendData(PacketTypes.PlayerUpdate, "", id); @@ -373,7 +373,7 @@ namespace TShockAPI // client side (but only if it passed the range check) (i.e., return false) if (type == 0) { - if (TShock.CheckRangePermission(args.Player, (int)(Main.item[id].position.X / 16f), (int)(Main.item[id].position.Y / 16f))) + if (!args.Player.IsInRange((int)(Main.item[id].position.X / 16f), (int)(Main.item[id].position.Y / 16f))) { // Causes item duplications. Will be re added if necessary //args.Player.SendData(PacketTypes.ItemDrop, "", id); @@ -385,7 +385,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, (int)(pos.X / 16f), (int)(pos.Y / 16f))) + if (!args.Player.IsInRange((int)(pos.X / 16f), (int)(pos.Y / 16f))) { args.Player.SendData(PacketTypes.ItemDrop, "", id); args.Handled = true; @@ -465,7 +465,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, TShock.Players[id].TileX, TShock.Players[id].TileY, 50)) + if (!args.Player.IsInRange(TShock.Players[id].TileX, TShock.Players[id].TileY, 50)) { args.Player.SendData(PacketTypes.PlayerAddBuff, "", id); args.Handled = true; @@ -516,7 +516,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, Main.chest[id].x, Main.chest[id].y)) + if (!args.Player.IsInRange(Main.chest[id].x, Main.chest[id].y)) { args.Handled = true; return; @@ -544,7 +544,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, x, y)) + if (!args.Player.IsInRange(x, y)) { args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, Convert.ToByte(Main.npc[id].homeless)); @@ -564,7 +564,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, args.X, args.Y)) + if (!args.Player.IsInRange(args.X, args.Y)) { args.Handled = true; return; @@ -632,7 +632,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, tileX, tileY)) + if (!args.Player.IsInRange(tileX, tileY)) { args.Player.SendTileSquare(tileX, tileY, 3); args.Handled = true; @@ -765,7 +765,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, tileX, tileY, 16)) + if (!args.Player.IsInRange(tileX, tileY, 16)) { args.Player.SendTileSquare(tileX, tileY, 1); args.Handled = true; @@ -1160,7 +1160,7 @@ namespace TShockAPI || type != TileID.SilkRope || type != TileID.VineRope || type != TileID.WebRope) - && TShock.CheckRangePermission(args.Player, x, y)) + && !args.Player.IsInRange(x, y)) { args.Player.SendTileSquare(x, y, 4); args.Handled = true; @@ -1484,7 +1484,7 @@ namespace TShockAPI return; } - if (TShock.CheckRangePermission(args.Player, tileX, tileY)) + if (!args.Player.IsInRange(tileX, tileY)) { if (action == EditAction.PlaceTile && (editData == TileID.Rope || editData == TileID.SilkRope || editData == TileID.VineRope || editData == TileID.WebRope)) { @@ -1661,7 +1661,7 @@ namespace TShockAPI var tile = Main.tile[realx, realy]; var newtile = tiles[x, y]; if (TShock.CheckTilePermission(args.Player, realx, realy) || - TShock.CheckRangePermission(args.Player, realx, realy)) + !args.Player.IsInRange(realx, realy)) { continue; } diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index a8524597..310a3b64 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2578,7 +2578,7 @@ namespace TShockAPI return true; } - if (TShock.CheckRangePermission(args.Player, x, y)) + if (!args.Player.IsInRange(x, y)) { args.Player.SendData(PacketTypes.SignNew, "", id); return true; @@ -2898,7 +2898,7 @@ namespace TShockAPI if (args.Player.IsBouncerThrottled() || TShock.CheckTilePermission(args.Player, x, y, true) || - TShock.CheckRangePermission(args.Player, x, y)) + !args.Player.IsInRange(x, y)) { args.Player.SendData(PacketTypes.PaintTile, "", x, y, Main.tile[x, y].color()); return true; @@ -2942,7 +2942,7 @@ namespace TShockAPI if (args.Player.IsBouncerThrottled() || TShock.CheckTilePermission(args.Player, x, y, true) || - TShock.CheckRangePermission(args.Player, x, y)) + !args.Player.IsInRange(x, y)) { args.Player.SendData(PacketTypes.PaintWall, "", x, y, Main.tile[x, y].wallColor()); return true; @@ -3293,7 +3293,7 @@ namespace TShockAPI return true; } - if (TShock.CheckRangePermission(args.Player, (int)position.X, (int)position.Y)) + if (!args.Player.IsInRange((int)position.X, (int)position.Y)) { return true; } diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 69181389..8071ea37 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -321,6 +321,20 @@ namespace TShockAPI public bool SilentJoinInProgress; + /// Checks if a player has permission to modify a tile dependent on range checks. + /// The x coordinate of the tile. + /// The y coordinate of the tile. + /// The range to check for. + /// True if the player is in range of a tile or if range checks are off. False if not. + public bool IsInRange(int x, int y, int range = 32) + { + if (TShock.Config.RangeChecks && ((Math.Abs(TileX - x) > range) || (Math.Abs(TileY - y) > range))) + { + return false; + } + return true; + } + /// /// A list of points where ice tiles have been placed. /// diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 44c6b9f1..8e2ea548 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -585,7 +585,7 @@ namespace TShockAPI return; } - if (CheckRangePermission(tsplr, args.Chest.x, args.Chest.y)) + if (!tsplr.IsInRange(args.Chest.x, args.Chest.y)) { args.Handled = true; return; @@ -1757,24 +1757,6 @@ namespace TShockAPI e.Handled = true; } - - - - /// CheckRangePermission - Checks if a player has permission to modify a tile dependent on range checks. - /// player - The TSPlayer object. - /// x - The x coordinate of the tile. - /// y - The y coordinate of the tile. - /// range - The range to check for. - /// bool - True if the player should not be able to place the tile. False if they can, or if range checks are off. - public static bool CheckRangePermission(TSPlayer player, int x, int y, int range = 32) - { - if (Config.RangeChecks && ((Math.Abs(player.TileX - x) > range) || (Math.Abs(player.TileY - y) > range))) - { - return true; - } - return false; - } - /// CheckTilePermission - Checks to see if a player has permission to modify a tile in general. /// player - The TSPlayer object. /// tileX - The x coordinate of the tile. From 195a23a7e5fbd48e2eb9027fee82ad0447534f5f Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 22 Dec 2017 00:31:02 -0700 Subject: [PATCH 2/9] Migrate most of CheckTilePermission to TSPlayer TShock.CheckTilePermission(ply, x, y, paint) has been replaced with TSPlayer.HasPaintPermission and TSPlayer.HasBuildPermission respectively. These two methods dramatically simplify the logic required to process build permissions. Still todo after this commit: 1. Implement warnings for when a player fails a permission check on build. This is probably going to be accomplished by a default argument to the HasBuildPermission signature. 2. Create a variant that checks for ice tile permission and use like HasPaintPermission() so as to remove the other TShock.CheckTilePermission method. Annoyingly, all of the existing methods that start with Check seem to "check" if they player _doesn't_ have permission (true = no permission), which makes nearly every call negative. --- TShockAPI/Bouncer.cs | 20 +++++------ TShockAPI/GetDataHandlers.cs | 8 ++--- TShockAPI/TSPlayer.cs | 50 ++++++++++++++++++++++++++ TShockAPI/TShock.cs | 69 ------------------------------------ 4 files changed, 64 insertions(+), 83 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index d4727962..0bbf77e5 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -76,7 +76,7 @@ namespace TShockAPI return; } - if (TShock.CheckTilePermission(args.Player, args.X, args.Y)) + if (!args.Player.HasBuildPermission(args.X, args.Y)) { NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1); args.Handled = true; @@ -114,7 +114,7 @@ namespace TShockAPI return; } - if (TShock.CheckTilePermission(args.Player, args.X, args.Y)) + if (!args.Player.HasBuildPermission(args.X, args.Y)) { args.Handled = true; return; @@ -132,7 +132,7 @@ namespace TShockAPI return; } - if (TShock.CheckTilePermission(args.Player, args.X, args.Y)) + if (!args.Player.HasBuildPermission(args.X, args.Y)) { args.Handled = true; return; @@ -183,7 +183,7 @@ namespace TShockAPI return; } - if (TShock.CheckTilePermission(args.Player, x, y)) + if (!args.Player.HasBuildPermission(x, y)) { args.Handled = true; return; @@ -510,7 +510,7 @@ namespace TShockAPI return; } - if (TShock.CheckTilePermission(args.Player, Main.chest[id].x, Main.chest[id].y) && TShock.Config.RegionProtectChests) + if (!args.Player.HasBuildPermission(Main.chest[id].x, Main.chest[id].y) && TShock.Config.RegionProtectChests) { args.Handled = true; return; @@ -535,7 +535,7 @@ namespace TShockAPI // Calls to TShock.CheckTilePermission need to be broken up into different subsystems // In particular, this handles both regions and other things. Ouch. - if (TShock.CheckTilePermission(args.Player, x, y)) + if (!args.Player.HasBuildPermission(x, y)) { args.Player.SendErrorMessage("You do not have access to modify this area."); args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, @@ -570,7 +570,7 @@ namespace TShockAPI return; } - if (TShock.CheckTilePermission(args.Player, args.X, args.Y) && TShock.Config.RegionProtectChests) + if (!args.Player.HasBuildPermission(args.X, args.Y) && TShock.Config.RegionProtectChests) { args.Handled = true; return; @@ -625,7 +625,7 @@ namespace TShockAPI } } - if (TShock.CheckTilePermission(args.Player, tileX, tileY)) + if (!args.Player.HasBuildPermission(tileX, tileY)) { args.Player.SendTileSquare(tileX, tileY, 3); args.Handled = true; @@ -758,7 +758,7 @@ namespace TShockAPI } } - if (TShock.CheckTilePermission(args.Player, tileX, tileY)) + if (!args.Player.HasBuildPermission(tileX, tileY)) { args.Player.SendTileSquare(tileX, tileY, 1); args.Handled = true; @@ -1660,7 +1660,7 @@ namespace TShockAPI var tile = Main.tile[realx, realy]; var newtile = tiles[x, y]; - if (TShock.CheckTilePermission(args.Player, realx, realy) || + if (!args.Player.HasBuildPermission(realx, realy) || !args.Player.IsInRange(realx, realy)) { continue; diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 310a3b64..6d7000a1 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -2532,7 +2532,7 @@ namespace TShockAPI args.Player.ActiveChest = id; - if (TShock.CheckTilePermission(args.Player, x, y) && TShock.Config.RegionProtectChests) + if (!args.Player.HasBuildPermission(x, y) && TShock.Config.RegionProtectChests) { args.Player.SendData(PacketTypes.ChestOpen, "", -1); return true; @@ -2572,7 +2572,7 @@ namespace TShockAPI if (OnSignEvent(id, x, y)) return true; - if (TShock.CheckTilePermission(args.Player, x, y)) + if (!args.Player.HasBuildPermission(x, y)) { args.Player.SendData(PacketTypes.SignNew, "", id); return true; @@ -2897,7 +2897,7 @@ namespace TShockAPI } if (args.Player.IsBouncerThrottled() || - TShock.CheckTilePermission(args.Player, x, y, true) || + !args.Player.HasPaintPermission(x, y) || !args.Player.IsInRange(x, y)) { args.Player.SendData(PacketTypes.PaintTile, "", x, y, Main.tile[x, y].color()); @@ -2941,7 +2941,7 @@ namespace TShockAPI } if (args.Player.IsBouncerThrottled() || - TShock.CheckTilePermission(args.Player, x, y, true) || + !args.Player.HasPaintPermission(x, y) || !args.Player.IsInRange(x, y)) { args.Player.SendData(PacketTypes.PaintWall, "", x, y, Main.tile[x, y].wallColor()); diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 8071ea37..05d9844c 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -335,6 +335,56 @@ namespace TShockAPI return true; } + private enum BuildPermissionFailPoint + { + GeneralBuild, + SpawnProtect, + Regions + } + + /// Determines if the player can build on a given point. + /// The x coordinate they want to build at. + /// The y coordinate they want to paint at. + /// True if the player can build at the given point from build, spawn, and region protection. + public bool HasBuildPermission(int x, int y) + { + BuildPermissionFailPoint failure = BuildPermissionFailPoint.GeneralBuild; + // The goal is to short circuit on easy stuff as much as possible. + // Don't compute permissions unless needed, and don't compute taxing stuff unless needed. + + // If the player has bypass on build protection or building is enabled; continue + // (General build protection takes precedence over spawn protection) + if (!TShock.Config.DisableBuild || HasPermission(Permissions.antibuild)) + { + // If they have spawn protect bypass, or it isn't spawn, or it isn't in spawn; continue + // (If they have spawn protect bypass, we don't care if it's spawn or not) + if (!TShock.Config.SpawnProtection || HasPermission(Permissions.editspawn) || !Utils.IsInSpawn(x, y)) + { + // If they have build permission in this region, then they're allowed to continue + if (TShock.Regions.CanBuild(x, y, this)) + { + return true; + } + } + } + + // TODO: Implement warning system. + + // If they lack build permission, they end up here. + // If they have build permission but lack the ability to edit spawn and it's spawn, they end up here. + // If they have build, it isn't spawn, or they can edit spawn, but they fail the region check, they end up here. + return false; + } + + /// Determines if the player can paint on a given point. Checks general build permissions, then paint. + /// The x coordinate they want to paint at. + /// The y coordinate they want to paint at. + /// True if they can paint. + public bool HasPaintPermission(int x, int y) + { + return HasBuildPermission(x, y) || HasPermission(Permissions.canpaint); + } + /// /// A list of points where ice tiles have been placed. /// diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 1cc17e37..f35efda8 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1842,75 +1842,6 @@ namespace TShockAPI return false; } - /// CheckTilePermission - Checks to see if a player has the ability to modify a tile at a given position. - /// player - The TSPlayer object. - /// tileX - The x coordinate of the tile. - /// tileY - The y coordinate of the tile. - /// paint - Whether or not the tile is paint. - /// bool - True if the player should not be able to modify the tile. - public static bool CheckTilePermission(TSPlayer player, int tileX, int tileY, bool paint = false) - { - if ((!paint && !player.HasPermission(Permissions.canbuild)) || - (paint && !player.HasPermission(Permissions.canpaint))) - { - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.BPm) > 2000) - { - if (paint) - { - player.SendErrorMessage("You do not have permission to paint!"); - } - else - { - player.SendErrorMessage("You do not have permission to build!"); - } - player.BPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - - if (!Regions.CanBuild(tileX, tileY, player)) - { - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.RPm) > 2000) - { - player.SendErrorMessage("This region is protected from changes."); - player.RPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - - if (Config.DisableBuild) - { - if (!player.HasPermission(Permissions.antibuild)) - { - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.WPm) > 2000) - { - player.SendErrorMessage("The world is protected from changes."); - player.WPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - } - - if (Config.SpawnProtection) - { - if (!player.HasPermission(Permissions.editspawn)) - { - if (Utils.IsInSpawn(tileX, tileY)) - { - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.SPm) > 1000) - { - player.SendErrorMessage("Spawn is protected from changes."); - player.SPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - } - } - return false; - } - - - /// Distance - Determines the distance between two vectors. /// value1 - The first vector location. /// value2 - The second vector location. From 7b2a4494b547cfd81f006bb79df28f43a1cf3819 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 22 Dec 2017 01:16:18 -0700 Subject: [PATCH 3/9] Move ice tile processing to TSPlayer. This moves ice tile placement and processing from TShock.CheckTilePermissions to TSPlayer in the form of the new boolean 'HasModifiedIceSuccessfully.' This is such a stupid thing we have to track, but we have to track it. Previously, we duplicated all of the check permission code and inserted special ice code paths. This duplicated a ton of code for little gain. The result of moving everything is that the control flow is easier to follow. In Terraria ice tiles are placed and melt on a timer so it's necessary to track them being placed and removed to permit tile events that would otherwise be blocked due to region checks and stuff. They're usually fairly harmless blocks, and without this code, ice wouldn't work properly. It's not ideal for this to be in TShock at all. --- TShockAPI/Bouncer.cs | 6 ++- TShockAPI/TSPlayer.cs | 40 ++++++++++++++++++++ TShockAPI/TShock.cs | 85 ------------------------------------------- 3 files changed, 44 insertions(+), 87 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 0bbf77e5..77d602a7 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -1146,7 +1146,8 @@ namespace TShockAPI { for (int j = y; j < y + tileData.Height; j++) { - if (TShock.CheckTilePermission(args.Player, i, j, type, EditAction.PlaceTile)) + if (!args.Player.HasModifiedIceSuccessfully(i, j, type, EditAction.PlaceTile) + && !args.Player.HasBuildPermission(i, j)) { args.Player.SendTileSquare(i, j, 4); args.Handled = true; @@ -1477,7 +1478,8 @@ namespace TShockAPI return; } - if (TShock.CheckTilePermission(args.Player, tileX, tileY, editData, action)) + if (!args.Player.HasModifiedIceSuccessfully(tileX, tileY, editData, action) + && !args.Player.HasBuildPermission(tileX, tileY)) { args.Player.SendTileSquare(tileX, tileY, 4); args.Handled = true; diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 05d9844c..a5aa17f2 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -385,6 +385,46 @@ namespace TShockAPI return HasBuildPermission(x, y) || HasPermission(Permissions.canpaint); } + /// Checks if a player can place ice, and if they can, tracks ice placements and removals. + /// The x coordinate of the suspected ice block. + /// The y coordinate of the suspected ice block. + /// The tile type of the suspected ice block. + /// The EditAction on the suspected ice block. + /// True if a player successfully places an ice tile or removes one of their past ice tiles. + public bool HasModifiedIceSuccessfully(int x, int y, short tileType, GetDataHandlers.EditAction editAction) + { + // The goal is to short circuit ASAP. + // A subsequent call to HasBuildPermission can figure this out if not explicitly ice. + if (!TShock.Config.AllowIce) + { + return false; + } + + // They've placed some ice. Horrible! + if (editAction == GetDataHandlers.EditAction.PlaceTile && tileType == TileID.MagicalIceBlock) + { + IceTiles.Add(new Point(x, y)); + return true; + } + + // The edit wasn't an add, so we check to see if the position matches any of the known ice tiles + if (editAction == GetDataHandlers.EditAction.KillTile) + { + foreach (Point p in IceTiles) + { + // If they're trying to kill ice or dirt, and the tile was in the list, we allow it. + if (p.X == x && p.Y == y && (Main.tile[p.X, p.Y].type == TileID.Dirt || Main.tile[p.X, p.Y].type == TileID.MagicalIceBlock)) + { + IceTiles.Remove(p); + return true; + } + } + } + + // Only a small number of cases let this happen. + return false; + } + /// /// A list of points where ice tiles have been placed. /// diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index f35efda8..5c03f7ee 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -1757,91 +1757,6 @@ namespace TShockAPI e.Handled = true; } - /// CheckTilePermission - Checks to see if a player has permission to modify a tile in general. - /// player - The TSPlayer object. - /// tileX - The x coordinate of the tile. - /// tileY - The y coordinate of the tile. - /// tileType - The tile type. - /// actionType - The type of edit that took place. - /// bool - True if the player should not be able to modify a tile. - public static bool CheckTilePermission(TSPlayer player, int tileX, int tileY, short tileType, GetDataHandlers.EditAction actionType) - { - if (!player.HasPermission(Permissions.canbuild)) - { - if (TShock.Config.AllowIce && actionType != GetDataHandlers.EditAction.PlaceTile) - { - foreach (Point p in player.IceTiles) - { - if (p.X == tileX && p.Y == tileY && (Main.tile[p.X, p.Y].type == 0 || Main.tile[p.X, p.Y].type == 127)) - { - player.IceTiles.Remove(p); - return false; - } - } - - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.BPm) > 2000) - { - player.SendErrorMessage("You do not have permission to build!"); - player.BPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - - if (TShock.Config.AllowIce && actionType == GetDataHandlers.EditAction.PlaceTile && tileType == 127) - { - player.IceTiles.Add(new Point(tileX, tileY)); - return false; - } - - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.BPm) > 2000) - { - player.SendErrorMessage("You do not have permission to build!"); - player.BPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - - if (!Regions.CanBuild(tileX, tileY, player)) - { - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.RPm) > 2000) - { - player.SendErrorMessage("This region is protected from changes."); - player.RPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - - if (Config.DisableBuild) - { - if (!player.HasPermission(Permissions.antibuild)) - { - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.WPm) > 2000) - { - player.SendErrorMessage("The world is protected from changes."); - player.WPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - } - - if (Config.SpawnProtection) - { - if (!player.HasPermission(Permissions.editspawn)) - { - if (Utils.IsInSpawn(tileX, tileY)) - { - if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.SPm) > 2000) - { - player.SendErrorMessage("Spawn is protected from changes."); - player.SPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } - return true; - } - } - } - return false; - } - /// Distance - Determines the distance between two vectors. /// value1 - The first vector location. /// value2 - The second vector location. From e370873fff5a3d511c0a7a05da5f1d70f95aa973 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 22 Dec 2017 01:27:14 -0700 Subject: [PATCH 4/9] Warn players if they can't modify the world This reimplements warnings that CheckTilePermissions previously had. It defaults to on because every single call currently in TShock expects it to be on. --- CHANGELOG.md | 1 + TShockAPI/Bouncer.cs | 3 --- TShockAPI/TSPlayer.cs | 26 ++++++++++++++++++++++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e081c0b6..db04f777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Fix message requiring login not using the command specifier set in the config file. (@hakusaro) * Move `TShock.CheckRangePermission()` to `TSPlayer.IsInRange` which **returns the opposite** of what the previous method did (see updated docs). (@hakusaro) * Move `TShock.CheckSpawn` to `Utils.IsInSpawn`. (@hakusaro) +* Replace `TShock.CheckTilePermission` with `TSPlayer.HasBuildPermission`, `TSPlayer.HasPaintPermission`, and `TSPlayer.HasModifiedIceSuccessfully` respectively. (@hakusaro) ## TShock 4.3.25 * Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6. diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 77d602a7..d17c5266 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -533,11 +533,8 @@ namespace TShockAPI short y = args.Y; byte homeless = args.Homeless; - // Calls to TShock.CheckTilePermission need to be broken up into different subsystems - // In particular, this handles both regions and other things. Ouch. if (!args.Player.HasBuildPermission(x, y)) { - args.Player.SendErrorMessage("You do not have access to modify this area."); args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY, Convert.ToByte(Main.npc[id].homeless)); args.Handled = true; diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index a5aa17f2..dad3ca4e 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -346,7 +346,7 @@ namespace TShockAPI /// The x coordinate they want to build at. /// The y coordinate they want to paint at. /// True if the player can build at the given point from build, spawn, and region protection. - public bool HasBuildPermission(int x, int y) + public bool HasBuildPermission(int x, int y, bool shouldWarnPlayer = true) { BuildPermissionFailPoint failure = BuildPermissionFailPoint.GeneralBuild; // The goal is to short circuit on easy stuff as much as possible. @@ -356,10 +356,12 @@ namespace TShockAPI // (General build protection takes precedence over spawn protection) if (!TShock.Config.DisableBuild || HasPermission(Permissions.antibuild)) { + failure = BuildPermissionFailPoint.SpawnProtect; // If they have spawn protect bypass, or it isn't spawn, or it isn't in spawn; continue // (If they have spawn protect bypass, we don't care if it's spawn or not) if (!TShock.Config.SpawnProtection || HasPermission(Permissions.editspawn) || !Utils.IsInSpawn(x, y)) { + failure = BuildPermissionFailPoint.Regions; // If they have build permission in this region, then they're allowed to continue if (TShock.Regions.CanBuild(x, y, this)) { @@ -367,12 +369,28 @@ namespace TShockAPI } } } - - // TODO: Implement warning system. - // If they lack build permission, they end up here. // If they have build permission but lack the ability to edit spawn and it's spawn, they end up here. // If they have build, it isn't spawn, or they can edit spawn, but they fail the region check, they end up here. + + // If they shouldn't be warned, exit early. + if (!shouldWarnPlayer) + return false; + + // If they should be warned, warn them. + switch (failure) + { + case BuildPermissionFailPoint.GeneralBuild: + SendErrorMessage("You lack permission to build on this server."); + break; + case BuildPermissionFailPoint.SpawnProtect: + SendErrorMessage("You lack permission to build in the spawn point."); + break; + case BuildPermissionFailPoint.Regions: + SendErrorMessage("You lack permission to build in this region."); + break; + } + return false; } From bfbe58e9edd321a479cac347e4c28a6d984add12 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 22 Dec 2017 10:03:59 -0700 Subject: [PATCH 5/9] Implement cooldown warnings on build permissions --- TShockAPI/TSPlayer.cs | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index dad3ca4e..a4402849 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -377,6 +377,12 @@ namespace TShockAPI if (!shouldWarnPlayer) return false; + // Space out warnings by 2 seconds so that they don't get spammed. + if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - lastPermissionWarning) < 2000) + { + return false; + } + // If they should be warned, warn them. switch (failure) { @@ -391,6 +397,9 @@ namespace TShockAPI break; } + // Set the last warning time to now. + lastPermissionWarning = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; + return false; } @@ -449,24 +458,10 @@ namespace TShockAPI public List IceTiles; /// - /// Unused, can be removed. + /// The last time the player was warned for build permissions. + /// In MS, defaults to 1 (so it will warn on the first attempt). /// - public long RPm = 1; - - /// - /// World protection message cool down. - /// - public long WPm = 1; - - /// - /// Spawn protection message cool down. - /// - public long SPm = 1; - - /// - /// Permission to build message cool down. - /// - public long BPm = 1; + public long lastPermissionWarning = 1; /// /// The time in ms when the player has logged in. From c501a128ff2dc62b1939db132382ef4222e04fbf Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 22 Dec 2017 21:02:53 -0700 Subject: [PATCH 6/9] Clarify summary of TSPlayer.IsInRange --- TShockAPI/TSPlayer.cs | 218 ++---------------------------------------- 1 file changed, 8 insertions(+), 210 deletions(-) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index d526e851..d0d1bb3c 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -307,215 +307,6 @@ namespace TShockAPI || !IsLoggedIn && TShock.Config.RequireLogin; } - /// Checks to see if a player has hacked item stacks in their inventory, and messages them as it checks. - /// If the check should send a message to the player with the results of the check. - /// True if any stacks don't conform. - public bool HasHackedItemStacks(bool shouldWarnPlayer = false) - { - // Iterates through each inventory location a player has. - // This section is sub divided into number ranges for what each range of slots corresponds to. - bool check = false; - - Item[] inventory = TPlayer.inventory; - Item[] armor = TPlayer.armor; - Item[] dye = TPlayer.dye; - Item[] miscEquips = TPlayer.miscEquips; - Item[] miscDyes = TPlayer.miscDyes; - Item[] piggy = TPlayer.bank.item; - Item[] safe = TPlayer.bank2.item; - Item[] forge = TPlayer.bank3.item; - Item trash = TPlayer.trashItem; - for (int i = 0; i < NetItem.MaxInventory; i++) - { - if (i < NetItem.InventoryIndex.Item2) - { - // From above: this is slots 0-58 in the inventory. - // 0-58 - Item item = new Item(); - if (inventory[i] != null && inventory[i].netID != 0) - { - item.netDefaults(inventory[i].netID); - item.Prefix(inventory[i].prefix); - item.AffixName(); - if (inventory[i].stack > item.maxStack || inventory[i].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove item {0} ({1}) and then rejoin.", item.Name, inventory[i].stack); - } - } - } - } - else if (i < NetItem.ArmorIndex.Item2) - { - // 59-78 - var index = i - NetItem.ArmorIndex.Item1; - Item item = new Item(); - if (armor[index] != null && armor[index].netID != 0) - { - item.netDefaults(armor[index].netID); - item.Prefix(armor[index].prefix); - item.AffixName(); - if (armor[index].stack > item.maxStack || armor[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove armor {0} ({1}) and then rejoin.", item.Name, armor[index].stack); - } - } - } - } - else if (i < NetItem.DyeIndex.Item2) - { - // 79-88 - var index = i - NetItem.DyeIndex.Item1; - Item item = new Item(); - if (dye[index] != null && dye[index].netID != 0) - { - item.netDefaults(dye[index].netID); - item.Prefix(dye[index].prefix); - item.AffixName(); - if (dye[index].stack > item.maxStack || dye[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove dye {0} ({1}) and then rejoin.", item.Name, dye[index].stack); - } - } - } - } - else if (i < NetItem.MiscEquipIndex.Item2) - { - // 89-93 - var index = i - NetItem.MiscEquipIndex.Item1; - Item item = new Item(); - if (miscEquips[index] != null && miscEquips[index].netID != 0) - { - item.netDefaults(miscEquips[index].netID); - item.Prefix(miscEquips[index].prefix); - item.AffixName(); - if (miscEquips[index].stack > item.maxStack || miscEquips[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove item {0} ({1}) and then rejoin.", item.Name, miscEquips[index].stack); - } - } - } - } - else if (i < NetItem.MiscDyeIndex.Item2) - { - // 93-98 - var index = i - NetItem.MiscDyeIndex.Item1; - Item item = new Item(); - if (miscDyes[index] != null && miscDyes[index].netID != 0) - { - item.netDefaults(miscDyes[index].netID); - item.Prefix(miscDyes[index].prefix); - item.AffixName(); - if (miscDyes[index].stack > item.maxStack || miscDyes[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove item dye {0} ({1}) and then rejoin.", item.Name, miscDyes[index].stack); - } - } - } - } - else if (i < NetItem.PiggyIndex.Item2) - { - // 98-138 - var index = i - NetItem.PiggyIndex.Item1; - Item item = new Item(); - if (piggy[index] != null && piggy[index].netID != 0) - { - item.netDefaults(piggy[index].netID); - item.Prefix(piggy[index].prefix); - item.AffixName(); - - if (piggy[index].stack > item.maxStack || piggy[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove piggy-bank item {0} ({1}) and then rejoin.", item.Name, piggy[index].stack); - } - } - } - } - else if (i < NetItem.SafeIndex.Item2) - { - // 138-178 - var index = i - NetItem.SafeIndex.Item1; - Item item = new Item(); - if (safe[index] != null && safe[index].netID != 0) - { - item.netDefaults(safe[index].netID); - item.Prefix(safe[index].prefix); - item.AffixName(); - - if (safe[index].stack > item.maxStack || safe[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove safe item {0} ({1}) and then rejoin.", item.Name, safe[index].stack); - } - } - } - } - else if (i < NetItem.TrashIndex.Item2) - { - // 179-219 - Item item = new Item(); - if (trash != null && trash.netID != 0) - { - item.netDefaults(trash.netID); - item.Prefix(trash.prefix); - item.AffixName(); - - if (trash.stack > item.maxStack) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove trash item {0} ({1}) and then rejoin.", item.Name, trash.stack); - } - } - } - } - else - { - // 220 - var index = i - NetItem.ForgeIndex.Item1; - Item item = new Item(); - if (forge[index] != null && forge[index].netID != 0) - { - item.netDefaults(forge[index].netID); - item.Prefix(forge[index].prefix); - item.AffixName(); - - if (forge[index].stack > item.maxStack || forge[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove Defender's Forge item {0} ({1}) and then rejoin.", item.Name, forge[index].stack); - } - } - } - - } - } - - return check; - } - /// /// The player's server side inventory data. /// @@ -530,7 +321,7 @@ namespace TShockAPI public bool SilentJoinInProgress; - /// Checks if a player has permission to modify a tile dependent on range checks. + /// Checks if a player is in range of a given tile if range checks are enabled. /// The x coordinate of the tile. /// The y coordinate of the tile. /// The range to check for. @@ -1549,7 +1340,14 @@ namespace TShockAPI StackFrame frame = null; frame = trace.GetFrame(1); if (frame != null && frame.GetMethod().DeclaringType != null) + { TShock.Log.Debug(frame.GetMethod().DeclaringType.Name + " called Disable()."); + TShock.Log.Debug("SSC: " + IsDisabledForSSC); + TShock.Log.Debug("Stacks: " + IsDisabledForStackDetection); + TShock.Log.Debug("Wearables: " + IsDisabledForBannedWearable); + TShock.Log.Debug("Trash: " + IsDisabledPendingTrashRemoval); + TShock.Log.Debug("Logged in?" + IsLoggedIn); + } } /// From 604b386cbaca45e6f392bccac9f0ce07aed25489 Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 22 Dec 2017 21:12:56 -0700 Subject: [PATCH 7/9] Rescue lost work --- TShockAPI/TSPlayer.cs | 218 ++---------------------------------------- 1 file changed, 8 insertions(+), 210 deletions(-) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index d526e851..d0d1bb3c 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -307,215 +307,6 @@ namespace TShockAPI || !IsLoggedIn && TShock.Config.RequireLogin; } - /// Checks to see if a player has hacked item stacks in their inventory, and messages them as it checks. - /// If the check should send a message to the player with the results of the check. - /// True if any stacks don't conform. - public bool HasHackedItemStacks(bool shouldWarnPlayer = false) - { - // Iterates through each inventory location a player has. - // This section is sub divided into number ranges for what each range of slots corresponds to. - bool check = false; - - Item[] inventory = TPlayer.inventory; - Item[] armor = TPlayer.armor; - Item[] dye = TPlayer.dye; - Item[] miscEquips = TPlayer.miscEquips; - Item[] miscDyes = TPlayer.miscDyes; - Item[] piggy = TPlayer.bank.item; - Item[] safe = TPlayer.bank2.item; - Item[] forge = TPlayer.bank3.item; - Item trash = TPlayer.trashItem; - for (int i = 0; i < NetItem.MaxInventory; i++) - { - if (i < NetItem.InventoryIndex.Item2) - { - // From above: this is slots 0-58 in the inventory. - // 0-58 - Item item = new Item(); - if (inventory[i] != null && inventory[i].netID != 0) - { - item.netDefaults(inventory[i].netID); - item.Prefix(inventory[i].prefix); - item.AffixName(); - if (inventory[i].stack > item.maxStack || inventory[i].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove item {0} ({1}) and then rejoin.", item.Name, inventory[i].stack); - } - } - } - } - else if (i < NetItem.ArmorIndex.Item2) - { - // 59-78 - var index = i - NetItem.ArmorIndex.Item1; - Item item = new Item(); - if (armor[index] != null && armor[index].netID != 0) - { - item.netDefaults(armor[index].netID); - item.Prefix(armor[index].prefix); - item.AffixName(); - if (armor[index].stack > item.maxStack || armor[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove armor {0} ({1}) and then rejoin.", item.Name, armor[index].stack); - } - } - } - } - else if (i < NetItem.DyeIndex.Item2) - { - // 79-88 - var index = i - NetItem.DyeIndex.Item1; - Item item = new Item(); - if (dye[index] != null && dye[index].netID != 0) - { - item.netDefaults(dye[index].netID); - item.Prefix(dye[index].prefix); - item.AffixName(); - if (dye[index].stack > item.maxStack || dye[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove dye {0} ({1}) and then rejoin.", item.Name, dye[index].stack); - } - } - } - } - else if (i < NetItem.MiscEquipIndex.Item2) - { - // 89-93 - var index = i - NetItem.MiscEquipIndex.Item1; - Item item = new Item(); - if (miscEquips[index] != null && miscEquips[index].netID != 0) - { - item.netDefaults(miscEquips[index].netID); - item.Prefix(miscEquips[index].prefix); - item.AffixName(); - if (miscEquips[index].stack > item.maxStack || miscEquips[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove item {0} ({1}) and then rejoin.", item.Name, miscEquips[index].stack); - } - } - } - } - else if (i < NetItem.MiscDyeIndex.Item2) - { - // 93-98 - var index = i - NetItem.MiscDyeIndex.Item1; - Item item = new Item(); - if (miscDyes[index] != null && miscDyes[index].netID != 0) - { - item.netDefaults(miscDyes[index].netID); - item.Prefix(miscDyes[index].prefix); - item.AffixName(); - if (miscDyes[index].stack > item.maxStack || miscDyes[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove item dye {0} ({1}) and then rejoin.", item.Name, miscDyes[index].stack); - } - } - } - } - else if (i < NetItem.PiggyIndex.Item2) - { - // 98-138 - var index = i - NetItem.PiggyIndex.Item1; - Item item = new Item(); - if (piggy[index] != null && piggy[index].netID != 0) - { - item.netDefaults(piggy[index].netID); - item.Prefix(piggy[index].prefix); - item.AffixName(); - - if (piggy[index].stack > item.maxStack || piggy[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove piggy-bank item {0} ({1}) and then rejoin.", item.Name, piggy[index].stack); - } - } - } - } - else if (i < NetItem.SafeIndex.Item2) - { - // 138-178 - var index = i - NetItem.SafeIndex.Item1; - Item item = new Item(); - if (safe[index] != null && safe[index].netID != 0) - { - item.netDefaults(safe[index].netID); - item.Prefix(safe[index].prefix); - item.AffixName(); - - if (safe[index].stack > item.maxStack || safe[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove safe item {0} ({1}) and then rejoin.", item.Name, safe[index].stack); - } - } - } - } - else if (i < NetItem.TrashIndex.Item2) - { - // 179-219 - Item item = new Item(); - if (trash != null && trash.netID != 0) - { - item.netDefaults(trash.netID); - item.Prefix(trash.prefix); - item.AffixName(); - - if (trash.stack > item.maxStack) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove trash item {0} ({1}) and then rejoin.", item.Name, trash.stack); - } - } - } - } - else - { - // 220 - var index = i - NetItem.ForgeIndex.Item1; - Item item = new Item(); - if (forge[index] != null && forge[index].netID != 0) - { - item.netDefaults(forge[index].netID); - item.Prefix(forge[index].prefix); - item.AffixName(); - - if (forge[index].stack > item.maxStack || forge[index].stack < 0) - { - check = true; - if (shouldWarnPlayer) - { - SendErrorMessage("Stack cheat detected. Remove Defender's Forge item {0} ({1}) and then rejoin.", item.Name, forge[index].stack); - } - } - } - - } - } - - return check; - } - /// /// The player's server side inventory data. /// @@ -530,7 +321,7 @@ namespace TShockAPI public bool SilentJoinInProgress; - /// Checks if a player has permission to modify a tile dependent on range checks. + /// Checks if a player is in range of a given tile if range checks are enabled. /// The x coordinate of the tile. /// The y coordinate of the tile. /// The range to check for. @@ -1549,7 +1340,14 @@ namespace TShockAPI StackFrame frame = null; frame = trace.GetFrame(1); if (frame != null && frame.GetMethod().DeclaringType != null) + { TShock.Log.Debug(frame.GetMethod().DeclaringType.Name + " called Disable()."); + TShock.Log.Debug("SSC: " + IsDisabledForSSC); + TShock.Log.Debug("Stacks: " + IsDisabledForStackDetection); + TShock.Log.Debug("Wearables: " + IsDisabledForBannedWearable); + TShock.Log.Debug("Trash: " + IsDisabledPendingTrashRemoval); + TShock.Log.Debug("Logged in?" + IsLoggedIn); + } } /// From 5991802dce1c5d29606dce97b67ff0014bf8f44d Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 22 Dec 2017 21:15:26 -0700 Subject: [PATCH 8/9] Revert "Clarify summary of TSPlayer.IsInRange" This reverts commit c501a128ff2dc62b1939db132382ef4222e04fbf. --- TShockAPI/TSPlayer.cs | 218 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 210 insertions(+), 8 deletions(-) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index d0d1bb3c..d526e851 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -307,6 +307,215 @@ namespace TShockAPI || !IsLoggedIn && TShock.Config.RequireLogin; } + /// Checks to see if a player has hacked item stacks in their inventory, and messages them as it checks. + /// If the check should send a message to the player with the results of the check. + /// True if any stacks don't conform. + public bool HasHackedItemStacks(bool shouldWarnPlayer = false) + { + // Iterates through each inventory location a player has. + // This section is sub divided into number ranges for what each range of slots corresponds to. + bool check = false; + + Item[] inventory = TPlayer.inventory; + Item[] armor = TPlayer.armor; + Item[] dye = TPlayer.dye; + Item[] miscEquips = TPlayer.miscEquips; + Item[] miscDyes = TPlayer.miscDyes; + Item[] piggy = TPlayer.bank.item; + Item[] safe = TPlayer.bank2.item; + Item[] forge = TPlayer.bank3.item; + Item trash = TPlayer.trashItem; + for (int i = 0; i < NetItem.MaxInventory; i++) + { + if (i < NetItem.InventoryIndex.Item2) + { + // From above: this is slots 0-58 in the inventory. + // 0-58 + Item item = new Item(); + if (inventory[i] != null && inventory[i].netID != 0) + { + item.netDefaults(inventory[i].netID); + item.Prefix(inventory[i].prefix); + item.AffixName(); + if (inventory[i].stack > item.maxStack || inventory[i].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove item {0} ({1}) and then rejoin.", item.Name, inventory[i].stack); + } + } + } + } + else if (i < NetItem.ArmorIndex.Item2) + { + // 59-78 + var index = i - NetItem.ArmorIndex.Item1; + Item item = new Item(); + if (armor[index] != null && armor[index].netID != 0) + { + item.netDefaults(armor[index].netID); + item.Prefix(armor[index].prefix); + item.AffixName(); + if (armor[index].stack > item.maxStack || armor[index].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove armor {0} ({1}) and then rejoin.", item.Name, armor[index].stack); + } + } + } + } + else if (i < NetItem.DyeIndex.Item2) + { + // 79-88 + var index = i - NetItem.DyeIndex.Item1; + Item item = new Item(); + if (dye[index] != null && dye[index].netID != 0) + { + item.netDefaults(dye[index].netID); + item.Prefix(dye[index].prefix); + item.AffixName(); + if (dye[index].stack > item.maxStack || dye[index].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove dye {0} ({1}) and then rejoin.", item.Name, dye[index].stack); + } + } + } + } + else if (i < NetItem.MiscEquipIndex.Item2) + { + // 89-93 + var index = i - NetItem.MiscEquipIndex.Item1; + Item item = new Item(); + if (miscEquips[index] != null && miscEquips[index].netID != 0) + { + item.netDefaults(miscEquips[index].netID); + item.Prefix(miscEquips[index].prefix); + item.AffixName(); + if (miscEquips[index].stack > item.maxStack || miscEquips[index].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove item {0} ({1}) and then rejoin.", item.Name, miscEquips[index].stack); + } + } + } + } + else if (i < NetItem.MiscDyeIndex.Item2) + { + // 93-98 + var index = i - NetItem.MiscDyeIndex.Item1; + Item item = new Item(); + if (miscDyes[index] != null && miscDyes[index].netID != 0) + { + item.netDefaults(miscDyes[index].netID); + item.Prefix(miscDyes[index].prefix); + item.AffixName(); + if (miscDyes[index].stack > item.maxStack || miscDyes[index].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove item dye {0} ({1}) and then rejoin.", item.Name, miscDyes[index].stack); + } + } + } + } + else if (i < NetItem.PiggyIndex.Item2) + { + // 98-138 + var index = i - NetItem.PiggyIndex.Item1; + Item item = new Item(); + if (piggy[index] != null && piggy[index].netID != 0) + { + item.netDefaults(piggy[index].netID); + item.Prefix(piggy[index].prefix); + item.AffixName(); + + if (piggy[index].stack > item.maxStack || piggy[index].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove piggy-bank item {0} ({1}) and then rejoin.", item.Name, piggy[index].stack); + } + } + } + } + else if (i < NetItem.SafeIndex.Item2) + { + // 138-178 + var index = i - NetItem.SafeIndex.Item1; + Item item = new Item(); + if (safe[index] != null && safe[index].netID != 0) + { + item.netDefaults(safe[index].netID); + item.Prefix(safe[index].prefix); + item.AffixName(); + + if (safe[index].stack > item.maxStack || safe[index].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove safe item {0} ({1}) and then rejoin.", item.Name, safe[index].stack); + } + } + } + } + else if (i < NetItem.TrashIndex.Item2) + { + // 179-219 + Item item = new Item(); + if (trash != null && trash.netID != 0) + { + item.netDefaults(trash.netID); + item.Prefix(trash.prefix); + item.AffixName(); + + if (trash.stack > item.maxStack) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove trash item {0} ({1}) and then rejoin.", item.Name, trash.stack); + } + } + } + } + else + { + // 220 + var index = i - NetItem.ForgeIndex.Item1; + Item item = new Item(); + if (forge[index] != null && forge[index].netID != 0) + { + item.netDefaults(forge[index].netID); + item.Prefix(forge[index].prefix); + item.AffixName(); + + if (forge[index].stack > item.maxStack || forge[index].stack < 0) + { + check = true; + if (shouldWarnPlayer) + { + SendErrorMessage("Stack cheat detected. Remove Defender's Forge item {0} ({1}) and then rejoin.", item.Name, forge[index].stack); + } + } + } + + } + } + + return check; + } + /// /// The player's server side inventory data. /// @@ -321,7 +530,7 @@ namespace TShockAPI public bool SilentJoinInProgress; - /// Checks if a player is in range of a given tile if range checks are enabled. + /// Checks if a player has permission to modify a tile dependent on range checks. /// The x coordinate of the tile. /// The y coordinate of the tile. /// The range to check for. @@ -1340,14 +1549,7 @@ namespace TShockAPI StackFrame frame = null; frame = trace.GetFrame(1); if (frame != null && frame.GetMethod().DeclaringType != null) - { TShock.Log.Debug(frame.GetMethod().DeclaringType.Name + " called Disable()."); - TShock.Log.Debug("SSC: " + IsDisabledForSSC); - TShock.Log.Debug("Stacks: " + IsDisabledForStackDetection); - TShock.Log.Debug("Wearables: " + IsDisabledForBannedWearable); - TShock.Log.Debug("Trash: " + IsDisabledPendingTrashRemoval); - TShock.Log.Debug("Logged in?" + IsLoggedIn); - } } /// From 5e7cb92de05dcd89bc6fb881aa597363e7a0b92c Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Fri, 22 Dec 2017 21:18:47 -0700 Subject: [PATCH 9/9] Re-apply some changes --- TShockAPI/TSPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index d526e851..3c747b9d 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -530,7 +530,7 @@ namespace TShockAPI public bool SilentJoinInProgress; - /// Checks if a player has permission to modify a tile dependent on range checks. + /// Checks if a player is in range of a given tile if range checks are enabled. /// The x coordinate of the tile. /// The y coordinate of the tile. /// The range to check for.