Merge branch 'general-devel' into player-data

This commit is contained in:
Lucas Nicodemus 2018-06-08 09:50:15 -06:00 committed by GitHub
commit 16ec98f684
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 120 additions and 23 deletions

View file

@ -85,6 +85,9 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
* Removed `TSPlayer.InitSpawn` field. (@DankRank) * Removed `TSPlayer.InitSpawn` field. (@DankRank)
* `OnPlayerSpawn`'s player ID field is now `PlayerId`. (@DankRank) * `OnPlayerSpawn`'s player ID field is now `PlayerId`. (@DankRank)
* Fixed null reference console spam in non-SSC mode (@QuiCM) * Fixed null reference console spam in non-SSC mode (@QuiCM)
* `Utils.TryParseTime` can now take spaces (e.g., `3d 5h 2m 3s`) (@QuiCM)
* Enabled banning unregistered users (@QuiCM)
* Added filtering and validation on packet 96 (Teleport player through portal) (@QuiCM)
## TShock 4.3.25 ## 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. * 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

@ -155,9 +155,10 @@ You need to re-run the patcher any time `OTAPI` updates. You need to rebuild `Te
1. You need to get NuGet. Download the latest `nuget.exe` from [NuGet](https://www.nuget.org/downloads). 1. You need to get NuGet. Download the latest `nuget.exe` from [NuGet](https://www.nuget.org/downloads).
1. Make a `~/bin` folder if you don't have one. 1. Make a `~/bin` folder if you don't have one. Then, put `nuget.exe` inside it.
$ mkdir ~/bin/ $ mkdir ~/bin/
$ cp ~/downloads/nuget.exe ~/bin/
1. Set an environment variable to store if you plan to build in debug or release. 1. Set an environment variable to store if you plan to build in debug or release.

View file

@ -64,6 +64,7 @@ namespace TShockAPI
GetDataHandlers.HealOtherPlayer += OnHealOtherPlayer; GetDataHandlers.HealOtherPlayer += OnHealOtherPlayer;
GetDataHandlers.TileEdit += OnTileEdit; GetDataHandlers.TileEdit += OnTileEdit;
GetDataHandlers.MassWireOperation += OnMassWireOperation; GetDataHandlers.MassWireOperation += OnMassWireOperation;
GetDataHandlers.PortalTeleport += OnPlayerPortalTeleport;
} }
internal void OnGetSection(object sender, GetDataHandlers.GetSectionEventArgs args) internal void OnGetSection(object sender, GetDataHandlers.GetSectionEventArgs args)
@ -1721,6 +1722,34 @@ namespace TShockAPI
args.Handled = true; args.Handled = true;
} }
internal void OnPlayerPortalTeleport(object sender, GetDataHandlers.TeleportThroughPortalEventArgs args)
{
//Packet 96 (player teleport through portal) has no validation on whether or not the player id provided
//belongs to the player who sent the packet.
if (args.Player.Index != args.TargetPlayerIndex)
{
//If the player who sent the packet is not the player being teleported, cancel this packet
args.Player.Disable("Malicious portal attempt.", DisableFlags.WriteToLogAndConsole); //Todo: this message is not particularly clear - suggestions wanted
args.Handled = true;
return;
}
//Generic bounds checking, though I'm not sure if anyone would willingly hack themselves outside the map?
if (args.NewPosition.X > Main.maxTilesX || args.NewPosition.X < 0
|| args.NewPosition.Y > Main.maxTilesY || args.NewPosition.Y < 0)
{
args.Handled = true;
return;
}
//May as well reject teleport attempts if the player is being throttled
if (args.Player.IsBeingDisabled() || args.Player.IsBouncerThrottled())
{
args.Handled = true;
return;
}
}
/// <summary> /// <summary>
/// Tile IDs that can be oriented: /// Tile IDs that can be oriented:
/// Cannon, /// Cannon,

View file

@ -1309,16 +1309,20 @@ namespace TShockAPI
// Effective ban target assignment // Effective ban target assignment
List<TSPlayer> players = TSPlayer.FindByNameOrID(args.Parameters[1]); List<TSPlayer> players = TSPlayer.FindByNameOrID(args.Parameters[1]);
// Bad case: Players contains more than 1 person so we can't ban them
if (players.Count > 1)
{
//Fail fast
args.Player.SendMultipleMatchError(players.Select(p => p.Name));
return;
}
UserAccount offlineUserAccount = TShock.UserAccounts.GetUserAccountByName(args.Parameters[1]); UserAccount offlineUserAccount = TShock.UserAccounts.GetUserAccountByName(args.Parameters[1]);
// Storage variable to determine if the command executor is the server console // Storage variable to determine if the command executor is the server console
// If it is, we assume they have full control and let them override permission checks // If it is, we assume they have full control and let them override permission checks
bool callerIsServerConsole = false; bool callerIsServerConsole = args.Player is TSServerPlayer;
if (args.Player is TSServerPlayer)
{
callerIsServerConsole = true;
}
// The ban reason the ban is going to have // The ban reason the ban is going to have
string banReason = "Unknown."; string banReason = "Unknown.";
@ -1331,16 +1335,18 @@ namespace TShockAPI
if (args.Parameters.Count >= 3) if (args.Parameters.Count >= 3)
{ {
bool parsedOkay = false; bool parsedOkay = false;
if (!(args.Parameters[2] == "0")) if (args.Parameters[2] != "0")
{ {
parsedOkay = TShock.Utils.TryParseTime(args.Parameters[2], out banLengthInSeconds); parsedOkay = TShock.Utils.TryParseTime(args.Parameters[2], out banLengthInSeconds);
} else { }
else
{
parsedOkay = true; parsedOkay = true;
} }
if (!parsedOkay) if (!parsedOkay)
{ {
args.Player.SendErrorMessage("Invalid time format. Example: 10d+5h+3m-2s."); args.Player.SendErrorMessage("Invalid time format. Example: 10d 5h 3m 2s.");
args.Player.SendErrorMessage("Use 0 (zero) for a permanent ban."); args.Player.SendErrorMessage("Use 0 (zero) for a permanent ban.");
return; return;
} }
@ -1352,13 +1358,6 @@ namespace TShockAPI
banReason = String.Join(" ", args.Parameters.Skip(3)); banReason = String.Join(" ", args.Parameters.Skip(3));
} }
// Bad case: Players contains more than 1 person so we can't ban them
if (players.Count > 1)
{
args.Player.SendMultipleMatchError(players.Select(p => p.Name));
return;
}
// Good case: Online ban for matching character. // Good case: Online ban for matching character.
if (players.Count == 1) if (players.Count == 1)
{ {
@ -1371,7 +1370,7 @@ namespace TShockAPI
} }
targetGeneralizedName = target.Name; targetGeneralizedName = target.Name;
success = TShock.Bans.AddBan(target.IP, target.Name, target.UUID, target.Account.Name, banReason, false, args.Player.Account.Name, success = TShock.Bans.AddBan(target.IP, target.Name, target.UUID, target.Account?.Name ?? "", banReason, false, args.Player.Account.Name,
banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s")); banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s"));
// Since this is an online ban, we need to dc the player and tell them now. // Since this is an online ban, we need to dc the player and tell them now.
@ -1399,7 +1398,8 @@ namespace TShockAPI
// If the target is a valid IP... // If the target is a valid IP...
string pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; string pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
Regex r = new Regex(pattern, RegexOptions.IgnoreCase); Regex r = new Regex(pattern, RegexOptions.IgnoreCase);
if (r.IsMatch(args.Parameters[1])) { if (r.IsMatch(args.Parameters[1]))
{
targetGeneralizedName = "IP: " + args.Parameters[1]; targetGeneralizedName = "IP: " + args.Parameters[1];
success = TShock.Bans.AddBan(args.Parameters[1], "", "", "", banReason, success = TShock.Bans.AddBan(args.Parameters[1], "", "", "", banReason,
false, args.Player.Account.Name, banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s")); false, args.Player.Account.Name, banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s"));
@ -1409,7 +1409,9 @@ namespace TShockAPI
args.Player.SendErrorMessage("Note: An account named with this IP address also exists."); args.Player.SendErrorMessage("Note: An account named with this IP address also exists.");
args.Player.SendErrorMessage("Note: It will also be banned."); args.Player.SendErrorMessage("Note: It will also be banned.");
} }
} else { }
else
{
// Apparently there is no way to not IP ban someone // Apparently there is no way to not IP ban someone
// This means that where we would normally just ban a "character name" here // This means that where we would normally just ban a "character name" here
// We can't because it requires some IP as a primary key. // We can't because it requires some IP as a primary key.

View file

@ -1488,7 +1488,8 @@ namespace TShockAPI
{ PacketTypes.PlayerHealOther, HandleHealOther }, { PacketTypes.PlayerHealOther, HandleHealOther },
{ PacketTypes.CrystalInvasionStart, HandleOldOnesArmy }, { PacketTypes.CrystalInvasionStart, HandleOldOnesArmy },
{ PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 }, { PacketTypes.PlayerHurtV2, HandlePlayerDamageV2 },
{ PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 } { PacketTypes.PlayerDeathV2, HandlePlayerKillMeV2 },
{ PacketTypes.PlayerTeleportPortal, HandlePlayerPortalTeleport }
}; };
} }
@ -1510,6 +1511,67 @@ namespace TShockAPI
return false; return false;
} }
/// <summary>The event args object for the PortalTeleport event</summary>
public class TeleportThroughPortalEventArgs : GetDataHandledEventArgs
{
/// <summary>The Terraria player index of the target player</summary>
public byte TargetPlayerIndex { get; set; }
/// <summary>
/// The position the target player will be at after going through the portal
/// </summary>
public Vector2 NewPosition { get; set; }
/// <summary>
/// The velocity the target player will have after going through the portal
/// </summary>
public Vector2 NewVelocity { get; set; }
/// <summary>
/// Index of the portal's color (for use with <see cref="Terraria.GameContent.PortalHelper.GetPortalColor(int)"/>)
/// </summary>
public int PortalColorIndex { get; set; }
}
/// <summary>When a player passes through a portal</summary>
public static HandlerList<TeleportThroughPortalEventArgs> PortalTeleport = new HandlerList<TeleportThroughPortalEventArgs>();
private static bool OnPlayerTeleportThroughPortal(TSPlayer sender, byte targetPlayerIndex, MemoryStream data, Vector2 position, Vector2 velocity, int colorIndex)
{
TeleportThroughPortalEventArgs args = new TeleportThroughPortalEventArgs
{
TargetPlayerIndex = targetPlayerIndex,
Data = data,
Player = sender,
NewPosition = position,
NewVelocity = velocity,
PortalColorIndex = colorIndex
};
PortalTeleport.Invoke(null, args);
return args.Handled;
}
private static bool HandlePlayerPortalTeleport(GetDataHandlerArgs args)
{
byte plr = args.Data.ReadInt8();
short portalColorIndex = args.Data.ReadInt16();
float newPositionX = args.Data.ReadSingle();
float newPositionY = args.Data.ReadSingle();
float newVelocityX = args.Data.ReadSingle();
float newVelocityY = args.Data.ReadSingle();
return OnPlayerTeleportThroughPortal(
args.Player,
plr,
args.Data,
new Vector2(newPositionX, newPositionY),
new Vector2(newVelocityX, newVelocityY),
portalColorIndex
);
}
private static bool HandleHealOther(GetDataHandlerArgs args) private static bool HandleHealOther(GetDataHandlerArgs args)
{ {
byte plr = args.Data.ReadInt8(); byte plr = args.Data.ReadInt8();

View file

@ -534,7 +534,7 @@ namespace TShockAPI
var sb = new StringBuilder(3); var sb = new StringBuilder(3);
for (int i = 0; i < str.Length; i++) for (int i = 0; i < str.Length; i++)
{ {
if (Char.IsDigit(str[i]) || (str[i] == '-' || str[i] == '+')) if (Char.IsDigit(str[i]) || (str[i] == '-' || str[i] == '+' || str[i] == ' '))
sb.Append(str[i]); sb.Append(str[i]);
else else
{ {