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 @@
-
+