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 @@ + + + + + + +