Merge branch 'general-devel' into warnings

This commit is contained in:
Chris 2017-12-24 01:36:36 +00:00 committed by GitHub
commit 39d411d695
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 333 additions and 395 deletions

View file

@ -60,8 +60,11 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
* Added `TSPlayer.CheckIgnores()` and removed `TShock.CheckIgnores(TSPlayer)`. (@hakusaro)
* Hooks inside TShock can now be registered with their `Register` method and can be prioritized according to the TShock HandlerList system. (@hakusaro)
* Fix message requiring login not using the command specifier set in the config file. (@hakusaro)
* Move `TShock.CheckRangePermission()` to `TSPlayer.IsInRange` which **returns the opposite** of what the previous method did (see updated docs). (@hakusaro)
* Move `TShock.CheckSpawn` to `Utils.IsInSpawn`. (@hakusaro)
* Replace `TShock.CheckTilePermission` with `TSPlayer.HasBuildPermission`, `TSPlayer.HasPaintPermission`, and `TSPlayer.HasModifiedIceSuccessfully` respectively. (@hakusaro)
* Fix stack hack detection being inconsistent between two different check points. Moved `TShock.HackedInventory` to `TSPlayer.HasHackedItemStacks`. Added `GetDataHandlers.GetDataHandledEventArgs` which is where most hooks will inherit from in the future. (@hakusaro)
* All `GetDataHandlers` hooks now inherit from `GetDataHandledEventArgs` which includes a `TSPlayer` and a `MemoryStream` of raw data. (@hakusaro)
## TShock 4.3.25
* Fixed a critical exploit in the Terraria protocol that could cause massive unpreventable world corruption as well as a number of other problems. Thanks to @bartico6 for reporting. Fixed by the efforts of @QuiCM, @hakusaro, and tips in the right directioon from @bartico6.

View file

@ -99,14 +99,14 @@ namespace TShockAPI
return;
}
if (TShock.CheckTilePermission(args.Player, args.X, args.Y))
if (!args.Player.HasBuildPermission(args.X, args.Y))
{
NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1);
args.Handled = true;
return;
}
if (TShock.CheckRangePermission(args.Player, args.X, args.Y))
if (!args.Player.IsInRange(args.X, args.Y))
{
NetMessage.SendData((int)PacketTypes.UpdateTileEntity, -1, -1, NetworkText.Empty, args.ItemFrame.ID, 0, 1);
args.Handled = true;
@ -137,7 +137,7 @@ namespace TShockAPI
return;
}
if (TShock.CheckTilePermission(args.Player, args.X, args.Y))
if (!args.Player.HasBuildPermission(args.X, args.Y))
{
args.Handled = true;
return;
@ -155,13 +155,13 @@ namespace TShockAPI
return;
}
if (TShock.CheckTilePermission(args.Player, args.X, args.Y))
if (!args.Player.HasBuildPermission(args.X, args.Y))
{
args.Handled = true;
return;
}
if (TShock.CheckRangePermission(args.Player, args.X, args.Y))
if (!args.Player.IsInRange(args.X, args.Y))
{
args.Handled = true;
return;
@ -206,7 +206,7 @@ namespace TShockAPI
return;
}
if (TShock.CheckTilePermission(args.Player, x, y))
if (!args.Player.HasBuildPermission(x, y))
{
args.Handled = true;
return;
@ -276,7 +276,7 @@ namespace TShockAPI
}
if (TShock.Config.RangeChecks &&
TShock.CheckRangePermission(args.Player, (int)(Main.npc[id].position.X / 16f), (int)(Main.npc[id].position.Y / 16f), 128))
!args.Player.IsInRange((int)(Main.npc[id].position.X / 16f), (int)(Main.npc[id].position.Y / 16f), 128))
{
args.Player.SendData(PacketTypes.NpcUpdate, "", id);
args.Handled = true;
@ -342,7 +342,7 @@ namespace TShockAPI
return;
}
if (TShock.CheckRangePermission(args.Player, TShock.Players[id].TileX, TShock.Players[id].TileY, 100))
if (!args.Player.IsInRange(TShock.Players[id].TileX, TShock.Players[id].TileY, 100))
{
args.Player.SendData(PacketTypes.PlayerHp, "", id);
args.Player.SendData(PacketTypes.PlayerUpdate, "", id);
@ -396,7 +396,7 @@ namespace TShockAPI
// client side (but only if it passed the range check) (i.e., return false)
if (type == 0)
{
if (TShock.CheckRangePermission(args.Player, (int)(Main.item[id].position.X / 16f), (int)(Main.item[id].position.Y / 16f)))
if (!args.Player.IsInRange((int)(Main.item[id].position.X / 16f), (int)(Main.item[id].position.Y / 16f)))
{
// Causes item duplications. Will be re added if necessary
//args.Player.SendData(PacketTypes.ItemDrop, "", id);
@ -408,7 +408,7 @@ namespace TShockAPI
return;
}
if (TShock.CheckRangePermission(args.Player, (int)(pos.X / 16f), (int)(pos.Y / 16f)))
if (!args.Player.IsInRange((int)(pos.X / 16f), (int)(pos.Y / 16f)))
{
args.Player.SendData(PacketTypes.ItemDrop, "", id);
args.Handled = true;
@ -488,7 +488,7 @@ namespace TShockAPI
return;
}
if (TShock.CheckRangePermission(args.Player, TShock.Players[id].TileX, TShock.Players[id].TileY, 50))
if (!args.Player.IsInRange(TShock.Players[id].TileX, TShock.Players[id].TileY, 50))
{
args.Player.SendData(PacketTypes.PlayerAddBuff, "", id);
args.Handled = true;
@ -533,13 +533,13 @@ namespace TShockAPI
return;
}
if (TShock.CheckTilePermission(args.Player, Main.chest[id].x, Main.chest[id].y) && TShock.Config.RegionProtectChests)
if (!args.Player.HasBuildPermission(Main.chest[id].x, Main.chest[id].y) && TShock.Config.RegionProtectChests)
{
args.Handled = true;
return;
}
if (TShock.CheckRangePermission(args.Player, Main.chest[id].x, Main.chest[id].y))
if (!args.Player.IsInRange(Main.chest[id].x, Main.chest[id].y))
{
args.Handled = true;
return;
@ -556,18 +556,15 @@ namespace TShockAPI
short y = args.Y;
byte homeless = args.Homeless;
// Calls to TShock.CheckTilePermission need to be broken up into different subsystems
// In particular, this handles both regions and other things. Ouch.
if (TShock.CheckTilePermission(args.Player, x, y))
if (!args.Player.HasBuildPermission(x, y))
{
args.Player.SendErrorMessage("You do not have access to modify this area.");
args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY,
Convert.ToByte(Main.npc[id].homeless));
args.Handled = true;
return;
}
if (TShock.CheckRangePermission(args.Player, x, y))
if (!args.Player.IsInRange(x, y))
{
args.Player.SendData(PacketTypes.UpdateNPCHome, "", id, Main.npc[id].homeTileX, Main.npc[id].homeTileY,
Convert.ToByte(Main.npc[id].homeless));
@ -587,13 +584,13 @@ namespace TShockAPI
return;
}
if (TShock.CheckRangePermission(args.Player, args.X, args.Y))
if (!args.Player.IsInRange(args.X, args.Y))
{
args.Handled = true;
return;
}
if (TShock.CheckTilePermission(args.Player, args.X, args.Y) && TShock.Config.RegionProtectChests)
if (!args.Player.HasBuildPermission(args.X, args.Y) && TShock.Config.RegionProtectChests)
{
args.Handled = true;
return;
@ -648,14 +645,14 @@ namespace TShockAPI
}
}
if (TShock.CheckTilePermission(args.Player, tileX, tileY))
if (!args.Player.HasBuildPermission(tileX, tileY))
{
args.Player.SendTileSquare(tileX, tileY, 3);
args.Handled = true;
return;
}
if (TShock.CheckRangePermission(args.Player, tileX, tileY))
if (!args.Player.IsInRange(tileX, tileY))
{
args.Player.SendTileSquare(tileX, tileY, 3);
args.Handled = true;
@ -781,14 +778,14 @@ namespace TShockAPI
}
}
if (TShock.CheckTilePermission(args.Player, tileX, tileY))
if (!args.Player.HasBuildPermission(tileX, tileY))
{
args.Player.SendTileSquare(tileX, tileY, 1);
args.Handled = true;
return;
}
if (TShock.CheckRangePermission(args.Player, tileX, tileY, 16))
if (!args.Player.IsInRange(tileX, tileY, 16))
{
args.Player.SendTileSquare(tileX, tileY, 1);
args.Handled = true;
@ -1169,7 +1166,8 @@ namespace TShockAPI
{
for (int j = y; j < y + tileData.Height; j++)
{
if (TShock.CheckTilePermission(args.Player, i, j, type, EditAction.PlaceTile))
if (!args.Player.HasModifiedIceSuccessfully(i, j, type, EditAction.PlaceTile)
&& !args.Player.HasBuildPermission(i, j))
{
args.Player.SendTileSquare(i, j, 4);
args.Handled = true;
@ -1183,7 +1181,7 @@ namespace TShockAPI
|| type != TileID.SilkRope
|| type != TileID.VineRope
|| type != TileID.WebRope)
&& TShock.CheckRangePermission(args.Player, x, y))
&& !args.Player.IsInRange(x, y))
{
args.Player.SendTileSquare(x, y, 4);
args.Handled = true;
@ -1500,14 +1498,15 @@ namespace TShockAPI
return;
}
if (TShock.CheckTilePermission(args.Player, tileX, tileY, editData, action))
if (!args.Player.HasModifiedIceSuccessfully(tileX, tileY, editData, action)
&& !args.Player.HasBuildPermission(tileX, tileY))
{
args.Player.SendTileSquare(tileX, tileY, 4);
args.Handled = true;
return;
}
if (TShock.CheckRangePermission(args.Player, tileX, tileY))
if (!args.Player.IsInRange(tileX, tileY))
{
if (action == EditAction.PlaceTile && (editData == TileID.Rope || editData == TileID.SilkRope || editData == TileID.VineRope || editData == TileID.WebRope))
{
@ -1683,8 +1682,8 @@ namespace TShockAPI
var tile = Main.tile[realx, realy];
var newtile = tiles[x, y];
if (TShock.CheckTilePermission(args.Player, realx, realy) ||
TShock.CheckRangePermission(args.Player, realx, realy))
if (!args.Player.HasBuildPermission(realx, realy) ||
!args.Player.IsInRange(realx, realy))
{
continue;
}

File diff suppressed because it is too large Load diff

View file

@ -530,30 +530,147 @@ namespace TShockAPI
public bool SilentJoinInProgress;
/// <summary>Checks if a player is in range of a given tile if range checks are enabled.</summary>
/// <param name="x"> The x coordinate of the tile.</param>
/// <param name="y">The y coordinate of the tile.</param>
/// <param name="range">The range to check for.</param>
/// <returns>True if the player is in range of a tile or if range checks are off. False if not.</returns>
public bool IsInRange(int x, int y, int range = 32)
{
if (TShock.Config.RangeChecks && ((Math.Abs(TileX - x) > range) || (Math.Abs(TileY - y) > range)))
{
return false;
}
return true;
}
private enum BuildPermissionFailPoint
{
GeneralBuild,
SpawnProtect,
Regions
}
/// <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>
/// <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)
{
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.
// If the player has bypass on build protection or building is enabled; continue
// (General build protection takes precedence over spawn protection)
if (!TShock.Config.DisableBuild || HasPermission(Permissions.antibuild))
{
failure = BuildPermissionFailPoint.SpawnProtect;
// If they have spawn protect bypass, or it isn't spawn, or it isn't in spawn; continue
// (If they have spawn protect bypass, we don't care if it's spawn or not)
if (!TShock.Config.SpawnProtection || HasPermission(Permissions.editspawn) || !Utils.IsInSpawn(x, y))
{
failure = BuildPermissionFailPoint.Regions;
// If they have build permission in this region, then they're allowed to continue
if (TShock.Regions.CanBuild(x, y, this))
{
return true;
}
}
}
// If they lack build permission, they end up here.
// If they have build permission but lack the ability to edit spawn and it's spawn, they end up here.
// If they have build, it isn't spawn, or they can edit spawn, but they fail the region check, they end up here.
// If they shouldn't be warned, exit early.
if (!shouldWarnPlayer)
return false;
// Space out warnings by 2 seconds so that they don't get spammed.
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - lastPermissionWarning) < 2000)
{
return false;
}
// If they should be warned, warn them.
switch (failure)
{
case BuildPermissionFailPoint.GeneralBuild:
SendErrorMessage("You lack permission to build on this server.");
break;
case BuildPermissionFailPoint.SpawnProtect:
SendErrorMessage("You lack permission to build in the spawn point.");
break;
case BuildPermissionFailPoint.Regions:
SendErrorMessage("You lack permission to build in this region.");
break;
}
// Set the last warning time to now.
lastPermissionWarning = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
return false;
}
/// <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>
/// <returns>True if they can paint.</returns>
public bool HasPaintPermission(int x, int y)
{
return HasBuildPermission(x, y) || HasPermission(Permissions.canpaint);
}
/// <summary>Checks if a player can place ice, and if they can, tracks ice placements and removals.</summary>
/// <param name="x">The x coordinate of the suspected ice block.</param>
/// <param name="y">The y coordinate of the suspected ice block.</param>
/// <param name="tileType">The tile type of the suspected ice block.</param>
/// <param name="editAction">The EditAction on the suspected ice block.</param>
/// <returns>True if a player successfully places an ice tile or removes one of their past ice tiles.</returns>
public bool HasModifiedIceSuccessfully(int x, int y, short tileType, GetDataHandlers.EditAction editAction)
{
// The goal is to short circuit ASAP.
// A subsequent call to HasBuildPermission can figure this out if not explicitly ice.
if (!TShock.Config.AllowIce)
{
return false;
}
// They've placed some ice. Horrible!
if (editAction == GetDataHandlers.EditAction.PlaceTile && tileType == TileID.MagicalIceBlock)
{
IceTiles.Add(new Point(x, y));
return true;
}
// The edit wasn't an add, so we check to see if the position matches any of the known ice tiles
if (editAction == GetDataHandlers.EditAction.KillTile)
{
foreach (Point p in IceTiles)
{
// If they're trying to kill ice or dirt, and the tile was in the list, we allow it.
if (p.X == x && p.Y == y && (Main.tile[p.X, p.Y].type == TileID.Dirt || Main.tile[p.X, p.Y].type == TileID.MagicalIceBlock))
{
IceTiles.Remove(p);
return true;
}
}
}
// Only a small number of cases let this happen.
return false;
}
/// <summary>
/// A list of points where ice tiles have been placed.
/// </summary>
public List<Point> IceTiles;
/// <summary>
/// Unused, can be removed.
/// The last time the player was warned for build permissions.
/// In MS, defaults to 1 (so it will warn on the first attempt).
/// </summary>
public long RPm = 1;
/// <summary>
/// World protection message cool down.
/// </summary>
public long WPm = 1;
/// <summary>
/// Spawn protection message cool down.
/// </summary>
public long SPm = 1;
/// <summary>
/// Permission to build message cool down.
/// </summary>
public long BPm = 1;
public long lastPermissionWarning = 1;
/// <summary>
/// The time in ms when the player has logged in.

View file

@ -576,7 +576,7 @@ namespace TShockAPI
return;
}
if (CheckRangePermission(tsplr, args.Chest.x, args.Chest.y))
if (!tsplr.IsInRange(args.Chest.x, args.Chest.y))
{
args.Handled = true;
return;
@ -1726,178 +1726,6 @@ namespace TShockAPI
e.Handled = true;
}
/// <summary>CheckRangePermission - Checks if a player has permission to modify a tile dependent on range checks.</summary>
/// <param name="player">player - The TSPlayer object.</param>
/// <param name="x">x - The x coordinate of the tile.</param>
/// <param name="y">y - The y coordinate of the tile.</param>
/// <param name="range">range - The range to check for.</param>
/// <returns>bool - True if the player should not be able to place the tile. False if they can, or if range checks are off.</returns>
public static bool CheckRangePermission(TSPlayer player, int x, int y, int range = 32)
{
if (Config.RangeChecks && ((Math.Abs(player.TileX - x) > range) || (Math.Abs(player.TileY - y) > range)))
{
return true;
}
return false;
}
/// <summary>CheckTilePermission - Checks to see if a player has permission to modify a tile in general.</summary>
/// <param name="player">player - The TSPlayer object.</param>
/// <param name="tileX">tileX - The x coordinate of the tile.</param>
/// <param name="tileY">tileY - The y coordinate of the tile.</param>
/// <param name="tileType">tileType - The tile type.</param>
/// <param name="actionType">actionType - The type of edit that took place.</param>
/// <returns>bool - True if the player should not be able to modify a tile.</returns>
public static bool CheckTilePermission(TSPlayer player, int tileX, int tileY, short tileType, GetDataHandlers.EditAction actionType)
{
if (!player.HasPermission(Permissions.canbuild))
{
if (TShock.Config.AllowIce && actionType != GetDataHandlers.EditAction.PlaceTile)
{
foreach (Point p in player.IceTiles)
{
if (p.X == tileX && p.Y == tileY && (Main.tile[p.X, p.Y].type == 0 || Main.tile[p.X, p.Y].type == 127))
{
player.IceTiles.Remove(p);
return false;
}
}
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.BPm) > 2000)
{
player.SendErrorMessage("You do not have permission to build!");
player.BPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
if (TShock.Config.AllowIce && actionType == GetDataHandlers.EditAction.PlaceTile && tileType == 127)
{
player.IceTiles.Add(new Point(tileX, tileY));
return false;
}
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.BPm) > 2000)
{
player.SendErrorMessage("You do not have permission to build!");
player.BPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
if (!Regions.CanBuild(tileX, tileY, player))
{
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.RPm) > 2000)
{
player.SendErrorMessage("This region is protected from changes.");
player.RPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
if (Config.DisableBuild)
{
if (!player.HasPermission(Permissions.antibuild))
{
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.WPm) > 2000)
{
player.SendErrorMessage("The world is protected from changes.");
player.WPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
}
if (Config.SpawnProtection)
{
if (!player.HasPermission(Permissions.editspawn))
{
if (Utils.IsInSpawn(tileX, tileY))
{
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.SPm) > 2000)
{
player.SendErrorMessage("Spawn is protected from changes.");
player.SPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
}
}
return false;
}
/// <summary>CheckTilePermission - Checks to see if a player has the ability to modify a tile at a given position.</summary>
/// <param name="player">player - The TSPlayer object.</param>
/// <param name="tileX">tileX - The x coordinate of the tile.</param>
/// <param name="tileY">tileY - The y coordinate of the tile.</param>
/// <param name="paint">paint - Whether or not the tile is paint.</param>
/// <returns>bool - True if the player should not be able to modify the tile.</returns>
public static bool CheckTilePermission(TSPlayer player, int tileX, int tileY, bool paint = false)
{
if ((!paint && !player.HasPermission(Permissions.canbuild)) ||
(paint && !player.HasPermission(Permissions.canpaint)))
{
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.BPm) > 2000)
{
if (paint)
{
player.SendErrorMessage("You do not have permission to paint!");
}
else
{
player.SendErrorMessage("You do not have permission to build!");
}
player.BPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
if (!Regions.CanBuild(tileX, tileY, player))
{
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.RPm) > 2000)
{
player.SendErrorMessage("This region is protected from changes.");
player.RPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
if (Config.DisableBuild)
{
if (!player.HasPermission(Permissions.antibuild))
{
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.WPm) > 2000)
{
player.SendErrorMessage("The world is protected from changes.");
player.WPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
}
if (Config.SpawnProtection)
{
if (!player.HasPermission(Permissions.editspawn))
{
if (Utils.IsInSpawn(tileX, tileY))
{
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - player.SPm) > 1000)
{
player.SendErrorMessage("Spawn is protected from changes.");
player.SPm = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
return true;
}
}
}
return false;
}
/// <summary>Distance - Determines the distance between two vectors.</summary>
/// <param name="value1">value1 - The first vector location.</param>
/// <param name="value2">value2 - The second vector location.</param>