From 64006d2351617cce8c1b6c9c70803f10638dce0d Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Sat, 9 Sep 2017 14:11:35 -0400 Subject: [PATCH] Added retainer inventory code. Renamed CURRENCY inv type to CURRENCY_CRYSTALS so I don't forget. --- FFXIVClassic Map Server/Database.cs | 187 +++++++++++++++++- .../actors/chara/npc/Retainer.cs | 57 +++--- .../actors/chara/player/Inventory.cs | 54 ++--- .../actors/chara/player/Player.cs | 22 +-- .../dataobjects/Session.cs | 1 + 5 files changed, 251 insertions(+), 70 deletions(-) diff --git a/FFXIVClassic Map Server/Database.cs b/FFXIVClassic Map Server/Database.cs index 9ead6d61..c6308ee5 100644 --- a/FFXIVClassic Map Server/Database.cs +++ b/FFXIVClassic Map Server/Database.cs @@ -11,6 +11,7 @@ using FFXIVClassic_Map_Server.dataobjects; using FFXIVClassic_Map_Server.Actors; using FFXIVClassic_Map_Server.actors.chara.player; using FFXIVClassic_Map_Server.packets.receive.supportdesk; +using FFXIVClassic_Map_Server.actors.chara.npc; namespace FFXIVClassic_Map_Server { @@ -1076,7 +1077,7 @@ namespace FFXIVClassic_Map_Server player.GetInventory(Inventory.NORMAL).InitList(GetInventory(player, 0, Inventory.NORMAL)); player.GetInventory(Inventory.KEYITEMS).InitList(GetInventory(player, 0, Inventory.KEYITEMS)); - player.GetInventory(Inventory.CURRENCY).InitList(GetInventory(player, 0, Inventory.CURRENCY)); + player.GetInventory(Inventory.CURRENCY_CRYSTALS).InitList(GetInventory(player, 0, Inventory.CURRENCY_CRYSTALS)); player.GetInventory(Inventory.BAZAAR).InitList(GetInventory(player, 0, Inventory.BAZAAR)); player.GetInventory(Inventory.MELDREQUEST).InitList(GetInventory(player, 0, Inventory.MELDREQUEST)); player.GetInventory(Inventory.LOOT).InitList(GetInventory(player, 0, Inventory.LOOT)); @@ -1283,6 +1284,79 @@ namespace FFXIVClassic_Map_Server return items; } + public static List GetInventory(Retainer retainer, uint type) + { + List items = new List(); + + 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 = @" + SELECT + serverItemId, + itemId, + quantity, + itemType, + quality, + durability, + spiritBind, + materia1, + materia2, + materia3, + materia4, + materia5 + FROM retainers_inventory + INNER JOIN server_items ON serverItemId = server_items.id + WHERE retainerId = @retainerId AND inventoryType = @type"; + + MySqlCommand cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@retainerId", retainer.getRetainerId()); + cmd.Parameters.AddWithValue("@type", type); + + ushort slot = 0; + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + uint uniqueId = reader.GetUInt32("serverItemId"); + uint itemId = reader.GetUInt32("itemId"); + int quantity = reader.GetInt32("quantity"); + + byte itemType = reader.GetByte("itemType"); + byte qualityNumber = reader.GetByte("quality"); + + int durability = reader.GetInt32("durability"); + ushort spiritBind = reader.GetUInt16("spiritBind"); + + 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); + } + } + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + + return items; + } + public static InventoryItem CreateItem(uint itemId, int quantity, byte quality, byte itemType, int durability) { InventoryItem insertedItem = null; @@ -1425,6 +1499,105 @@ namespace FFXIVClassic_Map_Server } + public static void AddItem(Retainer retainer, 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 + { + conn.Open(); + + string query = @" + INSERT INTO retainers_inventory + (retainerId, inventoryType, serverItemId, quantity) + VALUES + (@retainerId, @inventoryType, @serverItemId, @quantity) + "; + + MySqlCommand cmd = new MySqlCommand(query, conn); + + cmd.Parameters.AddWithValue("@serverItemId", addedItem.uniqueId); + cmd.Parameters.AddWithValue("@retainerId", retainer.getRetainerId()); + cmd.Parameters.AddWithValue("@inventoryType", type); + cmd.Parameters.AddWithValue("@quantity", addedItem.quantity); + + cmd.ExecuteNonQuery(); + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + } + + public static void SetQuantity(Retainer retainer, 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))) + { + try + { + conn.Open(); + + string query = @" + UPDATE retainers_inventory + SET quantity = @quantity + WHERE retainerId = @retainerId and serverItemId = @serverItemId; + "; + + MySqlCommand cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@retainerId", retainer.getRetainerId()); + cmd.Parameters.AddWithValue("@serverItemId", serverItemId); + cmd.Parameters.AddWithValue("@quantity", quantity); + cmd.ExecuteNonQuery(); + + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + + } + + public static void RemoveItem(Retainer retainer, 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))) + { + try + { + conn.Open(); + + string query = @" + DELETE FROM retainers_inventory + WHERE retainerId = @retainerId and serverItemId = @serverItemId; + "; + + MySqlCommand cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@retainerId", retainer.getRetainerId()); + cmd.Parameters.AddWithValue("@serverItemId", serverItemId); + cmd.ExecuteNonQuery(); + + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + + } + public static SubPacket GetLatestAchievements(Player player) { uint[] latestAchievements = new uint[5]; @@ -1897,9 +2070,9 @@ namespace FFXIVClassic_Map_Server } - public static Tuple GetRetainer(Player player, int retainerIndex) + public static Retainer LoadRetainer(Player player, int retainerIndex) { - Tuple data = null; + Retainer retainer = 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))) { @@ -1926,7 +2099,11 @@ namespace FFXIVClassic_Map_Server uint retainerId = reader.GetUInt32("retainerId"); string name = reader.GetString("name"); uint actorClassId = reader.GetUInt32("actorClassId"); - data = new Tuple(retainerId, actorClassId, name); + + ActorClass actorClass = Server.GetWorldManager().GetActorClass(actorClassId); + + retainer = new Retainer(retainerId, actorClass, player, 0, 0, 0, 0); + retainer.LoadEventConditions(actorClass.eventConditions); } } @@ -1940,7 +2117,7 @@ namespace FFXIVClassic_Map_Server conn.Dispose(); } - return data; + return retainer; } } diff --git a/FFXIVClassic Map Server/actors/chara/npc/Retainer.cs b/FFXIVClassic Map Server/actors/chara/npc/Retainer.cs index 563652b6..ec82890e 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Retainer.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Retainer.cs @@ -12,45 +12,50 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc { class Retainer : Npc { - Player player; + public const int MAXSIZE_INVENTORY_NORMAL = 150; + public const int MAXSIZE_INVENTORY_CURRANCY = 320; + public const int MAXSIZE_INVENTORY_BAZAAR = 10; - 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) + private uint retainerId; + private Player ownerPlayer; + private Dictionary inventories = new Dictionary(); + + public Retainer(uint retainerId, ActorClass actorClass, Player player, float posX, float posY, float posZ, float rot) + : base(0, actorClass, "myretainer", player.GetZone(), posX, posY, posZ, rot, 0, 0, null) { - this.player = player; + this.retainerId = retainerId; + this.ownerPlayer = player; this.actorName = String.Format("_rtnre{0:x7}", actorId); + + inventories[Inventory.NORMAL] = new Inventory(this, MAXSIZE_INVENTORY_NORMAL, Inventory.NORMAL); + inventories[Inventory.CURRENCY_CRYSTALS] = new Inventory(this, MAXSIZE_INVENTORY_CURRANCY, Inventory.CURRENCY_CRYSTALS); + inventories[Inventory.BAZAAR] = new Inventory(this, MAXSIZE_INVENTORY_BAZAAR, Inventory.BAZAAR); + + inventories[Inventory.NORMAL].InitList(Database.GetInventory(this, Inventory.NORMAL)); + inventories[Inventory.CURRENCY_CRYSTALS].InitList(Database.GetInventory(this, Inventory.CURRENCY_CRYSTALS)); + inventories[Inventory.BAZAAR].InitList(Database.GetInventory(this, Inventory.BAZAAR)); } - public void SendBazaarItems(Player player) + public Inventory GetInventory(ushort type) { - 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)); - + if (inventories.ContainsKey(type)) + return inventories[type]; + else + return null; } - public void SendStorageItems(Player player) - { - Inventory storage = new Inventory(this, 10, Inventory.CURRENCY); - storage.AddItem(1000001); - storage.AddItem(3020519); + public void SendFullRetainerInventory(Player player) + { player.QueuePacket(InventoryBeginChangePacket.BuildPacket(actorId)); - storage.SendFullInventory(player); + inventories[Inventory.NORMAL].SendFullInventory(player); + inventories[Inventory.CURRENCY_CRYSTALS].SendFullInventory(player); + inventories[Inventory.BAZAAR].SendFullInventory(player); player.QueuePacket(InventoryEndChangePacket.BuildPacket(actorId)); } - public void SendHuhItems(Player player) + public uint getRetainerId() { - 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)); + return retainerId; } - } } diff --git a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs index c5bed982..95f6346e 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Inventory.cs @@ -13,19 +13,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.player { class Inventory { - public const ushort NORMAL = 0x0000; //Max 0xC8 - public const ushort TRADE = 0x0001; //Max 0x96 - public const ushort LOOT = 0x0004; //Max 0xA - public const ushort MELDREQUEST = 0x0005; //Max 0x04 - public const ushort BAZAAR = 0x0007; //Max 0x0A - public const ushort RETAINER_BAZAAR = 0x0008; //???? - 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 - - private int endOfListIndex = 0; - + public const ushort NORMAL = 0; //Max 0xC8 + public const ushort TRADE = 1; //Max 0x96 + public const ushort LOOT = 4; //Max 0xA + public const ushort MELDREQUEST = 5; //Max 0x04 + public const ushort BAZAAR = 7; //Max 0x0A + public const ushort CURRENCY_CRYSTALS = 99; //Max 0x140 + public const ushort KEYITEMS = 100; //Max 0x500 + public const ushort EQUIPMENT = 0x00FE; //Max 0x23 + public const ushort EQUIPMENT_OTHERPLAYER = 0x00F9; //Max 0x23 + private Character owner; private ushort inventoryCapacity; private ushort inventoryCode; @@ -33,6 +30,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.player private InventoryItem[] list; private bool[] isDirty; + private int endOfListIndex = 0; + public Inventory(Character ownerPlayer, ushort capacity, ushort code, bool temporary = false) { owner = ownerPlayer; @@ -281,7 +280,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity); isDirty[slot] = true; - SendUpdatePackets((Player)owner); + SendUpdatePackets(); } } @@ -431,9 +430,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player if (owner is Player) Database.AddItem((Player)owner, addedItem, inventoryCode); else if (owner is Retainer) - { - - } + Database.AddItem((Retainer)owner, addedItem, inventoryCode); } private void DoDatabaseQuantity(ulong itemDBId, int quantity) @@ -444,9 +441,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.player if (owner is Player) Database.SetQuantity((Player)owner, itemDBId, inventoryCode); else if (owner is Retainer) - { - - } + Database.SetQuantity((Retainer)owner, itemDBId, inventoryCode); } private void DoDatabaseRemove(ulong itemDBId) @@ -457,18 +452,18 @@ namespace FFXIVClassic_Map_Server.actors.chara.player if (owner is Player) Database.RemoveItem((Player)owner, itemDBId); else if (owner is Retainer) - { - - } + Database.RemoveItem((Retainer)owner, itemDBId); } private void SendUpdatePackets() { if (owner is Player) - SendUpdatePackets((Player)owner); + { + SendUpdatePackets((Player)owner, true); + } } - private void SendUpdatePackets(Player player) + public void SendUpdatePackets(Player player, bool doneImmediate = false) { List items = new List(); List slotsToRemove = new List(); @@ -487,7 +482,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.player slotsToRemove.Add((ushort)i); } - Array.Clear(isDirty, 0, isDirty.Length); + if (doneImmediate) + DoneSendUpdate(); player.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); @@ -498,6 +494,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.player player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); player.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); } + + public void DoneSendUpdate() + { + Array.Clear(isDirty, 0, isDirty.Length); + } + #endregion #region Inventory Utils diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 6e314c58..b6b6de8e 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -161,7 +161,7 @@ namespace FFXIVClassic_Map_Server.Actors inventories[Inventory.NORMAL] = new Inventory(this, MAXSIZE_INVENTORY_NORMAL, Inventory.NORMAL); inventories[Inventory.KEYITEMS] = new Inventory(this, MAXSIZE_INVENTORY_KEYITEMS, Inventory.KEYITEMS); - inventories[Inventory.CURRENCY] = new Inventory(this, MAXSIZE_INVENTORY_CURRANCY, Inventory.CURRENCY); + inventories[Inventory.CURRENCY_CRYSTALS] = new Inventory(this, MAXSIZE_INVENTORY_CURRANCY, Inventory.CURRENCY_CRYSTALS); inventories[Inventory.MELDREQUEST] = new Inventory(this, MAXSIZE_INVENTORY_MELDREQUEST, Inventory.MELDREQUEST); inventories[Inventory.BAZAAR] = new Inventory(this, MAXSIZE_INVENTORY_BAZAAR, Inventory.BAZAAR); inventories[Inventory.LOOT] = new Inventory(this, MAXSIZE_INVENTORY_LOOT, Inventory.LOOT); @@ -535,7 +535,7 @@ namespace FFXIVClassic_Map_Server.Actors #region Inventory & Equipment QueuePacket(InventoryBeginChangePacket.BuildPacket(actorId)); inventories[Inventory.NORMAL].SendFullInventory(this); - inventories[Inventory.CURRENCY].SendFullInventory(this); + inventories[Inventory.CURRENCY_CRYSTALS].SendFullInventory(this); inventories[Inventory.KEYITEMS].SendFullInventory(this); inventories[Inventory.BAZAAR].SendFullInventory(this); inventories[Inventory.MELDREQUEST].SendFullInventory(this); @@ -1048,8 +1048,8 @@ namespace FFXIVClassic_Map_Server.Actors public int GetCurrentGil() { - if (GetInventory(Inventory.CURRENCY).HasItem(1000001)) - return GetInventory(Inventory.CURRENCY).GetItemByCatelogId(1000001).quantity; + if (GetInventory(Inventory.CURRENCY_CRYSTALS).HasItem(1000001)) + return GetInventory(Inventory.CURRENCY_CRYSTALS).GetItemByCatelogId(1000001).quantity; else return 0; } @@ -1745,20 +1745,16 @@ namespace FFXIVClassic_Map_Server.Actors public Retainer SpawnMyRetainer(Npc bell, int retainerIndex) { - Tuple retainerData = Database.GetRetainer(this, retainerIndex); - - ActorClass actorClass = Server.GetWorldManager().GetActorClass(retainerData.Item2); - - if (actorClass == null) - return null; + Retainer retainer = Database.LoadRetainer(this, retainerIndex); 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.Item3, actorClass, this, posX, bell.positionY, positionZ, (float)Math.Atan2(positionX - posX, positionZ - posZ)); - - retainer.LoadEventConditions(actorClass.eventConditions); + retainer.positionX = posX; + retainer.positionY = positionY; + retainer.positionZ = posZ; + retainer.rotation = (float)Math.Atan2(positionX - posX, positionZ - posZ); retainerMeetingGroup = new RetainerMeetingRelationGroup(5555, this, retainer); retainerMeetingGroup.SendGroupPackets(playerSession); diff --git a/FFXIVClassic Map Server/dataobjects/Session.cs b/FFXIVClassic Map Server/dataobjects/Session.cs index bd6a1639..e7d11f02 100644 --- a/FFXIVClassic Map Server/dataobjects/Session.cs +++ b/FFXIVClassic Map Server/dataobjects/Session.cs @@ -118,6 +118,7 @@ namespace FFXIVClassic_Map_Server.dataobjects QueuePacket(actor.GetInitPackets()); QueuePacket(actor.GetSetEventStatusPackets()); actorInstanceList.Add(actor); + ((Npc)actor).DoOnActorSpawn(playerActor); playerActor.sentRetainerSpawn = true; }