Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions TShockAPI/Bouncer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors

Expand Down Expand Up @@ -1312,6 +1312,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
short type = args.Type;
int index = args.Index;
float[] ai = args.Ai;
int generation = args.Generation;

// Clients do send NaN values so we can't just kick them
// See https://github.com/Pryaxis/TShock/issues/3076
Expand All @@ -1332,7 +1333,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
if (index > Main.maxProjectiles)
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from above projectile limit from {0}", args.Player.Name));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand All @@ -1343,7 +1344,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA

// Client will fight the server if we remove pets, silently reject instead
if (!Main.projPet[type] && !ProjectileID.Sets.LightPet[type])
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);

args.Handled = true;
return;
Expand All @@ -1355,7 +1356,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA

// Client will fight the server if we remove pets, silently reject instead
if (!Main.projPet[type] && !ProjectileID.Sets.LightPet[type])
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);

args.Handled = true;
return;
Expand All @@ -1366,7 +1367,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
args.Player.Disable(GetString("Player does not have permission to create projectile {0}.", type), DisableFlags.WriteToLogAndConsole);
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from permission check from {0} {1}", args.Player.Name, type));
args.Player.SendErrorMessage(GetString("You do not have permission to create that projectile."));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand All @@ -1375,7 +1376,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
{
args.Player.Disable(GetString("Projectile damage is higher than {0}.", TShock.Config.Settings.MaxProjDamage), DisableFlags.WriteToLogAndConsole);
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from projectile damage limit from {0} {1}/{2}", args.Player.Name, damage, TShock.Config.Settings.MaxProjDamage));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand Down Expand Up @@ -1412,7 +1413,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
if (Main.projHostile[type])
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from hostile projectile from {0}", args.Player.Name));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand All @@ -1423,7 +1424,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
if (type == ProjectileID.Tombstone)
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from tombstones from {0}", args.Player.Name));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand All @@ -1439,7 +1440,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
if (discreteDirection is < -3 or > 4)
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from portal gate from {0} (invalid angle: {1})", args.Player.Name, discreteDirection));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand All @@ -1449,7 +1450,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
if (boltProjectileData.Type == 0 || boltProjectileData.Killed)
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from portal gate from {0} (missing active Portal Gun bolt)", args.Player.Name, discreteDirection));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand Down Expand Up @@ -1478,7 +1479,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile please report to tshock about this! normally this is a reject from {0} {1}", args.Player.Name, type));
// args.Player.Disable(String.Format("Does not have projectile permission to update projectile. ({0})", type), DisableFlags.WriteToLogAndConsole);
// args.Player.RemoveProjectile(ident, owner);
// args.Player.RemoveProjectile(ident, owner, generation);
}
// args.Handled = false;
// return;
Expand All @@ -1493,7 +1494,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
else
{
args.Player.Disable(GetString("Reached projectile create threshold."), DisableFlags.WriteToLogAndConsole);
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
}

TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from projectile create threshold from {0} {1}/{2}", args.Player.Name, args.Player.ProjectileThreshold, TShock.Config.Settings.ProjectileThreshold));
Expand All @@ -1510,7 +1511,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
)
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from bouncer modified AI from {0}.", args.Player.Name));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand All @@ -1526,7 +1527,7 @@ internal void OnNewProjectile(object sender, GetDataHandlers.NewProjectileEventA
if (TShock.Config.Settings.DisableModifiedZenith && type == ProjectileID.FinalFractal && (ai[0] < -100 || ai[0] > 101) && !Terraria.Graphics.FinalFractalHelper._fractalProfiles.ContainsKey((int)ai[1]))
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnNewProjectile rejected from bouncer modified Zenith projectile from {0}.", args.Player.Name));
args.Player.RemoveProjectile(ident, owner);
args.Player.RemoveProjectile(ident, owner, generation);
args.Handled = true;
return;
}
Expand Down Expand Up @@ -1632,15 +1633,15 @@ internal void OnProjectileKill(object sender, GetDataHandlers.ProjectileKillEven
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnProjectileKill rejected from disabled from {0}", args.Player.Name));
args.Player.RemoveProjectile(args.ProjectileIdentity, args.ProjectileOwner);
args.Player.RemoveProjectile(args.ProjectileIdentity, args.ProjectileOwner, args.ProjectileGeneration);
args.Handled = true;
return;
}

if (args.Player.IsBouncerThrottled())
{
TShock.Log.ConsoleDebug(GetString("Bouncer / OnProjectileKill rejected from bouncer throttle from {0}", args.Player.Name));
args.Player.RemoveProjectile(args.ProjectileIdentity, args.ProjectileOwner);
args.Player.RemoveProjectile(args.ProjectileIdentity, args.ProjectileOwner, args.ProjectileGeneration);
args.Handled = true;
return;
}
Expand Down Expand Up @@ -2676,7 +2677,7 @@ internal void OnPlaceItemFrame(object sender, GetDataHandlers.PlaceItemFrameEven

if (!args.Player.HasBuildPermission(args.X, args.Y))
{
int num = Item.NewItem(null, (args.X * 16) + 8, (args.Y * 16) + 8, args.Player.TPlayer.width, args.Player.TPlayer.height, args.ItemID, args.Stack, noBroadcast: true, args.Prefix, noGrabDelay: true);
int num = Item.NewItem(null, (args.X * 16) + 8, (args.Y * 16) + 8, args.Player.TPlayer.width, args.Player.TPlayer.height, args.ItemID, args.Stack, noBroadcast: true, args.Prefix, Terraria.NewItemOwnership.None);
Main.item[num].playerIndexTheItemIsReservedFor = args.Player.Index;
NetMessage.SendData((int)PacketTypes.ItemDrop, args.Player.Index, -1, NetworkText.Empty, num, 1f);
NetMessage.SendData((int)PacketTypes.ItemOwner, args.Player.Index, -1, NetworkText.Empty, num);
Expand Down
2 changes: 1 addition & 1 deletion TShockAPI/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2537,7 +2537,7 @@ private static void Rain(CommandArgs args)
}
else
{
Main.StartRain(garenteeCoinRain: true);
Main.StartRain(guaranteeCoinRain: true);
TSPlayer.All.SendData(PacketTypes.WorldInfo);
TSPlayer.All.SendInfoMessage(GetString("{0} caused it to coin rain.", args.Player.Name));
}
Expand Down
107 changes: 86 additions & 21 deletions TShockAPI/GetDataHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,11 @@ public class NewProjectileEventArgs : GetDataHandledEventArgs
/// </summary>
public int Index { get; set; }

/// <summary>
/// Slot-reuse counter from the sender's ProjectileKey.
/// </summary>
public int Generation { get; set; }

/// <summary>
/// The special meaning of the projectile.
/// </summary>
Expand All @@ -709,7 +714,7 @@ public class NewProjectileEventArgs : GetDataHandledEventArgs
/// NewProjectile - Called when a client creates a new projectile
/// </summary>
public static HandlerList<NewProjectileEventArgs> NewProjectile = new HandlerList<NewProjectileEventArgs>();
private static bool OnNewProjectile(MemoryStream data, short ident, Vector2 pos, Vector2 vel, float knockback, short dmg, byte owner, short type, int index, TSPlayer player, float[] ai)
private static bool OnNewProjectile(MemoryStream data, short ident, Vector2 pos, Vector2 vel, float knockback, short dmg, byte owner, short type, int index, TSPlayer player, float[] ai, int generation)
{
if (NewProjectile == null)
return false;
Expand All @@ -726,7 +731,8 @@ private static bool OnNewProjectile(MemoryStream data, short ident, Vector2 pos,
Type = type,
Index = index,
Player = player,
Ai = ai
Ai = ai,
Generation = generation
};
NewProjectile.Invoke(null, args);
return args.Handled;
Expand Down Expand Up @@ -790,6 +796,8 @@ public class ProjectileKillEventArgs : GetDataHandledEventArgs
public byte ProjectileOwner;
/// <summary>The index of the projectile in Main.projectile.</summary>
public int ProjectileIndex;
/// <summary>Slot-reuse counter from the sender's ProjectileKey.</summary>
public int ProjectileGeneration;
}
/// <summary>The event fired when a projectile kill packet is received.</summary>
public static HandlerList<ProjectileKillEventArgs> ProjectileKill = new HandlerList<ProjectileKillEventArgs>();
Expand All @@ -800,7 +808,7 @@ public class ProjectileKillEventArgs : GetDataHandledEventArgs
/// <param name="owner">The projectile's owner (from the packet).</param>
/// <param name="index">The projectile's index (from Main.projectiles).</param>
/// <returns>bool</returns>
private static bool OnProjectileKill(TSPlayer player, MemoryStream data, int identity, byte owner, int index)
private static bool OnProjectileKill(TSPlayer player, MemoryStream data, int identity, byte owner, int index, int generation)
{
if (ProjectileKill == null)
return false;
Expand All @@ -812,6 +820,7 @@ private static bool OnProjectileKill(TSPlayer player, MemoryStream data, int ide
ProjectileIdentity = identity,
ProjectileOwner = owner,
ProjectileIndex = index,
ProjectileGeneration = generation,
};

ProjectileKill.Invoke(null, args);
Expand Down Expand Up @@ -3260,8 +3269,19 @@ private static bool HandleItemDrop(GetDataHandlerArgs args)
var vel = new Vector2(args.Data.ReadSingle(), args.Data.ReadSingle());
var stacks = args.Data.ReadInt16();
var prefix = args.Data.ReadInt8();
var noDelay = args.Data.ReadInt8() == 1;
BitsByte flags = args.Data.ReadInt8();
var ownership = (byte)((flags[0] ? 1 : 0) | (flags[1] ? 2 : 0));
var noDelay = ownership <= 1;
var type = args.Data.ReadInt16();
if (flags[2])
{
args.Data.ReadBoolean(); // shimmered
args.Data.ReadSingle(); // shimmerTime
}
if (flags[3])
{
args.Data.ReadInt8(); // enemyGrabDelayTime
}

if (OnItemDrop(args.Player, args.Data, id, pos, vel, stacks, prefix, noDelay, type))
return true;
Expand Down Expand Up @@ -3295,10 +3315,11 @@ private static bool HandleNpcItemStrike(GetDataHandlerArgs args)

private static bool HandleProjectileNew(GetDataHandlerArgs args)
{
short ident = args.Data.ReadInt16();
var key = (ProjectileKey)args.Data.ReadInt32();
byte owner = (byte)key.Spawner;
short ident = (short)key.Index;
Vector2 pos = args.Data.ReadVector2();
Vector2 vel = args.Data.ReadVector2();
byte owner = args.Data.ReadInt8();
short type = args.Data.ReadInt16();
BitsByte bitsByte = (BitsByte)args.Data.ReadByte();
BitsByte bitsByte2 = (BitsByte)(bitsByte[2] ? args.Data.ReadByte() : 0);
Expand All @@ -3310,11 +3331,21 @@ private static bool HandleProjectileNew(GetDataHandlerArgs args)
short dmg = (short)(bitsByte[4] ? args.Data.ReadInt16() : 0);
float knockback = bitsByte[5] ? args.Data.ReadSingle() : 0f;
short origDmg = (short)(bitsByte[6] ? args.Data.ReadInt16() : 0);
short projUUID = (short)(bitsByte[7] ? args.Data.ReadInt16() : -1);
if (projUUID >= 1000) projUUID = -1;
ai[2] = (bitsByte2[0] ? args.Data.ReadSingle() : 0f);

var index = TShock.Utils.SearchProjectile(ident, owner);
if (type < 0 || type >= Main.projHostile.Length || Main.projHostile[type])
{
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleProjectileNew rejected hostile projectile type {0}", args.Player.Name));
return true;
}

if (owner != args.Player.Index)
{
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleProjectileNew rejected key spawner mismatch {0}", args.Player.Name));
return true;
}

var index = TShock.Utils.SearchProjectile(ident, owner, key.Generation);

// Cattiva's dig ability can bypass build permissions via vanilla exploit in Terraria v1.4.5
// Block ai[0] == 3 (dig state)
Expand All @@ -3324,7 +3355,7 @@ private static bool HandleProjectileNew(GetDataHandlerArgs args)
return true;
}

if (OnNewProjectile(args.Data, ident, pos, vel, knockback, dmg, owner, type, index, args.Player, ai))
if (OnNewProjectile(args.Data, ident, pos, vel, knockback, dmg, owner, type, index, args.Player, ai, key.Generation))
return true;

lock (args.Player.RecentlyCreatedProjectiles)
Expand All @@ -3344,12 +3375,22 @@ private static bool HandleProjectileNew(GetDataHandlerArgs args)

private static bool HandleNpcStrike(GetDataHandlerArgs args)
{
var id = args.Data.ReadInt16();
short id = args.Data.ReadInt8();
var generation = args.Data.ReadInt8();
var dmg = args.Data.ReadInt16();
var knockback = args.Data.ReadSingle();
var direction = (byte)(args.Data.ReadInt8() - 1);
var crit = args.Data.ReadInt8();

if (id >= Main.npc.Length)
return true;

if (Main.npc[id].generation != generation)
{
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleNpcStrike rejected npc generation mismatch {0}", args.Player.Name));
return true;
}

if (OnNPCStrike(args.Player, args.Data, id, direction, dmg, knockback, crit))
return true;

Expand Down Expand Up @@ -3393,12 +3434,33 @@ private static bool HandleNpcStrike(GetDataHandlerArgs args)

private static bool HandleProjectileKill(GetDataHandlerArgs args)
{
var ident = args.Data.ReadInt16();
var owner = args.Data.ReadInt8();
owner = (byte)args.Player.Index;
var index = TShock.Utils.SearchProjectile(ident, owner);
var key = (ProjectileKey)args.Data.ReadInt32();
var killPos = args.Data.ReadVector2();
var ident = (short)key.Index;
var owner = (byte)args.Player.Index;

// TryGet does not bounds check, and Index is wider than keyToIndex
if (key.Index > Main.maxProjectiles)
{
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleProjectileKill rejected out of range projectile index {0}", args.Player.Name));
return true;
}

if (!key.TryGet(out var killed) || !killed.active)
{
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleProjectileKill rejected stale projectile key {0}", args.Player.Name));
return true;
}

var index = killed.whoAmI;

if (killed.owner != args.Player.Index)
{
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleProjectileKill rejected owner mismatch {0}", args.Player.Name));
return true;
}

if (OnProjectileKill(args.Player, args.Data, ident, owner, index))
if (OnProjectileKill(args.Player, args.Data, ident, owner, index, key.Generation))
{
return true;
}
Expand Down Expand Up @@ -4281,7 +4343,12 @@ private static bool HandleHealOther(GetDataHandlerArgs args)
private static bool HandleCatchNpc(GetDataHandlerArgs args)
{
var npcID = args.Data.ReadInt16();
var who = args.Data.ReadByte();

if (npcID < 0 || npcID >= Main.maxNPCs)
{
TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleCatchNpc rejected out of range npc {0}", args.Player.Name));
return true;
}

if (Main.npc[npcID]?.catchItem == 0)
{
Expand Down Expand Up @@ -5193,18 +5260,16 @@ public enum NetModuleType
Ping,
Ambience,
Bestiary,
CreativeUnlocks,
CreativePowers,
CreativeUnlocksPlayerReport,
TeleportPylon,
Particles,
CreativePowerPermissions,
Banners,
CraftingRequests,
TagEffectState,
LeashedEntity,
UnbreakableWallScan,
[Obsolete("Removed in 1.4.5")]
CreativeUnlocks
UnbreakableWallScan
}

public enum CreativePowerTypes
Expand Down
Loading
Loading