Merge branch 'general-devel' into otapi3

This commit is contained in:
Luke 2021-11-27 17:53:28 +10:00
commit cec53c780f
7 changed files with 231 additions and 17 deletions

View file

@ -44,6 +44,20 @@ namespace TShockAPI
internal Handlers.LandGolfBallInCupHandler LandGolfBallInCupHandler { get; private set; }
internal Handlers.SyncTilePickingHandler SyncTilePickingHandler { get; private set; }
/// <summary>
/// Represents a place style corrector.
/// </summary>
/// <param name="player">The player placing the tile.</param>
/// <param name="requestedPlaceStyle">The requested place style to be placed.</param>
/// <param name="actualItemPlaceStyle">The actual place style that should be placed, based of the player's held item.</param>
/// <returns>The correct place style in the current context.</returns>
internal delegate int PlaceStyleCorrector(Player player, int requestedPlaceStyle, int actualItemPlaceStyle);
/// <summary>
/// Represents a dictionary of <see cref="PlaceStyleCorrector"/>s, the key is the tile ID and the value is the corrector.
/// </summary>
internal Dictionary<int, PlaceStyleCorrector> PlaceStyleCorrectors = new Dictionary<int, PlaceStyleCorrector>();
/// <summary>Constructor call initializes Bouncer and related functionality.</summary>
/// <returns>A new Bouncer.</returns>
internal Bouncer()
@ -100,6 +114,122 @@ namespace TShockAPI
GetDataHandlers.KillMe += OnKillMe;
GetDataHandlers.FishOutNPC += OnFishOutNPC;
GetDataHandlers.FoodPlatterTryPlacing += OnFoodPlatterTryPlacing;
// The following section is based off Player.PlaceThing_Tiles_PlaceIt and Player.PlaceThing_Tiles_PlaceIt_GetLegacyTileStyle.
// Multi-block tiles are intentionally ignored because they don't pass through OnTileEdit.
PlaceStyleCorrectors.Add(TileID.Torches,
(player, requestedPlaceStyle, actualItemPlaceStyle) =>
{
// If the client is attempting to place a default torch, we need to check that the torch they are attempting to place is valid.
// The place styles may mismatch if the player is placing a biome torch.
// Biome torches can only be placed if the player has unlocked them (Torch God's Favor)
// Therefore, the following conditions need to be true:
// - The client's selected item will create a default torch(this should be true if this handler is running)
// - The client's selected item's place style will be that of a default torch
// - The client has unlocked biome torches
if (actualItemPlaceStyle == TorchID.Torch && player.unlockedBiomeTorches)
{
// The server isn't notified when the player turns on biome torches.
// So on the client it can be on, while on the server it's off.
// BiomeTorchPlaceStyle returns placeStyle as-is if biome torches is off.
// Because of the uncertainty, we:
// 1. Ensure that UsingBiomeTorches is on, so we can get the correct
// value from BiomeTorchPlaceStyle.
// 2. Check if the torch is either 0 or the biome torch since we aren't
// sure if the player has biome torches on
var usingBiomeTorches = player.UsingBiomeTorches;
player.UsingBiomeTorches = true;
// BiomeTorchPlaceStyle returns the place style of the player's current biome's biome torch
var biomeTorchPlaceStyle = player.BiomeTorchPlaceStyle(actualItemPlaceStyle);
// Reset UsingBiomeTorches value
player.UsingBiomeTorches = usingBiomeTorches;
return biomeTorchPlaceStyle;
}
else
{
// If the player isn't holding the default torch, then biome torches don't apply and return item place style.
// Or, they are holding the default torch but haven't unlocked biome torches yet, so return item place style.
return actualItemPlaceStyle;
}
});
PlaceStyleCorrectors.Add(TileID.Presents,
(player, requestedPlaceStyle, actualItemPlaceStyle) =>
{
// RNG only generates placeStyles less than 7, so permit only <7
// Note: there's an 8th present(blue, golden stripes) that's unplaceable.
// https://terraria.fandom.com/wiki/Presents, last present of the 8 displayed
if (requestedPlaceStyle < 7)
{
return requestedPlaceStyle;
}
else
{
// Return 0 for now, but ideally 0-7 should be returned.
return 0;
}
});
PlaceStyleCorrectors.Add(TileID.Explosives,
(player, requestedPlaceStyle, actualItemPlaceStyle) =>
{
// RNG only generates placeStyles less than 2, so permit only <2
if (requestedPlaceStyle < 2)
{
return requestedPlaceStyle;
}
else
{
// Return 0 for now, but ideally 0-1 should be returned.
return 0;
}
});
PlaceStyleCorrectors.Add(TileID.Crystals,
(player, requestedPlaceStyle, actualItemPlaceStyle) =>
{
// RNG only generates placeStyles less than 18, so permit only <18.
// Note: Gelatin Crystals(Queen Slime summon) share the same ID as Crystal Shards.
// <18 includes all shards except Gelatin Crystals.
if (requestedPlaceStyle < 18)
{
return requestedPlaceStyle;
}
else
{
// Return 0 for now, but ideally 0-17 should be returned.
return 0;
}
});
PlaceStyleCorrectors.Add(TileID.MinecartTrack,
(player, requestedPlaceStyle, actualItemPlaceStyle) =>
{
// Booster tracks have 2 variations, but only 1 item.
// The variation depends on the direction the player is facing.
if (actualItemPlaceStyle == 2)
{
// Check the direction the player is facing.
// 1 is right and -1 is left, these are the only possible values.
if (player.direction == 1)
{
// Right-facing booster tracks
return 3;
}
else if (player.direction == -1)
{
// Left-facing booster tracks
return 2;
}
else
{
throw new InvalidOperationException("Unrecognized player direction");
}
}
else
{
// Not a booster track, return as-is.
return actualItemPlaceStyle;
}
});
}
internal void OnGetSection(object sender, GetDataHandlers.GetSectionEventArgs args)
@ -255,7 +385,10 @@ namespace TShockAPI
int tileY = args.Y;
short editData = args.EditData;
EditType type = args.editDetail;
byte style = args.Style;
// 'placeStyle' is a term used in Terraria land to determine which frame of a sprite is displayed when the sprite is placed. The placeStyle
// determines the frameX and frameY offsets
byte requestedPlaceStyle = args.Style;
try
{
@ -306,14 +439,38 @@ namespace TShockAPI
return;
}
if ((args.Player.TPlayer.BiomeTorchHoldStyle(style) != args.Player.TPlayer.BiomeTorchPlaceStyle(style))
&& (selectedItem.placeStyle != style))
// This is the actual tile ID we expect the selected item to create. If the tile ID from the packet and the tile ID from the item do not match
// we need to inspect further to determine if Terraria is sending funny information (which it does sometimes) or if someone is being malicious
var actualTileToBeCreated = selectedItem.createTile;
// This is the actual place style we expect the selected item to create. Same as above - if it differs from what the client tells us,
// we need to do some inspection to check if its valid
var actualItemPlaceStyle = selectedItem.placeStyle;
// The client has requested to place a style that does not match their held item's actual place style
if (requestedPlaceStyle != actualItemPlaceStyle)
{
TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}",
args.Player.Name, action, editData, style, selectedItem.placeStyle);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Handled = true;
return;
var tplayer = args.Player.TPlayer;
// Search for an extraneous tile corrector
// If none found then it can't be a false positive so deny the action
if (!PlaceStyleCorrectors.TryGetValue(actualTileToBeCreated, out PlaceStyleCorrector corrector))
{
TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}",
args.Player.Name, action, editData, requestedPlaceStyle, actualItemPlaceStyle);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Handled = true;
return;
}
// See if the corrector's expected style matches
var correctedPlaceStyle = corrector(tplayer, requestedPlaceStyle, actualItemPlaceStyle);
if (requestedPlaceStyle != correctedPlaceStyle)
{
TShock.Log.ConsoleError("Bouncer / OnTileEdit rejected from (placestyle) {0} {1} {2} placeStyle: {3} expectedStyle: {4}",
args.Player.Name, action, editData, requestedPlaceStyle, correctedPlaceStyle);
args.Player.SendTileSquare(tileX, tileY, 1);
args.Handled = true;
return;
}
}
}
@ -382,8 +539,7 @@ namespace TShockAPI
else if (action == EditAction.PlaceTile || action == EditAction.ReplaceTile || action == EditAction.PlaceWall || action == EditAction.ReplaceWall)
{
if ((action == EditAction.PlaceTile && TShock.Config.Settings.PreventInvalidPlaceStyle) &&
(MaxPlaceStyles.ContainsKey(editData) && style > MaxPlaceStyles[editData]) &&
(ExtraneousPlaceStyles.ContainsKey(editData) && style > ExtraneousPlaceStyles[editData]))
requestedPlaceStyle > GetMaxPlaceStyle(editData))
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (ms1) {0} {1} {2}", args.Player.Name, action, editData);
args.Player.SendTileSquare(tileX, tileY, 4);
@ -2206,6 +2362,25 @@ namespace TShockAPI
});
}
/// <summary>
/// Returns the max <see cref="Item.placeStyle"/> associated with the given <paramref name="tileID"/>. Or -1 if there's no association
/// </summary>
/// <param name="tileID">Tile ID to query for</param>
/// <returns>The max <see cref="Item.placeStyle"/>, otherwise -1 if there's no association</returns>
internal static int GetMaxPlaceStyle(int tileID)
{
int result;
if (ExtraneousPlaceStyles.TryGetValue(tileID, out result)
|| MaxPlaceStyles.TryGetValue(tileID, out result))
{
return result;
}
else
{
return -1;
}
}
// These time values are references from Projectile.cs, at npc.AddBuff() calls.
private static Dictionary<int, short> NPCAddBuffTimeMax = new Dictionary<int, short>()
{

View file

@ -1934,6 +1934,7 @@ namespace TShockAPI
return args.Handled;
}
/// <summary>
/// For use in an Emoji event.
/// </summary>
public class EmojiEventArgs : GetDataHandledEventArgs
@ -1967,6 +1968,7 @@ namespace TShockAPI
return args.Handled;
}
/// <summary>
/// For use in a TileEntityDisplayDollItemSync event.
/// </summary>
public class DisplayDollItemSyncEventArgs : GetDataHandledEventArgs
@ -2025,6 +2027,7 @@ namespace TShockAPI
return args.Handled;
}
/// <summary>
/// For use in an OnRequestTileEntityInteraction event.
/// </summary>
public class RequestTileEntityInteractionEventArgs : GetDataHandledEventArgs
@ -4262,6 +4265,10 @@ namespace TShockAPI
/// </summary>
internal static Dictionary<int, int> ExtraneousPlaceStyles = new Dictionary<int, int>
{
{TileID.Presents, 6},
{TileID.Explosives, 1},
{TileID.MagicalIceBlock, 0},
{TileID.Crystals, 17},
{TileID.MinecartTrack, 3}
};

View file

@ -1561,6 +1561,18 @@ namespace TShockAPI
Main.player[Index].team = team;
NetMessage.SendData((int)PacketTypes.PlayerTeam, -1, -1, NetworkText.Empty, Index);
}
/// <summary>
/// Sets the player's pvp.
/// </summary>
/// <param name="mode">The state of the pvp mode.</param>
public virtual void SetPvP(bool mode, bool withMsg = false)
{
Main.player[Index].hostile = mode;
NetMessage.SendData((int)PacketTypes.TogglePvp, -1, -1, NetworkText.Empty, Index);
if (withMsg)
TSPlayer.All.SendMessage(Language.GetTextValue(mode ? "LegacyMultiplayer.11" : "LegacyMultiplayer.12", Name), Main.teamColor[Team]);
}
private DateTime LastDisableNotification = DateTime.UtcNow;

View file

@ -80,7 +80,7 @@ namespace TShockAPI
/// <summary>Players - Contains all TSPlayer objects for accessing TSPlayers currently on the server</summary>
public static TSPlayer[] Players = new TSPlayer[Main.maxPlayers];
/// <summary>Bans - Static reference to the ban manager for accessing bans & related functions.</summary>
/// <summary>Bans - Static reference to the ban manager for accessing bans &amp; related functions.</summary>
public static BanManager Bans;
/// <summary>Warps - Static reference to the warp manager for accessing the warp system.</summary>
public static WarpManager Warps;
@ -149,7 +149,7 @@ namespace TShockAPI
/// </summary>
public static event Action Initialized;
/// <summary>Version - The version required by the TerrariaAPI to be passed back for checking & loading the plugin.</summary>
/// <summary>Version - The version required by the TerrariaAPI to be passed back for checking &amp; loading the plugin.</summary>
/// <value>value - The version number specified in the Assembly, based on the VersionNum variable set in this class.</value>
public override Version Version
{
@ -690,15 +690,24 @@ namespace TShockAPI
}
}
private bool tryingToShutdown = false;
/// <summary> ConsoleCancelHandler - Handles when Ctrl + C is sent to the server for a safe shutdown. </summary>
/// <param name="sender">The sender</param>
/// <param name="args">The ConsoleCancelEventArgs associated with the event.</param>
private void ConsoleCancelHandler(object sender, ConsoleCancelEventArgs args)
{
if (tryingToShutdown)
{
System.Environment.Exit(1);
return;
}
// Cancel the default behavior
args.Cancel = true;
Log.ConsoleInfo("Interrupt received. Saving the world and shutting down.");
tryingToShutdown = true;
Log.ConsoleInfo("Shutting down safely. To force shutdown, send SIGINT (CTRL + C) again.");
// Perform a safe shutdown
TShock.Utils.StopServer(true, "Server console interrupted!");

View file

@ -894,7 +894,7 @@ namespace TShockAPI
Main.recipe[i] = new Recipe();
}
/// <summary>Dumps a matrix of all permissions & all groups in Markdown table format.</summary>
/// <summary>Dumps a matrix of all permissions &amp; all groups in Markdown table format.</summary>
/// <param name="path">The save destination.</param>
internal void DumpPermissionMatrix(string path)
{
@ -1230,7 +1230,7 @@ namespace TShockAPI
for (int i = 0; i < Main.maxItemTypes; i++)
{
item.netDefaults(i);
if (item.placeStyle > 0)
if (item.placeStyle >= 0)
{
if (GetDataHandlers.MaxPlaceStyles.ContainsKey(item.createTile))
{