From 07becc3c8f4cee37f5dd205e96189519aa4f2f5e Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Sat, 2 Apr 2022 11:53:53 -0700 Subject: [PATCH 01/16] Change language from fatal startup exceptions The previous language was true but slightly unhelpful to non-native English speakers and users who aren't familiar with server software. When a fatal startup exception occurs now, TShock tells you what this means and that it won't be able to start until this is resolved. --- CHANGELOG.md | 1 + TShockAPI/TShock.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6693bb1..bf3dfed3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin ## Upcoming changes * Fixed `TSPlayer.GiveItem` not working if the player is in lava. (@gohjoseph) * Only allow using Teleportation Potions, Magic Conch, and Demon Conch whilst holding them. (@drunderscore) +* Updated server startup language to be more clear when encountering a fatal startup error. Now, the server gives more context as to what happened so that there's a better chance of people being able to help themselves. (@hakusaro) ## TShock 4.5.17 * Fixed duplicate characters (twins) after repeatedly logging in as the same character due to connection not being immediately closed during `NetHooks_NameCollision`. (@gohjoseph) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index e326c6f9..2c604513 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -386,7 +386,8 @@ namespace TShockAPI } catch (Exception ex) { - Log.ConsoleError("Fatal Startup Exception"); + Log.ConsoleError("TShock encountered a problem from which it cannot recover. The following message may help diagnose the problem."); + Log.ConsoleError("Until the problem is resolved, TShock will not be able to start (and will crash on startup)."); Log.ConsoleError(ex.ToString()); Environment.Exit(1); } From 516b8efca409925faab5bff4461b4d60356e0c21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jun 2022 16:34:04 +0000 Subject: [PATCH 02/16] Bump Newtonsoft.Json from 10.0.3 to 13.0.1 in /TShockAPI Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 10.0.3 to 13.0.1. - [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases) - [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/10.0.3...13.0.1) --- updated-dependencies: - dependency-name: Newtonsoft.Json dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- TShockAPI/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockAPI/packages.config b/TShockAPI/packages.config index 691052d5..b0cddcdc 100644 --- a/TShockAPI/packages.config +++ b/TShockAPI/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file From 953622c8e1b0615ba1be9e3f8f920fa36e6d1d18 Mon Sep 17 00:00:00 2001 From: NotGeri <46295973+NotGeri@users.noreply.github.com> Date: Wed, 29 Jun 2022 17:10:18 +0200 Subject: [PATCH 03/16] Add `-worldevil` startup parameter --- TShockAPI/TShock.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 2c604513..6bfd8006 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -768,6 +768,28 @@ namespace TShockAPI } }) + .AddFlag("-worldevil", (value) => + { + + int worldEvil; + switch (value.ToLower()) + { + case "random": + worldEvil = -1; + break; + case "corrupt": + worldEvil = 0; + break; + case "crimson": + worldEvil = 1; + break; + default: + throw new InvalidOperationException("Invalid value given for command line argument \"-worldevil\"."); + } + + ServerApi.LogWriter.PluginWriteLine(this, String.Format("New worlds will be generated with the {0} world evil type!", value), TraceLevel.Verbose); + WorldGen.WorldGenParam_Evil = worldEvil; + }) //Flags without arguments .AddFlag("-logclear", () => LogClear = true) From 7e9c79cfd9c2a298fef2deac1b2e7940e23f281a Mon Sep 17 00:00:00 2001 From: NotGeri <46295973+NotGeri@users.noreply.github.com> Date: Wed, 29 Jun 2022 17:10:27 +0200 Subject: [PATCH 04/16] Add relevant changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3dfed3..f03c092d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Fixed `TSPlayer.GiveItem` not working if the player is in lava. (@gohjoseph) * Only allow using Teleportation Potions, Magic Conch, and Demon Conch whilst holding them. (@drunderscore) * Updated server startup language to be more clear when encountering a fatal startup error. Now, the server gives more context as to what happened so that there's a better chance of people being able to help themselves. (@hakusaro) +* Added `-worldevil ` command line argument (@NotGeri) ## TShock 4.5.17 * Fixed duplicate characters (twins) after repeatedly logging in as the same character due to connection not being immediately closed during `NetHooks_NameCollision`. (@gohjoseph) From b19a928fd8d34985c2ca7911058ad617a794432e Mon Sep 17 00:00:00 2001 From: Anzhelika Date: Wed, 20 Jan 2021 18:54:26 +0300 Subject: [PATCH 05/16] PlayerHasBuildPermission hook (#8) --- TShockAPI/Hooks/PlayerHooks.cs | 52 ++++++++++++++++++++++++++++++++++ TShockAPI/TSPlayer.cs | 6 ++++ 2 files changed, 58 insertions(+) diff --git a/TShockAPI/Hooks/PlayerHooks.cs b/TShockAPI/Hooks/PlayerHooks.cs index 3114020b..7a3e2067 100644 --- a/TShockAPI/Hooks/PlayerHooks.cs +++ b/TShockAPI/Hooks/PlayerHooks.cs @@ -272,6 +272,32 @@ namespace TShockAPI.Hooks } } + /// + /// EventArgs used for the event. + /// + public class PlayerHasBuildPermissionEventArgs + { + /// + /// The player who fired the event. + /// + public TSPlayer Player { get; set; } + + /// + /// The X coordinate being checked. + /// + public int X { get; set; } + + /// + /// The Y coordinate being checked. + /// + public int Y { get; set; } + + /// + /// of the hook. + /// + public PermissionHookResult Result { get; set; } + } + /// /// A collection of events fired by players that can be hooked to. /// @@ -368,6 +394,16 @@ namespace TShockAPI.Hooks /// public static event PlayerTilebanPermissionD PlayerTilebanPermission; + /// + /// The delegate of the event. + /// + /// The EventArgs for this event. + public delegate void PlayerHasBuildPermissionD(PlayerHasBuildPermissionEventArgs e); + /// + /// Fired by players every time a build permission check occurs. + /// + public static event PlayerHasBuildPermissionD PlayerHasBuildPermission; + /// /// Fires the event. @@ -525,6 +561,22 @@ namespace TShockAPI.Hooks return args.Result; } + /// + /// Fires the event. + /// + /// The player firing the event. + /// Event result if the event has been handled, otherwise . + public static PermissionHookResult OnPlayerHasBuildPermission(TSPlayer player, int x, int y) + { + if (PlayerHasBuildPermission == null) + return PermissionHookResult.Unhandled; + + var args = new PlayerHasBuildPermissionEventArgs {Player = player, X = x, Y = y}; + PlayerHasBuildPermission(args); + + return args.Result; + } + } /// diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 93db08bc..1aa7203d 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -651,6 +651,12 @@ namespace TShockAPI /// True if the player can build at the given point from build, spawn, and region protection. public bool HasBuildPermission(int x, int y, bool shouldWarnPlayer = true) { + PermissionHookResult hookResult = PlayerHooks.OnPlayerHasBuildPermission(this, x, y); + if (hookResult != PermissionHookResult.Unhandled) + { + return hookResult == PermissionHookResult.Granted; + } + 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. From 9768aeaf86b76e4cfb73967986d6358d01622d9d Mon Sep 17 00:00:00 2001 From: Killia0 Date: Sun, 24 Jul 2022 19:31:26 -0400 Subject: [PATCH 06/16] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f03c092d..9493586b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Only allow using Teleportation Potions, Magic Conch, and Demon Conch whilst holding them. (@drunderscore) * Updated server startup language to be more clear when encountering a fatal startup error. Now, the server gives more context as to what happened so that there's a better chance of people being able to help themselves. (@hakusaro) * Added `-worldevil ` command line argument (@NotGeri) +* Added OnPlayerHasBuildPermission hook to PlayerHooks. (@AnzhelikaO, @Killia0) ## TShock 4.5.17 * Fixed duplicate characters (twins) after repeatedly logging in as the same character due to connection not being immediately closed during `NetHooks_NameCollision`. (@gohjoseph) From 17a1dd13aa0c7fb0f615d50d3f4a632543c2e661 Mon Sep 17 00:00:00 2001 From: Killia0 Date: Sun, 24 Jul 2022 19:33:28 -0400 Subject: [PATCH 07/16] Use right name in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9493586b..318e82b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Only allow using Teleportation Potions, Magic Conch, and Demon Conch whilst holding them. (@drunderscore) * Updated server startup language to be more clear when encountering a fatal startup error. Now, the server gives more context as to what happened so that there's a better chance of people being able to help themselves. (@hakusaro) * Added `-worldevil ` command line argument (@NotGeri) -* Added OnPlayerHasBuildPermission hook to PlayerHooks. (@AnzhelikaO, @Killia0) +* Added PlayerHasBuildPermission hook to PlayerHooks. (@AnzhelikaO, @Killia0) ## TShock 4.5.17 * Fixed duplicate characters (twins) after repeatedly logging in as the same character due to connection not being immediately closed during `NetHooks_NameCollision`. (@gohjoseph) From 3383c630e332298a35ccd0209da3b6683fc28fec Mon Sep 17 00:00:00 2001 From: punchready Date: Mon, 25 Jul 2022 11:29:25 +0200 Subject: [PATCH 08/16] Strictly filter tile types in send tile rect handling --- TShockAPI/Handlers/SendTileRectHandler.cs | 73 ++++++++++++++++++++--- 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index b5c0bafd..3a313299 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -145,14 +145,68 @@ namespace TShockAPI.Handlers int objHeight = data.Height; int offsetY = 0; - if (newTile.Type == TileID.TrapdoorClosed) + // Verify that the changes are actually valid conceptually + if (Main.tile[realX, realY].type == newTile.Type) { - // Trapdoors can modify a 2x3 space. When it closes it will have leftover tiles either on top or bottom. - // If we don't update these tiles, the trapdoor gets confused and disappears. - // So we capture all 6 possible tiles and offset ourselves 1 tile above the closed trapdoor to capture the entire 2x3 area - objWidth = 2; - objHeight = 3; - offsetY = -1; + switch (newTile.Type) + { + // Some individual cases might still allow crashing exploits, as the actual framing is not being checked here + // Doing so requires hard-coding the individual valid framing values and is a lot of effort + case TileID.ProjectilePressurePad: + case TileID.WirePipe: + case TileID.Traps: + case TileID.Candles: + case TileID.PeaceCandle: + case TileID.WaterCandle: + case TileID.PlatinumCandle: + case TileID.Chairs: + case TileID.Bathtubs: + case TileID.Beds: + case TileID.Firework: + case TileID.WaterFountain: + case TileID.BloodMoonMonolith: + case TileID.VoidMonolith: + case TileID.LunarMonolith: + case TileID.MusicBoxes: + case TileID.ArrowSign: + case TileID.PaintedArrowSign: + case TileID.Cannon: + case TileID.Campfire: + case TileID.Plants: + case TileID.MinecartTrack: + case TileID.ChristmasTree: + { + // allowed changes + } + break; + default: + { + continue; + } + } + } + else + { + // Together with Flower Boots and Land Mine destruction, these are the only cases where a tile type is allowed to be modified + switch (newTile.Type) + { + case TileID.LogicSensor: + case TileID.FoodPlatter: + case TileID.WeaponsRack2: + case TileID.ItemFrame: + case TileID.HatRack: + case TileID.DisplayDoll: + case TileID.TeleportationPylon: + case TileID.TargetDummy: + { + // allowed placements + } + break; + default: + { + continue; + } + } } // Ensure the tile object fits inside the rect before processing it @@ -249,7 +303,10 @@ namespace TShockAPI.Handlers UpdateServerTileState(tile, newTile, TileDataType.Tile); } - ProcessConversionSpreads(Main.tile[realX, realY], newTile); + if (rectWidth == 1 && rectLength == 1) // Conversion only sends a 1x1 rect + { + ProcessConversionSpreads(Main.tile[realX, realY], newTile); + } // All other single tile updates should not be processed. } From abaf41452341fffe8cd9d1b09bbfd39b510ea0b4 Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 26 Jul 2022 00:01:25 +0200 Subject: [PATCH 09/16] Remove tiles which are no longer sent in this packet --- TShockAPI/Handlers/SendTileRectHandler.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 3a313299..564590fd 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -159,9 +159,6 @@ namespace TShockAPI.Handlers case TileID.PeaceCandle: case TileID.WaterCandle: case TileID.PlatinumCandle: - case TileID.Chairs: - case TileID.Bathtubs: - case TileID.Beds: case TileID.Firework: case TileID.WaterFountain: case TileID.BloodMoonMonolith: From 681c6de1f7f4108d97d8d99433e21960eec361fd Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 26 Jul 2022 08:24:46 +0200 Subject: [PATCH 10/16] add strict STR size checking --- TShockAPI/Configuration/TShockConfig.cs | 8 -------- TShockAPI/Handlers/SendTileRectHandler.cs | 14 ++++---------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/TShockAPI/Configuration/TShockConfig.cs b/TShockAPI/Configuration/TShockConfig.cs index d87dfa22..8a5e3321 100644 --- a/TShockAPI/Configuration/TShockConfig.cs +++ b/TShockAPI/Configuration/TShockConfig.cs @@ -445,14 +445,6 @@ namespace TShockAPI.Configuration [Description("Whether or not to kick users when they surpass the HealOther threshold.")] public bool KickOnHealOtherThresholdBroken = false; - /// Disables a player if this number of tiles is present in a Tile Rectangle packet - [Description("Disables a player if this number of tiles is present in a Tile Rectangle packet")] - public int TileRectangleSizeThreshold = 50; - - /// Whether or not to kick users when they surpass the TileRectangleSize threshold. - [Description("Whether or not to kick users when they surpass the TileRectangleSize threshold.")] - public bool KickOnTileRectangleSizeThresholdBroken = false; - /// Whether or not the server should suppress build permission failure warnings from regions, spawn point, or server edit failure. [Description("Whether or not the server should suppress build permission failure warnings from regions, spawn point, or server edit failure.")] public bool SuppressPermissionFailureNotices = false; diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 564590fd..8f6c33c8 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -232,7 +232,7 @@ namespace TShockAPI.Handlers } } } - + /// /// Processes a tile object consisting of multiple tiles from the tile rect packet /// @@ -290,12 +290,12 @@ namespace TShockAPI.Handlers ITile tile = Main.tile[realX, realY]; - if (tile.type == TileID.LandMine && !newTile.Active) + if (rectWidth == 1 && rectLength == 1 && tile.type == TileID.LandMine && !newTile.Active) { UpdateServerTileState(tile, newTile, TileDataType.Tile); } - if (tile.type == TileID.WirePipe) + if (rectWidth == 1 && rectLength == 1 && tile.type == TileID.WirePipe) { UpdateServerTileState(tile, newTile, TileDataType.Tile); } @@ -500,15 +500,9 @@ namespace TShockAPI.Handlers return true; } - var rectSize = args.Width * args.Length; - if (rectSize > TShock.Config.Settings.TileRectangleSizeThreshold) + if (args.Width > 4 || args.Length > 4) // as of 1.4.3.6 this is the biggest size the client will send in any case { TShock.Log.ConsoleDebug("Bouncer / SendTileRect rejected from non-vanilla tilemod from {0}", args.Player.Name); - if (TShock.Config.Settings.KickOnTileRectangleSizeThresholdBroken) - { - args.Player.Kick("Unexpected tile threshold reached"); - } - return true; } From 82a095f3cf8bd66d30c727d242c79edc98be8561 Mon Sep 17 00:00:00 2001 From: punchready Date: Tue, 26 Jul 2022 08:58:56 +0200 Subject: [PATCH 11/16] fix a region bypass exploit using the ice rod --- TShockAPI/Bouncer.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index dae66461..736fc6d7 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -401,6 +401,18 @@ namespace TShockAPI return; } + // i do not understand the ice tile check enough to be able to modify it, however i do know that it can be used to completely bypass region protection + // this check ensures that build permission is always checked no matter what + if (!args.Player.HasBuildPermission(tileX, tileY)) + { + TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from build from {0} {1} {2}", args.Player.Name, action, editData); + + GetRollbackRectSize(tileX, tileY, out byte width, out byte length, out int offsetY); + args.Player.SendTileRect((short)(tileX - width), (short)(tileY + offsetY), (byte)(width * 2), (byte)(length + 1)); + args.Handled = true; + return; + } + if (editData < 0 || ((action == EditAction.PlaceTile || action == EditAction.ReplaceTile) && editData >= Main.maxTileSets) || ((action == EditAction.PlaceWall || action == EditAction.ReplaceWall) && editData >= Main.maxWallTypes)) From 8b4dc3a1a40b0f82ce2af07af76094e153787184 Mon Sep 17 00:00:00 2001 From: punchready Date: Wed, 27 Jul 2022 06:23:34 +0200 Subject: [PATCH 12/16] Fix flower boots check, clean up code --- TShockAPI/Handlers/SendTileRectHandler.cs | 111 +++++++++++++--------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 8f6c33c8..7d073266 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -19,14 +19,45 @@ namespace TShockAPI.Handlers /// public class SendTileRectHandler : IPacketHandler { - /// - /// Maps grass-type blocks to flowers that can be grown on them with flower boots - /// - public static Dictionary> GrassToPlantMap = new Dictionary> + private static readonly Dictionary> PlantToGrassMap = new Dictionary> { - { TileID.Grass, new List { TileID.Plants, TileID.Plants2 } }, - { TileID.HallowedGrass, new List { TileID.HallowedPlants, TileID.HallowedPlants2 } }, - { TileID.JungleGrass, new List { TileID.JunglePlants, TileID.JunglePlants2 } } + { TileID.Plants, new HashSet() + { + TileID.Grass, TileID.GolfGrass + } }, + { TileID.HallowedPlants, new HashSet() + { + TileID.HallowedGrass, TileID.GolfGrassHallowed + } }, + { TileID.HallowedPlants2, new HashSet() + { + TileID.HallowedGrass, TileID.GolfGrassHallowed + } }, + { TileID.JunglePlants2, new HashSet() + { + TileID.JungleGrass + } }, + }; + + private static readonly Dictionary> PlantToStyleMap = new Dictionary>() + { + { TileID.Plants, new HashSet() + { + 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 24, 27, 30, 33, 36, 39, 42, + 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, + } }, + { TileID.HallowedPlants, new HashSet() + { + 4, 6, + } }, + { TileID.HallowedPlants2, new HashSet() + { + 2, 3, 4, 6, 7, + } }, + { TileID.JunglePlants2, new HashSet() + { + 9, 10, 11, 12, 13, 14, 15, 16, + } }, }; /// @@ -40,7 +71,7 @@ namespace TShockAPI.Handlers /// /// Maps TileIDs to Tile Entity IDs. - /// Note: is empty at the time of writing, but entities are dynamically assigned their ID at initialize time + /// Note: is empty at the time of writing, but entities are dynamically assigned their ID at initialize time /// which is why we can use the _myEntityId field on each entity type /// public static Dictionary TileEntityIdToTileIdMap = new Dictionary @@ -139,13 +170,8 @@ namespace TShockAPI.Handlers // and process them as a tile object if (newTile.Type < TileObjectData._data.Count && TileObjectData._data[newTile.Type] != null) { - data = TileObjectData._data[newTile.Type]; - NetTile[,] newTiles; - int objWidth = data.Width; - int objHeight = data.Height; - int offsetY = 0; - // Verify that the changes are actually valid conceptually + // Many tiles that are never placed or modified using this packet are valid TileObjectData entries, which is the main attack vector for most exploits using this packet if (Main.tile[realX, realY].type == newTile.Type) { switch (newTile.Type) @@ -206,8 +232,13 @@ namespace TShockAPI.Handlers } } + data = TileObjectData._data[newTile.Type]; + NetTile[,] newTiles; + int objWidth = data.Width; + int objHeight = data.Height; + // Ensure the tile object fits inside the rect before processing it - if (!DoesTileObjectFitInTileRect(x, y, objWidth, objHeight, width, length, offsetY, processed)) + if (!DoesTileObjectFitInTileRect(x, y, objWidth, objHeight, width, length, processed)) { continue; } @@ -218,11 +249,11 @@ namespace TShockAPI.Handlers { for (int j = 0; j < objHeight; j++) { - newTiles[i, j] = tiles[x + i, y + j + offsetY]; - processed[x + i, y + j + offsetY] = true; + newTiles[i, j] = tiles[x + i, y + j]; + processed[x + i, y + j] = true; } } - ProcessTileObject(newTile.Type, realX, realY + offsetY, objWidth, objHeight, newTiles, args); + ProcessTileObject(newTile.Type, realX, realY, objWidth, objHeight, newTiles, args); continue; } @@ -282,9 +313,9 @@ namespace TShockAPI.Handlers { // Some boots allow growing flowers on grass. This process sends a 1x1 tile rect to grow the flowers // The rect size must be 1 and the player must have an accessory that allows growing flowers in order for this rect to be valid - if (rectWidth == 1 && rectLength == 1 && args.Player.Accessories.Any(a => a != null && FlowerBootItems.Contains(a.type))) + if (rectWidth == 1 && rectLength == 1 && WorldGen.InWorld(realX, realY + 1) && args.Player.Accessories.Any(a => a != null && FlowerBootItems.Contains(a.type))) { - ProcessFlowerBoots(realX, realY, newTile, args); + ProcessFlowerBoots(realX, realY, newTile); return; } @@ -302,7 +333,7 @@ namespace TShockAPI.Handlers if (rectWidth == 1 && rectLength == 1) // Conversion only sends a 1x1 rect { - ProcessConversionSpreads(Main.tile[realX, realY], newTile); + ProcessConversionSpreads(tile, newTile); } // All other single tile updates should not be processed. @@ -314,24 +345,18 @@ namespace TShockAPI.Handlers /// The tile x position of the tile rect packet - this is where the flowers are intending to grow /// The tile y position of the tile rect packet - this is where the flowers are intending to grow /// The NetTile containing information about the flowers that are being grown - /// SendTileRectEventArgs containing event information - internal void ProcessFlowerBoots(int realX, int realY, NetTile newTile, GetDataHandlers.SendTileRectEventArgs args) + internal void ProcessFlowerBoots(int realX, int realY, NetTile newTile) { - // We need to get the tile below the tile rect to determine what grass types are allowed - if (!WorldGen.InWorld(realX, realY + 1)) + ITile tile = Main.tile[realX, realY]; + // Ensure that the placed plant is valid for the grass below, that the target tile is empty, and that the placed plant has valid framing + if ( + PlantToGrassMap.TryGetValue(newTile.Type, out HashSet grassTiles) && + !tile.active() && grassTiles.Contains(Main.tile[realX, realY + 1].type) && + PlantToStyleMap[newTile.Type].Contains((ushort)(newTile.FrameX / 18)) + ) { - // If the tile below the tile rect isn't valid, we return here and don't update the server tile state - return; + UpdateServerTileState(tile, newTile, TileDataType.Tile); } - - ITile tile = Main.tile[realX, realY + 1]; - if (!GrassToPlantMap.TryGetValue(tile.type, out List plantTiles) && !plantTiles.Contains(newTile.Type)) - { - // If the tile below the tile rect isn't a valid plant tile (eg grass) then we don't update the server tile state - return; - } - - UpdateServerTileState(Main.tile[realX, realY], newTile, TileDataType.Tile); } /// @@ -532,24 +557,16 @@ namespace TShockAPI.Handlers /// /// /// - /// /// /// - static bool DoesTileObjectFitInTileRect(int x, int y, int width, int height, short rectWidth, short rectLength, int offsetY, bool[,] processed) + static bool DoesTileObjectFitInTileRect(int x, int y, int width, int height, short rectWidth, short rectLength, bool[,] processed) { - // If the starting y position of this tile object is at (x, 0) and the y offset is negative, we'll be accessing tiles outside the rect - if (y + offsetY < 0) - { - TShock.Log.ConsoleDebug("Bouncer / SendTileRectHandler - rejected tile object because object dimensions fall outside the tile rect (negative y value)"); - return false; - } - - if (x + width > rectWidth || y + height + offsetY > rectLength) + if (x + width > rectWidth || y + height > rectLength) { // This is ugly, but we want to mark all these tiles as processed so that we're not hitting this check multiple times for one dodgy tile object for (int i = x; i < rectWidth; i++) { - for (int j = Math.Max(0, y + offsetY); j < rectLength; j++) // This is also ugly. Using Math.Max to make sure y + offsetY >= 0 + for (int j = y; j < rectLength; j++) { processed[i, j] = true; } From cf9240d56dd37a3b9d667003c595e31dfddab67b Mon Sep 17 00:00:00 2001 From: punchready Date: Wed, 27 Jul 2022 11:35:21 +0200 Subject: [PATCH 13/16] Support grass mowing --- TShockAPI/Handlers/SendTileRectHandler.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 7d073266..455eaf64 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -331,6 +331,13 @@ namespace TShockAPI.Handlers UpdateServerTileState(tile, newTile, TileDataType.Tile); } + if (rectWidth == 1 && rectLength == 1 && + (tile.type == TileID.Grass && newTile.Type == TileID.GolfGrass || + tile.type == TileID.HallowedGrass && newTile.Type == TileID.GolfGrassHallowed)) + { + UpdateServerTileState(tile, newTile, TileDataType.Tile); + } + if (rectWidth == 1 && rectLength == 1) // Conversion only sends a 1x1 rect { ProcessConversionSpreads(tile, newTile); From 9358f11e518a9d845aa6daada65308ea787bbc9c Mon Sep 17 00:00:00 2001 From: punchready Date: Sun, 14 Aug 2022 01:22:22 +0200 Subject: [PATCH 14/16] Improve comments --- TShockAPI/Bouncer.cs | 4 +- TShockAPI/Handlers/SendTileRectHandler.cs | 45 +++++++++++++++++------ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index 736fc6d7..0f578998 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -401,8 +401,8 @@ namespace TShockAPI return; } - // i do not understand the ice tile check enough to be able to modify it, however i do know that it can be used to completely bypass region protection - // this check ensures that build permission is always checked no matter what + // I do not understand the ice tile check enough to be able to modify it, however I do know that it can be used to completely bypass region protection + // This check ensures that build permission is always checked no matter what if (!args.Player.HasBuildPermission(tileX, tileY)) { TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from build from {0} {1} {2}", args.Player.Name, action, editData); diff --git a/TShockAPI/Handlers/SendTileRectHandler.cs b/TShockAPI/Handlers/SendTileRectHandler.cs index 455eaf64..dbe80ec6 100644 --- a/TShockAPI/Handlers/SendTileRectHandler.cs +++ b/TShockAPI/Handlers/SendTileRectHandler.cs @@ -19,7 +19,10 @@ namespace TShockAPI.Handlers /// public class SendTileRectHandler : IPacketHandler { - private static readonly Dictionary> PlantToGrassMap = new Dictionary> + /// + /// Maps plant tile types to their valid grass ground tiles when using flower boots + /// + private static readonly Dictionary> FlowerBootPlantToGrassMap = new Dictionary> { { TileID.Plants, new HashSet() { @@ -39,19 +42,27 @@ namespace TShockAPI.Handlers } }, }; - private static readonly Dictionary> PlantToStyleMap = new Dictionary>() + /// + /// Maps plant tile types to a list of valid styles, which are used to determine the FrameX value of the plant tile + /// See `Player.DoBootsEffect_PlaceFlowersOnTile` + /// + private static readonly Dictionary> FlowerBootPlantToStyleMap = new Dictionary>() { { TileID.Plants, new HashSet() { + // The upper line is from a `NextFromList` call + // The lower line is from an additional switch which will add the listed options by adding a random value to a select set of styles 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 24, 27, 30, 33, 36, 39, 42, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, } }, { TileID.HallowedPlants, new HashSet() { + // 5 is intentionally missing here because it is being skipped by vanilla 4, 6, } }, { TileID.HallowedPlants2, new HashSet() { + // 5 is intentionally missing here because it is being skipped by vanilla 2, 3, 4, 6, 7, } }, { TileID.JunglePlants2, new HashSet() @@ -199,7 +210,7 @@ namespace TShockAPI.Handlers case TileID.MinecartTrack: case TileID.ChristmasTree: { - // allowed changes + // Allowed changes } break; default: @@ -222,7 +233,7 @@ namespace TShockAPI.Handlers case TileID.TeleportationPylon: case TileID.TargetDummy: { - // allowed placements + // Allowed placements } break; default: @@ -263,7 +274,7 @@ namespace TShockAPI.Handlers } } } - + /// /// Processes a tile object consisting of multiple tiles from the tile rect packet /// @@ -321,24 +332,30 @@ namespace TShockAPI.Handlers ITile tile = Main.tile[realX, realY]; + // Triggering a single land mine tile if (rectWidth == 1 && rectLength == 1 && tile.type == TileID.LandMine && !newTile.Active) { UpdateServerTileState(tile, newTile, TileDataType.Tile); } + // Hammering a single junction box if (rectWidth == 1 && rectLength == 1 && tile.type == TileID.WirePipe) { UpdateServerTileState(tile, newTile, TileDataType.Tile); } + // Mowing a single grass tile: Grass -> GolfGrass OR HallowedGrass -> GolfGrassHallowed if (rectWidth == 1 && rectLength == 1 && - (tile.type == TileID.Grass && newTile.Type == TileID.GolfGrass || - tile.type == TileID.HallowedGrass && newTile.Type == TileID.GolfGrassHallowed)) + ( + tile.type == TileID.Grass && newTile.Type == TileID.GolfGrass || + tile.type == TileID.HallowedGrass && newTile.Type == TileID.GolfGrassHallowed + )) { UpdateServerTileState(tile, newTile, TileDataType.Tile); } - if (rectWidth == 1 && rectLength == 1) // Conversion only sends a 1x1 rect + // Conversion: only sends a 1x1 rect + if (rectWidth == 1 && rectLength == 1) { ProcessConversionSpreads(tile, newTile); } @@ -355,11 +372,15 @@ namespace TShockAPI.Handlers internal void ProcessFlowerBoots(int realX, int realY, NetTile newTile) { ITile tile = Main.tile[realX, realY]; - // Ensure that the placed plant is valid for the grass below, that the target tile is empty, and that the placed plant has valid framing + // Ensure that: + // - the placed plant is valid for the grass below + // - the target tile is empty + // - and the placed plant has valid framing (style * 18 = FrameX) if ( - PlantToGrassMap.TryGetValue(newTile.Type, out HashSet grassTiles) && - !tile.active() && grassTiles.Contains(Main.tile[realX, realY + 1].type) && - PlantToStyleMap[newTile.Type].Contains((ushort)(newTile.FrameX / 18)) + FlowerBootPlantToGrassMap.TryGetValue(newTile.Type, out HashSet grassTiles) && + !tile.active() && + grassTiles.Contains(Main.tile[realX, realY + 1].type) && + FlowerBootPlantToStyleMap[newTile.Type].Contains((ushort)(newTile.FrameX / 18)) ) { UpdateServerTileState(tile, newTile, TileDataType.Tile); From 0ce7c94f1bf794bff1e0d0f23ae3ea243216ad02 Mon Sep 17 00:00:00 2001 From: punchready Date: Sun, 14 Aug 2022 01:22:29 +0200 Subject: [PATCH 15/16] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f03c092d..1bc0b9cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Only allow using Teleportation Potions, Magic Conch, and Demon Conch whilst holding them. (@drunderscore) * Updated server startup language to be more clear when encountering a fatal startup error. Now, the server gives more context as to what happened so that there's a better chance of people being able to help themselves. (@hakusaro) * Added `-worldevil ` command line argument (@NotGeri) +* Fixed an exploit in which the Ice Block deletion allowance from the Ice Rod bypassed region protection, allowing for deleting all tiles in a protected region and/or replacing them with Ice Blocks. (@punchready) +* Changed SendTileRect handling from a denylist to an allowlist with stricter checks. This prevents essentially all exploits involving this packet. Most notably this stops people from placing arbitrary tiles with arbitrary framing values, which are the root of most exploits. (@punchready) +* Removed the config options `TileRectangleSizeThreshold` and `KickOnTileRectangleSizeThresholdBroken` because they are made obsolete by the new system, which will only allow valid rectangle sizes (at a maximum of only 4 by 4 tiles in 1.4.3.6). (@punchready) ## TShock 4.5.17 * Fixed duplicate characters (twins) after repeatedly logging in as the same character due to connection not being immediately closed during `NetHooks_NameCollision`. (@gohjoseph) From 1d4d19f9612a530f36333c2b754b9fe1b7a2854f Mon Sep 17 00:00:00 2001 From: Lucas Nicodemus Date: Tue, 16 Aug 2022 20:57:15 -0700 Subject: [PATCH 16/16] Version tick: 4.5.18 And also, add changelog entry for dependabot change --- CHANGELOG.md | 4 ++++ TShockAPI/Properties/AssemblyInfo.cs | 4 ++-- TShockAPI/TShock.cs | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3252c26e..2940e13e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * If there is no section called "Upcoming changes" below this line, please add one with `## Upcoming changes` as the first line, and then a bulleted item directly after with the first change. ## Upcoming changes +* Your changes could be here! + +## TShock 4.5.18 * Fixed `TSPlayer.GiveItem` not working if the player is in lava. (@gohjoseph) * Only allow using Teleportation Potions, Magic Conch, and Demon Conch whilst holding them. (@drunderscore) * Updated server startup language to be more clear when encountering a fatal startup error. Now, the server gives more context as to what happened so that there's a better chance of people being able to help themselves. (@hakusaro) @@ -21,6 +24,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin * Fixed an exploit in which the Ice Block deletion allowance from the Ice Rod bypassed region protection, allowing for deleting all tiles in a protected region and/or replacing them with Ice Blocks. (@punchready) * Changed SendTileRect handling from a denylist to an allowlist with stricter checks. This prevents essentially all exploits involving this packet. Most notably this stops people from placing arbitrary tiles with arbitrary framing values, which are the root of most exploits. (@punchready) * Removed the config options `TileRectangleSizeThreshold` and `KickOnTileRectangleSizeThresholdBroken` because they are made obsolete by the new system, which will only allow valid rectangle sizes (at a maximum of only 4 by 4 tiles in 1.4.3.6). (@punchready) +* Bumped Newtonsoft Json to 13.0.1. (@dependabot) ## TShock 4.5.17 * Fixed duplicate characters (twins) after repeatedly logging in as the same character due to connection not being immediately closed during `NetHooks_NameCollision`. (@gohjoseph) diff --git a/TShockAPI/Properties/AssemblyInfo.cs b/TShockAPI/Properties/AssemblyInfo.cs index a826096c..f4d9e4d5 100644 --- a/TShockAPI/Properties/AssemblyInfo.cs +++ b/TShockAPI/Properties/AssemblyInfo.cs @@ -53,5 +53,5 @@ using System.Runtime.InteropServices; // Also, be sure to release on github with the exact assembly version tag as below // so that the update manager works correctly (via the Github releases api and mimic) -[assembly: AssemblyVersion("4.5.17")] -[assembly: AssemblyFileVersion("4.5.17")] +[assembly: AssemblyVersion("4.5.18")] +[assembly: AssemblyFileVersion("4.5.18")] diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 6bfd8006..5bd88598 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -58,7 +58,7 @@ namespace TShockAPI /// VersionNum - The version number the TerrariaAPI will return back to the API. We just use the Assembly info. public static readonly Version VersionNum = Assembly.GetExecutingAssembly().GetName().Version; /// VersionCodename - The version codename is displayed when the server starts. Inspired by software codenames conventions. - public static readonly string VersionCodename = "Volodymyr Oleksandrovych Zelenskyy"; + public static readonly string VersionCodename = "Audaciously Artistic"; /// SavePath - This is the path TShock saves its data in. This path is relative to the TerrariaServer.exe (not in ServerPlugins). public static string SavePath = "tshock";