Introduce support for loadouts, and save current loadout index to SSC

We needed to modify `NetItem` to know that these new inventory now
exist.

`PlayerData` can now re/store these items, and properly sync them. It
also now knows of the player's currently selected index, and how to sync
it.
This commit is contained in:
James Puleo 2022-10-05 05:43:24 -04:00
parent 3163c88d4a
commit bfaa47ad1a
No known key found for this signature in database
GPG key ID: 3E16C7EFA34FB15D
5 changed files with 300 additions and 14 deletions

View file

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

View file

@ -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<int>("usingBiomeTorches");
playerData.happyFunTorchTime = reader.Get<int>("happyFunTorchTime");
playerData.unlockedBiomeTorches = reader.Get<int>("unlockedBiomeTorches");
playerData.currentLoadoutIndex = reader.Get<int>("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)

View file

@ -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<int, int> GetArmorSlotsForLoadoutIndex(int index)
{
return index switch
{
0 => NetItem.Loadout1Armor,
1 => NetItem.Loadout2Armor,
2 => NetItem.Loadout3Armor
};
}
Tuple<int, int> 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;
}

View file

@ -81,10 +81,22 @@ namespace TShockAPI
/// </summary>
public static readonly int TrashSlots = 1;
/// <summary>
/// The number of armor slots in a loadout.
/// </summary>
public static readonly int LoadoutArmorSlots = ArmorSlots;
/// <summary>
/// The number of dye slots in a loadout.
/// </summary>
public static readonly int LoadoutDyeSlots = DyeSlots;
/// <summary>
/// 180 - The inventory size (inventory, held item, armour, dies, coins, ammo, piggy, safe, and trash)
/// </summary>
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<int, int> InventoryIndex = new Tuple<int, int>(0, InventorySlots);
public static readonly Tuple<int, int> ArmorIndex = new Tuple<int, int>(InventoryIndex.Item2, InventoryIndex.Item2 + ArmorSlots);
@ -97,6 +109,15 @@ namespace TShockAPI
public static readonly Tuple<int, int> ForgeIndex = new Tuple<int, int>(TrashIndex.Item2, TrashIndex.Item2 + ForgeSlots);
public static readonly Tuple<int, int> VoidIndex = new Tuple<int, int>(ForgeIndex.Item2, ForgeIndex.Item2 + VoidSlots);
public static readonly Tuple<int, int> Loadout1Armor = new Tuple<int, int>(VoidIndex.Item2, VoidIndex.Item2 + LoadoutArmorSlots);
public static readonly Tuple<int, int> Loadout1Dye = new Tuple<int, int>(Loadout1Armor.Item2, Loadout1Armor.Item2 + LoadoutDyeSlots);
public static readonly Tuple<int, int> Loadout2Armor = new Tuple<int, int>(Loadout1Dye.Item2, Loadout1Dye.Item2 + LoadoutArmorSlots);
public static readonly Tuple<int, int> Loadout2Dye = new Tuple<int, int>(Loadout2Armor.Item2, Loadout2Armor.Item2 + LoadoutDyeSlots);
public static readonly Tuple<int, int> Loadout3Armor = new Tuple<int, int>(Loadout2Dye.Item2, Loadout2Dye.Item2 + LoadoutArmorSlots);
public static readonly Tuple<int, int> Loadout3Dye = new Tuple<int, int>(Loadout3Armor.Item2, Loadout3Armor.Item2 + LoadoutDyeSlots);
[JsonProperty("netID")]
private int _netId;
[JsonProperty("prefix")]

View file

@ -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,7 +404,80 @@ 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++;
}