diff --git a/Intersect (Core)/Collections/Slotting/ISlot.cs b/Intersect (Core)/Collections/Slotting/ISlot.cs new file mode 100644 index 0000000000..93e3e8110f --- /dev/null +++ b/Intersect (Core)/Collections/Slotting/ISlot.cs @@ -0,0 +1,11 @@ +namespace Intersect.Collections.Slotting; + + +public interface ISlot +{ + + int Slot { get; } + + bool IsEmpty { get; } + +} diff --git a/Intersect (Core)/Collections/Slotting/SlotList.cs b/Intersect (Core)/Collections/Slotting/SlotList.cs new file mode 100644 index 0000000000..047a042f8f --- /dev/null +++ b/Intersect (Core)/Collections/Slotting/SlotList.cs @@ -0,0 +1,101 @@ +using System.Collections; + +namespace Intersect.Collections.Slotting; + +public class SlotList : IList where TSlot : ISlot +{ + private readonly SortedList _slots; + private readonly Func _factory; + + public SlotList(int capacity, Func factory) + { + _slots = new SortedList(capacity); + _factory = factory; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public IEnumerator GetEnumerator() => _slots.Values.GetEnumerator(); + + public void Add(TSlot slot) + { + if (_slots.TryGetValue(slot.Slot, out var existingSlot)) + { + if (existingSlot.IsEmpty) + { + _slots[slot.Slot] = slot; + } + else + { + throw new ArgumentException($"Slot {slot.Slot} is already filled with a non-empty slot.", nameof(slot)); + } + } + else + { + _slots.Add(slot.Slot, slot); + } + } + + public void Clear() => _slots.Clear(); + + public bool Contains(TSlot slot) => _slots.ContainsValue(slot); + + public void CopyTo(TSlot[] array, int arrayIndex) => + _slots.Select(kvp => kvp.Value).ToArray().CopyTo(array, arrayIndex); + + public bool Remove(TSlot slot) => _slots.Remove(slot.Slot); + + public int Capacity + { + get => _slots.Capacity; + set => _slots.Capacity = value; + } + + public int Count => _slots.Count; + + public bool IsReadOnly => ((IDictionary)_slots).IsReadOnly; + + public int IndexOf(TSlot slot) => _slots.IndexOfValue(slot); + + public void Insert(int slotIndex, TSlot slot) + { + if (slotIndex != slot.Slot) + { + throw new ArgumentException($"Tried to insert slot {slot.Slot} to slot index {slotIndex}", nameof(slotIndex)); + } + + _slots.Add(slotIndex, slot); + } + + public void RemoveAt(int slotIndex) => _slots.Remove(slotIndex); + + public TSlot this[int slotIndex] + { + get + { + if (_slots.TryGetValue(slotIndex, out var slot)) + { + return slot; + } + + slot = _factory(slotIndex); + _slots[slotIndex] = slot; + return slot; + } + set => _slots[slotIndex] = value; + } + + public int FindIndex(Func predicate) + { + var copy = this.ToArray(); + for (var index = 0; index < copy.Length; ++index) + { + if (predicate(copy[index])) + { + return index; + } + } + + return -1; + } +} \ No newline at end of file diff --git a/Intersect (Core)/Config/Options.cs b/Intersect (Core)/Config/Options.cs index 3739f63d18..7ac36402ed 100644 --- a/Intersect (Core)/Config/Options.cs +++ b/Intersect (Core)/Config/Options.cs @@ -119,8 +119,6 @@ public static ushort ServerPort public static int MaxInvItems => Instance.PlayerOpts.MaxInventory; - public static int MaxPlayerSkills => Instance.PlayerOpts.MaxSpells; - public static int MaxCharacters => Instance.PlayerOpts.MaxCharacters; public static int ItemDropChance => Instance.PlayerOpts.ItemDropChance; diff --git a/Intersect.Client.Core/Entities/Entity.cs b/Intersect.Client.Core/Entities/Entity.cs index c9b10acaa4..c0bf13282d 100644 --- a/Intersect.Client.Core/Entities/Entity.cs +++ b/Intersect.Client.Core/Entities/Entity.cs @@ -173,7 +173,7 @@ public Guid SpellCast } } - public Spell[] Spells { get; set; } = new Spell[Options.MaxPlayerSkills]; + public Spell[] Spells { get; set; } = new Spell[Options.Instance.PlayerOpts.MaxSpells]; IReadOnlyList IEntity.Spells => Spells.Select(x => x.Id).ToList(); @@ -233,7 +233,7 @@ public Entity(Guid id, EntityPacket? packet, EntityType entityType) Inventory[i] = new Item(); } - for (var i = 0; i < Options.MaxPlayerSkills; i++) + for (var i = 0; i < Options.Instance.PlayerOpts.MaxSpells; i++) { Spells[i] = new Spell(); } diff --git a/Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs b/Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs index 7ffc7e7c52..a56507332d 100644 --- a/Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs +++ b/Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs @@ -86,7 +86,7 @@ void pnl_RightClicked(Base sender, ClickedEventArgs arguments) else { Globals.Me.TryForgetSpell(mYindex); - } + } } void pnl_HoverLeave(Base sender, EventArgs arguments) @@ -263,7 +263,7 @@ public void Update() //Check spell first. if (mSpellWindow.RenderBounds().IntersectsWith(dragRect)) { - for (var i = 0; i < Options.MaxInvItems; i++) + for (var i = 0; i < Options.Instance.PlayerOpts.MaxSpells; i++) { if (i < mSpellWindow.Items.Count && mSpellWindow.Items[i].RenderBounds().IntersectsWith(dragRect)) diff --git a/Intersect.Client.Core/Interface/Game/Spells/SpellsWindow.cs b/Intersect.Client.Core/Interface/Game/Spells/SpellsWindow.cs index 8119c0af11..3734a2b102 100644 --- a/Intersect.Client.Core/Interface/Game/Spells/SpellsWindow.cs +++ b/Intersect.Client.Core/Interface/Game/Spells/SpellsWindow.cs @@ -120,7 +120,7 @@ public void Update() X = mSpellWindow.X; Y = mSpellWindow.Y; - for (var i = 0; i < Options.MaxPlayerSkills; i++) + for (var i = 0; i < Options.Instance.PlayerOpts.MaxSpells; i++) { var spell = SpellBase.Get(Globals.Me.Spells[i].Id); Items[i].Pnl.IsHidden = spell == null || Items[i].IsDragging; @@ -133,7 +133,7 @@ public void Update() private void InitItemContainer() { - for (var i = 0; i < Options.MaxPlayerSkills; i++) + for (var i = 0; i < Options.Instance.PlayerOpts.MaxSpells; i++) { Items.Add(new SpellItem(this, i)); Items[i].Container = new ImagePanel(mItemContainer, "Spell"); diff --git a/Intersect.Server.Core/Database/DbInterface.cs b/Intersect.Server.Core/Database/DbInterface.cs index e6d2d474a9..8c8493f57e 100644 --- a/Intersect.Server.Core/Database/DbInterface.cs +++ b/Intersect.Server.Core/Database/DbInterface.cs @@ -522,8 +522,7 @@ public static Player GetUserCharacter(User user, Guid playerId, bool explicitLoa try { using var playerContext = CreatePlayerContext(readOnly: true, explicitLoad: false); - player.LoadRelationships(playerContext); - _ = Player.Validate(player); + _ = player.LoadRelationships(playerContext); } catch (Exception exception) { diff --git a/Intersect.Server.Core/Database/Item.cs b/Intersect.Server.Core/Database/Item.cs index f45309155e..daabf3a830 100644 --- a/Intersect.Server.Core/Database/Item.cs +++ b/Intersect.Server.Core/Database/Item.cs @@ -10,7 +10,7 @@ namespace Intersect.Server.Database; -public class Item: IItem +public class Item : IItem { [JsonIgnore][NotMapped] public double DropChance { get; set; } = 100; diff --git a/Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs index 681f08ef3a..3c89555710 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; - +using Intersect.Collections.Slotting; using Newtonsoft.Json; // ReSharper disable UnusedAutoPropertyAccessor.Local @@ -23,6 +23,8 @@ public BagSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + public bool IsEmpty => ItemId == default; + [JsonIgnore] public Guid ParentBagId { get; private set; } @@ -30,5 +32,4 @@ public BagSlot(int slot) public virtual Bag ParentBag { get; private set; } public int Slot { get; private set; } - } diff --git a/Intersect.Server.Core/Database/PlayerData/Players/BankSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/BankSlot.cs index f3e5c106dc..73b512f13e 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/BankSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/BankSlot.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; - +using Intersect.Collections.Slotting; using Intersect.Server.Entities; using Newtonsoft.Json; @@ -12,6 +12,7 @@ namespace Intersect.Server.Database.PlayerData.Players; public partial class BankSlot : Item, ISlot, IPlayerOwned { + public static BankSlot Create(int slotIndex) => new(slotIndex); public BankSlot() { @@ -25,6 +26,8 @@ public BankSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + public bool IsEmpty => ItemId == default; + [JsonIgnore] public Guid PlayerId { get; private set; } diff --git a/Intersect.Server.Core/Database/PlayerData/Players/Guild.cs b/Intersect.Server.Core/Database/PlayerData/Players/Guild.cs index 6487a2c189..3aa9fa11fb 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/Guild.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/Guild.cs @@ -5,6 +5,7 @@ using Intersect.Server.Entities; using Intersect.Server.Networking; using System.Collections.Concurrent; +using Intersect.Collections.Slotting; using Microsoft.EntityFrameworkCore; using Intersect.Network.Packets.Server; using Intersect.GameObjects; @@ -50,7 +51,10 @@ public Guild() /// Guild bank slots /// [JsonIgnore] - public virtual List Bank { get; set; } = new List(); + public virtual SlotList Bank { get; set; } = new SlotList( + Options.Instance.Guild.InitialBankSlots, + GuildBankSlot.Create + ); /// /// Sets the number of bank slots alotted to this guild. Banks lots can only expand. @@ -117,8 +121,7 @@ public static Guild CreateGuild(Player creator, string name) GuildInstanceId = Guid.NewGuid() }; - SlotHelper.ValidateSlots(guild.Bank, Options.Instance.Guild.InitialBankSlots); - guild.Bank = guild.Bank.OrderBy(bankSlot => bankSlot?.Slot).ToList(); + SlotHelper.ValidateSlotList(guild.Bank, Options.Instance.Guild.InitialBankSlots); var player = context.Players.FirstOrDefault(p => p.Id == creator.Id); if (player != null) @@ -181,8 +184,7 @@ public static Guild LoadGuild(Guid id) guild.Members.AddOrUpdate(member.Key, guildMember, (key, oldValue) => guildMember); } - SlotHelper.ValidateSlots(guild.Bank, guild.BankSlotsCount); - guild.Bank = guild.Bank.OrderBy(bankSlot => bankSlot?.Slot).ToList(); + SlotHelper.ValidateSlotList(guild.Bank, guild.BankSlotsCount); Guilds.AddOrUpdate(id, guild, (key, oldValue) => guild); @@ -310,7 +312,7 @@ public void RemoveMember(Guid targetId, Player targetPlayer, Player initiator = } /// - /// + /// /// public void Update() { @@ -690,14 +692,16 @@ public void BankSlotUpdated(int slot) /// public void ExpandBankSlots(int count) { - if (BankSlotsCount < count && count <= Options.Instance.Bank.MaxSlots) + if (BankSlotsCount >= count || count > Options.Instance.Bank.MaxSlots) { - lock (mLock) - { - BankSlotsCount = count; - SlotHelper.ValidateSlots(Bank, BankSlotsCount); - DbInterface.Pool.QueueWorkItem(Save); - } + return; + } + + lock (mLock) + { + BankSlotsCount = count; + SlotHelper.ValidateSlotList(Bank, BankSlotsCount); + DbInterface.Pool.QueueWorkItem(Save); } } diff --git a/Intersect.Server.Core/Database/PlayerData/Players/GuildBankSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/GuildBankSlot.cs index 9edee080d0..2b28907d58 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/GuildBankSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/GuildBankSlot.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; - +using Intersect.Collections.Slotting; using Newtonsoft.Json; // ReSharper disable UnusedAutoPropertyAccessor.Local @@ -10,6 +10,7 @@ namespace Intersect.Server.Database.PlayerData.Players; public partial class GuildBankSlot : Item, ISlot { + public static GuildBankSlot Create(int slotIndex) => new(slotIndex); public GuildBankSlot() { @@ -23,6 +24,8 @@ public GuildBankSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + public bool IsEmpty => ItemId == default; + [JsonIgnore] public Guid GuildId { get; private set; } @@ -30,5 +33,4 @@ public GuildBankSlot(int slot) public virtual Guild Guild { get; private set; } public int Slot { get; private set; } - } diff --git a/Intersect.Server.Core/Database/PlayerData/Players/HotbarSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/HotbarSlot.cs index dc85b59176..95ca33a414 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/HotbarSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/HotbarSlot.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; - +using Intersect.Collections.Slotting; using Intersect.Enums; using Intersect.Server.Entities; using Intersect.Utilities; @@ -14,6 +14,7 @@ namespace Intersect.Server.Database.PlayerData.Players; public partial class HotbarSlot : ISlot, IPlayerOwned { + public static HotbarSlot Create(int slotIndex) => new(slotIndex); public HotbarSlot() { @@ -27,6 +28,8 @@ public HotbarSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + public bool IsEmpty => ItemOrSpellId == default; + public Guid ItemOrSpellId { get; set; } = Guid.Empty; public Guid BagId { get; set; } = Guid.Empty; diff --git a/Intersect.Server.Core/Database/PlayerData/Players/ISlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/ISlot.cs deleted file mode 100644 index b0f1fd05e0..0000000000 --- a/Intersect.Server.Core/Database/PlayerData/Players/ISlot.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Intersect.Server.Database.PlayerData.Players; - - -public interface ISlot -{ - - int Slot { get; } - -} diff --git a/Intersect.Server.Core/Database/PlayerData/Players/InventorySlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/InventorySlot.cs index a9be54cc77..1a37eeaf07 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/InventorySlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/InventorySlot.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; - +using Intersect.Collections.Slotting; using Intersect.Server.Entities; using Newtonsoft.Json; @@ -12,6 +12,7 @@ namespace Intersect.Server.Database.PlayerData.Players; public partial class InventorySlot : Item, ISlot, IPlayerOwned { + public static InventorySlot Create(int slotIndex) => new(slotIndex); public InventorySlot() { @@ -25,6 +26,8 @@ public InventorySlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + public bool IsEmpty => ItemId == default; + [JsonIgnore] public Guid PlayerId { get; private set; } diff --git a/Intersect.Server.Core/Database/PlayerData/Players/SlotHelper.cs b/Intersect.Server.Core/Database/PlayerData/Players/SlotHelper.cs index 9ff69b7971..1f3da6f2c9 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/SlotHelper.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/SlotHelper.cs @@ -1,4 +1,6 @@ -namespace Intersect.Server.Database.PlayerData.Players; +using Intersect.Collections.Slotting; + +namespace Intersect.Server.Database.PlayerData.Players; public static partial class SlotHelper @@ -41,10 +43,28 @@ public static TSlot ConstructSlot(int slotIndex) where TSlot : ISlot } } + public static bool ValidateSlotList(SlotList slots, int capacity) where TSlot : ISlot + { + if (slots.Capacity == capacity) + { + return true; + } + + if (slots.Count > capacity) + { + return false; + } + + slots.Capacity = capacity; + return true; + } + public static bool ValidateSlots( List slots, int targetCount, - Func factory = null + Func factory = null, + Action onSlotCreated = null, + bool createMissing = true ) where TSlot : ISlot { if (slots.Count >= targetCount) @@ -61,7 +81,14 @@ public static bool ValidateSlots( continue; } - slots.Add(factory == null ? ConstructSlot(slotIndex) : factory.Invoke(slotIndex)); + if (!createMissing) + { + continue; + } + + var createdSlot = factory == null ? ConstructSlot(slotIndex) : factory.Invoke(slotIndex); + slots.Add(createdSlot); + onSlotCreated?.Invoke(createdSlot, slotIndex); } return true; diff --git a/Intersect.Server.Core/Database/PlayerData/Players/SpellSlot.cs b/Intersect.Server.Core/Database/PlayerData/Players/SpellSlot.cs index 95c5725f7d..be3606a91a 100644 --- a/Intersect.Server.Core/Database/PlayerData/Players/SpellSlot.cs +++ b/Intersect.Server.Core/Database/PlayerData/Players/SpellSlot.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; - +using Intersect.Collections.Slotting; using Intersect.Server.Entities; using Newtonsoft.Json; @@ -12,6 +12,7 @@ namespace Intersect.Server.Database.PlayerData.Players; public partial class SpellSlot : Spell, ISlot, IPlayerOwned { + public static SpellSlot Create(int slotIndex) => new(slotIndex); public SpellSlot() { @@ -25,6 +26,8 @@ public SpellSlot(int slot) [DatabaseGenerated(DatabaseGeneratedOption.Identity), JsonIgnore] public Guid Id { get; private set; } + public bool IsEmpty => SpellId == default; + [JsonIgnore] public Guid PlayerId { get; private set; } diff --git a/Intersect.Server.Core/Database/PlayerData/User.cs b/Intersect.Server.Core/Database/PlayerData/User.cs index 8c7f26aaae..56abba9ca5 100644 --- a/Intersect.Server.Core/Database/PlayerData/User.cs +++ b/Intersect.Server.Core/Database/PlayerData/User.cs @@ -199,7 +199,7 @@ public bool TryAddCharacter(Player? newCharacter) Players.Add(newCharacter); - Player.Validate(newCharacter); + _ = Player.Validate(newCharacter); context.ChangeTracker.DetectChanges(); diff --git a/Intersect.Server.Core/Entities/BankInterface.cs b/Intersect.Server.Core/Entities/BankInterface.cs index ccb9dc993c..3d6849ee67 100644 --- a/Intersect.Server.Core/Entities/BankInterface.cs +++ b/Intersect.Server.Core/Entities/BankInterface.cs @@ -6,102 +6,104 @@ using Intersect.Server.Localization; using Intersect.Server.Networking; using System.Diagnostics; +using Intersect.Collections.Slotting; using Log = Intersect.Logging.Log; namespace Intersect.Server.Entities; -public partial class BankInterface +public partial class BankInterface : IBankInterface where TSlot : Item, ISlot { - private Player mPlayer; + private readonly Player _player; - private IList mBank; + private readonly SlotList _bank; - private Guild mGuild; + private readonly Guild _guild; - private object mLock; + private readonly object _lock; - private int mMaxSlots; - - public BankInterface(Player player, IList bank, object bankLock, Guild guild, int maxSlots) + public BankInterface(Player player, SlotList bank, Guild? guild = default) { - mPlayer = player; - mBank = bank; - mGuild = guild; - mLock = bankLock; - mMaxSlots = maxSlots; + _player = player; + _bank = bank; + _guild = guild; + _lock = guild?.Lock ?? new object(); } public void SendOpenBank() { - var items = new List(); + var slotUpdatePackets = new List(); - for (var slot = 0; slot < mMaxSlots; slot++) + for (var slot = 0; slot < _bank.Capacity; slot++) { - if (mBank[slot] != null && mBank[slot].ItemId != Guid.Empty && mBank[slot].Quantity > 0) - { - items.Add( - new BankUpdatePacket( - slot, mBank[slot].ItemId, mBank[slot].Quantity, mBank[slot].BagId, - mBank[slot].Properties - ) - ); - } - else - { - items.Add(new BankUpdatePacket(slot, Guid.Empty, 0, null, null)); - } + var slotItem = slot < _bank.Count ? _bank[slot] : default; + slotUpdatePackets.Add( + new BankUpdatePacket( + slot, + slotItem?.ItemId ?? Guid.Empty, + slotItem?.Quantity ?? 0, + slotItem?.BagId, + slotItem?.Properties + ) + ); } - mPlayer?.SendPacket(new BankPacket(false, mGuild != null, mMaxSlots, items.ToArray())); + _player?.SendPacket( + new BankPacket( + false, + _guild != null, + _bank.Capacity, + slotUpdatePackets.ToArray() + ) + ); } //BankUpdatePacket public void SendBankUpdate(int slot, bool sendToAll = true) { - if (sendToAll && mGuild != null) + if (sendToAll && _guild != null) { - mGuild.BankSlotUpdated(slot); + _guild.BankSlotUpdated(slot); return; } - if (mBank[slot] != null && mBank[slot].ItemId != Guid.Empty && mBank[slot].Quantity > 0) + if (_bank[slot] != null && _bank[slot].ItemId != Guid.Empty && _bank[slot].Quantity > 0) { - mPlayer?.SendPacket( + _player?.SendPacket( new BankUpdatePacket( - slot, mBank[slot].ItemId, mBank[slot].Quantity, mBank[slot].BagId, - mBank[slot].Properties + slot, _bank[slot].ItemId, _bank[slot].Quantity, _bank[slot].BagId, + _bank[slot].Properties ) ); } else { - mPlayer?.SendPacket(new BankUpdatePacket(slot, Guid.Empty, 0, null, null)); + _player?.SendPacket(new BankUpdatePacket(slot, Guid.Empty, 0, null, null)); } } public void SendCloseBank() { - mPlayer?.SendPacket(new BankPacket(true, false, -1, null)); + _player?.SendPacket(new BankPacket(true, false, -1, null)); } public bool TryDepositItem(Item? slot, int inventorySlotIndex, int quantityHint, int bankSlotIndex = -1, bool sendUpdate = true) { //Permission Check - if (mGuild != null) + if (_guild != null) { - var rank = Options.Instance.Guild.Ranks[Math.Max(0, Math.Min(Options.Instance.Guild.Ranks.Length - 1, mPlayer.GuildRank))]; - if (!rank.Permissions.BankDeposit && mPlayer.GuildRank != 0) + var rank = Options.Instance.Guild.Ranks[Math.Max(0, Math.Min(Options.Instance.Guild.Ranks.Length - 1, _player.GuildRank))]; + if (!rank.Permissions.BankDeposit && _player.GuildRank != 0) { - PacketSender.SendChatMsg(mPlayer, Strings.Guilds.NotAllowedDeposit.ToString(mGuild.Name), ChatMessageType.Bank, CustomColors.Alerts.Error); + PacketSender.SendChatMsg(_player, Strings.Guilds.NotAllowedDeposit.ToString(_guild.Name), ChatMessageType.Bank, CustomColors.Alerts.Error); return false; } } - slot ??= mPlayer.Items[inventorySlotIndex]; + slot ??= _player.Items[inventorySlotIndex]; if (!ItemBase.TryGet(slot.ItemId, out var itemDescriptor)) { PacketSender.SendChatMsg( - mPlayer, + _player, Strings.Banks.DepositInvalid, ChatMessageType.Bank, CustomColors.Alerts.Error @@ -109,22 +111,22 @@ public bool TryDepositItem(Item? slot, int inventorySlotIndex, int quantityHint, return false; } - var canBank = (itemDescriptor.CanBank && mGuild == default) || - (itemDescriptor.CanGuildBank && mGuild != default); + var canBank = (itemDescriptor.CanBank && _guild == default) || + (itemDescriptor.CanGuildBank && _guild != default); if (!canBank) { - PacketSender.SendChatMsg(mPlayer, Strings.Items.NoBank, ChatMessageType.Bank, CustomColors.Items.Bound); + PacketSender.SendChatMsg(_player, Strings.Items.NoBank, ChatMessageType.Bank, CustomColors.Items.Bound); return false; } - var sourceSlots = mPlayer.Items.ToArray(); + var sourceSlots = _player.Items.ToArray(); var maximumStack = itemDescriptor.Stackable ? itemDescriptor.MaxBankStack : 1; var sourceQuantity = Item.FindQuantityOfItem(itemDescriptor.Id, sourceSlots); - var targetSlots = mBank.ToArray(); + var targetSlots = _bank.ToArray(); - lock (mLock) + lock (_lock) { var movableQuantity = Item.FindSpaceForItem( itemDescriptor.Id, @@ -138,7 +140,7 @@ public bool TryDepositItem(Item? slot, int inventorySlotIndex, int quantityHint, if (movableQuantity < 1) { PacketSender.SendChatMsg( - mPlayer, + _player, Strings.Items.NoSpaceForItem, ChatMessageType.Bank, CustomColors.Alerts.Error @@ -164,7 +166,7 @@ out var slotIndicesToRemoveFrom )) { PacketSender.SendChatMsg( - mPlayer, + _player, Strings.Banks.WithdrawInvalid, ChatMessageType.Bank, CustomColors.Alerts.Error @@ -178,7 +180,7 @@ out var slotIndicesToRemoveFrom { if (slotIndicesToRemoveFrom.Length <= nextSlotIndexToRemoveFrom) { - Log.Warn($"Ran out of slots to remove from for {mPlayer.Id}"); + Log.Warn($"Ran out of slots to remove from for {_player.Id}"); break; } @@ -221,7 +223,7 @@ out var slotIndicesToRemoveFrom { if (remainingQuantityToRemove < 1) { - Log.Error($"Potential inventory corruption for {mPlayer.Id}"); + Log.Error($"Potential inventory corruption for {_player.Id}"); } var slotToRemoveFrom = sourceSlots[slotIndexToRemoveFrom]; @@ -232,7 +234,7 @@ out var slotIndicesToRemoveFrom // If the item is equipped equipment, we need to unequip it before taking it out of the inventory. if (itemDescriptor.ItemType == ItemType.Equipment && slotIndexToRemoveFrom > -1) { - mPlayer.EquipmentProcessItemLoss(slotIndexToRemoveFrom); + _player.EquipmentProcessItemLoss(slotIndexToRemoveFrom); } if (slotToRemoveFrom.Quantity < 1) @@ -246,28 +248,28 @@ out var slotIndicesToRemoveFrom // ReSharper disable once ConvertIfStatementToSwitchStatement if (remainingQuantity < 0) { - Log.Error($"{mPlayer.Id} was accidentally given {-remainingQuantity}x extra {itemDescriptor.Id}"); + Log.Error($"{_player.Id} was accidentally given {-remainingQuantity}x extra {itemDescriptor.Id}"); } else if (remainingQuantity > 0) { - Log.Error($"{mPlayer.Id} was not given {remainingQuantity}x {itemDescriptor.Id}"); + Log.Error($"{_player.Id} was not given {remainingQuantity}x {itemDescriptor.Id}"); } // ReSharper disable once ConvertIfStatementToSwitchStatement if (remainingQuantityToRemove < 0) { - Log.Error($"{mPlayer.Id} was scammed {-remainingQuantity}x {itemDescriptor.Id}"); + Log.Error($"{_player.Id} was scammed {-remainingQuantity}x {itemDescriptor.Id}"); } else if (remainingQuantityToRemove > 0) { - Log.Error($"{mPlayer.Id} did not have {remainingQuantity}x {itemDescriptor.Id} taken"); + Log.Error($"{_player.Id} did not have {remainingQuantity}x {itemDescriptor.Id} taken"); } if (sendUpdate) { foreach (var slotIndexToUpdate in slotIndicesToRemoveFrom) { - PacketSender.SendInventoryItemUpdate(mPlayer, slotIndexToUpdate); + PacketSender.SendInventoryItemUpdate(_player, slotIndexToUpdate); } foreach (var slotIndexToFill in slotIndicesToFill) @@ -277,20 +279,20 @@ out var slotIndicesToRemoveFrom if (inventorySlotIndex > -1) { - PacketSender.SendInventoryItemUpdate(mPlayer, inventorySlotIndex); + PacketSender.SendInventoryItemUpdate(_player, inventorySlotIndex); } } - if (mGuild != null) + if (_guild != null) { - DbInterface.Pool.QueueWorkItem(mGuild.Save); + DbInterface.Pool.QueueWorkItem(_guild.Save); } var successMessage = movableQuantity > 1 ? Strings.Banks.DepositSuccessStackable.ToString(movableQuantity, itemDescriptor.Name) : Strings.Banks.DepositSuccessNonStackable.ToString(itemDescriptor.Name); - PacketSender.SendChatMsg(mPlayer, successMessage, ChatMessageType.Bank, CustomColors.Alerts.Success); + PacketSender.SendChatMsg(_player, successMessage, ChatMessageType.Bank, CustomColors.Alerts.Success); return true; } @@ -302,14 +304,14 @@ public bool TryDepositItem(Item item, bool sendUpdate = true) => public bool TryWithdrawItem(Item? slot, int bankSlotIndex, int quantityHint, int inventorySlotIndex = -1) { //Permission Check - if (mGuild != null) + if (_guild != null) { - var rank = Options.Instance.Guild.Ranks[Math.Max(0, Math.Min(Options.Instance.Guild.Ranks.Length - 1, mPlayer.GuildRank))]; - if (!rank.Permissions.BankRetrieve && mPlayer.GuildRank != 0) + var rank = Options.Instance.Guild.Ranks[Math.Max(0, Math.Min(Options.Instance.Guild.Ranks.Length - 1, _player.GuildRank))]; + if (!rank.Permissions.BankRetrieve && _player.GuildRank != 0) { PacketSender.SendChatMsg( - mPlayer, - Strings.Guilds.NotAllowedWithdraw.ToString(mGuild.Name), + _player, + Strings.Guilds.NotAllowedWithdraw.ToString(_guild.Name), ChatMessageType.Bank, CustomColors.Alerts.Error ); @@ -317,11 +319,11 @@ public bool TryWithdrawItem(Item? slot, int bankSlotIndex, int quantityHint, int } } - slot ??= mBank[bankSlotIndex]; + slot ??= _bank[bankSlotIndex]; if (!ItemBase.TryGet(slot.ItemId, out var itemDescriptor)) { PacketSender.SendChatMsg( - mPlayer, + _player, Strings.Banks.WithdrawInvalid, ChatMessageType.Bank, CustomColors.Alerts.Error @@ -330,12 +332,12 @@ public bool TryWithdrawItem(Item? slot, int bankSlotIndex, int quantityHint, int } var maximumStack = itemDescriptor.Stackable ? itemDescriptor.MaxInventoryStack : 1; - var sourceSlots = mBank.ToArray(); + var sourceSlots = _bank.ToArray(); var sourceQuantity = Item.FindQuantityOfItem(itemDescriptor.Id, sourceSlots); - var targetSlots = mPlayer.Items.ToArray(); + var targetSlots = _player.Items.ToArray(); - lock (mLock) + lock (_lock) { var movableQuantity = Item.FindSpaceForItem( itemDescriptor.Id, @@ -349,7 +351,7 @@ public bool TryWithdrawItem(Item? slot, int bankSlotIndex, int quantityHint, int if (movableQuantity < 1) { PacketSender.SendChatMsg( - mPlayer, + _player, Strings.Items.NoSpaceForItem, ChatMessageType.Inventory, CustomColors.Alerts.Error @@ -375,7 +377,7 @@ out var slotIndicesToRemoveFrom )) { PacketSender.SendChatMsg( - mPlayer, + _player, Strings.Banks.WithdrawInvalid, ChatMessageType.Bank, CustomColors.Alerts.Error @@ -389,7 +391,7 @@ out var slotIndicesToRemoveFrom { if (slotIndicesToRemoveFrom.Length <= nextSlotIndexToRemoveFrom) { - Log.Warn($"Ran out of slots to remove from for {mPlayer.Id}"); + Log.Warn($"Ran out of slots to remove from for {_player.Id}"); break; } @@ -437,7 +439,7 @@ out var slotIndicesToRemoveFrom { if (remainingQuantityToRemove < 1) { - Log.Error($"Potential bank corruption for {mPlayer.Id}"); + Log.Error($"Potential bank corruption for {_player.Id}"); } var slotToRemoveFrom = sourceSlots[slotIndexToRemoveFrom]; @@ -455,26 +457,26 @@ out var slotIndicesToRemoveFrom // ReSharper disable once ConvertIfStatementToSwitchStatement if (remainingQuantity < 0) { - Log.Error($"{mPlayer.Id} was accidentally given {-remainingQuantity}x extra {itemDescriptor.Id}"); + Log.Error($"{_player.Id} was accidentally given {-remainingQuantity}x extra {itemDescriptor.Id}"); } else if (remainingQuantity > 0) { - Log.Error($"{mPlayer.Id} was not given {remainingQuantity}x {itemDescriptor.Id}"); + Log.Error($"{_player.Id} was not given {remainingQuantity}x {itemDescriptor.Id}"); } // ReSharper disable once ConvertIfStatementToSwitchStatement if (remainingQuantityToRemove < 0) { - Log.Error($"{mPlayer.Id} was scammed {-remainingQuantity}x {itemDescriptor.Id}"); + Log.Error($"{_player.Id} was scammed {-remainingQuantity}x {itemDescriptor.Id}"); } else if (remainingQuantityToRemove > 0) { - Log.Error($"{mPlayer.Id} did not have {remainingQuantity}x {itemDescriptor.Id} taken"); + Log.Error($"{_player.Id} did not have {remainingQuantity}x {itemDescriptor.Id} taken"); } foreach (var slotIndexToUpdate in slotIndicesToFill) { - PacketSender.SendInventoryItemUpdate(mPlayer, slotIndexToUpdate); + PacketSender.SendInventoryItemUpdate(_player, slotIndexToUpdate); } foreach (var slotIndexToUpdate in slotIndicesToRemoveFrom) @@ -482,16 +484,16 @@ out var slotIndicesToRemoveFrom SendBankUpdate(slotIndexToUpdate); } - if (mGuild != null) + if (_guild != null) { - DbInterface.Pool.QueueWorkItem(mGuild.Save); + DbInterface.Pool.QueueWorkItem(_guild.Save); } var successMessage = movableQuantity > 1 ? Strings.Banks.WithdrawSuccessStackable.ToString(quantityHint, itemDescriptor.Name) : Strings.Banks.WithdrawSuccessNonStackable.ToString(itemDescriptor.Name); - PacketSender.SendChatMsg(mPlayer, successMessage, ChatMessageType.Bank, CustomColors.Alerts.Success); + PacketSender.SendChatMsg(_player, successMessage, ChatMessageType.Bank, CustomColors.Alerts.Success); return true; } @@ -499,36 +501,36 @@ out var slotIndicesToRemoveFrom public void SwapBankItems(int slotFrom, int slotTo) { - var bank = mBank; + var bank = _bank; if (bank == null) { - Log.Error($"SwapBankItems() called on invalid bank for {mPlayer.Id}"); + Log.Error($"SwapBankItems() called on invalid bank for {_player.Id}"); return; } - if (Math.Clamp(slotFrom, 0, bank.Count - 1) != slotFrom || Math.Clamp(slotTo, 0, bank.Count - 1) != slotTo) + if (Math.Clamp(slotFrom, 0, bank.Capacity - 1) != slotFrom || Math.Clamp(slotTo, 0, bank.Capacity - 1) != slotTo) { PacketSender.SendChatMsg( - mPlayer, + _player, Strings.Banks.InvalidSlotToSwap, ChatMessageType.Bank, CustomColors.Alerts.Error ); - Log.Error($"Invalid slot indices SwapBankItems({slotFrom}, {slotTo}) ({bank.Count}, {mPlayer.Id})"); + Log.Error($"Invalid slot indices SwapBankItems({slotFrom}, {slotTo}) ({bank.Capacity}, {_player.Id})"); return; } //Permission Check - if (mGuild != null) + if (_guild != null) { var ranks = Options.Instance.Guild.Ranks; - var rankIndex = Math.Clamp(mPlayer.GuildRank, 0, ranks.Length - 1); + var rankIndex = Math.Clamp(_player.GuildRank, 0, ranks.Length - 1); var rank = ranks[rankIndex]; - if (mPlayer.GuildRank != rankIndex || (!rank.Permissions.BankMove && mPlayer.GuildRank != 0)) + if (_player.GuildRank != rankIndex || (!rank.Permissions.BankMove && _player.GuildRank != 0)) { PacketSender.SendChatMsg( - mPlayer, - Strings.Guilds.NotAllowedSwap.ToString(mGuild.Name), + _player, + Strings.Guilds.NotAllowedSwap.ToString(_guild.Name), ChatMessageType.Bank, CustomColors.Alerts.Error ); @@ -536,26 +538,26 @@ public void SwapBankItems(int slotFrom, int slotTo) } } - lock (mLock) + lock (_lock) { try { - var destinationSlot = (bank[slotTo] ??= Item.None); - var sourceSlot = (bank[slotFrom] ??= Item.None); + var destinationSlot = bank[slotTo]; + var sourceSlot = bank[slotFrom]; var temporarySlot = destinationSlot.Clone(); if (destinationSlot.ItemId == sourceSlot.ItemId) { if (sourceSlot.ItemId == default) { - Log.Warn($"SwapBankItems({slotFrom}, {slotTo}) for {mPlayer.Id} with empty item ID"); + Log.Warn($"SwapBankItems({slotFrom}, {slotTo}) for {_player.Id} with empty item ID"); return; } /* Items are the same, move the maximum quantity */ if (!ItemBase.TryGet(sourceSlot.ItemId, out var itemDescriptor)) { - Log.Error($"SwapBankItems({slotFrom}, {slotTo}) for {mPlayer.Id} failed due to missing item {sourceSlot.ItemId}"); + Log.Error($"SwapBankItems({slotFrom}, {slotTo}) for {_player.Id} failed due to missing item {sourceSlot.ItemId}"); return; } @@ -591,13 +593,13 @@ public void SwapBankItems(int slotFrom, int slotTo) } catch (Exception exception) { - Log.Error(exception, $"Error in SwapBankItems({slotFrom}, {slotTo}) for {mPlayer.Id}"); + Log.Error(exception, $"Error in SwapBankItems({slotFrom}, {slotTo}) for {_player.Id}"); return; } - if (mGuild != null) + if (_guild != null) { - DbInterface.Pool.QueueWorkItem(mGuild.Save); + DbInterface.Pool.QueueWorkItem(_guild.Save); } } @@ -609,7 +611,7 @@ public void SwapBankItems(int slotFrom, int slotTo) public void Dispose() { SendCloseBank(); - mPlayer.GuildBank = false; - mPlayer.BankInterface = null; + _player.GuildBank = false; + _player.BankInterface = null; } } diff --git a/Intersect.Server.Core/Entities/Entity.cs b/Intersect.Server.Core/Entities/Entity.cs index 5c08fb5bbb..4953fa8844 100644 --- a/Intersect.Server.Core/Entities/Entity.cs +++ b/Intersect.Server.Core/Entities/Entity.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.ComponentModel.DataAnnotations.Schema; +using Intersect.Collections.Slotting; using Intersect.Enums; using Intersect.GameObjects; using Intersect.GameObjects.Events; @@ -27,8 +28,8 @@ public abstract partial class Entity : IEntity { //Instance Values private Guid _id = Guid.NewGuid(); - - [NotMapped] + + [NotMapped] public Guid MapInstanceId { get; set; } = Guid.Empty; [JsonProperty("MaxVitals"), NotMapped] private long[] _maxVital = new long[Enum.GetValues().Length]; @@ -138,11 +139,17 @@ public string StatPointsJson //Inventory [JsonIgnore] - public virtual List Items { get; set; } = new List(); + public virtual SlotList Items { get; set; } = new( + Options.Instance.PlayerOpts.MaxInventory, + InventorySlot.Create + ); //Spells [JsonIgnore] - public virtual List Spells { get; set; } = new List(); + public virtual SlotList Spells { get; set; } = new( + Options.Instance.PlayerOpts.MaxSpells, + SpellSlot.Create + ); [JsonIgnore, Column(nameof(NameColor))] public string NameColorJson @@ -386,7 +393,7 @@ public virtual void Update(long timeMs) /// public virtual void UpdateSpellCooldown(SpellBase spellBase, int spellSlot) { - if (spellSlot < 0 || spellSlot >= Options.MaxPlayerSkills) + if (spellSlot < 0 || spellSlot >= Options.Instance.PlayerOpts.MaxSpells) { return; } diff --git a/Intersect.Server.Core/Entities/IBankInterface.cs b/Intersect.Server.Core/Entities/IBankInterface.cs new file mode 100644 index 0000000000..24c0128666 --- /dev/null +++ b/Intersect.Server.Core/Entities/IBankInterface.cs @@ -0,0 +1,14 @@ +using Intersect.Server.Database; + +namespace Intersect.Server.Entities; + +public interface IBankInterface : IDisposable +{ + void SendOpenBank(); + void SendBankUpdate(int slot, bool sendToAll = true); + void SendCloseBank(); + bool TryDepositItem(Item slot, int inventorySlotIndex, int quantityHint, int bankSlotIndex = -1, bool sendUpdate = true); + bool TryDepositItem(Item item, bool sendUpdate = true); + bool TryWithdrawItem(Item slot, int bankSlotIndex, int quantityHint, int inventorySlotIndex = -1); + void SwapBankItems(int slotFrom, int slotTo); +} \ No newline at end of file diff --git a/Intersect.Server.Core/Entities/Player.Database.cs b/Intersect.Server.Core/Entities/Player.Database.cs index 72cbc8d874..2ada42b872 100644 --- a/Intersect.Server.Core/Entities/Player.Database.cs +++ b/Intersect.Server.Core/Entities/Player.Database.cs @@ -78,10 +78,10 @@ public static Player Find(Guid playerId) try { - using (var context = DbInterface.CreatePlayerContext()) - { - return Validate(QueryPlayerById(context, playerId)); - } + using var context = DbInterface.CreatePlayerContext(); + player = QueryPlayerById(context, playerId); + _ = Validate(player); + return player; } catch (Exception ex) { @@ -105,10 +105,10 @@ public static Player Find(string playerName) try { - using (var context = DbInterface.CreatePlayerContext()) - { - return Validate(QueryPlayerByName(context, playerName)); - } + using var context = DbInterface.CreatePlayerContext(); + player = QueryPlayerByName(context, playerName); + _ = Validate(player); + return player; } catch (Exception ex) { @@ -148,14 +148,14 @@ public static bool PlayerExists(string name) #region Loading - public void LoadRelationships(PlayerContext playerContext) + public bool LoadRelationships(PlayerContext playerContext) { lock (_savingLock) { if (_saving) { Log.Warn($"Skipping loading relationships for player {Id} because it is being saved."); - return; + return false; } } @@ -166,40 +166,41 @@ public void LoadRelationships(PlayerContext playerContext) entityEntry.Collection(p => p.Quests).Load(); entityEntry.Collection(p => p.Spells).Load(); entityEntry.Collection(p => p.Variables).Load(); - Validate(this); + return Validate(this, playerContext); } public static Player Load(Guid playerId) { var player = Find(playerId); - - return Validate(player); + _ = Validate(player); + return player; } public static Player Load(string playerName) { var player = Find(playerName); - - return Validate(player); + _ = Validate(player); + return player; } - public static Player Validate(Player player) + public static bool Validate(Player? player, PlayerContext? playerContext = default) { if (player == null) { - return null; + return false; } - // ReSharper disable once InvertIf - if (!player.ValidateLists()) + if (player.ValidateLists(playerContext)) { - player.Bank = player.Bank.OrderBy(bankSlot => bankSlot?.Slot).ToList(); - player.Items = player.Items.OrderBy(inventorySlot => inventorySlot?.Slot).ToList(); - player.Hotbar = player.Hotbar.OrderBy(hotbarSlot => hotbarSlot?.Slot).ToList(); - player.Spells = player.Spells.OrderBy(spellSlot => spellSlot?.Slot).ToList(); + return false; } - return player; + // player.Bank = player.Bank.OrderBy(bankSlot => bankSlot?.Slot) + // player.Items = player.Items.OrderBy(inventorySlot => inventorySlot?.Slot).ToList(); + // player.Hotbar = player.Hotbar.OrderBy(hotbarSlot => hotbarSlot?.Slot).ToList(); + // player.Spells = player.Spells.OrderBy(spellSlot => spellSlot?.Slot).ToList(); + + return true; } #endregion @@ -367,7 +368,7 @@ public static int Count() compiledQuery = sortDirection == SortDirection.Ascending ? compiledQuery.OrderBy(u => u.Name.ToUpper()) : compiledQuery.OrderByDescending(u => u.Name.ToUpper()); break; } - + return compiledQuery.Skip(skip).Take(take).ToList(); } } diff --git a/Intersect.Server.Core/Entities/Player.cs b/Intersect.Server.Core/Entities/Player.cs index 1c1a83ba30..8c50b62ccb 100644 --- a/Intersect.Server.Core/Entities/Player.cs +++ b/Intersect.Server.Core/Entities/Player.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics; +using Intersect.Collections.Slotting; using Intersect.Enums; using Intersect.GameObjects; using Intersect.GameObjects.Crafting; @@ -141,7 +142,10 @@ public ulong PlayTimeSeconds //Bank [JsonIgnore] - public virtual List Bank { get; set; } = new List(); + public virtual SlotList Bank { get; set; } = new( + Options.Instance.PlayerOpts.InitialBankslots, + BankSlot.Create + ); //Friends -- Not used outside of EF [JsonIgnore] @@ -153,7 +157,10 @@ public ulong PlayTimeSeconds //HotBar [JsonIgnore] - public virtual List Hotbar { get; set; } = new List(); + public virtual SlotList Hotbar { get; set; } = new( + Options.Instance.PlayerOpts.HotbarSlotCount, + HotbarSlot.Create + ); //Quests [JsonIgnore] @@ -287,24 +294,14 @@ public static Player FindOnline(string charName) return OnlinePlayers.Values.FirstOrDefault(s => s.Name.ToLower().Trim() == charName.ToLower().Trim()); } - public bool ValidateLists() + public bool ValidateLists(PlayerContext? playerContext = default) { var changes = false; - changes |= SlotHelper.ValidateSlots(Spells, Options.MaxPlayerSkills); - changes |= SlotHelper.ValidateSlots(Items, Options.MaxInvItems); - changes |= SlotHelper.ValidateSlots(Bank, Options.Instance.PlayerOpts.InitialBankslots); - - if (Hotbar.Count < Options.Instance.PlayerOpts.HotbarSlotCount) - { - Hotbar.Sort((a, b) => a?.Slot - b?.Slot ?? 0); - for (var i = Hotbar.Count; i < Options.Instance.PlayerOpts.HotbarSlotCount; i++) - { - Hotbar.Add(new HotbarSlot(i)); - } - - changes = true; - } + changes |= SlotHelper.ValidateSlotList(Spells, Options.Instance.PlayerOpts.MaxSpells); + changes |= SlotHelper.ValidateSlotList(Items, Options.Instance.PlayerOpts.MaxInventory); + changes |= SlotHelper.ValidateSlotList(Bank, Options.Instance.PlayerOpts.InitialBankslots); + changes |= SlotHelper.ValidateSlotList(Hotbar, Options.Instance.PlayerOpts.HotbarSlotCount); return changes; } @@ -912,7 +909,7 @@ public override void Update(long timeMs) /// public override void UpdateSpellCooldown(SpellBase spellBase, int spellSlot) { - if (spellSlot < 0 || spellSlot >= Options.MaxPlayerSkills) + if (spellSlot < 0 || spellSlot >= Options.Instance.PlayerOpts.MaxSpells) { return; } @@ -925,7 +922,7 @@ public override void UpdateSpellCooldown(SpellBase spellBase, int spellSlot) this.UpdateGlobalCooldown(); } } - + public void RemoveEvent(Guid id, bool sendLeave = true) { Event outInstance; @@ -1509,7 +1506,7 @@ public override void TryAttack( bool onHitTrigger = false, bool trapTrigger = false ) - { + { if (!trapTrigger && !ValidTauntTarget(target)) //Traps ignore taunts. { return; @@ -2818,10 +2815,10 @@ public bool TryGiveItem(Item item, ItemHandling handler = ItemHandling.Normal, b return true; } - var bankInterface = new BankInterface(this, ((IEnumerable)Bank).ToList(), new object(), null, Options.Instance.Bank.MaxSlots); + var bankInterface = new BankInterface(this, Bank); return bankOverflow && bankInterface.TryDepositItem(item, sendUpdate); } - + /// /// Creates an item source for the player entity. /// @@ -2835,7 +2832,7 @@ public bool TryGiveItem(Item item, ItemHandling handler = ItemHandling.Normal, b Id = this.Id }; } - + /// /// Gives the player an item. NOTE: This method MAKES ZERO CHECKS to see if this is possible! /// Use TryGiveItem where possible! @@ -4269,28 +4266,33 @@ private bool IsBusy() => FriendRequester != default; //Bank - public bool OpenBank(bool guild = false) + public bool OpenBank(bool openGuildBank = false) { if (IsBusy()) { return false; } - if (guild && Guild == null) + if (openGuildBank) { - return false; - } - - var bankItems = ((IEnumerable)Bank).ToList(); + var guild = Guild; + if (guild == default) + { + return false; + } - if (guild) + BankInterface = new BankInterface( + this, + guild.Bank, + guild + ); + } + else { - bankItems = ((IEnumerable)Guild.Bank).ToList(); + BankInterface = new BankInterface(this, Bank); } - BankInterface = new BankInterface(this, bankItems, guild ? Guild.Lock : new object(), guild ? Guild : null, guild ? Guild.BankSlotsCount : Options.Instance.PlayerOpts.InitialBankslots); - - GuildBank = guild; + GuildBank = openGuildBank; BankInterface.SendOpenBank(); return true; @@ -5256,7 +5258,7 @@ public bool TryTeachSpell(Spell spell, bool sendUpdate = true) return false; } - for (var i = 0; i < Options.MaxPlayerSkills; i++) + for (var i = 0; i < Options.Instance.PlayerOpts.MaxSpells; i++) { if (Spells[i].SpellId == Guid.Empty) { @@ -5278,7 +5280,7 @@ public bool TryTeachSpell(Spell spell, bool sendUpdate = true) public bool KnowsSpell(Guid spellId) { - for (var i = 0; i < Options.MaxPlayerSkills; i++) + for (var i = 0; i < Options.Instance.PlayerOpts.MaxSpells; i++) { if (Spells[i].SpellId == spellId) { @@ -5291,7 +5293,7 @@ public bool KnowsSpell(Guid spellId) public int FindSpell(Guid spellId) { - for (var i = 0; i < Options.MaxPlayerSkills; i++) + for (var i = 0; i < Options.Instance.PlayerOpts.MaxSpells; i++) { if (Spells[i].SpellId == spellId) { @@ -5810,14 +5812,14 @@ public void ProcessEquipmentUpdated(bool sendPackets, bool ignoreEvents = false) PacketSender.SendPlayerEquipmentToProximity(this); PacketSender.SendEntityStats(this); } - + CacheEquipmentTriggers(); UnequipInvalidItems(); } [NotMapped, JsonIgnore] private List CachedEquipmentOnHitTriggers { get; set; } = new List(); - + [NotMapped, JsonIgnore] private List CachedEquipmentOnDamageTriggers { get; set; } = new List(); @@ -6896,7 +6898,7 @@ public void EnqueueStartCommonEvent( CommonEventTrigger trigger = CommonEventTrigger.None, string command = default, string parameter = default - ) + ) { if (eventDescriptor == null) { @@ -7564,7 +7566,7 @@ public bool TryAddToInstanceController() [NotMapped, JsonIgnore] public CraftingState CraftingState { get; set; } [NotMapped, JsonIgnore] public Guid OpenCraftingTableId { get; set; } - + [NotMapped, JsonIgnore] public bool CraftJournalMode { get; set; } #endregion @@ -7612,14 +7614,14 @@ public bool TryAddToInstanceController() private bool JsonInShop => InShop != null; [JsonIgnore, NotMapped] public Bag InBag; - + [JsonIgnore, NotMapped] public bool IsInBag => InBag != null; [JsonIgnore, NotMapped] public ShopBase InShop; [NotMapped] public bool InBank => BankInterface != null; - [NotMapped][JsonIgnore] public BankInterface BankInterface; + [NotMapped, JsonIgnore] public IBankInterface BankInterface; #endregion diff --git a/Intersect.Server.Core/Networking/PacketSender.cs b/Intersect.Server.Core/Networking/PacketSender.cs index a8f11990aa..f89338edb5 100644 --- a/Intersect.Server.Core/Networking/PacketSender.cs +++ b/Intersect.Server.Core/Networking/PacketSender.cs @@ -1175,7 +1175,7 @@ public static void SendInventory(Player player) } var invItems = new InventoryUpdatePacket[Options.MaxInvItems]; - if (player.Items.Count < Options.MaxInvItems) + if (player.Items.Capacity < Options.Instance.PlayerOpts.MaxInventory) { throw new InvalidOperationException($"Tried to send inventory before fully loading player {player.Id}"); } @@ -1219,8 +1219,8 @@ public static void SendPlayerSpells(Player player) return; } - var spells = new SpellUpdatePacket[Options.MaxPlayerSkills]; - for (var i = 0; i < Options.MaxPlayerSkills; i++) + var spells = new SpellUpdatePacket[Options.Instance.PlayerOpts.MaxSpells]; + for (var i = 0; i < Options.Instance.PlayerOpts.MaxSpells; i++) { spells[i] = new SpellUpdatePacket(i, player.Spells[i].SpellId); }