diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index dc5bb180..e15f14af 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -779,6 +779,33 @@ namespace TShockAPI return; } + // We need to emulate the checks done in Player.TrySwitchingLoadout, because otherwise the server is not allowed to sync the + // loadout index to the player, causing catastrophic desync. + // The player must not be dead, using an item, or CC'd to switch loadouts. + // FIXME: There is always the chance that in-between the time we check these requirements on the server, and the loadout sync + // packet reaches the client, that the client state has changed, causing the loadout sync to be rejected, even though + // we expected it to succeed. + + if (args.TPlayer.dead) + { + args.Player.SendErrorMessage(GetString("You cannot login whilst dead.")); + return; + } + + // FIXME: This check is not correct -- even though we reject PlayerAnimation whilst disabled, we don't re-sync it to the client, + // meaning these will still be set on the client, and they WILL reject the loadout sync. + if (args.TPlayer.itemTime > 0 || args.TPlayer.itemAnimation > 0) + { + args.Player.SendErrorMessage(GetString("You cannot login whilst using an item.")); + return; + } + + if (args.TPlayer.CCed) + { + args.Player.SendErrorMessage(GetString("You cannot login whilst crowd controlled.")); + return; + } + UserAccount account = TShock.UserAccounts.GetUserAccountByName(args.Player.Name); string password = ""; bool usingUUID = false; diff --git a/TShockAPI/DB/CharacterManager.cs b/TShockAPI/DB/CharacterManager.cs index 4e2e07d1..4b43dcc1 100644 --- a/TShockAPI/DB/CharacterManager.cs +++ b/TShockAPI/DB/CharacterManager.cs @@ -58,7 +58,8 @@ namespace TShockAPI.DB new SqlColumn("questsCompleted", MySqlDbType.Int32), new SqlColumn("usingBiomeTorches", MySqlDbType.Int32), new SqlColumn("happyFunTorchTime", MySqlDbType.Int32), - new SqlColumn("unlockedBiomeTorches", MySqlDbType.Int32) + new SqlColumn("unlockedBiomeTorches", MySqlDbType.Int32), + new SqlColumn("currentLoadoutIndex", MySqlDbType.Int32) ); var creator = new SqlTableCreator(db, db.GetSqlType() == SqlType.Sqlite @@ -114,6 +115,7 @@ namespace TShockAPI.DB playerData.usingBiomeTorches = reader.Get("usingBiomeTorches"); playerData.happyFunTorchTime = reader.Get("happyFunTorchTime"); playerData.unlockedBiomeTorches = reader.Get("unlockedBiomeTorches"); + playerData.currentLoadoutIndex = reader.Get("currentLoadoutIndex"); return playerData; } } @@ -180,8 +182,8 @@ namespace TShockAPI.DB try { database.Query( - "INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted, usingBiomeTorches, happyFunTorchTime, unlockedBiomeTorches) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23);", - player.Account.ID, playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, String.Join("~", playerData.inventory), playerData.extraSlot, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.skinVariant, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor),TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisibleAccessory), TShock.Utils.EncodeColor(player.TPlayer.skinColor),TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.UsingBiomeTorches ? 1 : 0, player.TPlayer.happyFunTorchTime ? 1 : 0, player.TPlayer.unlockedBiomeTorches ? 1 : 0); + "INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted, usingBiomeTorches, happyFunTorchTime, unlockedBiomeTorches, currentLoadoutIndex) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24);", + player.Account.ID, playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, String.Join("~", playerData.inventory), playerData.extraSlot, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.skinVariant, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor),TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisibleAccessory), TShock.Utils.EncodeColor(player.TPlayer.skinColor),TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.UsingBiomeTorches ? 1 : 0, player.TPlayer.happyFunTorchTime ? 1 : 0, player.TPlayer.unlockedBiomeTorches ? 1 : 0, player.TPlayer.CurrentLoadoutIndex); return true; } catch (Exception ex) @@ -194,8 +196,8 @@ namespace TShockAPI.DB try { database.Query( - "UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20, usingBiomeTorches = @21, happyFunTorchTime = @22, unlockedBiomeTorches = @23 WHERE Account = @5;", - playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, String.Join("~", playerData.inventory), player.Account.ID, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor), TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisibleAccessory), TShock.Utils.EncodeColor(player.TPlayer.skinColor), TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.skinVariant, player.TPlayer.extraAccessory ? 1 : 0, player.TPlayer.UsingBiomeTorches ? 1 : 0, player.TPlayer.happyFunTorchTime ? 1 : 0, player.TPlayer.unlockedBiomeTorches ? 1 : 0); + "UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20, usingBiomeTorches = @21, happyFunTorchTime = @22, unlockedBiomeTorches = @23, currentLoadoutIndex = @24 WHERE Account = @5;", + playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, String.Join("~", playerData.inventory), player.Account.ID, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor), TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisibleAccessory), TShock.Utils.EncodeColor(player.TPlayer.skinColor), TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.skinVariant, player.TPlayer.extraAccessory ? 1 : 0, player.TPlayer.UsingBiomeTorches ? 1 : 0, player.TPlayer.happyFunTorchTime ? 1 : 0, player.TPlayer.unlockedBiomeTorches ? 1 : 0, player.TPlayer.CurrentLoadoutIndex); return true; } catch (Exception ex) @@ -250,7 +252,7 @@ namespace TShockAPI.DB try { database.Query( - "INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted, usingBiomeTorches, happyFunTorchTime, unlockedBiomeTorches) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23);", + "INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted, usingBiomeTorches, happyFunTorchTime, unlockedBiomeTorches, currentLoadoutIndex) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24);", player.Account.ID, playerData.health, playerData.maxHealth, @@ -274,7 +276,8 @@ namespace TShockAPI.DB playerData.questsCompleted, playerData.usingBiomeTorches, playerData.happyFunTorchTime, - playerData.unlockedBiomeTorches); + playerData.unlockedBiomeTorches, + playerData.currentLoadoutIndex); return true; } catch (Exception ex) @@ -287,7 +290,7 @@ namespace TShockAPI.DB try { database.Query( - "UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20, usingBiomeTorches = @21, happyFunTorchTime = @22, unlockedBiomeTorches = @23 WHERE Account = @5;", + "UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20, usingBiomeTorches = @21, happyFunTorchTime = @22, unlockedBiomeTorches = @23, currentLoadoutIndex = @24 WHERE Account = @5;", playerData.health, playerData.maxHealth, playerData.mana, @@ -311,7 +314,8 @@ namespace TShockAPI.DB playerData.extraSlot ?? 0, playerData.usingBiomeTorches, playerData.happyFunTorchTime, - playerData.unlockedBiomeTorches); + playerData.unlockedBiomeTorches, + playerData.currentLoadoutIndex); return true; } catch (Exception ex) diff --git a/TShockAPI/GetDataHandlers.cs b/TShockAPI/GetDataHandlers.cs index 0929d928..22feee47 100644 --- a/TShockAPI/GetDataHandlers.cs +++ b/TShockAPI/GetDataHandlers.cs @@ -4350,14 +4350,76 @@ namespace TShockAPI if (loadoutIndex == args.TPlayer.CurrentLoadoutIndex) return false; - if (args.Player.IsBeingDisabled()) + if (loadoutIndex >= args.TPlayer.Loadouts.Length) { - TShock.Log.ConsoleDebug("GetDataHandlers / HandleSyncLoadout rejected loadout index sync {0}", args.Player.Name); + TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSyncLoadout rejected loadout index sync out of bounds {0}", + args.Player.Name)); NetMessage.SendData((int)PacketTypes.SyncLoadout, number: args.Player.Index, number2: args.TPlayer.CurrentLoadoutIndex); return true; } + if (args.Player.IsBeingDisabled()) + { + TShock.Log.ConsoleDebug(GetString("GetDataHandlers / HandleSyncLoadout rejected loadout index sync {0}", args.Player.Name)); + NetMessage.SendData((int)PacketTypes.SyncLoadout, number: args.Player.Index, number2: args.TPlayer.CurrentLoadoutIndex); + + return true; + } + + // The client does not sync slot changes when changing loadouts, it only tells the server the loadout index changed, + // and the server will replicate the changes the client did. This means that PlayerData.StoreSlot is never called, so we need to + // swap around the PlayerData items ourself. + + Tuple GetArmorSlotsForLoadoutIndex(int index) + { + return index switch + { + 0 => NetItem.Loadout1Armor, + 1 => NetItem.Loadout2Armor, + 2 => NetItem.Loadout3Armor + }; + } + + Tuple GetDyeSlotsForLoadoutIndex(int index) + { + return index switch + { + 0 => NetItem.Loadout1Dye, + 1 => NetItem.Loadout2Dye, + 2 => NetItem.Loadout3Dye + }; + } + + var (currentLoadoutArmorSlotStartIndex, _) = GetArmorSlotsForLoadoutIndex(args.TPlayer.CurrentLoadoutIndex); + var (currentLoadoutDyeSlotStartIndex, _) = GetDyeSlotsForLoadoutIndex(args.TPlayer.CurrentLoadoutIndex); + + var (switchedLoadoutArmorSlotStartIndex, _) = GetArmorSlotsForLoadoutIndex(loadoutIndex); + var (switchedLoadoutDyeSlotStartIndex, _) = GetDyeSlotsForLoadoutIndex(loadoutIndex); + + // Emulate what is seen in Player.TrySwitchingLoadout: + // - Swap the current loadout items with the player's equipment + // - Swap the switching loadout items with the player's equipment + + // At the end of all of this: + // - The current loadout will contain the player's original equipment + // - The switched loadout will contain the current loadout's items + // - The player's equipment will contain the switched loadout's item + + for (var i = 0; i < NetItem.LoadoutArmorSlots; i++) + Terraria.Utils.Swap(ref args.Player.PlayerData.inventory[currentLoadoutArmorSlotStartIndex + i], + ref args.Player.PlayerData.inventory[NetItem.ArmorIndex.Item1 + i]); + for (var i = 0; i < NetItem.LoadoutDyeSlots; i++) + Terraria.Utils.Swap(ref args.Player.PlayerData.inventory[currentLoadoutDyeSlotStartIndex + i], + ref args.Player.PlayerData.inventory[NetItem.DyeIndex.Item1 + i]); + + for (var i = 0; i < NetItem.LoadoutArmorSlots; i++) + Terraria.Utils.Swap(ref args.Player.PlayerData.inventory[switchedLoadoutArmorSlotStartIndex + i], + ref args.Player.PlayerData.inventory[NetItem.ArmorIndex.Item1 + i]); + for (var i = 0; i < NetItem.LoadoutDyeSlots; i++) + Terraria.Utils.Swap(ref args.Player.PlayerData.inventory[switchedLoadoutDyeSlotStartIndex + i], + ref args.Player.PlayerData.inventory[NetItem.DyeIndex.Item1 + i]); + return false; } diff --git a/TShockAPI/NetItem.cs b/TShockAPI/NetItem.cs index 13ac0989..278fda1c 100644 --- a/TShockAPI/NetItem.cs +++ b/TShockAPI/NetItem.cs @@ -81,10 +81,22 @@ namespace TShockAPI /// public static readonly int TrashSlots = 1; + /// + /// The number of armor slots in a loadout. + /// + public static readonly int LoadoutArmorSlots = ArmorSlots; + + /// + /// The number of dye slots in a loadout. + /// + public static readonly int LoadoutDyeSlots = DyeSlots; + /// /// 180 - The inventory size (inventory, held item, armour, dies, coins, ammo, piggy, safe, and trash) /// - public static readonly int MaxInventory = InventorySlots + ArmorSlots + DyeSlots + MiscEquipSlots + MiscDyeSlots + PiggySlots + SafeSlots + ForgeSlots + VoidSlots + 1; + public static readonly int MaxInventory = InventorySlots + ArmorSlots + DyeSlots + MiscEquipSlots + MiscDyeSlots + PiggySlots + + SafeSlots + ForgeSlots + VoidSlots + TrashSlots + (LoadoutArmorSlots * 3) + + (LoadoutDyeSlots * 3); public static readonly Tuple InventoryIndex = new Tuple(0, InventorySlots); public static readonly Tuple ArmorIndex = new Tuple(InventoryIndex.Item2, InventoryIndex.Item2 + ArmorSlots); @@ -97,6 +109,15 @@ namespace TShockAPI public static readonly Tuple ForgeIndex = new Tuple(TrashIndex.Item2, TrashIndex.Item2 + ForgeSlots); public static readonly Tuple VoidIndex = new Tuple(ForgeIndex.Item2, ForgeIndex.Item2 + VoidSlots); + public static readonly Tuple Loadout1Armor = new Tuple(VoidIndex.Item2, VoidIndex.Item2 + LoadoutArmorSlots); + public static readonly Tuple Loadout1Dye = new Tuple(Loadout1Armor.Item2, Loadout1Armor.Item2 + LoadoutDyeSlots); + + public static readonly Tuple Loadout2Armor = new Tuple(Loadout1Dye.Item2, Loadout1Dye.Item2 + LoadoutArmorSlots); + public static readonly Tuple Loadout2Dye = new Tuple(Loadout2Armor.Item2, Loadout2Armor.Item2 + LoadoutDyeSlots); + + public static readonly Tuple Loadout3Armor = new Tuple(Loadout2Dye.Item2, Loadout2Dye.Item2 + LoadoutArmorSlots); + public static readonly Tuple Loadout3Dye = new Tuple(Loadout3Armor.Item2, Loadout3Armor.Item2 + LoadoutDyeSlots); + [JsonProperty("netID")] private int _netId; [JsonProperty("prefix")] diff --git a/TShockAPI/PlayerData.cs b/TShockAPI/PlayerData.cs index 5e24570b..eec2b60b 100644 --- a/TShockAPI/PlayerData.cs +++ b/TShockAPI/PlayerData.cs @@ -52,6 +52,7 @@ namespace TShockAPI public int usingBiomeTorches; public int happyFunTorchTime; public int unlockedBiomeTorches; + public int currentLoadoutIndex; public PlayerData(TSPlayer player) { @@ -120,6 +121,7 @@ namespace TShockAPI this.usingBiomeTorches = player.TPlayer.UsingBiomeTorches ? 1 : 0; this.happyFunTorchTime = player.TPlayer.happyFunTorchTime ? 1 : 0; this.unlockedBiomeTorches = player.TPlayer.unlockedBiomeTorches ? 1 : 0; + this.currentLoadoutIndex = player.TPlayer.CurrentLoadoutIndex; Item[] inventory = player.TPlayer.inventory; Item[] armor = player.TPlayer.armor; @@ -131,6 +133,12 @@ namespace TShockAPI Item[] forge = player.TPlayer.bank3.item; Item[] voidVault = player.TPlayer.bank4.item; Item trash = player.TPlayer.trashItem; + Item[] loadout1Armor = player.TPlayer.Loadouts[0].Armor; + Item[] loadout1Dye = player.TPlayer.Loadouts[0].Dye; + Item[] loadout2Armor = player.TPlayer.Loadouts[1].Armor; + Item[] loadout2Dye = player.TPlayer.Loadouts[1].Dye; + Item[] loadout3Armor = player.TPlayer.Loadouts[2].Armor; + Item[] loadout3Dye = player.TPlayer.Loadouts[2].Dye; for (int i = 0; i < NetItem.MaxInventory; i++) { @@ -186,12 +194,42 @@ namespace TShockAPI var index = i - NetItem.ForgeIndex.Item1; this.inventory[i] = (NetItem)forge[index]; } - else + else if(i < NetItem.VoidIndex.Item2) { //220 var index = i - NetItem.VoidIndex.Item1; this.inventory[i] = (NetItem)voidVault[index]; } + else if(i < NetItem.Loadout1Armor.Item2) + { + var index = i - NetItem.Loadout1Armor.Item1; + this.inventory[i] = (NetItem)loadout1Armor[index]; + } + else if(i < NetItem.Loadout1Dye.Item2) + { + var index = i - NetItem.Loadout1Dye.Item1; + this.inventory[i] = (NetItem)loadout1Dye[index]; + } + else if(i < NetItem.Loadout2Armor.Item2) + { + var index = i - NetItem.Loadout2Armor.Item1; + this.inventory[i] = (NetItem)loadout2Armor[index]; + } + else if(i < NetItem.Loadout2Dye.Item2) + { + var index = i - NetItem.Loadout2Dye.Item1; + this.inventory[i] = (NetItem)loadout2Dye[index]; + } + else if(i < NetItem.Loadout3Armor.Item2) + { + var index = i - NetItem.Loadout3Armor.Item1; + this.inventory[i] = (NetItem)loadout3Armor[index]; + } + else if(i < NetItem.Loadout3Dye.Item2) + { + var index = i - NetItem.Loadout3Dye.Item1; + this.inventory[i] = (NetItem)loadout3Dye[index]; + } } } @@ -217,6 +255,7 @@ namespace TShockAPI player.TPlayer.UsingBiomeTorches = this.usingBiomeTorches == 1; player.TPlayer.happyFunTorchTime = this.happyFunTorchTime == 1; player.TPlayer.unlockedBiomeTorches = this.unlockedBiomeTorches == 1; + player.TPlayer.CurrentLoadoutIndex = this.currentLoadoutIndex; if (extraSlot != null) player.TPlayer.extraAccessory = extraSlot.Value == 1 ? true : false; @@ -353,7 +392,7 @@ namespace TShockAPI player.TPlayer.bank3.item[index].Prefix((byte)this.inventory[i].PrefixId); } } - else + else if (i < NetItem.VoidIndex.Item2) { //260 var index = i - NetItem.VoidIndex.Item1; @@ -365,8 +404,81 @@ namespace TShockAPI player.TPlayer.bank4.item[index].Prefix((byte)this.inventory[i].PrefixId); } } + else if (i < NetItem.Loadout1Armor.Item2) + { + var index = i - NetItem.Loadout1Armor.Item1; + player.TPlayer.Loadouts[0].Armor[index].netDefaults(this.inventory[i].NetId); + + if (player.TPlayer.Loadouts[0].Armor[index].netID != 0) + { + player.TPlayer.Loadouts[0].Armor[index].stack = this.inventory[i].Stack; + player.TPlayer.Loadouts[0].Armor[index].Prefix((byte)this.inventory[i].PrefixId); + } + } + else if (i < NetItem.Loadout1Dye.Item2) + { + var index = i - NetItem.Loadout1Dye.Item1; + player.TPlayer.Loadouts[0].Dye[index].netDefaults(this.inventory[i].NetId); + + if (player.TPlayer.Loadouts[0].Dye[index].netID != 0) + { + player.TPlayer.Loadouts[0].Dye[index].stack = this.inventory[i].Stack; + player.TPlayer.Loadouts[0].Dye[index].Prefix((byte)this.inventory[i].PrefixId); + } + } + else if (i < NetItem.Loadout2Armor.Item2) + { + var index = i - NetItem.Loadout2Armor.Item1; + player.TPlayer.Loadouts[1].Armor[index].netDefaults(this.inventory[i].NetId); + + if (player.TPlayer.Loadouts[1].Armor[index].netID != 0) + { + player.TPlayer.Loadouts[1].Armor[index].stack = this.inventory[i].Stack; + player.TPlayer.Loadouts[1].Armor[index].Prefix((byte)this.inventory[i].PrefixId); + } + } + else if (i < NetItem.Loadout2Dye.Item2) + { + var index = i - NetItem.Loadout2Dye.Item1; + player.TPlayer.Loadouts[1].Dye[index].netDefaults(this.inventory[i].NetId); + + if (player.TPlayer.Loadouts[1].Dye[index].netID != 0) + { + player.TPlayer.Loadouts[1].Dye[index].stack = this.inventory[i].Stack; + player.TPlayer.Loadouts[1].Dye[index].Prefix((byte)this.inventory[i].PrefixId); + } + } + else if (i < NetItem.Loadout3Armor.Item2) + { + var index = i - NetItem.Loadout3Armor.Item1; + player.TPlayer.Loadouts[2].Armor[index].netDefaults(this.inventory[i].NetId); + + if (player.TPlayer.Loadouts[2].Armor[index].netID != 0) + { + player.TPlayer.Loadouts[2].Armor[index].stack = this.inventory[i].Stack; + player.TPlayer.Loadouts[2].Armor[index].Prefix((byte)this.inventory[i].PrefixId); + } + } + else if (i < NetItem.Loadout3Dye.Item2) + { + var index = i - NetItem.Loadout3Dye.Item1; + player.TPlayer.Loadouts[2].Dye[index].netDefaults(this.inventory[i].NetId); + + if (player.TPlayer.Loadouts[2].Dye[index].netID != 0) + { + player.TPlayer.Loadouts[2].Dye[index].stack = this.inventory[i].Stack; + player.TPlayer.Loadouts[2].Dye[index].Prefix((byte)this.inventory[i].PrefixId); + } + } } + // Just like in MessageBuffer when the client receives a ContinueConnecting, let's sync the CurrentLoadoutIndex _before_ any of + // the items. + // This is sent to everyone BUT this player, and then ONLY this player. When using UUID login, it is too soon for the server to + // broadcast packets to this client. + NetMessage.SendData((int)PacketTypes.SyncLoadout, remoteClient: player.Index, number: player.Index, number2: player.TPlayer.CurrentLoadoutIndex); + NetMessage.SendData((int)PacketTypes.SyncLoadout, ignoreClient: player.Index, number: player.Index, number2: player.TPlayer.CurrentLoadoutIndex); + float slot = 0f; for (int k = 0; k < NetItem.InventorySlots; k++) { @@ -414,6 +526,36 @@ namespace TShockAPI NetMessage.SendData(5, -1, -1, NetworkText.FromLiteral(Main.player[player.Index].bank4.item[k].Name), player.Index, slot, (float)Main.player[player.Index].bank4.item[k].prefix); slot++; } + for (int k = 0; k < NetItem.LoadoutArmorSlots; k++) + { + NetMessage.SendData(5, -1, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[0].Armor[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[0].Armor[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutDyeSlots; k++) + { + NetMessage.SendData(5, -1, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[0].Dye[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[0].Dye[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutArmorSlots; k++) + { + NetMessage.SendData(5, -1, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[1].Armor[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[1].Armor[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutDyeSlots; k++) + { + NetMessage.SendData(5, -1, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[1].Dye[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[1].Dye[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutArmorSlots; k++) + { + NetMessage.SendData(5, -1, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[2].Armor[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[2].Armor[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutDyeSlots; k++) + { + NetMessage.SendData(5, -1, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[1].Dye[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[2].Dye[k].prefix); + slot++; + } NetMessage.SendData(4, -1, -1, NetworkText.FromLiteral(player.Name), player.Index, 0f, 0f, 0f, 0); @@ -467,6 +609,36 @@ namespace TShockAPI NetMessage.SendData(5, player.Index, -1, NetworkText.FromLiteral(Main.player[player.Index].bank4.item[k].Name), player.Index, slot, (float)Main.player[player.Index].bank4.item[k].prefix); slot++; } + for (int k = 0; k < NetItem.LoadoutArmorSlots; k++) + { + NetMessage.SendData(5, player.Index, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[0].Armor[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[0].Armor[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutDyeSlots; k++) + { + NetMessage.SendData(5, player.Index, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[0].Dye[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[0].Dye[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutArmorSlots; k++) + { + NetMessage.SendData(5, player.Index, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[1].Armor[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[1].Armor[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutDyeSlots; k++) + { + NetMessage.SendData(5, player.Index, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[1].Dye[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[1].Dye[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutArmorSlots; k++) + { + NetMessage.SendData(5, player.Index, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[2].Armor[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[2].Armor[k].prefix); + slot++; + } + for (int k = 0; k < NetItem.LoadoutDyeSlots; k++) + { + NetMessage.SendData(5, player.Index, -1, NetworkText.FromLiteral(Main.player[player.Index].Loadouts[2].Dye[k].Name), player.Index, slot, (float)Main.player[player.Index].Loadouts[2].Dye[k].prefix); + slot++; + }