WIP - splitting STS into new class and processing methods

This commit is contained in:
Chris 2020-05-29 14:05:43 +09:30
parent 6e4b6e1f5e
commit d4a0f47e45
5 changed files with 384 additions and 202 deletions

View file

@ -36,15 +36,19 @@ namespace TShockAPI
/// <summary>Bouncer is the TShock anti-hack and anti-cheat system.</summary>
internal sealed class Bouncer
{
internal Handlers.SendTileSquareHandler STSHandler { get; set; }
/// <summary>Constructor call initializes Bouncer and related functionality.</summary>
/// <returns>A new Bouncer.</returns>
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
}
}
/// <summary>Bouncer's SendTileSquare hook halts large scope world destruction.</summary>
/// <param name="sender">The object that triggered the event.</param>
/// <param name="args">The packet arguments that the event has.</param>
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;
}
/// <summary>Registered when items fall to the ground to prevent cheating.</summary>
/// <param name="sender">The object that triggered the event.</param>
/// <param name="args">The packet arguments that the event has.</param>

View file

@ -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<ushort, List<ushort>> _grassToPlantMap = new Dictionary<ushort, List<ushort>>
{
{ TileID.Grass, new List<ushort> { TileID.Plants, TileID.Plants2 } },
{ TileID.HallowedGrass, new List<ushort> { TileID.HallowedPlants, TileID.HallowedPlants2 } },
{ TileID.JungleGrass, new List<ushort> { TileID.JunglePlants, TileID.JunglePlants2 } }
};
List<int> _flowerBootItems = new List<int>
{
ItemID.FlowerBoots,
ItemID.FairyBoots
};
/// <summary>
/// Updates a single tile
/// </summary>
/// <param name="tile"></param>
/// <param name="newTile"></param>
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);
}
/// <summary>
/// Determines if a Tile Square for flower-growing boots should be accepted or not
/// </summary>
/// <param name="realx"></param>
/// <param name="realy"></param>
/// <param name="newTile"></param>
/// <param name="player"></param>
/// <returns></returns>
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<ushort> 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);
}
}
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="newTiles"></param>
/// <param name="realx"></param>
/// <param name="realy"></param>
/// <param name="player"></param>
public void ProcessTileObject(TileObjectData data, NetTile[,] newTiles, int realx, int realy, TSPlayer player)
{
}
/// <summary>
/// Invoked when a SendTileSquare packet is received
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
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);
}
}*/
}

View file

@ -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])
{

View file

@ -620,7 +620,8 @@ namespace TShockAPI
/// <summary>Determines if the player can build on a given point.</summary>
/// <param name="x">The x coordinate they want to build at.</param>
/// <param name="y">The y coordinate they want to paint at.</param>
/// <param name="y">The y coordinate they want to build at.</param>
/// <param name="shouldWarnPlayer">Whether or not the player should be warned if their build attempt fails</param>
/// <returns>True if the player can build at the given point from build, spawn, and region protection.</returns>
public bool HasBuildPermission(int x, int y, bool shouldWarnPlayer = true)
{
@ -679,6 +680,31 @@ namespace TShockAPI
return false;
}
/// <summary>
/// Determines if the player can build a tile object on a given point.
/// </summary>
/// <param name="x">The x coordinate they want to build at.</param>
/// <param name="y">The y coordinate they want to build at.</param>
/// <param name="width">The width of the tile object</param>
/// <param name="height">The height of the tile object</param>
/// <param name="shouldWarnPlayer">Whether or not the player should be warned if their build attempt fails</param>
/// <returns>True if the player can build at the given point from build, spawn, and region protection.</returns>
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;
}
/// <summary>Determines if the player can paint on a given point. Checks general build permissions, then paint.</summary>
/// <param name="x">The x coordinate they want to paint at.</param>
/// <param name="y">The y coordinate they want to paint at.</param>

View file

@ -88,6 +88,7 @@
<Compile Include="DB\ResearchDatastore.cs" />
<Compile Include="DB\TileManager.cs" />
<Compile Include="Extensions\ExceptionExt.cs" />
<Compile Include="Handlers\SendTileSquareHandler.cs" />
<Compile Include="Hooks\AccountHooks.cs" />
<Compile Include="Hooks\GeneralHooks.cs" />
<Compile Include="Hooks\PlayerHooks.cs" />
@ -211,7 +212,7 @@
</PropertyGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" BuildVersion_BuildAction="Both" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_StartDate="2011/6/17" BuildVersion_IncrementBeforeBuild="False" />
<UserProperties BuildVersion_IncrementBeforeBuild="False" BuildVersion_StartDate="2011/6/17" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_BuildAction="Both" BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" />
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.