From d4a0f47e451ccd095a8f1ea4e8d0e85dafe678e1 Mon Sep 17 00:00:00 2001 From: Chris <2648373+QuiCM@users.noreply.github.com> Date: Fri, 29 May 2020 14:05:43 +0930 Subject: [PATCH] WIP - splitting STS into new class and processing methods --- TShockAPI/Bouncer.cs | 204 +----------- TShockAPI/Handlers/SendTileSquareHandler.cs | 344 ++++++++++++++++++++ TShockAPI/Net/NetTile.cs | 7 +- TShockAPI/TSPlayer.cs | 28 +- TShockAPI/TShockAPI.csproj | 3 +- 5 files changed, 384 insertions(+), 202 deletions(-) create mode 100644 TShockAPI/Handlers/SendTileSquareHandler.cs diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs index abd5fe05..7a5bc82c 100644 --- a/TShockAPI/Bouncer.cs +++ b/TShockAPI/Bouncer.cs @@ -36,15 +36,19 @@ namespace TShockAPI /// Bouncer is the TShock anti-hack and anti-cheat system. internal sealed class Bouncer { + internal Handlers.SendTileSquareHandler STSHandler { get; set; } + /// Constructor call initializes Bouncer and related functionality. /// A new Bouncer. internal Bouncer() { + STSHandler = new Handlers.SendTileSquareHandler(); + // Setup hooks GetDataHandlers.GetSection += OnGetSection; GetDataHandlers.PlayerUpdate += OnPlayerUpdate; GetDataHandlers.TileEdit += OnTileEdit; - GetDataHandlers.SendTileSquare += OnSendTileSquare; + GetDataHandlers.SendTileSquare += STSHandler.OnReceiveSendTileSquare; GetDataHandlers.ItemDrop += OnItemDrop; GetDataHandlers.NewProjectile += OnNewProjectile; GetDataHandlers.NPCStrike += OnNPCStrike; @@ -517,204 +521,6 @@ namespace TShockAPI } } - /// Bouncer's SendTileSquare hook halts large scope world destruction. - /// The object that triggered the event. - /// The packet arguments that the event has. - internal void OnSendTileSquare(object sender, GetDataHandlers.SendTileSquareEventArgs args) - { - short size = args.Size; - int tileX = args.TileX; - int tileY = args.TileY; - - if (args.Player.HasPermission(Permissions.allowclientsideworldedit)) - { - TShock.Log.ConsoleDebug("Bouncer / SendTileSquare accepted clientside world edit from {0}", args.Player.Name); - args.Handled = false; - return; - } - - // From White: - // IIRC it's because 5 means a 5x5 square which is normal for a tile square, and anything bigger is a non-vanilla tile modification attempt - if (size > 5) - { - TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from non-vanilla tilemod from {0}", args.Player.Name); - args.Handled = true; - return; - } - - if (args.Player.IsBouncerThrottled()) - { - TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from throttle from {0}", args.Player.Name); - args.Player.SendTileSquare(tileX, tileY, size); - args.Handled = true; - return; - } - - if (args.Player.IsBeingDisabled()) - { - TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from being disabled from {0}", args.Player.Name); - args.Player.SendTileSquare(tileX, tileY, size); - args.Handled = true; - return; - } - - try - { - var tiles = new NetTile[size, size]; - for (int x = 0; x < size; x++) - { - for (int y = 0; y < size; y++) - { - tiles[x, y] = new NetTile(args.Data); - } - } - - bool changed = false; - for (int x = 0; x < size; x++) - { - int realx = tileX + x; - if (realx < 0 || realx >= Main.maxTilesX) - continue; - - for (int y = 0; y < size; y++) - { - int realy = tileY + y; - if (realy < 0 || realy >= Main.maxTilesY) - continue; - - var tile = Main.tile[realx, realy]; - var newtile = tiles[x, y]; - if (!args.Player.HasBuildPermission(realx, realy) || - !args.Player.IsInRange(realx, realy)) - { - continue; - } - - // Fixes the Flower Boots not creating flowers issue - if (size == 1 && args.Player.Accessories.Any(i => i.active && i.netID == ItemID.FlowerBoots)) - { - if (Main.tile[realx, realy + 1].type == TileID.Grass && (newtile.Type == TileID.Plants || newtile.Type == TileID.Plants2)) - { - args.Handled = false; - return; - } - - if (Main.tile[realx, realy + 1].type == TileID.HallowedGrass && (newtile.Type == TileID.HallowedPlants || newtile.Type == TileID.HallowedPlants2)) - { - args.Handled = false; - return; - } - - if (Main.tile[realx, realy + 1].type == TileID.JungleGrass && newtile.Type == TileID.JunglePlants2) - { - args.Handled = false; - return; - } - } - - // Junction Box - if (tile.type == TileID.WirePipe) - { - args.Handled = false; - return; - } - - // Orientable tiles - if (tile.type == newtile.Type && orientableTiles.Contains(tile.type)) - { - Main.tile[realx, realy].frameX = newtile.FrameX; - Main.tile[realx, realy].frameY = newtile.FrameY; - changed = true; - } - - // Landmine - if (tile.type == TileID.LandMine && !newtile.Active) - { - Main.tile[realx, realy].active(false); - changed = true; - } - - // Tile entities: sensors, item frames, training dummies - // here it handles all tile entities listed in `TileEntityID` - if ((newtile.Type == TileID.LogicSensor || - newtile.Type == TileID.ItemFrame || - newtile.Type == TileID.TargetDummy) && - !Main.tile[realx, realy].active()) - { - Main.tile[realx, realy].type = newtile.Type; - Main.tile[realx, realy].frameX = newtile.FrameX; - Main.tile[realx, realy].frameY = newtile.FrameY; - Main.tile[realx, realy].active(true); - changed = true; - } - - if (tile.active() && newtile.Active && tile.type != newtile.Type) - { - // Grass <-> Grass - if ((TileID.Sets.Conversion.Grass[tile.type] && TileID.Sets.Conversion.Grass[newtile.Type]) || - // Dirt <-> Dirt - ((tile.type == 0 || tile.type == 59) && - (newtile.Type == 0 || newtile.Type == 59)) || - // Ice <-> Ice - (TileID.Sets.Conversion.Ice[tile.type] && TileID.Sets.Conversion.Ice[newtile.Type]) || - // Stone <-> Stone - ((TileID.Sets.Conversion.Stone[tile.type] || Main.tileMoss[tile.type]) && - (TileID.Sets.Conversion.Stone[newtile.Type] || Main.tileMoss[newtile.Type])) || - // Sand <-> Sand - (TileID.Sets.Conversion.Sand[tile.type] && TileID.Sets.Conversion.Sand[newtile.Type]) || - // Sandstone <-> Sandstone - (TileID.Sets.Conversion.Sandstone[tile.type] && TileID.Sets.Conversion.Sandstone[newtile.Type]) || - // Hardened Sand <-> Hardened Sand - (TileID.Sets.Conversion.HardenedSand[tile.type] && TileID.Sets.Conversion.HardenedSand[newtile.Type])) - { - Main.tile[realx, realy].type = newtile.Type; - changed = true; - } - } - - // Stone wall <-> Stone wall - if (((tile.wall == 1 || tile.wall == 3 || tile.wall == 28 || tile.wall == 83) && - (newtile.Wall == 1 || newtile.Wall == 3 || newtile.Wall == 28 || newtile.Wall == 83)) || - // Leaf wall <-> Leaf wall - (((tile.wall >= 63 && tile.wall <= 70) || tile.wall == 81) && - ((newtile.Wall >= 63 && newtile.Wall <= 70) || newtile.Wall == 81))) - { - Main.tile[realx, realy].wall = newtile.Wall; - changed = true; - } - - if ((tile.type == TileID.TrapdoorClosed && (newtile.Type == TileID.TrapdoorOpen || !newtile.Active)) || - (tile.type == TileID.TrapdoorOpen && (newtile.Type == TileID.TrapdoorClosed || !newtile.Active)) || - (!tile.active() && newtile.Active && (newtile.Type == TileID.TrapdoorOpen || newtile.Type == TileID.TrapdoorClosed))) - { - Main.tile[realx, realy].type = newtile.Type; - Main.tile[realx, realy].frameX = newtile.FrameX; - Main.tile[realx, realy].frameY = newtile.FrameY; - Main.tile[realx, realy].active(newtile.Active); - changed = true; - } - } - } - - if (changed) - { - TSPlayer.All.SendTileSquare(tileX, tileY, size + 1); - WorldGen.RangeFrame(tileX, tileY, tileX + size, tileY + size); - } - else - { - args.Player.SendTileSquare(tileX, tileY, size); - } - } - catch - { - args.Player.SendTileSquare(tileX, tileY, size); - } - - TShock.Log.ConsoleDebug("Bouncer / SendTileSquare reimplemented from spaghetti from {0}", args.Player.Name); - args.Handled = true; - } - /// Registered when items fall to the ground to prevent cheating. /// The object that triggered the event. /// The packet arguments that the event has. diff --git a/TShockAPI/Handlers/SendTileSquareHandler.cs b/TShockAPI/Handlers/SendTileSquareHandler.cs new file mode 100644 index 00000000..273c4d35 --- /dev/null +++ b/TShockAPI/Handlers/SendTileSquareHandler.cs @@ -0,0 +1,344 @@ +using OTAPI.Tile; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using Terraria.ID; +using Terraria.ObjectData; +using TShockAPI.Net; + +namespace TShockAPI.Handlers +{ + public class SendTileSquareHandler + { + Dictionary> _grassToPlantMap = 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 } } + }; + + List _flowerBootItems = new List + { + ItemID.FlowerBoots, + ItemID.FairyBoots + }; + + /// + /// Updates a single tile + /// + /// + /// + public void UpdateTile(ITile tile, NetTile newTile) + { + tile.active(newTile.Active); + + if (newTile.Active && !newTile.Inactive) + { + tile.type = newTile.Type; + } + + if (newTile.FrameImportant) + { + tile.frameX = newTile.FrameX; + tile.frameY = newTile.FrameY; + } + + if (newTile.HasWall) + { + tile.wall = newTile.Wall; + } + + if (newTile.HasLiquid) + { + tile.liquid = newTile.Liquid; + tile.liquidType(newTile.LiquidType); + } + + tile.wire(newTile.Wire); + tile.wire2(newTile.Wire2); + tile.wire3(newTile.Wire3); + tile.wire4(newTile.Wire4); + + tile.halfBrick(newTile.IsHalf); + + if (newTile.HasColor) + { + tile.color(newTile.TileColor); + } + + if (newTile.HasWallColor) + { + tile.wallColor(newTile.WallColor); + } + + byte slope = 0; + if (newTile.Slope) + { + slope += 1; + } + if (newTile.Slope2) + { + slope += 2; + } + if (newTile.Slope3) + { + slope += 4; + } + + tile.slope(slope); + } + + /// + /// Determines if a Tile Square for flower-growing boots should be accepted or not + /// + /// + /// + /// + /// + /// + internal bool HandleFlowerBoots(int realx, int realy, NetTile newTile, TSPlayer player) + { + // We need to get the tile below the tile square to determine what grass types are allowed + if (!WorldGen.InWorld(realx, realy + 1)) + { + return true; + } + + ITile tile = Main.tile[realx, realy + 1]; + if (!_grassToPlantMap.TryGetValue(tile.type, out List plantTiles) && !plantTiles.Contains(newTile.Type)) + { + return true; + } + + return false; + } + + internal void ProcessSingleTile(int realx, int realy, NetTile newTile, int squareSize, GetDataHandlers.SendTileSquareEventArgs args) + { + if (squareSize == 1 && args.Player.Accessories.Any(a => a != null && _flowerBootItems.Contains(a.type))) + { + args.Handled = HandleFlowerBoots(realx, realy, newTile, args.Player); + return; + } + + ITile tile = Main.tile[realx, realy]; + + if (tile.type == TileID.LandMine && !newTile.Active) + { + UpdateTile(tile, newTile); + } + + if (tile.type == TileID.WirePipe) + { + UpdateTile(tile, newTile); + } + + } + + /// + /// + /// + /// + /// + /// + /// + /// + public void ProcessTileObject(TileObjectData data, NetTile[,] newTiles, int realx, int realy, TSPlayer player) + { + + } + + /// + /// Invoked when a SendTileSquare packet is received + /// + /// + /// + public void OnReceiveSendTileSquare(object sender, GetDataHandlers.SendTileSquareEventArgs args) + { + short size = args.Size; + int tileX = args.TileX; + int tileY = args.TileY; + + if (args.Player.HasPermission(Permissions.allowclientsideworldedit)) + { + TShock.Log.ConsoleDebug("Bouncer / SendTileSquare accepted clientside world edit from {0}", args.Player.Name); + args.Handled = false; + return; + } + + // From White: + // IIRC it's because 5 means a 5x5 square which is normal for a tile square, and anything bigger is a non-vanilla tile modification attempt + if (size > 5) + { + TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from non-vanilla tilemod from {0}", args.Player.Name); + args.Handled = true; + return; + } + + if (args.Player.IsBouncerThrottled()) + { + TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from throttle from {0}", args.Player.Name); + args.Player.SendTileSquare(tileX, tileY, size); + args.Handled = true; + return; + } + + if (args.Player.IsBeingDisabled()) + { + TShock.Log.ConsoleDebug("Bouncer / SendTileSquare rejected from being disabled from {0}", args.Player.Name); + args.Player.SendTileSquare(tileX, tileY, size); + args.Handled = true; + return; + } + + bool[,] processed = new bool[size, size]; + NetTile[,] tiles = new NetTile[size, size]; + for (int x = 0; x < size; x++) + { + for (int y = 0; y < size; y++) + { + tiles[x, y] = new NetTile(args.Data); + } + } + + for (int x = 0; x < size; x++) + { + for (int y = 0; y < size; y++) + { + // Do not handle already processed tiles + if (processed[x, y]) + { + continue; + } + + int realx = tileX + x; + int realy = tileY + y; + + if ((realx < 0 || realx >= Main.maxTilesX) + || (realy < 0 || realy < Main.maxTilesY)) + { + processed[x, y] = true; + continue; + } + + if (!args.Player.HasBuildPermission(realx, realy) || + !args.Player.IsInRange(realx, realy)) + { + continue; + } + + NetTile newTile = tiles[x, y]; + TileObjectData data; + + if (newTile.Type < TileObjectData._data.Count && (data = TileObjectData._data[newTile.Type]) != null) + { + NetTile[,] newTiles = new NetTile[data.Width, data.Height]; + for (int i = 0; i < data.Width; i++) + { + for (int j = 0; j < data.Height; j++) + { + newTiles[i, j] = tiles[x + i, y + j]; + processed[x + i, y + j] = true; + } + } + + ProcessTileObject(data, newTiles, realx, realy, args.Player); + continue; + } + + ProcessSingleTile(realx, realy, newTile, size, args); + processed[x, y] = true; + } + } + + TShock.Log.ConsoleDebug("Bouncer / SendTileSquare reimplemented from spaghetti from {0}", args.Player.Name); + args.Handled = true; + } + } + + /* + bool changed = false; + for (int x = 0; x < size; x++) + { + int realx = tileX + x; + if (realx < 0 || realx >= Main.maxTilesX) + continue; + + for (int y = 0; y < size; y++) + { + int realy = tileY + y; + if (realy < 0 || realy >= Main.maxTilesY) + continue; + + var tile = Main.tile[realx, realy]; + var newtile = tiles[x, y]; + + // Junction Box + if (tile.type == TileID.WirePipe) + { + args.Handled = false; + return; + } + + if (tile.active() && newtile.Active && tile.type != newtile.Type) + { + // Grass <-> Grass + if ((TileID.Sets.Conversion.Grass[tile.type] && TileID.Sets.Conversion.Grass[newtile.Type]) || + // Dirt <-> Dirt + ((tile.type == 0 || tile.type == 59) && + (newtile.Type == 0 || newtile.Type == 59)) || + // Ice <-> Ice + (TileID.Sets.Conversion.Ice[tile.type] && TileID.Sets.Conversion.Ice[newtile.Type]) || + // Stone <-> Stone + ((TileID.Sets.Conversion.Stone[tile.type] || Main.tileMoss[tile.type]) && + (TileID.Sets.Conversion.Stone[newtile.Type] || Main.tileMoss[newtile.Type])) || + // Sand <-> Sand + (TileID.Sets.Conversion.Sand[tile.type] && TileID.Sets.Conversion.Sand[newtile.Type]) || + // Sandstone <-> Sandstone + (TileID.Sets.Conversion.Sandstone[tile.type] && TileID.Sets.Conversion.Sandstone[newtile.Type]) || + // Hardened Sand <-> Hardened Sand + (TileID.Sets.Conversion.HardenedSand[tile.type] && TileID.Sets.Conversion.HardenedSand[newtile.Type])) + { + Main.tile[realx, realy].type = newtile.Type; + changed = true; + } + } + + // Stone wall <-> Stone wall + if (((tile.wall == 1 || tile.wall == 3 || tile.wall == 28 || tile.wall == 83) && + (newtile.Wall == 1 || newtile.Wall == 3 || newtile.Wall == 28 || newtile.Wall == 83)) || + // Leaf wall <-> Leaf wall + (((tile.wall >= 63 && tile.wall <= 70) || tile.wall == 81) && + ((newtile.Wall >= 63 && newtile.Wall <= 70) || newtile.Wall == 81))) + { + Main.tile[realx, realy].wall = newtile.Wall; + changed = true; + } + + if ((tile.type == TileID.TrapdoorClosed && (newtile.Type == TileID.TrapdoorOpen || !newtile.Active)) || + (tile.type == TileID.TrapdoorOpen && (newtile.Type == TileID.TrapdoorClosed || !newtile.Active)) || + (!tile.active() && newtile.Active && (newtile.Type == TileID.TrapdoorOpen || newtile.Type == TileID.TrapdoorClosed))) + { + Main.tile[realx, realy].type = newtile.Type; + Main.tile[realx, realy].frameX = newtile.FrameX; + Main.tile[realx, realy].frameY = newtile.FrameY; + Main.tile[realx, realy].active(newtile.Active); + changed = true; + } + } + } + + if (changed) + { + TSPlayer.All.SendTileSquare(tileX, tileY, size + 1); + WorldGen.RangeFrame(tileX, tileY, tileX + size, tileY + size); + } + else + { + args.Player.SendTileSquare(tileX, tileY, size); + } + }*/ +} diff --git a/TShockAPI/Net/NetTile.cs b/TShockAPI/Net/NetTile.cs index 76d8e943..c49aadea 100644 --- a/TShockAPI/Net/NetTile.cs +++ b/TShockAPI/Net/NetTile.cs @@ -36,6 +36,7 @@ namespace TShockAPI.Net public bool Wire { get; set; } public bool Wire2 { get; set; } public bool Wire3 { get; set; } + public bool Wire4 { get; set; } public byte HalfBrick { get; set; } public byte Actuator { get; set; } public bool Inactive { get; set; } @@ -83,6 +84,7 @@ namespace TShockAPI.Net Wire = false; Wire2 = false; Wire3 = false; + Wire4 = false; HalfBrick = 0; Actuator = 0; Inactive = false; @@ -151,8 +153,10 @@ namespace TShockAPI.Net if (Slope3) bits[6] = true; + if (Wire4) + bits[7] = true; - stream.WriteInt8((byte)bits); + stream.WriteByte(bits); if (HasColor) { @@ -194,6 +198,7 @@ namespace TShockAPI.Net Slope = flags2[4]; Slope2 = flags2[5]; Slope3 = flags2[6]; + Wire4 = flags2[7]; if (flags2[2]) { diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 1a204594..31de3282 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -620,7 +620,8 @@ namespace TShockAPI /// 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. + /// The y coordinate they want to build at. + /// Whether or not the player should be warned if their build attempt fails /// 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) { @@ -679,6 +680,31 @@ namespace TShockAPI return false; } + /// + /// Determines if the player can build a tile object on a given point. + /// + /// The x coordinate they want to build at. + /// The y coordinate they want to build at. + /// The width of the tile object + /// The height of the tile object + /// Whether or not the player should be warned if their build attempt fails + /// True if the player can build at the given point from build, spawn, and region protection. + public bool HasBuildPermissionForTileObject(int x, int y, int width, int height, bool shouldWarnPlayer = true) + { + for (int realx = x; realx < x + width; realx++) + { + for (int realy = y; realy < y + height; realy++) + { + if (!HasBuildPermission(realx, realy, shouldWarnPlayer)) + { + return false; + } + } + } + + return true; + } + /// 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. diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index f6a2d81c..ea6f4f48 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -88,6 +88,7 @@ + @@ -211,7 +212,7 @@ - +