From 81d82cd7a86555d948468e8e40001413cff7954f Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Tue, 5 Sep 2017 14:44:06 -0400 Subject: [PATCH 1/6] Rewrote the inventory code, splitting the packet updates from the list updates. --- FFXIVClassic Map Server/Database.cs | 57 ++- .../actors/chara/player/Equipment.cs | 6 +- .../actors/chara/player/Inventory.cs | 420 +++++++----------- .../actors/chara/player/Player.cs | 12 +- .../dataobjects/InventoryItem.cs | 6 +- 5 files changed, 224 insertions(+), 277 deletions(-) diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index 9642baf3..c50510e1 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -1265,7 +1265,7 @@ namespace FFXIVClassic_Map_Server byte materia4 = reader.GetByte(11); byte materia5 = reader.GetByte(12); - items.Add(new InventoryItem(uniqueId, itemId, quantity, slot, itemType, qualityNumber, durability, spiritBind, materia1, materia2, materia3, materia4, materia5)); + items.Add(new InventoryItem(uniqueId, itemId, quantity, itemType, qualityNumber, durability, spiritBind, materia1, materia2, materia3, materia4, materia5)); } } } @@ -1282,7 +1282,7 @@ namespace FFXIVClassic_Map_Server return items; } - public static InventoryItem AddItem(Player player, uint itemId, int quantity, byte quality, byte itemType, int durability, ushort type) + public static InventoryItem CreateItem(uint itemId, int quantity, byte quality, byte itemType, int durability) { InventoryItem insertedItem = null; @@ -1303,27 +1303,52 @@ namespace FFXIVClassic_Map_Server MySqlCommand cmd = new MySqlCommand(query, conn); - string query2 = @" - INSERT INTO characters_inventory - (characterId, slot, inventoryType, serverItemId, quantity) - SELECT @charId, IFNULL(MAX(SLOT)+1, 0), @inventoryType, LAST_INSERT_ID(), @quantity FROM characters_inventory WHERE characterId = @charId AND inventoryType = @inventoryType; - "; - - MySqlCommand cmd2 = new MySqlCommand(query2, conn); - cmd.Parameters.AddWithValue("@itemId", itemId); cmd.Parameters.AddWithValue("@quality", quality); cmd.Parameters.AddWithValue("@itemType", itemType); cmd.Parameters.AddWithValue("@durability", durability); - cmd2.Parameters.AddWithValue("@charId", player.actorId); - cmd2.Parameters.AddWithValue("@inventoryType", type); - cmd2.Parameters.AddWithValue("@quantity", quantity); - cmd.ExecuteNonQuery(); - cmd2.ExecuteNonQuery(); - insertedItem = new InventoryItem((uint)cmd.LastInsertedId, itemId, quantity, (ushort)player.GetInventory(type).GetNextEmptySlot(), itemType, quality, durability, 0, 0, 0, 0, 0, 0); + insertedItem = new InventoryItem((uint)cmd.LastInsertedId, itemId, quantity, itemType, quality, durability, 0, 0, 0, 0, 0, 0); + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + + return insertedItem; + } + + public static InventoryItem AddItem(Player player, InventoryItem addedItem, uint type) + { + InventoryItem insertedItem = null; + + using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + + string query = @" + INSERT INTO characters_inventory + (characterId, slot, inventoryType, serverItemId, quantity) + SELECT @charId, IFNULL(MAX(SLOT)+1, 0), @inventoryType, @uid, @quantity FROM characters_inventory WHERE characterId = @charId AND inventoryType = @inventoryType; + "; + + MySqlCommand cmd = new MySqlCommand(query, conn); + + cmd.Parameters.AddWithValue("@uid", addedItem.uniqueId); + cmd.Parameters.AddWithValue("@charId", player.actorId); + cmd.Parameters.AddWithValue("@inventoryType", type); + cmd.Parameters.AddWithValue("@quantity", insertedItem.quantity); + + cmd.ExecuteNonQuery(); } catch (MySqlException e) { diff --git a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs index 702af284..9c311e12 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs @@ -106,13 +106,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.player for (int i = 0; i < slots.Length; i++) { - InventoryItem item = normalInventory.GetItemBySlot(itemSlots[i]); + InventoryItem item = normalInventory.GetItemAtSlot(itemSlots[i]); if (item == null) continue; Database.EquipItem(owner, slots[i], item.uniqueId); - list[slots[i]] = normalInventory.GetItemBySlot(itemSlots[i]); + list[slots[i]] = normalInventory.GetItemAtSlot(itemSlots[i]); } owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); @@ -133,7 +133,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player public void Equip(ushort slot, ushort invSlot) { - InventoryItem item = normalInventory.GetItemBySlot(invSlot); + InventoryItem item = normalInventory.GetItemAtSlot(invSlot); if (item == null) return; diff --git a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs index bcd82d56..f9d8493f 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs @@ -20,14 +20,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.player public const ushort CURRENCY = 0x0063; //Max 0x140 public const ushort KEYITEMS = 0x0064; //Max 0x500 public const ushort EQUIPMENT = 0x00FE; //Max 0x23 - public const ushort EQUIPMENT_OTHERPLAYER = 0x00F9; //Max 0x23 + public const ushort EQUIPMENT_OTHERPLAYER = 0x00F9; //Max 0x23 + + private int endOfListIndex = 0; - private Player owner; + private Character owner; + private List viewer; private ushort inventoryCapacity; private ushort inventoryCode; - private List list; - - public Inventory(Player ownerPlayer, ushort capacity, ushort code) + private InventoryItem[] list; + private InventoryItem[] lastList; + private bool[] isDirty; + + public Inventory(Character ownerPlayer, ushort capacity, ushort code) { owner = ownerPlayer; inventoryCapacity = capacity; @@ -37,12 +42,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.player #region Inventory Management public void InitList(List itemsFromDB) { - list = itemsFromDB; + int i = 0; + foreach (InventoryItem item in itemsFromDB) + list[i++] = item; } - public InventoryItem GetItemBySlot(ushort slot) + public InventoryItem GetItemAtSlot(ushort slot) { - if (slot < list.Count) + if (slot < list.Length) return list[slot]; else return null; @@ -66,28 +73,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player return item; } return null; - } - - public void RefreshItem(InventoryItem item) - { - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - SendInventoryPackets(item); - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - public void RefreshItem(params InventoryItem[] items) - { - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - SendInventoryPackets(items.ToList()); - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - public void RefreshItem(List items) - { - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - SendInventoryPackets(items); - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } + } public void AddItem(uint itemId) { @@ -105,9 +91,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player return false; ItemData gItem = Server.GetItemGamedata(itemId); - List slotsToUpdate = new List(); - List addItemPackets = new List(); - + if (gItem == null) { Program.Log.Error("Inventory.AddItem: unable to find item %u", itemId); @@ -115,15 +99,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.player } //Check if item id exists - int quantityCount = quantity; - for (int i = 0; i < list.Count; i++) + int quantityCount = quantity; + for (int i = 0; i < endOfListIndex; i++) { - InventoryItem item = list[i]; + InventoryItem item = list[i]; + + if (item == null) + throw new Exception("Item slot was null!!!"); + if (item.itemId == itemId && item.quantity < gItem.maxStack) { - slotsToUpdate.Add(item.slot); int oldQuantity = item.quantity; - item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack); + item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack); + isDirty[i] = true; quantityCount -= (gItem.maxStack - oldQuantity); if (quantityCount <= 0) break; @@ -136,68 +124,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.player //If Inventory is full //if (quantityCount > 0 && isInventoryFull()) - // return ITEMERROR_FULL; - - //Update lists and db - owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - - //These had their quantities Changed - foreach (ushort slot in slotsToUpdate) - { - Database.SetQuantity(owner, slot, inventoryCode, list[slot].quantity); - - if (inventoryCode != CURRENCY && inventoryCode != KEYITEMS) - SendInventoryPackets(list[slot]); - } + // return ITEMERROR_FULL; //New item that spilled over while (quantityCount > 0) { - InventoryItem addedItem = Database.AddItem(owner, itemId, Math.Min(quantityCount, gItem.maxStack), quality, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability, inventoryCode); - - - list.Add(addedItem); - - if (inventoryCode != CURRENCY && inventoryCode != KEYITEMS) - SendInventoryPackets(addedItem); - + InventoryItem addedItem = Database.CreateItem(itemId, Math.Min(quantityCount, gItem.maxStack), quality, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability); + isDirty[endOfListIndex] = true; + list[endOfListIndex++] = addedItem; quantityCount -= gItem.maxStack; } - if (inventoryCode == CURRENCY || inventoryCode == KEYITEMS) - SendFullInventory(); - - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); return true; } - public void AddItem(uint[] itemId) - { - if (!IsSpaceForAdd(itemId[0], itemId.Length)) - return; - - //Update lists and db - owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - - int startPos = list.Count; - - //New item that spilled over - for (int i = 0; i < itemId.Length; i++) - { - ItemData gItem = Server.GetItemGamedata(itemId[i]); - InventoryItem addedItem = Database.AddItem(owner, itemId[i], 1, (byte)1, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability, inventoryCode); - list.Add(addedItem); - } - - SendInventoryPackets(startPos); - - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); - } - public void RemoveItem(uint itemId, int quantity) { if (!HasItem(itemId, quantity)) @@ -211,23 +151,24 @@ namespace FFXIVClassic_Map_Server.actors.chara.player //Remove as we go along int quantityCount = quantity; ushort lowestSlot = 0; - for (int i = list.Count - 1; i >= 0; i--) + for (int i = endOfListIndex - 1; i >= 0; i--) { - InventoryItem item = list[i]; + InventoryItem item = list[i]; + + if (item == null) + throw new Exception("Item slot was null!!!"); + if (item.itemId == itemId) { int oldQuantity = item.quantity; //Stack nomnomed - if (item.quantity - quantityCount <= 0) - { - itemsToRemove.Add(item); - slotsToRemove.Add(item.slot); - } - else - { - slotsToUpdate.Add(item.slot); - item.quantity -= quantityCount; //Stack reduced - } + if (item.quantity - quantityCount <= 0) + list[i] = null; + //Stack reduced + else + item.quantity -= quantityCount; + + isDirty[i] = true; quantityCount -= oldQuantity; lowestSlot = item.slot; @@ -235,43 +176,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.player if (quantityCount <= 0) break; } - } + } + + doRealign(); - for (int i = 0; i < slotsToUpdate.Count; i++) - { - Database.SetQuantity(owner, slotsToUpdate[i], inventoryCode, list[slotsToUpdate[i]].quantity); - } - - int oldListSize = list.Count; - for (int i = 0; i < itemsToRemove.Count; i++) - { - Database.RemoveItem(owner, itemsToRemove[i].uniqueId, inventoryCode); - list.Remove(itemsToRemove[i]); - } - - //Realign slots - for (int i = lowestSlot; i < list.Count; i++) - list[i].slot = (ushort)i; - - //Added tail end items that need to be cleared for slot realignment - for (int i = oldListSize-1; i >= oldListSize - itemsToRemove.Count; i--) - { - if (!slotsToRemove.Contains((ushort)i)) - slotsToRemove.Add((ushort)i); - } - - owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - - SendInventoryPackets(lowestSlot); - SendInventoryRemovePackets(slotsToRemove); - - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - - if (inventoryCode == NORMAL) - owner.GetEquipment().SendFullEquipment(false); - - owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); } public void RemoveItemByUniqueId(ulong itemDBId) @@ -291,58 +199,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.player if (toDelete == null) return; - int oldListSize = list.Count; - list.RemoveAt(slot); - Database.RemoveItem(owner, itemDBId, inventoryCode); - - //Realign slots - for (int i = slot; i < list.Count; i++) - list[i].slot = (ushort)i; - - owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - - SendInventoryPackets(slot); - SendInventoryRemovePackets(slot); - if (slot != oldListSize - 1) - SendInventoryRemovePackets((ushort)(oldListSize - 1)); - - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - - if (inventoryCode == NORMAL) - owner.GetEquipment().SendFullEquipment(false); - - owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); - + list[slot] = null; + isDirty[slot] = true; + doRealign(); } public void RemoveItemAtSlot(ushort slot) { - if (slot >= list.Count) + if (slot >= endOfListIndex) return; - - int oldListSize = list.Count; - list.RemoveAt((int)slot); - Database.RemoveItem(owner, slot, inventoryCode); - - //Realign slots - for (int i = slot; i < list.Count; i++) - list[i].slot = (ushort)i; - - owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - - SendInventoryPackets(slot); - SendInventoryRemovePackets(slot); - if (slot != oldListSize - 1) - SendInventoryRemovePackets((ushort)(oldListSize - 1)); - - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - - if (inventoryCode == NORMAL) - owner.GetEquipment().SendFullEquipment(false); - - owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); + + list[slot] = null; + isDirty[slot] = true; + doRealign(); } public void ChangeDurability(uint slot, uint durabilityChange) @@ -362,90 +231,92 @@ namespace FFXIVClassic_Map_Server.actors.chara.player #endregion #region Packet Functions - public void SendFullInventory() - { - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - SendInventoryPackets(0); - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - private void SendInventoryPackets(InventoryItem item) - { - owner.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, item)); - } - - private void SendInventoryPackets(List items) + public void SendFullInventory(Player player) + { + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); + SendInventoryPackets(player, 0); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + private void SendInventoryPackets(Player player, InventoryItem item) + { + player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, item)); + } + + private void SendInventoryPackets(Player player, List items) { int currentIndex = 0; while (true) { - if (items.Count - currentIndex >= 64) - owner.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, items, ref currentIndex)); - else if (items.Count - currentIndex >= 32) - owner.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, items, ref currentIndex)); - else if (items.Count - currentIndex >= 16) - owner.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, items, ref currentIndex)); - else if (items.Count - currentIndex > 1) - owner.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, items, ref currentIndex)); + if (items.Count - currentIndex >= 64) + player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, items, ref currentIndex)); + else if (items.Count - currentIndex >= 32) + player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, items, ref currentIndex)); + else if (items.Count - currentIndex >= 16) + player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, items, ref currentIndex)); + else if (items.Count - currentIndex > 1) + player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, items, ref currentIndex)); else if (items.Count - currentIndex == 1) - { - owner.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, items[currentIndex])); + { + player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, items[currentIndex])); currentIndex++; } else break; } - } - - private void SendInventoryPackets(int startOffset) + } + + private void SendInventoryPackets(Player player, int startOffset) { - int currentIndex = startOffset; + int currentIndex = startOffset; + + List lst = new List(list); while (true) { - if (list.Count - currentIndex >= 64) - owner.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, list, ref currentIndex)); - else if (list.Count - currentIndex >= 32) - owner.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, list, ref currentIndex)); - else if (list.Count - currentIndex >= 16) - owner.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, list, ref currentIndex)); - else if (list.Count - currentIndex > 1) - owner.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, list, ref currentIndex)); - else if (list.Count - currentIndex == 1) - { - owner.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, list[currentIndex])); + if (endOfListIndex - currentIndex >= 64) + player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); + else if (endOfListIndex - currentIndex >= 32) + player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); + else if (endOfListIndex - currentIndex >= 16) + player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); + else if (endOfListIndex - currentIndex > 1) + player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); + else if (endOfListIndex - currentIndex == 1) + { + player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, list[currentIndex])); currentIndex++; } else break; } - } - - private void SendInventoryRemovePackets(ushort index) + } + + private void SendInventoryRemovePackets(Player player, ushort index) { - owner.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, index)); - } + player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, index)); + } - private void SendInventoryRemovePackets(List indexes) + private void SendInventoryRemovePackets(Player player, List indexes) { int currentIndex = 0; while (true) { - if (indexes.Count - currentIndex >= 64) - owner.QueuePacket(InventoryRemoveX64Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); - else if (indexes.Count - currentIndex >= 32) - owner.QueuePacket(InventoryRemoveX32Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); - else if (indexes.Count - currentIndex >= 16) - owner.QueuePacket(InventoryRemoveX16Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); - else if (indexes.Count - currentIndex > 1) - owner.QueuePacket(InventoryRemoveX08Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); + if (indexes.Count - currentIndex >= 64) + player.QueuePacket(InventoryRemoveX64Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); + else if (indexes.Count - currentIndex >= 32) + player.QueuePacket(InventoryRemoveX32Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); + else if (indexes.Count - currentIndex >= 16) + player.QueuePacket(InventoryRemoveX16Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); + else if (indexes.Count - currentIndex > 1) + player.QueuePacket(InventoryRemoveX08Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); else if (indexes.Count - currentIndex == 1) - { - owner.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, indexes[currentIndex])); + { + player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, indexes[currentIndex])); currentIndex++; } else @@ -454,19 +325,47 @@ namespace FFXIVClassic_Map_Server.actors.chara.player } - #endregion - - #region Inventory Utils - + #endregion + + #region Client Updating + private void SendUpdatePackets(Player player) + { + List items = new List(); + List slotsToRemove = new List(); + + for (int i = 0; i < list.Length; i++) + { + if (i == endOfListIndex) + break; + if (isDirty[i]) + items.Add(list[i]); + } + + for (int i = endOfListIndex; i < lastList.Length; i++) + { + if (lastList[i] != null) + slotsToRemove.Add((ushort)i); + } + + //Send Updated Slots + SendInventoryPackets(player, items); + + //Send Remove packets for tail end + SendInventoryRemovePackets(player, slotsToRemove); + } + #endregion + + #region Inventory Utils + public bool IsFull() { - return list.Count >= inventoryCapacity; + return endOfListIndex >= inventoryCapacity; } public bool IsSpaceForAdd(uint itemId, int quantity) { - int quantityCount = quantity; - for (int i = 0; i < list.Count; i++) + int quantityCount = quantity; + for (int i = 0; i < endOfListIndex; i++) { InventoryItem item = list[i]; ItemData gItem = Server.GetItemGamedata(item.itemId); @@ -503,8 +402,33 @@ namespace FFXIVClassic_Map_Server.actors.chara.player } public int GetNextEmptySlot() - { - return list.Count == 0 ? 0 : list.Count(); + { + return endOfListIndex; + } + + private void doRealign() + { + int lastNullSlot = -1; + + for (int i = 0; i < endOfListIndex; i++) + { + if (list[i] == null && lastNullSlot != -1) + { + lastNullSlot = i; + continue; + } + else if (list[i] != null && lastNullSlot != -1) + { + list[lastNullSlot] = list[i]; + list[lastNullSlot].slot = (ushort)lastNullSlot; + list[i] = null; + isDirty[lastNullSlot] = true; + isDirty[i] = true; + lastNullSlot++; + } + } + + endOfListIndex = lastNullSlot; } #endregion diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 1e0ab866..c1ddd5d5 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -533,12 +533,12 @@ namespace FFXIVClassic_Map_Server.Actors #region Inventory & Equipment QueuePacket(InventoryBeginChangePacket.BuildPacket(actorId)); - inventories[Inventory.NORMAL].SendFullInventory(); - inventories[Inventory.CURRENCY].SendFullInventory(); - inventories[Inventory.KEYITEMS].SendFullInventory(); - inventories[Inventory.BAZAAR].SendFullInventory(); - inventories[Inventory.MELDREQUEST].SendFullInventory(); - inventories[Inventory.LOOT].SendFullInventory(); + inventories[Inventory.NORMAL].SendFullInventory(this); + inventories[Inventory.CURRENCY].SendFullInventory(this); + inventories[Inventory.KEYITEMS].SendFullInventory(this); + inventories[Inventory.BAZAAR].SendFullInventory(this); + inventories[Inventory.MELDREQUEST].SendFullInventory(this); + inventories[Inventory.LOOT].SendFullInventory(this); equipment.SendFullEquipment(false); playerSession.QueuePacket(InventoryEndChangePacket.BuildPacket(actorId)); #endregion diff --git a/FFXIVClassic Map Server/dataobjects/InventoryItem.cs b/FFXIVClassic Map Server/dataobjects/InventoryItem.cs index 318ff875..13c51499 100644 --- a/FFXIVClassic Map Server/dataobjects/InventoryItem.cs +++ b/FFXIVClassic Map Server/dataobjects/InventoryItem.cs @@ -23,12 +23,11 @@ namespace FFXIVClassic_Map_Server.dataobjects public byte materia5 = 0; //Bare Minimum - public InventoryItem(uint id, uint itemId, ushort slot) + public InventoryItem(uint id, uint itemId) { this.uniqueId = id; this.itemId = itemId; this.quantity = 1; - this.slot = slot; ItemData gItem = Server.GetItemGamedata(itemId); itemType = gItem.isExclusive ? (byte)0x3 : (byte)0x0; @@ -55,12 +54,11 @@ namespace FFXIVClassic_Map_Server.dataobjects this.materia5 = item.materia5; } - public InventoryItem(uint uniqueId, uint itemId, int quantity, ushort slot, byte itemType, byte qualityNumber, int durability, ushort spiritbind, byte materia1, byte materia2, byte materia3, byte materia4, byte materia5) + public InventoryItem(uint uniqueId, uint itemId, int quantity, byte itemType, byte qualityNumber, int durability, ushort spiritbind, byte materia1, byte materia2, byte materia3, byte materia4, byte materia5) { this.uniqueId = uniqueId; this.itemId = itemId; this.quantity = quantity; - this.slot = slot; this.itemType = itemType; this.quality = qualityNumber; this.durability = durability; From 5bec522c8ea28b8f932e788830d82c52c8813e04 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Tue, 5 Sep 2017 15:35:33 -0400 Subject: [PATCH 2/6] Fixed bugs in the new inventory code. --- FFXIVClassic Map Server/Database.cs | 4 +- .../actors/chara/player/Equipment.cs | 6 +- .../actors/chara/player/Inventory.cs | 126 ++++++++++++------ FFXIVClassic World Server/Server.cs | 4 +- 4 files changed, 94 insertions(+), 46 deletions(-) diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index c50510e1..adf7f2b0 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -1265,7 +1265,9 @@ namespace FFXIVClassic_Map_Server byte materia4 = reader.GetByte(11); byte materia5 = reader.GetByte(12); - items.Add(new InventoryItem(uniqueId, itemId, quantity, itemType, qualityNumber, durability, spiritBind, materia1, materia2, materia3, materia4, materia5)); + InventoryItem item = new InventoryItem(uniqueId, itemId, quantity, itemType, qualityNumber, durability, spiritBind, materia1, materia2, materia3, materia4, materia5); + item.slot = slot; + items.Add(item); } } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs index 9c311e12..9ab22d2a 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs @@ -152,9 +152,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.player owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); if (list[slot] != null) - normalInventory.RefreshItem(list[slot], item); + normalInventory.RefreshItem(owner, list[slot], item); else - normalInventory.RefreshItem(item); + normalInventory.RefreshItem(owner, item); owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); SendEquipmentPackets(slot, item); @@ -180,7 +180,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - normalInventory.RefreshItem(list[slot]); + normalInventory.RefreshItem(owner, list[slot]); owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); SendEquipmentPackets(slot, null); diff --git a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs index f9d8493f..b401727b 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs @@ -24,19 +24,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.player private int endOfListIndex = 0; - private Character owner; - private List viewer; + private Character owner; private ushort inventoryCapacity; private ushort inventoryCode; private InventoryItem[] list; - private InventoryItem[] lastList; private bool[] isDirty; public Inventory(Character ownerPlayer, ushort capacity, ushort code) { owner = ownerPlayer; inventoryCapacity = capacity; - inventoryCode = code; + inventoryCode = code; + list = new InventoryItem[capacity]; + isDirty = new bool[capacity]; } #region Inventory Management @@ -44,7 +44,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.player { int i = 0; foreach (InventoryItem item in itemsFromDB) - list[i++] = item; + list[i++] = item; + endOfListIndex = i; } public InventoryItem GetItemAtSlot(ushort slot) @@ -56,9 +57,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.player } public InventoryItem GetItemByUniqueId(ulong uniqueItemId) - { - foreach (InventoryItem item in list) + { + for (int i = 0; i < endOfListIndex; i++) { + InventoryItem item = list[i]; + + if (item == null) + throw new Exception("Item slot was null!!!"); + if (item.uniqueId == uniqueItemId) return item; } @@ -67,22 +73,27 @@ namespace FFXIVClassic_Map_Server.actors.chara.player public InventoryItem GetItemByCatelogId(ulong catelogId) { - foreach (InventoryItem item in list) + for (int i = 0; i < endOfListIndex; i++) { + InventoryItem item = list[i]; + + if (item == null) + throw new Exception("Item slot was null!!!"); + if (item.itemId == catelogId) return item; } return null; - } - - public void AddItem(uint itemId) + } + + public bool AddItem(uint itemId) { - AddItem(itemId, 1, 1); - } - - public void AddItem(uint itemId, int quantity) - { - AddItem(itemId, quantity, 1); + return AddItem(itemId, 1, 1); + } + + public bool AddItem(uint itemId, int quantity) + { + return AddItem(itemId, quantity, 1); } public bool AddItem(uint itemId, int quantity, byte quality) @@ -130,10 +141,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.player while (quantityCount > 0) { InventoryItem addedItem = Database.CreateItem(itemId, Math.Min(quantityCount, gItem.maxStack), quality, gItem.isExclusive ? (byte)0x3 : (byte)0x0, gItem.durability); + addedItem.slot = (ushort)endOfListIndex; isDirty[endOfListIndex] = true; list[endOfListIndex++] = addedItem; quantityCount -= gItem.maxStack; - } + } + + if (owner is Player) + SendUpdatePackets((Player)owner); return true; } @@ -178,8 +193,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.player } } - doRealign(); - + doRealign(); + if (owner is Player) + SendUpdatePackets((Player)owner); } public void RemoveItemByUniqueId(ulong itemDBId) @@ -211,22 +227,24 @@ namespace FFXIVClassic_Map_Server.actors.chara.player list[slot] = null; isDirty[slot] = true; - doRealign(); + doRealign(); + if (owner is Player) + SendUpdatePackets((Player)owner); } public void ChangeDurability(uint slot, uint durabilityChange) - { - + { + isDirty[slot] = true; } public void ChangeSpiritBind(uint slot, uint spiritBindChange) - { - + { + isDirty[slot] = true; } public void ChangeMateria(uint slot, byte materiaSlot, byte materiaId) - { - + { + isDirty[slot] = true; } #endregion @@ -272,10 +290,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.player { int currentIndex = startOffset; - List lst = new List(list); - - while (true) - { + List lst = new List(); + for (int i = 0; i < endOfListIndex; i++) + lst.Add(list[i]); + + while (true) + { if (endOfListIndex - currentIndex >= 64) player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); else if (endOfListIndex - currentIndex >= 32) @@ -284,13 +304,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.player player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); else if (endOfListIndex - currentIndex > 1) player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); - else if (endOfListIndex - currentIndex == 1) + else if (endOfListIndex - currentIndex == 1) { - player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, list[currentIndex])); - currentIndex++; - } - else - break; + player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, list[currentIndex])); + currentIndex++; + } + else + break; } } @@ -323,6 +343,27 @@ namespace FFXIVClassic_Map_Server.actors.chara.player break; } + } + + public void RefreshItem(Player player, InventoryItem item) + { + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); + SendInventoryPackets(player, item); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + public void RefreshItem(Player player, params InventoryItem[] items) + { + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); + SendInventoryPackets(player, items.ToList()); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + public void RefreshItem(Player player, List items) + { + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); + SendInventoryPackets(player, items); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); } #endregion @@ -341,17 +382,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.player items.Add(list[i]); } - for (int i = endOfListIndex; i < lastList.Length; i++) + for (int i = endOfListIndex; i < list.Length; i++) { - if (lastList[i] != null) + if (isDirty[i]) slotsToRemove.Add((ushort)i); } + Array.Clear(isDirty, 0, isDirty.Length); + + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); //Send Updated Slots SendInventoryPackets(player, items); - //Send Remove packets for tail end SendInventoryRemovePackets(player, slotsToRemove); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); } #endregion @@ -412,7 +456,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player for (int i = 0; i < endOfListIndex; i++) { - if (list[i] == null && lastNullSlot != -1) + if (list[i] == null && lastNullSlot == -1) { lastNullSlot = i; continue; diff --git a/FFXIVClassic World Server/Server.cs b/FFXIVClassic World Server/Server.cs index 964abb35..efc7a6e6 100644 --- a/FFXIVClassic World Server/Server.cs +++ b/FFXIVClassic World Server/Server.cs @@ -169,7 +169,9 @@ namespace FFXIVClassic_World_Server { uint sessionId = subpacket.header.targetId; Session session = GetSession(sessionId); - subpacket.DebugPrintSubPacket(); + + if (subpacket.gameMessage.opcode != 0x1 && subpacket.gameMessage.opcode != 0xca) + subpacket.DebugPrintSubPacket(); if (subpacket.gameMessage.opcode >= 0x1000) { //subpacket.DebugPrintSubPacket(); From 76f073d85f1935775b9711dacc38136d7208b45d Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Sat, 9 Sep 2017 10:54:40 -0400 Subject: [PATCH 3/6] Got retainer meeting group working and cleaned up retainer instancing. Added a RemoveItemAtSlot with quantity. --- FFXIVClassic Map Server/PacketProcessor.cs | 6 ++- .../actors/chara/npc/Retainer.cs | 39 +++++++++++++++---- .../actors/chara/player/Inventory.cs | 23 +++++++++++ .../actors/chara/player/Player.cs | 17 ++++---- 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/FFXIVClassic Map Server/PacketProcessor.cs b/FFXIVClassic Map Server/PacketProcessor.cs index 09b7b7ab..ed297041 100644 --- a/FFXIVClassic Map Server/PacketProcessor.cs +++ b/FFXIVClassic Map Server/PacketProcessor.cs @@ -182,8 +182,12 @@ namespace FFXIVClassic_Map_Server if (ownerActor == null) { + //Is it your retainer? + if (session.GetActor().currentSpawnedRetainer != null && session.GetActor().currentSpawnedRetainer.actorId == eventStart.scriptOwnerActorID) + ownerActor = session.GetActor().currentSpawnedRetainer; //Is it a instance actor? - ownerActor = session.GetActor().zone.FindActorInArea(session.GetActor().currentEventOwner); + if (ownerActor == null) + ownerActor = session.GetActor().zone.FindActorInArea(session.GetActor().currentEventOwner); if (ownerActor == null) { //Is it a Director? diff --git a/FFXIVClassic Map Server/actors/chara/npc/Retainer.cs b/FFXIVClassic Map Server/actors/chara/npc/Retainer.cs index ea545e9c..563652b6 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Retainer.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Retainer.cs @@ -1,5 +1,7 @@ -using FFXIVClassic_Map_Server.actors.chara.player; +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.actors.chara.player; using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.packets.send.actor.inventory; using System; using System.Collections.Generic; using System.Linq; @@ -10,22 +12,45 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc { class Retainer : Npc { - public Retainer(uint id, string retainerName, ActorClass actorClass, Player player, float posX, float posY, float posZ, float rot) - : base(0, actorClass, String.Format("_rtnre{0:x7}", id), player.GetZone(), posX, posY, posZ, rot, 0, 0, retainerName) + Player player; + + public Retainer(string retainerName, ActorClass actorClass, Player player, float posX, float posY, float posZ, float rot) + : base(0, actorClass, "myretainer", player.GetZone(), posX, posY, posZ, rot, 0, 0, retainerName) { - this.actorId = 0xD0000000 | id; + this.player = player; + this.actorName = String.Format("_rtnre{0:x7}", actorId); } public void SendBazaarItems(Player player) { - Inventory bazaar = new Inventory(this, 4, Inventory.RETAINER_BAZAAR); - bazaar.SendFullInventory(player); + Inventory bazaar = new Inventory(this, 150, (ushort)0); + bazaar.AddItem(1000001); + bazaar.AddItem(3020517); + player.QueuePacket(InventoryBeginChangePacket.BuildPacket(actorId)); + bazaar.SendFullInventory(player); + player.QueuePacket(InventoryEndChangePacket.BuildPacket(actorId)); + } public void SendStorageItems(Player player) { - Inventory storage = new Inventory(this, 4, 1); + Inventory storage = new Inventory(this, 10, Inventory.CURRENCY); + storage.AddItem(1000001); + storage.AddItem(3020519); + player.QueuePacket(InventoryBeginChangePacket.BuildPacket(actorId)); storage.SendFullInventory(player); + player.QueuePacket(InventoryEndChangePacket.BuildPacket(actorId)); } + + public void SendHuhItems(Player player) + { + Inventory storage = new Inventory(this, 20, 7); + storage.AddItem(1000003); + storage.AddItem(1000016); + player.QueuePacket(InventoryBeginChangePacket.BuildPacket(actorId)); + storage.SendFullInventory(player); + player.QueuePacket(InventoryEndChangePacket.BuildPacket(actorId)); + } + } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs index b401727b..8f9e5ef2 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs @@ -230,6 +230,27 @@ namespace FFXIVClassic_Map_Server.actors.chara.player doRealign(); if (owner is Player) SendUpdatePackets((Player)owner); + } + + public void RemoveItemAtSlot(ushort slot, int quantity) + { + if (slot >= endOfListIndex) + return; + + if (list[slot] != null) + { + list[slot].quantity -= quantity; + + if (list[slot].quantity <= 0) + { + list[slot] = null; + doRealign(); + } + + isDirty[slot] = true; + if (owner is Player) + SendUpdatePackets((Player)owner); + } } public void ChangeDurability(uint slot, uint durabilityChange) @@ -390,12 +411,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.player Array.Clear(isDirty, 0, isDirty.Length); + player.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); //Send Updated Slots SendInventoryPackets(player, items); //Send Remove packets for tail end SendInventoryRemovePackets(player, slotsToRemove); player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + player.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); } #endregion diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index c1ddd5d5..6e314c58 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -135,7 +135,8 @@ namespace FFXIVClassic_Map_Server.Actors public uint homepoint = 0; public byte homepointInn = 0; - //Instancing + //Retainer + RetainerMeetingRelationGroup retainerMeetingGroup = null; public Retainer currentSpawnedRetainer = null; public bool sentRetainerSpawn = false; @@ -1742,30 +1743,30 @@ namespace FFXIVClassic_Map_Server.Actors chocoboAppearance = appearanceId; } - public bool SpawnMyRetainer(Npc bell, int retainerIndex) + public Retainer SpawnMyRetainer(Npc bell, int retainerIndex) { Tuple retainerData = Database.GetRetainer(this, retainerIndex); ActorClass actorClass = Server.GetWorldManager().GetActorClass(retainerData.Item2); if (actorClass == null) - return false; + return null; float distance = (float)Math.Sqrt(((positionX - bell.positionX) * (positionX - bell.positionX)) + ((positionZ - bell.positionZ) * (positionZ - bell.positionZ))); float posX = bell.positionX - ((-1.0f * (bell.positionX - positionX)) / distance); float posZ = bell.positionZ - ((-1.0f * (bell.positionZ - positionZ)) / distance); - Retainer retainer = new Retainer(retainerData.Item1, retainerData.Item3, actorClass, this, posX, bell.positionY, positionZ, (float)Math.Atan2(positionX - posX, positionZ - posZ)); + Retainer retainer = new Retainer(retainerData.Item3, actorClass, this, posX, bell.positionY, positionZ, (float)Math.Atan2(positionX - posX, positionZ - posZ)); retainer.LoadEventConditions(actorClass.eventConditions); - //RetainerMeetingRelationGroup group = new RetainerMeetingRelationGroup(5555, this, retainer); - //group.SendGroupPackets(playerSession); + retainerMeetingGroup = new RetainerMeetingRelationGroup(5555, this, retainer); + retainerMeetingGroup.SendGroupPackets(playerSession); currentSpawnedRetainer = retainer; sentRetainerSpawn = false; - return true; + return retainer; } public void DespawnMyRetainer() @@ -1773,6 +1774,8 @@ namespace FFXIVClassic_Map_Server.Actors if (currentSpawnedRetainer != null) { currentSpawnedRetainer = null; + retainerMeetingGroup.SendDeletePacket(playerSession); + retainerMeetingGroup = null; } } From bc95c139dedec9b7df23447bec344a8dd4a10c00 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Sat, 9 Sep 2017 11:25:58 -0400 Subject: [PATCH 4/6] Implement DB updates for player. Cleaned up DB calls. No longer track slot, it's ordered by index id (no concept of slots anyway in client). --- FFXIVClassic Map Server/Database.cs | 108 +++++------------- .../actors/chara/player/Inventory.cs | 53 +++++++-- 2 files changed, 70 insertions(+), 91 deletions(-) diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index adf7f2b0..9c329835 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -1225,7 +1225,6 @@ namespace FFXIVClassic_Map_Server serverItemId, itemId, quantity, - slot, itemType, quality, durability, @@ -1237,36 +1236,36 @@ namespace FFXIVClassic_Map_Server materia5 FROM characters_inventory INNER JOIN server_items ON serverItemId = server_items.id - WHERE characterId = @charId AND inventoryType = @type AND slot >= @slot ORDER BY slot"; + WHERE characterId = @charId AND inventoryType = @type"; MySqlCommand cmd = new MySqlCommand(query, conn); cmd.Parameters.AddWithValue("@charId", player.actorId); - cmd.Parameters.AddWithValue("@slot", slotOffset); cmd.Parameters.AddWithValue("@type", type); + ushort slot = 0; using (MySqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { - uint uniqueId = reader.GetUInt32(0); - uint itemId = reader.GetUInt32(1); - int quantity = reader.GetInt32(2); - ushort slot = reader.GetUInt16(3); + uint uniqueId = reader.GetUInt32("serverItemId"); + uint itemId = reader.GetUInt32("itemId"); + int quantity = reader.GetInt32("quantity"); - byte itemType = reader.GetByte(4); - byte qualityNumber = reader.GetByte(5); + byte itemType = reader.GetByte("itemType"); + byte qualityNumber = reader.GetByte("quality"); - int durability = reader.GetInt32(6); - ushort spiritBind = reader.GetUInt16(7); + int durability = reader.GetInt32("durability"); + ushort spiritBind = reader.GetUInt16("spiritBind"); - byte materia1 = reader.GetByte(8); - byte materia2 = reader.GetByte(9); - byte materia3 = reader.GetByte(10); - byte materia4 = reader.GetByte(11); - byte materia5 = reader.GetByte(12); + byte materia1 = reader.GetByte("materia1"); + byte materia2 = reader.GetByte("materia2"); + byte materia3 = reader.GetByte("materia3"); + byte materia4 = reader.GetByte("materia4"); + byte materia5 = reader.GetByte("materia5"); InventoryItem item = new InventoryItem(uniqueId, itemId, quantity, itemType, qualityNumber, durability, spiritBind, materia1, materia2, materia3, materia4, materia5); item.slot = slot; + slot++; items.Add(item); } } @@ -1327,10 +1326,8 @@ namespace FFXIVClassic_Map_Server return insertedItem; } - public static InventoryItem AddItem(Player player, InventoryItem addedItem, uint type) - { - InventoryItem insertedItem = null; - + public static void AddItem(Player player, InventoryItem addedItem, uint type) + { using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) { try @@ -1339,8 +1336,8 @@ namespace FFXIVClassic_Map_Server string query = @" INSERT INTO characters_inventory - (characterId, slot, inventoryType, serverItemId, quantity) - SELECT @charId, IFNULL(MAX(SLOT)+1, 0), @inventoryType, @uid, @quantity FROM characters_inventory WHERE characterId = @charId AND inventoryType = @inventoryType; + (characterId, inventoryType, serverItemId, quantity) + SELECT @charId, @inventoryType, @uid, @quantity FROM characters_inventory WHERE characterId = @charId AND inventoryType = @inventoryType; "; MySqlCommand cmd = new MySqlCommand(query, conn); @@ -1348,7 +1345,7 @@ namespace FFXIVClassic_Map_Server cmd.Parameters.AddWithValue("@uid", addedItem.uniqueId); cmd.Parameters.AddWithValue("@charId", player.actorId); cmd.Parameters.AddWithValue("@inventoryType", type); - cmd.Parameters.AddWithValue("@quantity", insertedItem.quantity); + cmd.Parameters.AddWithValue("@quantity", addedItem.quantity); cmd.ExecuteNonQuery(); } @@ -1361,11 +1358,9 @@ namespace FFXIVClassic_Map_Server conn.Dispose(); } } - - return insertedItem; } - public static void SetQuantity(Player player, uint slot, ushort type, int quantity) + public static void SetQuantity(Player player, ulong serverItemId, int quantity) { using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) { @@ -1376,54 +1371,13 @@ namespace FFXIVClassic_Map_Server string query = @" UPDATE characters_inventory SET quantity = @quantity - WHERE characterId = @charId AND slot = @slot AND inventoryType = @type; - "; - - MySqlCommand cmd = new MySqlCommand(query, conn); - cmd.Parameters.AddWithValue("@charId", player.actorId); - cmd.Parameters.AddWithValue("@quantity", quantity); - cmd.Parameters.AddWithValue("@slot", slot); - cmd.Parameters.AddWithValue("@type", type); - cmd.ExecuteNonQuery(); - - } - catch (MySqlException e) - { - Program.Log.Error(e.ToString()); - } - finally - { - conn.Dispose(); - } - } - - } - - public static void RemoveItem(Player player, ulong serverItemId, ushort type) - { - using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}; Allow User Variables=True", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) - { - try - { - conn.Open(); - - string query = @" - SELECT slot INTO @slotToDelete FROM characters_inventory WHERE serverItemId = @serverItemId; - UPDATE characters_inventory - SET slot = slot - 1 - WHERE characterId = @charId AND slot > @slotToDelete AND inventoryType = @type; - - DELETE FROM characters_inventory - WHERE serverItemId = @serverItemId AND inventoryType = @type; - - DELETE FROM server_items - WHERE id = @serverItemId; + WHERE characterId = @charId and serverItemId = @serverItemId; "; MySqlCommand cmd = new MySqlCommand(query, conn); cmd.Parameters.AddWithValue("@charId", player.actorId); cmd.Parameters.AddWithValue("@serverItemId", serverItemId); - cmd.Parameters.AddWithValue("@type", type); + cmd.Parameters.AddWithValue("@quantity", quantity); cmd.ExecuteNonQuery(); } @@ -1439,7 +1393,7 @@ namespace FFXIVClassic_Map_Server } - public static void RemoveItem(Player player, ushort slot, ushort type) + public static void RemoveItem(Player player, ulong serverItemId) { using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}; Allow User Variables=True", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) { @@ -1448,23 +1402,13 @@ namespace FFXIVClassic_Map_Server conn.Open(); string query = @" - SELECT serverItemId INTO @serverItemId FROM characters_inventory WHERE characterId = @charId AND slot = @slot; - DELETE FROM characters_inventory - WHERE characterId = @charId AND slot = @slot AND inventoryType = @type; - - DELETE FROM server_items - WHERE id = @serverItemId; - - UPDATE characters_inventory - SET slot = slot - 1 - WHERE characterId = @charId AND slot > @slot AND inventoryType = @type; + WHERE characterId = @charId and serverItemId = @itemDBCode; "; MySqlCommand cmd = new MySqlCommand(query, conn); cmd.Parameters.AddWithValue("@charId", player.actorId); - cmd.Parameters.AddWithValue("@slot", slot); - cmd.Parameters.AddWithValue("@type", type); + cmd.Parameters.AddWithValue("@serverItemId", serverItemId); cmd.ExecuteNonQuery(); } diff --git a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs index 8f9e5ef2..7cfd11ac 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs @@ -123,7 +123,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.player int oldQuantity = item.quantity; item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack); isDirty[i] = true; - quantityCount -= (gItem.maxStack - oldQuantity); + quantityCount -= (gItem.maxStack - oldQuantity); + + if (owner is Player) + { + Database.SetQuantity((Player)owner, item.uniqueId, item.quantity); + } + if (quantityCount <= 0) break; } @@ -145,10 +151,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.player isDirty[endOfListIndex] = true; list[endOfListIndex++] = addedItem; quantityCount -= gItem.maxStack; + + if (owner is Player) + { + Database.AddItem((Player)owner, addedItem, inventoryCode); + } } if (owner is Player) - SendUpdatePackets((Player)owner); + { + SendUpdatePackets((Player)owner); + } return true; } @@ -176,12 +189,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.player if (item.itemId == itemId) { int oldQuantity = item.quantity; - //Stack nomnomed - if (item.quantity - quantityCount <= 0) - list[i] = null; - //Stack reduced - else + //Stack nomnomed + if (item.quantity - quantityCount <= 0) + { + if (owner is Player) + Database.RemoveItem((Player)owner, list[i].uniqueId); + list[i] = null; + } + //Stack reduced + else + { item.quantity -= quantityCount; + if (owner is Player) + Database.SetQuantity((Player)owner, list[i].uniqueId, list[i].quantity); + } isDirty[i] = true; @@ -213,7 +234,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.player } if (toDelete == null) - return; + return; + + if (owner is Player) + Database.RemoveItem((Player)owner, toDelete.uniqueId); list[slot] = null; isDirty[slot] = true; @@ -223,7 +247,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.player public void RemoveItemAtSlot(ushort slot) { if (slot >= endOfListIndex) - return; + return; + + if (owner is Player) + Database.RemoveItem((Player)owner, list[slot].uniqueId); list[slot] = null; isDirty[slot] = true; @@ -243,9 +270,17 @@ namespace FFXIVClassic_Map_Server.actors.chara.player if (list[slot].quantity <= 0) { + if (owner is Player) + Database.RemoveItem((Player)owner, list[slot].uniqueId); + list[slot] = null; doRealign(); } + else + { + if (owner is Player) + Database.SetQuantity((Player)owner, list[slot].uniqueId, list[slot].quantity); + } isDirty[slot] = true; if (owner is Player) From a14e36fadccd401083d11d88783fca8b7b01e5b9 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Sat, 9 Sep 2017 11:33:23 -0400 Subject: [PATCH 5/6] Bad old SQL line was adding exponentially increasing duplicates. --- FFXIVClassic Map Server/Database.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index 9c329835..0a118ffb 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -1337,7 +1337,8 @@ namespace FFXIVClassic_Map_Server string query = @" INSERT INTO characters_inventory (characterId, inventoryType, serverItemId, quantity) - SELECT @charId, @inventoryType, @uid, @quantity FROM characters_inventory WHERE characterId = @charId AND inventoryType = @inventoryType; + VALUES + (@charId, @inventoryType, uid, @quantity) "; MySqlCommand cmd = new MySqlCommand(query, conn); From 8755ca4f338b3a819c4a9da0c5bfd2b2d2439149 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Sat, 9 Sep 2017 12:21:37 -0400 Subject: [PATCH 6/6] Fixed a bunch of SQL errors. Added checks for quality so a remove command didn't take out items regardless of quality. Fixed errors. --- FFXIVClassic Map Server/Database.cs | 6 +- .../actors/chara/player/Inventory.cs | 96 ++++++++++++------- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index 0a118ffb..9ead6d61 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -1338,12 +1338,12 @@ namespace FFXIVClassic_Map_Server INSERT INTO characters_inventory (characterId, inventoryType, serverItemId, quantity) VALUES - (@charId, @inventoryType, uid, @quantity) + (@charId, @inventoryType, @serverItemId, @quantity) "; MySqlCommand cmd = new MySqlCommand(query, conn); - cmd.Parameters.AddWithValue("@uid", addedItem.uniqueId); + cmd.Parameters.AddWithValue("@serverItemId", addedItem.uniqueId); cmd.Parameters.AddWithValue("@charId", player.actorId); cmd.Parameters.AddWithValue("@inventoryType", type); cmd.Parameters.AddWithValue("@quantity", addedItem.quantity); @@ -1404,7 +1404,7 @@ namespace FFXIVClassic_Map_Server string query = @" DELETE FROM characters_inventory - WHERE characterId = @charId and serverItemId = @itemDBCode; + WHERE characterId = @charId and serverItemId = @serverItemId; "; MySqlCommand cmd = new MySqlCommand(query, conn); diff --git a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs index 7cfd11ac..dc7647e2 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs @@ -1,10 +1,11 @@ - + using FFXIVClassic.Common; -using FFXIVClassic_Map_Server.Actors; -using FFXIVClassic_Map_Server.dataobjects; -using FFXIVClassic_Map_Server.packets.send.actor.inventory; -using System; -using System.Collections.Generic; +using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.dataobjects; +using FFXIVClassic_Map_Server.packets.send.actor.inventory; +using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace FFXIVClassic_Map_Server.actors.chara.player @@ -62,8 +63,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player { InventoryItem item = list[i]; - if (item == null) - throw new Exception("Item slot was null!!!"); + Debug.Assert(item != null, "Item slot was null!!!"); if (item.uniqueId == uniqueItemId) return item; @@ -77,8 +77,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player { InventoryItem item = list[i]; - if (item == null) - throw new Exception("Item slot was null!!!"); + Debug.Assert(item != null, "Item slot was null!!!"); if (item.itemId == catelogId) return item; @@ -98,7 +97,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player public bool AddItem(uint itemId, int quantity, byte quality) { - if (!IsSpaceForAdd(itemId, quantity)) + if (!IsSpaceForAdd(itemId, quantity, quality)) return false; ItemData gItem = Server.GetItemGamedata(itemId); @@ -115,10 +114,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.player { InventoryItem item = list[i]; - if (item == null) - throw new Exception("Item slot was null!!!"); + Debug.Assert(item != null, "Item slot was null!!!"); - if (item.itemId == itemId && item.quantity < gItem.maxStack) + if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack) { int oldQuantity = item.quantity; item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack); @@ -166,9 +164,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.player return true; } - public void RemoveItem(uint itemId, int quantity) + public void RemoveItem(uint itemId) + { + RemoveItem(itemId, 1); + } + + public void RemoveItem(uint itemId, int quantity) + { + RemoveItem(itemId, quantity, 1); + } + + public void RemoveItem(uint itemId, int quantity, int quality) { - if (!HasItem(itemId, quantity)) + if (!HasItem(itemId, quantity, quality)) return; List slotsToUpdate = new List(); @@ -183,10 +191,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.player { InventoryItem item = list[i]; - if (item == null) - throw new Exception("Item slot was null!!!"); + Debug.Assert(item != null, "Item slot was null!!!"); - if (item.itemId == itemId) + if (item.itemId == itemId && item.quality == quality) { int oldQuantity = item.quantity; //Stack nomnomed @@ -222,9 +229,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.player public void RemoveItemByUniqueId(ulong itemDBId) { ushort slot = 0; - InventoryItem toDelete = null; - foreach (InventoryItem item in list) - { + InventoryItem toDelete = null; + for (int i = endOfListIndex - 1; i >= 0; i--) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + if (item.uniqueId == itemDBId) { toDelete = item; @@ -241,7 +252,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.player list[slot] = null; isDirty[slot] = true; - doRealign(); + + doRealign(); + if (owner is Player) + SendUpdatePackets((Player)owner); } public void RemoveItemAtSlot(ushort slot) @@ -254,6 +268,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player list[slot] = null; isDirty[slot] = true; + doRealign(); if (owner is Player) SendUpdatePackets((Player)owner); @@ -464,14 +479,14 @@ namespace FFXIVClassic_Map_Server.actors.chara.player return endOfListIndex >= inventoryCapacity; } - public bool IsSpaceForAdd(uint itemId, int quantity) + public bool IsSpaceForAdd(uint itemId, int quantity, int quality) { int quantityCount = quantity; for (int i = 0; i < endOfListIndex; i++) { InventoryItem item = list[i]; - ItemData gItem = Server.GetItemGamedata(item.itemId); - if (item.itemId == itemId && item.quantity < gItem.maxStack) + ItemData gItem = Server.GetItemGamedata(item.itemId); + if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack) { quantityCount -= (gItem.maxStack - item.quantity); if (quantityCount <= 0) @@ -485,19 +500,28 @@ namespace FFXIVClassic_Map_Server.actors.chara.player public bool HasItem(uint itemId) { return HasItem(itemId, 1); + } + + public bool HasItem(uint itemId, int minQuantity) + { + return HasItem(itemId, minQuantity, 1); } - public bool HasItem(uint itemId, int minQuantity) + public bool HasItem(uint itemId, int minQuantity, int quality) { - int count = 0; - - foreach (InventoryItem item in list) - { - if (item.itemId == itemId) - count += item.quantity; - - if (count >= minQuantity) - return true; + int count = 0; + + for (int i = endOfListIndex - 1; i >= 0; i--) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + + if (item.itemId == itemId && item.quality == quality) + count += item.quantity; + + if (count >= minQuantity) + return true; } return false;