diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs
index 909a84d68..52ec51c1b 100644
--- a/TShockAPI/Bouncer.cs
+++ b/TShockAPI/Bouncer.cs
@@ -1,4 +1,4 @@
-/*
+/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
@@ -2676,7 +2676,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);
diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs
index 0ce138f14..9c1d2a0b2 100644
--- a/TShockAPI/Commands.cs
+++ b/TShockAPI/Commands.cs
@@ -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));
}
diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs
index ef4b0c2e9..61d44ab6e 100644
--- a/TShockAPI/GetDataHandlers.cs
+++ b/TShockAPI/GetDataHandlers.cs
@@ -3260,8 +3260,21 @@ 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();
+ // Bits 0-1: Terraria.NewItemOwnership (0 None, 1 ReserveForLocalPlayer,
+ // 2 GrabDelayForLocalPlayer, 3 GrabDelayForAllPlayers)
+ 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;
@@ -3271,6 +3284,8 @@ private static bool HandleItemDrop(GetDataHandlerArgs args)
private static bool HandleItemOwner(GetDataHandlerArgs args)
{
+ // As of 1.4.5.7 this packet is server->client only and vanilla ignores it inbound;
+ // clients no longer send it (including the old slot-400 SSC echo).
var id = args.Data.ReadInt16();
var owner = args.Data.ReadInt8();
@@ -3295,10 +3310,14 @@ private static bool HandleNpcItemStrike(GetDataHandlerArgs args)
private static bool HandleProjectileNew(GetDataHandlerArgs args)
{
- short ident = args.Data.ReadInt16();
+ // 1.4.5.7: a packed ProjectileKey (spawner:8 | index:10 | generation:14) replaces
+ // the old identity short + owner byte; the trailing bit7 UUID short is gone.
+ uint key = (uint)args.Data.ReadInt32();
+ byte owner = (byte)(key & 255u);
+ short ident = (short)(key >> 8 & 1023u);
+ int generation = (int)(key >> 18 & 16383u);
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);
@@ -3310,11 +3329,17 @@ 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);
+ // Vanilla rejects keys whose spawner isn't the sending client; mirror that here.
+ if (owner != args.Player.Index)
+ {
+ TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleProjectileNew rejected key spawner mismatch {0}", args.Player.Name));
+ return true;
+ }
+
+ // The key's index is the projectile slot; the old identity scan no longer applies.
+ var index = (int)ident;
// Cattiva's dig ability can bypass build permissions via vanilla exploit in Terraria v1.4.5
// Block ai[0] == 3 (dig state)
@@ -3344,12 +3369,19 @@ private static bool HandleProjectileNew(GetDataHandlerArgs args)
private static bool HandleNpcStrike(GetDataHandlerArgs args)
{
- var id = args.Data.ReadInt16();
+ // 1.4.5.7: slot shrank to a byte and gained a generation byte (slot-reuse guard).
+ // Vanilla drops strikes whose generation doesn't match and acks each one with
+ // packet 162 (DamageNPCAck).
+ 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 (OnNPCStrike(args.Player, args.Data, id, direction, dmg, knockback, crit))
return true;
@@ -3393,10 +3425,15 @@ 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);
+ // 1.4.5.7: i32 ProjectileKey + kill-effect position replace the old identity/owner pair.
+ uint key = (uint)args.Data.ReadInt32();
+ var killPos = args.Data.ReadVector2();
+ var ident = (short)(key >> 8 & 1023u);
+ var owner = (byte)args.Player.Index;
+ var index = (int)ident;
+
+ if (index >= Main.projectile.Length)
+ return true;
if (OnProjectileKill(args.Player, args.Data, ident, owner, index))
{
@@ -4280,8 +4317,8 @@ private static bool HandleHealOther(GetDataHandlerArgs args)
private static bool HandleCatchNpc(GetDataHandlerArgs args)
{
+ // 1.4.5.7 removed the trailing "who" byte; the server uses the sender's index.
var npcID = args.Data.ReadInt16();
- var who = args.Data.ReadByte();
if (Main.npc[npcID]?.catchItem == 0)
{
@@ -5193,6 +5230,7 @@ public enum NetModuleType
Ping,
Ambience,
Bestiary,
+ CreativeUnlocks,
CreativePowers,
CreativeUnlocksPlayerReport,
TeleportPylon,
@@ -5200,11 +5238,8 @@ public enum NetModuleType
CreativePowerPermissions,
Banners,
CraftingRequests,
- TagEffectState,
LeashedEntity,
- UnbreakableWallScan,
- [Obsolete("Removed in 1.4.5")]
- CreativeUnlocks
+ UnbreakableWallScan
}
public enum CreativePowerTypes
diff --git a/TShockAPI/Net/ProjectileRemoveMsg.cs b/TShockAPI/Net/ProjectileRemoveMsg.cs
index ed4d57ffa..3255f8cd9 100644
--- a/TShockAPI/Net/ProjectileRemoveMsg.cs
+++ b/TShockAPI/Net/ProjectileRemoveMsg.cs
@@ -30,20 +30,21 @@ public override PacketTypes ID
public short Index { get; set; }
public byte Owner { get; set; }
+ /// Slot-reuse counter of the projectile being removed. A stale generation makes
+ /// clients treat the key as a fresh (type 0) projectile, which still clears the slot.
+ public int Generation { get; set; }
public override void Pack(Stream stream)
{
- stream.WriteInt16(Index);
+ // ProjectileKey: spawner:8 | index:10 | generation:14
+ int key = (Owner & 255) | (Index & 1023) << 8 | (Generation & 16383) << 18;
+ stream.WriteInt32(key);
stream.WriteSingle(-1);
stream.WriteSingle(-1);
stream.WriteSingle(0);
stream.WriteSingle(0);
- stream.WriteSingle(0);
- stream.WriteInt16(0);
- stream.WriteByte(Owner);
stream.WriteInt16(0);
- stream.WriteSingle(0);
- stream.WriteSingle(0);
+ stream.WriteByte(0);
}
}
}
\ No newline at end of file
diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs
index 977d2533b..6d4cdf7ed 100644
--- a/TShockAPI/Rest/RestManager.cs
+++ b/TShockAPI/Rest/RestManager.cs
@@ -976,9 +976,9 @@ private object PlayerReadV3(RestRequestArgs args)
return ret;
TSPlayer player = (TSPlayer)ret;
- var inventory = player.TPlayer.inventory.Where(p => p.active).ToList();
- var equipment = player.TPlayer.armor.Where(p => p.active).ToList();
- var dyes = player.TPlayer.dye.Where(p => p.active).ToList();
+ var inventory = player.TPlayer.inventory.Where(p => !p.IsAir).ToList();
+ var equipment = player.TPlayer.armor.Where(p => !p.IsAir).ToList();
+ var dyes = player.TPlayer.dye.Where(p => !p.IsAir).ToList();
return new RestObject()
{
{"nickname", player.Name},
@@ -1012,12 +1012,12 @@ private object PlayerReadV4(RestRequestArgs args)
object items = new
{
- inventory = player.TPlayer.inventory.Where(i => i.active).Select(item => (NetItem)item),
- equipment = player.TPlayer.armor.Where(i => i.active).Select(item => (NetItem)item),
- dyes = player.TPlayer.dye.Where(i => i.active).Select(item => (NetItem)item),
- piggy = player.TPlayer.bank.item.Where(i => i.active).Select(item => (NetItem)item),
- safe = player.TPlayer.bank2.item.Where(i => i.active).Select(item => (NetItem)item),
- forge = player.TPlayer.bank3.item.Where(i => i.active).Select(item => (NetItem)item)
+ inventory = player.TPlayer.inventory.Where(i => !i.IsAir).Select(item => (NetItem)item),
+ equipment = player.TPlayer.armor.Where(i => !i.IsAir).Select(item => (NetItem)item),
+ dyes = player.TPlayer.dye.Where(i => !i.IsAir).Select(item => (NetItem)item),
+ piggy = player.TPlayer.bank.item.Where(i => !i.IsAir).Select(item => (NetItem)item),
+ safe = player.TPlayer.bank2.item.Where(i => !i.IsAir).Select(item => (NetItem)item),
+ forge = player.TPlayer.bank3.item.Where(i => !i.IsAir).Select(item => (NetItem)item)
};
return new RestObject
diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs
index 78969f89b..d2c462f2b 100644
--- a/TShockAPI/TSPlayer.cs
+++ b/TShockAPI/TSPlayer.cs
@@ -1420,7 +1420,7 @@ public bool InventorySlotAvailable
{
for (int i = 0; i < 50; i++) //51 is trash can, 52-55 is coins, 56-59 is ammo
{
- if (TPlayer.inventory[i] == null || !TPlayer.inventory[i].active || TPlayer.inventory[i].Name == "")
+ if (TPlayer.inventory[i] == null || TPlayer.inventory[i].IsAir || TPlayer.inventory[i].Name == "")
{
flag = true;
break;
@@ -1984,7 +1984,7 @@ private Item GiveItemDirectly_FillEmptyInventorySlot(Item item, int slot)
private void GiveItemByDrop(int type, int stack, int prefix)
{
- int itemIndex = Item.NewItem(new EntitySource_DebugCommand(), (int)X, (int)Y, TPlayer.width, TPlayer.height, type, stack, true, prefix, true);
+ int itemIndex = Item.NewItem(new EntitySource_DebugCommand(), (int)X, (int)Y, TPlayer.width, TPlayer.height, type, stack, true, prefix, Terraria.NewItemOwnership.None);
Main.item[itemIndex].playerIndexTheItemIsReservedFor = this.Index;
SendData(PacketTypes.ItemDrop, "", itemIndex, 1);
SendData(PacketTypes.ItemOwner, null, itemIndex);
diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs
index 450c66e52..09e29a907 100644
--- a/TShockAPI/TShock.cs
+++ b/TShockAPI/TShock.cs
@@ -1,4 +1,4 @@
-/*
+/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
@@ -1066,7 +1066,7 @@ private void OnUpdate(EventArgs args)
// even if there are no clients connected
if (ServerApi.ForceUpdate)
{
- Netplay.HasClients = true;
+ Netplay.HasFullyConnectedClients = true;
}
if (Backups.IsBackupTime)
diff --git a/TShockAPI/Utils.cs b/TShockAPI/Utils.cs
index 8cc3be19f..f2410c541 100644
--- a/TShockAPI/Utils.cs
+++ b/TShockAPI/Utils.cs
@@ -779,11 +779,10 @@ public bool TryParseTime(string str, out ulong seconds)
/// projectile ID
public int SearchProjectile(short identity, int owner)
{
- for (int i = 0; i < Main.maxProjectiles; i++)
- {
- if (Main.projectile[i].identity == identity && Main.projectile[i].owner == owner)
- return i;
- }
+ // 1.4.5.7 replaced Projectile.identity with ProjectileKey, whose index component
+ // is the slot in Main.projectile, so the lookup is direct.
+ if (identity >= 0 && identity < Main.maxProjectiles && Main.projectile[identity].owner == owner)
+ return identity;
return 1000;
}
diff --git a/TShockLauncher/TShockLauncher.csproj b/TShockLauncher/TShockLauncher.csproj
index ae741c24f..b5d1e997f 100644
--- a/TShockLauncher/TShockLauncher.csproj
+++ b/TShockLauncher/TShockLauncher.csproj
@@ -38,7 +38,7 @@
-
+
PreserveNewest
true
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 000000000..4166b9413
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+