//
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//
namespace MUnique.OpenMU.Persistence;
using System.Collections.Concurrent;
using System.Threading;
///
/// A generator which generates id which should be unique within an instance of a generator.
/// Ids which are no longer used can be given back to the generator, e.g. when an object joins a game map,
/// it gets an id, later when it leaves the map, it gives the id back, so another player can use it.
///
///
/// The namespace for this class is probably not the right one - we're missing something like a "Utility" namespace for things like that.
///
public class IdGenerator
{
private readonly int _maxValue;
private readonly ConcurrentQueue _givenBack = new();
private int _currentValue;
///
/// Initializes a new instance of the class.
///
/// The first value.
/// The maximum value.
public IdGenerator(int firstValue, int maxValue)
{
this._maxValue = maxValue;
this._currentValue = firstValue - 1; // will be increased by 1 by GenerateId
}
///
/// Defines how ids are reused, which were given back with .
///
public enum ReUsePolicy
{
///
/// The given back id is reused on the next call of .
///
ReUseOnNextRetrieve,
///
/// The given back id is reused when all of the available ids were exceeded.
///
ReUseWhenExceeded,
}
///
/// Gets or sets the which defines how ids are reused, which were given back with .
///
public ReUsePolicy ReUseSetting { get; set; }
///
/// Gets an identifier which is unique within this generator instance.
///
/// An identifier which is unique within this generator instance.
/// Maximum object id exceeded.
public int GenerateId()
{
if (this.ReUseSetting == ReUsePolicy.ReUseOnNextRetrieve
&& this._givenBack.TryDequeue(out int next))
{
return next;
}
if (this._currentValue == this._maxValue)
{
if (this._givenBack.TryDequeue(out next))
{
return next;
}
throw new InvalidOperationException("Maximum object id exceeded");
}
return Interlocked.Increment(ref this._currentValue);
}
///
/// Gives the id back for further usage by the next object.
///
/// The identifier.
public void GiveBack(int id)
{
if (id <= this._maxValue)
{
this._givenBack.Enqueue(id);
}
}
}