Skip to content

Commit

Permalink
fix: avoid jackpots on changed slot collection sizes (#2426)
Browse files Browse the repository at this point in the history
  • Loading branch information
lodicolo authored Dec 24, 2024
1 parent d5fab54 commit 19565ee
Show file tree
Hide file tree
Showing 24 changed files with 398 additions and 226 deletions.
11 changes: 11 additions & 0 deletions Intersect (Core)/Collections/Slotting/ISlot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Intersect.Collections.Slotting;


public interface ISlot
{

int Slot { get; }

bool IsEmpty { get; }

}
101 changes: 101 additions & 0 deletions Intersect (Core)/Collections/Slotting/SlotList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System.Collections;

namespace Intersect.Collections.Slotting;

public class SlotList<TSlot> : IList<TSlot> where TSlot : ISlot
{
private readonly SortedList<int, TSlot> _slots;
private readonly Func<int, TSlot> _factory;

public SlotList(int capacity, Func<int, TSlot> factory)
{
_slots = new SortedList<int, TSlot>(capacity);
_factory = factory;
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

public IEnumerator<TSlot> 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<TSlot, bool> predicate)
{
var copy = this.ToArray();
for (var index = 0; index < copy.Length; ++index)
{
if (predicate(copy[index]))
{
return index;
}
}

return -1;
}
}
2 changes: 0 additions & 2 deletions Intersect (Core)/Config/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions Intersect.Client.Core/Entities/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Guid> IEntity.Spells => Spells.Select(x => x.Id).ToList();

Expand Down Expand Up @@ -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();
}
Expand Down
4 changes: 2 additions & 2 deletions Intersect.Client.Core/Interface/Game/Spells/SpellItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void pnl_RightClicked(Base sender, ClickedEventArgs arguments)
else
{
Globals.Me.TryForgetSpell(mYindex);
}
}
}

void pnl_HoverLeave(Base sender, EventArgs arguments)
Expand Down Expand Up @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions Intersect.Client.Core/Interface/Game/Spells/SpellsWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
Expand Down
3 changes: 1 addition & 2 deletions Intersect.Server.Core/Database/DbInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion Intersect.Server.Core/Database/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Intersect.Server.Database;

public class Item: IItem
public class Item : IItem
{
[JsonIgnore][NotMapped] public double DropChance { get; set; } = 100;

Expand Down
5 changes: 3 additions & 2 deletions Intersect.Server.Core/Database/PlayerData/Players/BagSlot.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.ComponentModel.DataAnnotations.Schema;

using Intersect.Collections.Slotting;
using Newtonsoft.Json;

// ReSharper disable UnusedAutoPropertyAccessor.Local
Expand All @@ -23,12 +23,13 @@ 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; }

[JsonIgnore]
public virtual Bag ParentBag { get; private set; }

public int Slot { get; private set; }

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.ComponentModel.DataAnnotations.Schema;

using Intersect.Collections.Slotting;
using Intersect.Server.Entities;

using Newtonsoft.Json;
Expand All @@ -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()
{
Expand All @@ -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; }

Expand Down
30 changes: 17 additions & 13 deletions Intersect.Server.Core/Database/PlayerData/Players/Guild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -50,7 +51,10 @@ public Guild()
/// Guild bank slots
/// </summary>
[JsonIgnore]
public virtual List<GuildBankSlot> Bank { get; set; } = new List<GuildBankSlot>();
public virtual SlotList<GuildBankSlot> Bank { get; set; } = new SlotList<GuildBankSlot>(
Options.Instance.Guild.InitialBankSlots,
GuildBankSlot.Create
);

/// <summary>
/// Sets the number of bank slots alotted to this guild. Banks lots can only expand.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -310,7 +312,7 @@ public void RemoveMember(Guid targetId, Player targetPlayer, Player initiator =
}

/// <summary>
///
///
/// </summary>
public void Update()
{
Expand Down Expand Up @@ -690,14 +692,16 @@ public void BankSlotUpdated(int slot)
/// <param name="count"></param>
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);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.ComponentModel.DataAnnotations.Schema;

using Intersect.Collections.Slotting;
using Newtonsoft.Json;

// ReSharper disable UnusedAutoPropertyAccessor.Local
Expand All @@ -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()
{
Expand All @@ -23,12 +24,13 @@ 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; }

[JsonIgnore]
public virtual Guild Guild { get; private set; }

public int Slot { get; private set; }

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.ComponentModel.DataAnnotations.Schema;

using Intersect.Collections.Slotting;
using Intersect.Enums;
using Intersect.Server.Entities;
using Intersect.Utilities;
Expand All @@ -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()
{
Expand All @@ -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;
Expand Down
9 changes: 0 additions & 9 deletions Intersect.Server.Core/Database/PlayerData/Players/ISlot.cs

This file was deleted.

Loading

0 comments on commit 19565ee

Please sign in to comment.