<commit_before>using System;
using System.Collections.Generic;
using System.Threading;

using log4net;

using ACE.Entity;

namespace ACE.Server.Managers
{
    /// <summary>
    /// Used to assign global guids and ensure they are unique to server.
    /// </summary>
    public static class GuidManager
    {
        private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        // Running server is guid master - database only read as startup to get current max per range.
        // weenie class templates Max 65,535 - took Turbine 17 years to get to 10K
        // these will be added by developers and not in game.
        // Nothing in this range is persisted by the game.   Only developers or content creators can create them to be persisted.
        // this is only here for documentation purposes.
        // Fragmentation: None

        /// <summary>
        /// Is equal to uint.MaxValue
        /// </summary>
        public static uint InvalidGuid { get; } = uint.MaxValue;

        private const uint LowIdLimit = 0x1000;

        private class PlayerGuidAllocator
        {
            private readonly uint max;
            private uint current;
            private readonly string name;

            public PlayerGuidAllocator(uint min, uint max, string name)
            {
                this.max = max;

                // Read current value out of ShardDatabase
                lock (this)
                {
                    bool done = false;
                    Database.DatabaseManager.Shard.GetMaxGuidFoundInRange(min, max, dbVal =>
                    {
                        lock (this)
                        {
                            current = dbVal;
                            done = true;
                            Monitor.Pulse(this);
                        }
                    });

                    while (!done)
                        Monitor.Wait(this);

                    if (current == InvalidGuid)
                        current = min;
                    else
                        // Need to start allocating at current value in db +1
                        current++;

                    log.Debug($"{name} GUID Allocator current is now {current:X8} of {max:X8}");

                    if ((max - current) < LowIdLimit)
                        log.Warn($"Dangerously low on {name} GUIDs: {current:X8} of {max:X8}");
                }

                this.name = name;
            }

            public uint Alloc()
            {
                lock (this)
                {
                    if (current == max)
                    {
                        log.Fatal($"Out of {name} GUIDs!");
                        return InvalidGuid;
                    }

                    if (current == max - LowIdLimit)
                        log.Warn($"Running dangerously low on {name} GUIDs, need to defrag");

                    uint ret = current;
                    current += 1;

                    return ret;
                }
            }

            /// <summary>
            /// For information purposes only, do not use the result. Use Alloc() instead
            /// This value represents the current DbMax + 1
            /// </summary>
            public uint Current()
            {
                return current;
            }
        }

        /// <summary>
        /// On a server with ~500 players, about 10,000,000 dynamic GUID's will be requested every 24hr period.
        /// </summary>
        private class DynamicGuidAllocator
        {
            private readonly uint max;
            private uint current;
            private readonly string name;

            private static readonly TimeSpan recycleTime = TimeSpan.FromMinutes(360);

            private readonly Queue<Tuple<DateTime, uint>> recycledGuids = new Queue<Tuple<DateTime, uint>>();

            private const int limitAvailableIDsReturnedInGetSequenceGaps = 100000000;
            private bool useSequenceGapExhaustedMessageDisplayed;
            private LinkedList<(uint start, uint end)> availableIDs = new LinkedList<(uint start, uint end)>();

            public DynamicGuidAllocator(uint min, uint max, string name)
            {
                this.max = max;

                // Read current value out of ShardDatabase
                lock (this)
                {
                    bool done = false;
                    Database.DatabaseManager.Shard.GetMaxGuidFoundInRange(min, max, dbVal =>
                    {
                        lock (this)
                        {
                            current = dbVal;
                            done = true;
                            Monitor.Pulse(this);<commit_msg>Revert limitAvailableIDsReturnedInGetSequenceGaps with comment (#2369)<commit_after>using System;
using System.Collections.Generic;
using System.Threading;

using log4net;

using ACE.Entity;

namespace ACE.Server.Managers
{
    /// <summary>
    /// Used to assign global guids and ensure they are unique to server.
    /// </summary>
    public static class GuidManager
    {
        private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        // Running server is guid master - database only read as startup to get current max per range.
        // weenie class templates Max 65,535 - took Turbine 17 years to get to 10K
        // these will be added by developers and not in game.
        // Nothing in this range is persisted by the game.   Only developers or content creators can create them to be persisted.
        // this is only here for documentation purposes.
        // Fragmentation: None

        /// <summary>
        /// Is equal to uint.MaxValue
        /// </summary>
        public static uint InvalidGuid { get; } = uint.MaxValue;

        private const uint LowIdLimit = 0x1000;

        private class PlayerGuidAllocator
        {
            private readonly uint max;
            private uint current;
            private readonly string name;

            public PlayerGuidAllocator(uint min, uint max, string name)
            {
                this.max = max;

                // Read current value out of ShardDatabase
                lock (this)
                {
                    bool done = false;
                    Database.DatabaseManager.Shard.GetMaxGuidFoundInRange(min, max, dbVal =>
                    {
                        lock (this)
                        {
                            current = dbVal;
                            done = true;
                            Monitor.Pulse(this);
                        }
                    });

                    while (!done)
                        Monitor.Wait(this);

                    if (current == InvalidGuid)
                        current = min;
                    else
                        // Need to start allocating at current value in db +1
                        current++;

                    log.Debug($"{name} GUID Allocator current is now {current:X8} of {max:X8}");

                    if ((max - current) < LowIdLimit)
                        log.Warn($"Dangerously low on {name} GUIDs: {current:X8} of {max:X8}");
                }

                this.name = name;
            }

            public uint Alloc()
            {
                lock (this)
                {
                    if (current == max)
                    {
                        log.Fatal($"Out of {name} GUIDs!");
                        return InvalidGuid;
                    }

                    if (current == max - LowIdLimit)
                        log.Warn($"Running dangerously low on {name} GUIDs, need to defrag");

                    uint ret = current;
                    current += 1;

                    return ret;
                }
            }

            /// <summary>
            /// For information purposes only, do not use the result. Use Alloc() instead
            /// This value represents the current DbMax + 1
            /// </summary>
            public uint Current()
            {
                return current;
            }
        }

        /// <summary>
        /// On a server with ~500 players, about 10,000,000 dynamic GUID's will be requested every 24hr period.
        /// </summary>
        private class DynamicGuidAllocator
        {
            private readonly uint max;
            private uint current;
            private readonly string name;

            private static readonly TimeSpan recycleTime = TimeSpan.FromMinutes(360);

            private readonly Queue<Tuple<DateTime, uint>> recycledGuids = new Queue<Tuple<DateTime, uint>>();

            /// <summary>
            /// The value here is the result of two factors:
            /// - A: The total number of GUIDs that are generated during a period of recycledTime (defined above)
            /// - B: The total number of GUIDs that are consumed and saved to the shard between server resets
            /// A safe value might be (2 * A) + (2 * B)
            /// On a shard with severe id fragmentation, this can end up eating more memory to store all the smaller gaps
            /// Once sequence gaps are depleted and there are no available id's in the recycle queue, DB Max + 1 is used
            /// You can monitor the amount of available id's using /serverstatus
            /// </summary>
            private const int limitAvailableIDsReturnedInGetSequenceGaps = 10000000;
            private bool useSequenceGapExhaustedMessageDisplayed;
            private LinkedList<(uint start, uint end)> availableIDs = new LinkedList<(uint start, uint end)>();

            public DynamicGuidAllocator(uint min, uint max, string name)
            {
                this.max = max;

                // Read current value out of ShardDatabase
                lock (this)
                {
                    bool done = false;
                    Database.DatabaseManager.Shard.GetMaxGuidFoundInRange(min, max, dbVal =>
                    {
                        lock (this)
                        {
                            current = dbVal;
                            done = true;
                            Monitor.Pulse(this);
{'fraction_non_alphanumeric': 0.07494667442311422, 'fraction_numerical': 0.00969555943377933, 'mean_word_length': 1.2640474100087797, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7576639', 'n_tokens_mistral': 2511, 'n_tokens_neox': 2279, 'n_words': 832}