diff --git a/TShockAPI/Handlers/SendTileSquareHandler.cs b/TShockAPI/Handlers/SendTileSquareHandler.cs index 39a7e751..2bf0c729 100644 --- a/TShockAPI/Handlers/SendTileSquareHandler.cs +++ b/TShockAPI/Handlers/SendTileSquareHandler.cs @@ -72,7 +72,7 @@ namespace TShockAPI.Handlers Debug.VisualiseTileSetDiff(args.TileX, args.TileY, args.Size, args.Size, tiles); - IterateTileSquare(tiles, processed, args); + IterateTileSquare(tiles, processed, args); // Uncommenting this function will send the same tile square 10 blocks above you for visualisation. This will modify your world and overwrite existing blocks. // Use in test worlds only. @@ -139,7 +139,7 @@ namespace TShockAPI.Handlers NetTile[,] newTiles; int width = data.Width; int height = data.Height; - int offset = 0; + int offsetY = 0; if (newTile.Type == TileID.TrapdoorClosed) { @@ -148,7 +148,13 @@ namespace TShockAPI.Handlers // So we capture all 6 possible tiles and offset ourselves 1 tile above the closed trapdoor to capture the entire 2x3 area width = 2; height = 3; - offset = -1; + offsetY = -1; + } + + // Ensure the tile object fits inside the square before processing it + if (!DoesTileObjectFitInTileSquare(x, y, width, height, size, offsetY, processed)) + { + continue; } newTiles = new NetTile[width, height]; @@ -157,11 +163,11 @@ namespace TShockAPI.Handlers { for (int j = 0; j < height; j++) { - newTiles[i, j] = tiles[x + i, y + j + offset]; - processed[x + i, y + j] = true; + newTiles[i, j] = tiles[x + i, y + j + offsetY]; + processed[x + i, y + j + offsetY] = true; } } - ProcessTileObject(newTile.Type, realX, realY + offset, width, height, newTiles, args); + ProcessTileObject(newTile.Type, realX, realY + offsetY, width, height, newTiles, args); continue; } @@ -392,7 +398,7 @@ namespace TShockAPI.Handlers /// /// /// - static NetTile[,] ReadNetTilesFromStream(System.IO.MemoryStream stream, int size) + static NetTile[,] ReadNetTilesFromStream(System.IO.MemoryStream stream, short size) { NetTile[,] tiles = new NetTile[size, size]; for (int x = 0; x < size; x++) @@ -420,8 +426,8 @@ namespace TShockAPI.Handlers return true; } - // 5x5 is the largest vanilla-sized tile square. Anything larger than this should not be seen in the vanilla game and should be rejected - if (args.Size > 5) + // 7x7 is the largest vanilla-sized tile square (used for lamp posts). Anything larger than this should not be sent by the vanilla game and should be rejected + if (args.Size > 7) { TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from non-vanilla tilemod from {0}", args.Player.Name); return true; @@ -444,6 +450,44 @@ namespace TShockAPI.Handlers return false; } + /// + /// Checks if a tile object fits inside the dimensions of a tile square + /// + /// + /// + /// + /// + /// + /// + /// + /// + static bool DoesTileObjectFitInTileSquare(int x, int y, int width, int height, int size, int offsetY, 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 square + if (y + offsetY < 0) + { + TShock.Log.ConsoleDebug("Bouncer / SendTileSquareHandler - rejected tile object because object dimensions fall outside the tile square (negative y value)"); + return false; + } + + if (x + width >= size || y + height + offsetY >= size) + { + // 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 < size; i++) + { + for (int j = Math.Max(0, y + offsetY); j < size; j++) // This is also ugly. Using Math.Max to make sure y + offsetY >= 0 + { + processed[i, j] = true; + } + } + + TShock.Log.ConsoleDebug("Bouncer / SendTileSquareHandler - rejected tile object because object dimensions fall outside the tile square (excessive size)"); + return false; + } + + return true; + } + class Debug { ///