diff --git a/docs/ServerEmu/ServerCommands.md b/docs/ServerEmu/ServerCommands.md index 1aa826d2..8334ad37 100644 --- a/docs/ServerEmu/ServerCommands.md +++ b/docs/ServerEmu/ServerCommands.md @@ -1,6 +1,6 @@ # Server Commands -This list was automatically generated on `2026.03.25 14:52:06 UTC` using server version `1.0.0`. +This list was automatically generated on `2026.03.09 15:06:03 UTC` using server version `1.0.0`. To see an up to date list of all commands, type !commands in the server console or the in-game chat. When invoking a command from in-game your account has to meet the user level requirement for the command. @@ -211,17 +211,16 @@ Region management commands. ## Server Server management commands. -| Command | Description | User Level | Invoker Type | -| --------------------------------- | ------------------------------------------------- | ---------- | ------------- | -| !server broadcast | Broadcasts a notification to all players. | Admin | Any | -| !server reloadaddg | Reloads the Add G page. | Admin | ServerConsole | -| !server reloadcatalog | Reloads MTX store catalog. | Admin | ServerConsole | -| !server reloaddashboard | Reloads the web dashboard. | Admin | ServerConsole | -| !server reloadlivetuning | Reloads live tuning settings. | Admin | ServerConsole | -| !server reloadplayernameblacklist | Reloads the player name blacklist. | Admin | ServerConsole | -| !server shutdown | Shuts the server down. | Admin | Any | -| !server status | Prints server status. | Any | Any | -| !server whitelist | Enables or disables account whitelist for logins. | Admin | ServerConsole | +| Command | Description | User Level | Invoker Type | +| --------------------------------- | ----------------------------------------- | ---------- | ------------- | +| !server broadcast | Broadcasts a notification to all players. | Admin | Any | +| !server reloadaddg | Reloads the Add G page. | Admin | ServerConsole | +| !server reloadcatalog | Reloads MTX store catalog. | Admin | ServerConsole | +| !server reloaddashboard | Reloads the web dashboard. | Admin | ServerConsole | +| !server reloadlivetuning | Reloads live tuning settings. | Admin | ServerConsole | +| !server reloadplayernameblacklist | Reloads the player name blacklist. | Admin | ServerConsole | +| !server shutdown | Shuts the server down. | Admin | Any | +| !server status | Prints server status. | Any | Any | ## Store Commands for interacting with the in-game store. diff --git a/src/MHServerEmu.Core/Collections/InvasiveList.cs b/src/MHServerEmu.Core/Collections/InvasiveList.cs index 7ff0a4a4..a2ab377a 100644 --- a/src/MHServerEmu.Core/Collections/InvasiveList.cs +++ b/src/MHServerEmu.Core/Collections/InvasiveList.cs @@ -1,59 +1,58 @@ using System.Collections; -using System.Runtime.CompilerServices; namespace MHServerEmu.Core.Collections { public class InvasiveList { - private readonly Iterator[] _iterators; - - private readonly Stack _iteratorPool; - private Iterator _reusableIterator; - - private int _numIterators; - public int Id { get; private set; } - public T Head { get; private set; } + public T Head { get; set; } public T Tail { get; private set; } public int Count { get; private set; } - public bool IsEmpty { get => Head == null; } + private Iterator[] _iterators; + private int _numIterators; + private int _maxIterators; - public InvasiveList(int maxIterators, int id = 0) + public InvasiveList(int maxIterators) { - _iterators = new Iterator[maxIterators]; - - if (maxIterators > 1) - _iteratorPool = new(); + _maxIterators = maxIterators; + _iterators = new Iterator[_maxIterators]; + } + public InvasiveList(int maxIterators, int id) + { + _maxIterators = maxIterators; + _iterators = new Iterator[_maxIterators]; Id = id; } - public IEnumerator GetEnumerator() + public IEnumerable Iterate() { - Iterator iterator; + var iterator = new Iterator(this); - if (_iteratorPool != null) + try { - if (_iteratorPool.TryPop(out iterator) == false) - iterator = new(this); + while (iterator.End() == false) + { + var element = iterator.Current; + iterator.MoveNext(); + yield return element; + } } - else + finally { - _reusableIterator ??= new(this); - iterator = _reusableIterator; + UnregisterIterator(iterator); } - - iterator.Initialize(); - return iterator; } + public bool IsEmpty() => Head == null; + public void Remove(T element) { if (element == null || Contains(element) == false) return; - ref var node = ref GetInvasiveListNode(element, Id); - if (Unsafe.IsNullRef(ref node)) return; + var node = GetInvasiveListNode(element, Id); + if (node == null) return; for (int i = 0; i < _numIterators; i++) { @@ -69,16 +68,16 @@ namespace MHServerEmu.Core.Collections if (node.Next != null) { T nextElement = node.Next; - ref var nextNode = ref GetInvasiveListNode(nextElement, Id); - if (Unsafe.IsNullRef(ref nextNode) == false) + var nextNode = GetInvasiveListNode(nextElement, Id); + if (nextNode != null) nextNode.Prev = node.Prev; } if (node.Prev != null) { T prevElement = node.Prev; - ref var prevNode = ref GetInvasiveListNode(prevElement, Id); - if (Unsafe.IsNullRef(ref prevNode) == false) + var prevNode = GetInvasiveListNode(prevElement, Id); + if (prevNode != null) prevNode.Next = node.Next; } @@ -94,11 +93,11 @@ namespace MHServerEmu.Core.Collections if (oldElement == null || Contains(oldElement) == false) return; if (element == null || Contains(element)) return; - ref var node = ref GetInvasiveListNode(element, Id); - if (Unsafe.IsNullRef(ref node)) return; + var node = GetInvasiveListNode(element, Id); + if (node == null) return; - ref var oldNode = ref GetInvasiveListNode(oldElement, Id); - if (Unsafe.IsNullRef(ref oldNode)) return; + var oldNode = GetInvasiveListNode(oldElement, Id); + if (oldNode == null) return; var oldPrev = oldNode.Prev; oldNode.Prev = element; @@ -107,8 +106,8 @@ namespace MHServerEmu.Core.Collections if (oldPrev != null) { - ref var oldPrevNode = ref GetInvasiveListNode(oldPrev, Id); - if (Unsafe.IsNullRef(ref oldPrevNode)) return; + var oldPrevNode = GetInvasiveListNode(oldPrev, Id); + if (oldPrevNode == null) return; oldPrevNode.Next = element; } else @@ -121,14 +120,14 @@ namespace MHServerEmu.Core.Collections { if (element == null || Contains(element)) return; - ref var node = ref GetInvasiveListNode(element, Id); - if (Unsafe.IsNullRef(ref node)) return; + var node = GetInvasiveListNode(element, Id); + if (node == null) return; node.Prev = Tail; if (Tail != null) { - ref var tailNode = ref GetInvasiveListNode(Tail, Id); - if (Unsafe.IsNullRef(ref tailNode)) return; + var tailNode = GetInvasiveListNode(Tail, Id); + if (tailNode == null) return; tailNode.Next = element; } else @@ -138,23 +137,20 @@ namespace MHServerEmu.Core.Collections Count++; } - public virtual ref InvasiveListNode GetInvasiveListNode(T element, int listId) - { - return ref Unsafe.NullRef>(); - } + public virtual InvasiveListNode GetInvasiveListNode(T element, int listId) => null; public bool Contains(T element) { if (element == null) return false; - ref var node = ref GetInvasiveListNode(element, Id); - if (Unsafe.IsNullRef(ref node)) return false; + var node = GetInvasiveListNode(element, Id); + if (node == null) return false; return node.Next != null || node.Prev != null || element.Equals(Head); } private void RegisterIterator(Iterator iterator) { - if (_numIterators >= _iterators.Length) - throw new InvalidOperationException($"Too many iterators '{_iterators.Length}' for invasive list"); + if (_numIterators >= _maxIterators) + throw new InvalidOperationException($"Too many iterators '{_maxIterators}' for invasive list"); _iterators[_numIterators++] = iterator; } @@ -169,75 +165,71 @@ namespace MHServerEmu.Core.Collections _iterators[_numIterators - 1] = null; _numIterators--; - - // pool iterator instance for reuse - iterator.Reset(); - _iteratorPool?.Push(iterator); - return; } throw new InvalidOperationException("Iterator not found in iterator collection of invasive list!"); } - public sealed class Iterator : IEnumerator + public class Iterator : IEnumerator { - private readonly InvasiveList _list; - - private bool _start = true; - - public T Current { get; private set; } - object IEnumerator.Current { get => Current; } - - public bool SkipNext { get; set; } = false; + private InvasiveList _list; + public bool SkipNext { get; set; } public Iterator(InvasiveList invasiveList) { _list = invasiveList; - } - - public void Initialize() - { + Current = _list.Head; + SkipNext = false; _list.RegisterIterator(this); } - public void Dispose() - { - _list.UnregisterIterator(this); - } - - public void Reset() - { - _start = true; - Current = default; - SkipNext = false; - } + public T Current { get; private set; } + object IEnumerator.Current => Current; + public void Dispose() { } + public void Reset() { } public bool MoveNext() { - if (_start) - { - Current = _list.Head; - _start = false; - } - else - { - if (SkipNext) - SkipNext = false; - else if (Current != null) - Current = _list.GetInvasiveListNode(Current, _list.Id).Next; - } + if (SkipNext) SkipNext = false; + else if (Current != null) + Current = _list.GetInvasiveListNode(Current, _list.Id).Next; - return Current != null; + return true; } + + public bool End() => Current == null; } + } - public struct InvasiveListNode + public class InvasiveListNode { - public T Next; - public T Prev; + public T Next { get; set; } + public T Prev { get; set; } public void Clear() => Next = Prev = default; } + + public class InvasiveListNodeCollection + { + private readonly InvasiveListNode[] _nodes; + private int _numLists; + + public InvasiveListNodeCollection(int numLists) + { + _numLists = numLists; + _nodes = new InvasiveListNode[_numLists]; + for (int i = 0; i < _numLists; i++) + _nodes[i] = new(); + } + + public InvasiveListNode GetInvasiveListNode(int listIndex) + { + if (listIndex >= 0 && listIndex < _numLists) + return _nodes[listIndex]; + else + return null; + } + } } diff --git a/src/MHServerEmu.Core/Network/GameServiceProtocol.cs b/src/MHServerEmu.Core/Network/GameServiceProtocol.cs index ce727dd2..a115f568 100644 --- a/src/MHServerEmu.Core/Network/GameServiceProtocol.cs +++ b/src/MHServerEmu.Core/Network/GameServiceProtocol.cs @@ -461,7 +461,7 @@ namespace MHServerEmu.Core.Network /// /// [Game -> PlayerManager] Relays a match region request command from a client. /// - public readonly struct MatchRegionRequestQueueCommand(ulong playerDbId, ulong regionProtoId, ulong difficultyTierProtoId, ulong metaStateProtoId, RegionRequestQueueCommandVar command, ulong regionRequestGroupId, ulong targetPlayerDbId, int teamSizeOverride) + public readonly struct MatchRegionRequestQueueCommand(ulong playerDbId, ulong regionProtoId, ulong difficultyTierProtoId, ulong metaStateProtoId, RegionRequestQueueCommandVar command, ulong regionRequestGroupId, ulong targetPlayerDbId) : IGameServiceMessage { public readonly ulong PlayerDbId = playerDbId; @@ -471,7 +471,6 @@ namespace MHServerEmu.Core.Network public readonly RegionRequestQueueCommandVar Command = command; public readonly ulong RegionRequestGroupId = regionRequestGroupId; public readonly ulong TargetPlayerDbId = targetPlayerDbId; - public readonly int TeamSizeOverride = teamSizeOverride; } // MatchQueueUpdate is based on PlayerMgrToGameServer.proto from 1.53 @@ -831,12 +830,6 @@ namespace MHServerEmu.Core.Network public readonly int ResultCode = resultCode; } - public readonly struct SetWhitelistEnabled(bool enable) - : IGameServiceMessage - { - public readonly bool Enable = enable; - } - #endregion } } diff --git a/src/MHServerEmu.Core/Network/MessagePackageOut.cs b/src/MHServerEmu.Core/Network/MessagePackageOut.cs index af6ef0e2..03854f1c 100644 --- a/src/MHServerEmu.Core/Network/MessagePackageOut.cs +++ b/src/MHServerEmu.Core/Network/MessagePackageOut.cs @@ -1,5 +1,4 @@ using Google.ProtocolBuffers; -using MHServerEmu.Core.Serialization; namespace MHServerEmu.Core.Network { @@ -44,15 +43,5 @@ namespace MHServerEmu.Core.Network stream.WriteRawVarint32((uint)Message.SerializedSize); Message.WriteTo(stream); } - - /// - /// Writes this to the provided . - /// - public void WriteTo(ICodedOutputStreamEx stream) - { - stream.WriteRawVarint32(Id); - stream.WriteRawVarint32((uint)Message.SerializedSize); - Message.WriteTo(stream); - } } } diff --git a/src/MHServerEmu.Core/Network/MuxPacket.cs b/src/MHServerEmu.Core/Network/MuxPacket.cs index 4957bab6..993bd537 100644 --- a/src/MHServerEmu.Core/Network/MuxPacket.cs +++ b/src/MHServerEmu.Core/Network/MuxPacket.cs @@ -1,9 +1,10 @@ -using System.Collections; +using System.Buffers; +using System.Collections; using Google.ProtocolBuffers; +using MHServerEmu.Core.Helpers; using MHServerEmu.Core.Logging; using MHServerEmu.Core.Memory; using MHServerEmu.Core.Network.Tcp; -using MHServerEmu.Core.Serialization; namespace MHServerEmu.Core.Network { @@ -16,6 +17,7 @@ namespace MHServerEmu.Core.Network // Packets apparently go as high as 2800+ messages based on logs, so we presize pooled lists to 4096 to fit that and extra. private static readonly ConcurrentPool> MessageListPool = new(4096, static () => new(4096)); + private static readonly ArrayPool BufferPool = ArrayPool.Create(); private readonly List _outboundMessageList = null; @@ -135,10 +137,15 @@ namespace MHServerEmu.Core.Network if (_outboundMessageList.Count == 0) return Logger.WarnReturn(false, "SerializeData(): Data packet contains no messages"); - using RecyclableCodedOutputStream cos = RecyclableCodedOutputStream.CreateInstance(stream); + // Use pooled buffers for coded output streams with reflection hackery, see ProtobufHelper for more info. + byte[] buffer = BufferPool.Rent(4096); + CodedOutputStream cos = ProtobufHelper.CodedOutputStreamEx.CreateInstance(stream, buffer); foreach (MessagePackageOut messagePackage in _outboundMessageList) messagePackage.WriteTo(cos); + cos.Flush(); + + BufferPool.Return(buffer); return true; } diff --git a/src/MHServerEmu.Core/Serialization/Archive.cs b/src/MHServerEmu.Core/Serialization/Archive.cs index 3e71cddb..2048e917 100644 --- a/src/MHServerEmu.Core/Serialization/Archive.cs +++ b/src/MHServerEmu.Core/Serialization/Archive.cs @@ -173,7 +173,7 @@ namespace MHServerEmu.Core.Serialization { ReadBuffer = new byte[4096]; WriteBuffer = new byte[32]; // We flush after every value, so we can use very small buffer sizes for output (default is 4096). - SharedAutoBuffer = new(65536); + SharedAutoBuffer = new(1024); } } diff --git a/src/MHServerEmu.Core/Serialization/ICodedOutputStreamEx.cs b/src/MHServerEmu.Core/Serialization/ICodedOutputStreamEx.cs deleted file mode 100644 index 0bd1577a..00000000 --- a/src/MHServerEmu.Core/Serialization/ICodedOutputStreamEx.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Google.ProtocolBuffers; - -namespace MHServerEmu.Core.Serialization -{ - /// - /// Extended version of that exposes additional low level writing functionality. - /// - public interface ICodedOutputStreamEx : ICodedOutputStream - { - void WriteRawVarint32(uint value); - - void WriteRawVarint64(ulong value); - - void WriteRawByte(byte value); - - void WriteRawBytes(byte[] value); - } -} diff --git a/src/MHServerEmu.Core/Serialization/RecyclableCodedOutputStream.cs b/src/MHServerEmu.Core/Serialization/RecyclableCodedOutputStream.cs deleted file mode 100644 index bdbdea34..00000000 --- a/src/MHServerEmu.Core/Serialization/RecyclableCodedOutputStream.cs +++ /dev/null @@ -1,459 +0,0 @@ -using System.Collections; -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Google.ProtocolBuffers; -using Google.ProtocolBuffers.Descriptors; -using MHServerEmu.Core.Helpers; - -namespace MHServerEmu.Core.Serialization -{ - /// - /// A more memory efficient version of . - /// - public sealed class RecyclableCodedOutputStream : ICodedOutputStreamEx, IDisposable - { - private static readonly ConcurrentBag Instances = new(); - - private readonly byte[] _primaryBuffer = new byte[CodedOutputStream.DefaultBufferSize]; - private readonly byte[] _floatBuffer = new byte[sizeof(float)]; - - private CodedOutputStream _cos; - - private RecyclableCodedOutputStream() { } - - private void Initialize(Stream stream) - { - _cos = ProtobufHelper.CodedOutputStreamEx.CreateInstance(stream, _primaryBuffer); - } - - public static RecyclableCodedOutputStream CreateInstance(Stream stream) - { - if (Instances.TryTake(out RecyclableCodedOutputStream cos) == false) - cos = new(); - - cos.Initialize(stream); - return cos; - } - - #region IDisposable - - public void Dispose() - { - if (_cos != null) - { - _cos.Flush(); - _cos = null; - } - - Instances.Add(this); - } - - #endregion - - #region ICodedOutputStream - - // For most of this we just pass everything to the default implementation. - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Flush() - { - _cos.Flush(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteArray(FieldType fieldType, int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteArray(fieldType, fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBool(int fieldNumber, string fieldName, bool value) - { - _cos.WriteBool(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBoolArray(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteBoolArray(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBytes(int fieldNumber, string fieldName, ByteString value) - { - _cos.WriteBytes(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBytesArray(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteBytesArray(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteDouble(int fieldNumber, string fieldName, double value) - { - _cos.WriteDouble(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteDoubleArray(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteDoubleArray(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteEnum(int fieldNumber, string fieldName, int value, object rawValue) - { - _cos.WriteEnum(fieldNumber, fieldName, value, rawValue); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteEnumArray(int fieldNumber, string fieldName, IEnumerable list) where T : struct, IComparable, IFormattable - { - _cos.WriteEnumArray(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteField(FieldType fieldType, int fieldNumber, string fieldName, object value) - { - _cos.WriteField(fieldType, fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFixed32(int fieldNumber, string fieldName, uint value) - { - _cos.WriteFixed32(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFixed32Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteFixed32Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFixed64(int fieldNumber, string fieldName, ulong value) - { - _cos.WriteFixed64(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFixed64Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteFixed64Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFloat(int fieldNumber, string fieldName, float value) - { - //_cos.WriteFloat(fieldNumber, fieldName, value); - - _cos.WriteTag(fieldNumber, WireFormat.WireType.Fixed32); - - MemoryMarshal.Cast(_floatBuffer)[0] = value; - _cos.WriteRawBytes(_floatBuffer, 0, 4); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFloatArray(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteFloatArray(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteGroup(int fieldNumber, string fieldName, IMessageLite value) - { - _cos.WriteGroup(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteGroupArray(int fieldNumber, string fieldName, IEnumerable list) where T : IMessageLite - { - _cos.WriteGroupArray(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt32(int fieldNumber, string fieldName, int value) - { - _cos.WriteInt32(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt32Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteInt32Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt64(int fieldNumber, string fieldName, long value) - { - _cos.WriteInt64(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt64Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteInt64Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteMessage(int fieldNumber, string fieldName, IMessageLite value) - { - _cos.WriteMessage(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteMessageArray(int fieldNumber, string fieldName, IEnumerable list) where T : IMessageLite - { - _cos.WriteMessageArray(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteMessageEnd() - { - _cos.Flush(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteMessageSetExtension(int fieldNumber, string fieldName, IMessageLite value) - { - _cos.WriteMessageSetExtension(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteMessageSetExtension(int fieldNumber, string fieldName, ByteString value) - { - _cos.WriteMessageSetExtension(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteMessageStart() - { - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedArray(FieldType fieldType, int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WritePackedArray(fieldType, fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedBoolArray(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedBoolArray(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedDoubleArray(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedDoubleArray(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedEnumArray(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) where T : struct, IComparable, IFormattable - { - _cos.WritePackedEnumArray(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedFixed32Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedFixed32Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedFixed64Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedFixed64Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedFloatArray(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedFloatArray(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedInt32Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedInt32Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedInt64Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedInt64Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedSFixed32Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedSFixed32Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedSFixed64Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedSFixed64Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedSInt32Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedSInt32Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedSInt64Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedSInt64Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedUInt32Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedUInt32Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WritePackedUInt64Array(int fieldNumber, string fieldName, int calculatedSize, IEnumerable list) - { - _cos.WritePackedUInt64Array(fieldNumber, fieldName, calculatedSize, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSFixed32(int fieldNumber, string fieldName, int value) - { - _cos.WriteSFixed32(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSFixed32Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteSFixed32Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSFixed64(int fieldNumber, string fieldName, long value) - { - _cos.WriteSFixed64(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSFixed64Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteSFixed64Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSInt32(int fieldNumber, string fieldName, int value) - { - _cos.WriteSInt32(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSInt32Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteSInt32Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSInt64(int fieldNumber, string fieldName, long value) - { - _cos.WriteSInt64(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSInt64Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteSInt64Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteString(int fieldNumber, string fieldName, string value) - { - _cos.WriteString(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteStringArray(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteStringArray(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt32(int fieldNumber, string fieldName, uint value) - { - _cos.WriteUInt32(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt32Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteUInt32Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt64(int fieldNumber, string fieldName, ulong value) - { - _cos.WriteUInt64(fieldNumber, fieldName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt64Array(int fieldNumber, string fieldName, IEnumerable list) - { - _cos.WriteUInt64Array(fieldNumber, fieldName, list); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUnknownBytes(int fieldNumber, ByteString value) - { - _cos.WriteUnknownBytes(fieldNumber, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUnknownField(int fieldNumber, WireFormat.WireType wireType, ulong value) - { - _cos.WriteUnknownField(fieldNumber, wireType, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete] - public void WriteUnknownGroup(int fieldNumber, IMessageLite value) - { - _cos.WriteUnknownGroup(fieldNumber, value); - } - - #endregion - - #region ICodedOutputStreamEx - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteRawVarint32(uint value) - { - _cos.WriteRawVarint32(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteRawVarint64(ulong value) - { - _cos.WriteRawVarint64(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteRawByte(byte value) - { - _cos.WriteRawByte(value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteRawBytes(byte[] value) - { - _cos.WriteRawBytes(value); - } - - #endregion - } -} diff --git a/src/MHServerEmu.DatabaseAccess/Models/DBAccount.cs b/src/MHServerEmu.DatabaseAccess/Models/DBAccount.cs index 6c297995..f34e017d 100644 --- a/src/MHServerEmu.DatabaseAccess/Models/DBAccount.cs +++ b/src/MHServerEmu.DatabaseAccess/Models/DBAccount.cs @@ -22,7 +22,6 @@ namespace MHServerEmu.DatabaseAccess.Models IsPasswordExpired = 1 << 2, // 1 << 3 was previously used in 0.x for the Linux compatibility mode, it should not be set in any 1.x+ databases. IsWhitelisted = 1 << 4, - BypassLoginQueue = 1 << 5, } /// diff --git a/src/MHServerEmu.Games/Achievements/AchievementState.cs b/src/MHServerEmu.Games/Achievements/AchievementState.cs index b3eee961..cbe31145 100644 --- a/src/MHServerEmu.Games/Achievements/AchievementState.cs +++ b/src/MHServerEmu.Games/Achievements/AchievementState.cs @@ -68,8 +68,6 @@ namespace MHServerEmu.Games.Achievements AchievementProgressMap.Clear(); _scoreCached = false; - AchievementProgressMap.EnsureCapacity((int)achievementCount); - for (uint i = 0; i < achievementCount; i++) { uint achievementId = 0; diff --git a/src/MHServerEmu.Games/Entities/Avatars/Avatar.cs b/src/MHServerEmu.Games/Entities/Avatars/Avatar.cs index 3afc88cb..9cdb157e 100644 --- a/src/MHServerEmu.Games/Entities/Avatars/Avatar.cs +++ b/src/MHServerEmu.Games/Entities/Avatars/Avatar.cs @@ -4716,6 +4716,9 @@ namespace MHServerEmu.Games.Entities.Avatars if (IsInWorld == false) return 0f; + if (Game.CustomGameOptions.DisableMissionXPBonuses) + return 1f; + TuningPrototype tuningProto = tuningTable.Prototype; if (tuningProto == null) return Logger.WarnReturn(0f, "GetMissionXPMultiplier(): tuningProto == null"); @@ -4728,10 +4731,7 @@ namespace MHServerEmu.Games.Entities.Avatars float multiplier = pctXPFromLevelDeltaCurve.GetAt(level - CharacterLevel); multiplier *= tuningProto.PctXPMultiplier; multiplier *= playerXPByDifficultyIndex.GetAt(tuningTable.DifficultyIndex); - - if (Game.CustomGameOptions.DisableMissionXPBonuses == false) - multiplier *= GetAvatarXPMultiplier(); - + multiplier *= GetAvatarXPMultiplier(); multiplier *= GetPartyXPMultiplier(tuningProto); multiplier *= GetLiveTuningXPMultiplier(); return multiplier; diff --git a/src/MHServerEmu.Games/Entities/Entity.cs b/src/MHServerEmu.Games/Entities/Entity.cs index c92a5fc3..4b5a3972 100644 --- a/src/MHServerEmu.Games/Entities/Entity.cs +++ b/src/MHServerEmu.Games/Entities/Entity.cs @@ -106,7 +106,7 @@ namespace MHServerEmu.Games.Entities private static readonly Logger Logger = LogManager.CreateLogger(); - private InlineArray3> _entityListNodes; + private readonly InvasiveListNodeCollection _entityListNodes = new(3); private readonly EventGroup _pendingEvents = new(); @@ -1307,15 +1307,9 @@ namespace MHServerEmu.Games.Entities }; } - public ref InvasiveListNode GetInvasiveListNode(int listId) + public InvasiveListNode GetInvasiveListNode(int listId) { - // InvasiveListNodeCollection is inlined into this function because of CS8170. - const int NumLists = 3; - - if (listId >= 0 && listId < NumLists) - return ref _entityListNodes[listId]; - else - return ref Unsafe.NullRef>(); + return _entityListNodes.GetInvasiveListNode(listId); } #endregion diff --git a/src/MHServerEmu.Games/Entities/EntityManager.cs b/src/MHServerEmu.Games/Entities/EntityManager.cs index d16b5aa2..ab710143 100644 --- a/src/MHServerEmu.Games/Entities/EntityManager.cs +++ b/src/MHServerEmu.Games/Entities/EntityManager.cs @@ -25,13 +25,13 @@ namespace MHServerEmu.Games.Entities { Simulated = 0, Locomotion = 1, - All = 2, // unused? + All = 3, } - public sealed class EntityInvasiveCollection : InvasiveList + public class EntityInvasiveCollection : InvasiveList { public EntityInvasiveCollection(EntityCollection collectionType, int maxIterators = 8) : base(maxIterators, (int)collectionType) { } - public override ref InvasiveListNode GetInvasiveListNode(Entity element, int listId) => ref element.GetInvasiveListNode(listId); + public override InvasiveListNode GetInvasiveListNode(Entity element, int listId) => element.GetInvasiveListNode(listId); } public readonly struct DestroyEntityEvent(Entity entity) : IGameEventData @@ -449,7 +449,7 @@ namespace MHServerEmu.Games.Entities public void LocomoteEntities() { - foreach (var entity in LocomotionEntities) + foreach (var entity in LocomotionEntities.Iterate()) if (entity is WorldEntity worldEntity) worldEntity?.Locomotor.Locomote(); } @@ -586,7 +586,7 @@ namespace MHServerEmu.Games.Entities IsAIEnabled = enable; if (enable) - foreach (var entity in SimulatedEntities) + foreach (var entity in SimulatedEntities.Iterate()) if (entity is Agent agent) agent.AIController?.SetIsEnabled(true); foreach (var entity in _entityDict.Values) diff --git a/src/MHServerEmu.Games/Entities/Hotspot.cs b/src/MHServerEmu.Games/Entities/Hotspot.cs index d0a20dea..a26180c5 100644 --- a/src/MHServerEmu.Games/Entities/Hotspot.cs +++ b/src/MHServerEmu.Games/Entities/Hotspot.cs @@ -49,8 +49,6 @@ namespace MHServerEmu.Games.Entities private int _activePowerTargetCount; private bool _killSelf; - private Picker _targetPicker; // Reusable picker for AppliesIntervalPowers hotspots, remove this if we implement picker pooling. - public Hotspot(Game game) : base(game) { SetFlag(EntityFlags.IsHotspot, true); @@ -69,9 +67,6 @@ namespace MHServerEmu.Games.Entities if (hotspotProto.DirectApplyToMissilesData?.EvalPropertiesToApply != null || hotspotProto.Negatable) SetFlag(EntityFlags.IsCollidableHotspot, true); - if (hotspotProto.IntervalPowersRandomTarget) - _targetPicker = new(Game.Random); - return true; } @@ -801,7 +796,7 @@ namespace MHServerEmu.Games.Entities if (hotspotProto.IntervalPowersRandomTarget) { - Picker picker = _targetPicker; + Picker picker = new(Game.Random); var hasLOS = TriBool.Undefined; ulong prevTargetId = InvalidId; diff --git a/src/MHServerEmu.Games/Entities/Physics/ForceSystem.cs b/src/MHServerEmu.Games/Entities/Physics/ForceSystem.cs index 05a8418e..306f71be 100644 --- a/src/MHServerEmu.Games/Entities/Physics/ForceSystem.cs +++ b/src/MHServerEmu.Games/Entities/Physics/ForceSystem.cs @@ -15,24 +15,31 @@ namespace MHServerEmu.Games.Entities.Physics } } - public sealed class ForceSystemMemberList : InvasiveList + public class ForceSystemMemberList : InvasiveList { public ForceSystemMemberList(int maxIterators = 1) : base(maxIterators) { } - public override ref InvasiveListNode GetInvasiveListNode(ForceSystemMember element, int listId) => ref element.InvasiveListNode; + public override InvasiveListNode GetInvasiveListNode(ForceSystemMember element, int listId) => element.InvasiveListNode; } public class ForceSystemMember { - private InvasiveListNode _invasiveListNode; - public ulong EntityId { get; set; } public Vector3 Position { get; set; } public Vector3 Direction { get; set; } public float Time { get; set; } public float Speed { get; set; } public float Acceleration { get; set; } - public ref InvasiveListNode InvasiveListNode { get => ref _invasiveListNode; } + public InvasiveListNode InvasiveListNode { get; private set; } - public ForceSystemMember() { } + public ForceSystemMember() + { + EntityId = 0; + Position = Vector3.Zero; + Direction = Vector3.Zero; + Time = 0.0f; + Speed = 0.0f; + Acceleration = 0.0f; + InvasiveListNode = new(); + } } } diff --git a/src/MHServerEmu.Games/Entities/Physics/PhysicsManager.cs b/src/MHServerEmu.Games/Entities/Physics/PhysicsManager.cs index 209782ce..39c39ded 100644 --- a/src/MHServerEmu.Games/Entities/Physics/PhysicsManager.cs +++ b/src/MHServerEmu.Games/Entities/Physics/PhysicsManager.cs @@ -196,7 +196,7 @@ namespace MHServerEmu.Games.Entities.Physics EntityManager entityManager = _game.EntityManager; - foreach (var member in forceSystem.Members) + foreach (var member in forceSystem.Members.Iterate()) { if (member == null) continue; bool active = false; @@ -684,7 +684,7 @@ namespace MHServerEmu.Games.Entities.Physics float distanceSq = Vector3.DistanceSquared(epicenter, member.Position); var pendingMembers = pendingForce.Members; - foreach (var pendingMember in pendingMembers) + foreach (var pendingMember in pendingMembers.Iterate()) if (distanceSq > Vector3.DistanceSquared(epicenter, pendingMember.Position)) { pendingForce.Members.InsertBefore(member, pendingMember); diff --git a/src/MHServerEmu.Games/Entities/Player.cs b/src/MHServerEmu.Games/Entities/Player.cs index 70e794c5..cc6d3341 100644 --- a/src/MHServerEmu.Games/Entities/Player.cs +++ b/src/MHServerEmu.Games/Entities/Player.cs @@ -2872,7 +2872,7 @@ namespace MHServerEmu.Games.Entities } public bool SendRegionRequestQueueCommandToPlayerManager(PrototypeId regionRef, PrototypeId difficultyTierRef, - RegionRequestQueueCommandVar command, ulong groupId = 0, ulong targetPlayerDbId = 0, int teamSizeOverride = -1) + RegionRequestQueueCommandVar command, ulong groupId = 0, ulong targetPlayerDbId = 0) { ulong playerDbId = DatabaseUniqueId; ulong regionProtoId = (ulong)regionRef; @@ -2904,7 +2904,7 @@ namespace MHServerEmu.Games.Entities break; } - ServiceMessage.MatchRegionRequestQueueCommand message = new(playerDbId, regionProtoId, difficultyTierProtoId, metaStateProtoId, command, groupId, targetPlayerDbId, teamSizeOverride); + ServiceMessage.MatchRegionRequestQueueCommand message = new(playerDbId, regionProtoId, difficultyTierProtoId, metaStateProtoId, command, groupId, targetPlayerDbId); ServerManager.Instance.SendMessageToService(GameServiceType.PlayerManager, message); return true; @@ -3029,12 +3029,11 @@ namespace MHServerEmu.Games.Entities public bool DiscoverMapPosition(Vector3 position, bool syncWithParty = true) { - Region region = CurrentAvatar?.Region; - if (region == null) - return false; + var region = CurrentAvatar?.Region; + if (region == null) return Logger.WarnReturn(false, "UpdateMapDiscovery(): region == null"); MapDiscoveryData mapDiscoveryData = GetMapDiscoveryDataForEntity(CurrentAvatar); - if (mapDiscoveryData == null) return Logger.WarnReturn(false, "DiscoverMapPosition(): mapDiscoveryData == null"); + if (mapDiscoveryData == null) return Logger.WarnReturn(false, "UpdateDiscoveryMap(): mapDiscoveryData == null"); bool reveal = mapDiscoveryData.RevealPosition(this, position); diff --git a/src/MHServerEmu.Games/Events/EventScheduler.cs b/src/MHServerEmu.Games/Events/EventScheduler.cs index f5a66975..a668c7fe 100644 --- a/src/MHServerEmu.Games/Events/EventScheduler.cs +++ b/src/MHServerEmu.Games/Events/EventScheduler.cs @@ -15,10 +15,7 @@ namespace MHServerEmu.Games.Events private static readonly Logger Logger = LogManager.CreateLogger(); -#if DEBUG private readonly Stopwatch _stopwatch = Stopwatch.StartNew(); -#endif - private readonly ScheduledEventPool _eventPool = new(); private readonly TimeSpan _quantumSize; @@ -198,16 +195,12 @@ namespace MHServerEmu.Games.Events @event.EventGroupNode.Remove(); @event.InvalidatePointers(); -#if DEBUG TimeSpan referenceTime = _stopwatch.Elapsed; @event.OnTriggered(); TimeSpan triggerTime = _stopwatch.Elapsed - referenceTime; if (triggerTime >= _quantumSize) Logger.Warn($"{@event.GetType().Name} took {(_stopwatch.Elapsed - referenceTime).TotalMilliseconds} ms"); -#else - @event.OnTriggered(); -#endif if (++numEvents > MaxEventsPerUpdate) throw new Exception($"Infinite loop detected in EventScheduler."); diff --git a/src/MHServerEmu.Games/Navi/NaviCdt.cs b/src/MHServerEmu.Games/Navi/NaviCdt.cs index 8ffdc20a..e810ddc0 100644 --- a/src/MHServerEmu.Games/Navi/NaviCdt.cs +++ b/src/MHServerEmu.Games/Navi/NaviCdt.cs @@ -55,7 +55,7 @@ namespace MHServerEmu.Games.Navi public void Release() { - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) RemoveTriangle(triangle); _lastTriangle = null; @@ -126,7 +126,7 @@ namespace MHServerEmu.Games.Navi using var collinearEdgesHandle = ListPool.Instance.Get(out List collinearEdges); using NaviSerialCheck naviSerialCheck = new(this); - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) foreach (var edge in triangle.Edges) { if (edge.Triangles[0] == null || edge.Triangles[1] == null) continue; @@ -925,7 +925,7 @@ namespace MHServerEmu.Games.Navi { StringBuilder hashes = new(); int id = 0; - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) hashes.AppendLine($"[{id++}] {triangle.ToHashString()}"); FileHelper.SaveTextFileToRoot(fileName, hashes.ToString()); } @@ -934,7 +934,7 @@ namespace MHServerEmu.Games.Navi { StringBuilder hashes = new(); int id = 0; - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) hashes.AppendLine($"[{id++}] {triangle.ToHashString2()}"); FileHelper.SaveTextFileToRoot(fileName, hashes.ToString()); } @@ -943,12 +943,12 @@ namespace MHServerEmu.Games.Navi { NaviSvgHelper svg = new(this); Stack influences = new(); - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) foreach (var edge in triangle.Edges) foreach (var point in edge.Points) if (point.InfluenceRadius > 0 && influences.Contains(point) == false) influences.Push(point); - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) svg.AddTriangle(triangle); foreach (var point in influences) svg.AddCircle(point.Pos, point.InfluenceRadius); @@ -962,7 +962,7 @@ namespace MHServerEmu.Games.Navi // Vertices int newId = 1; - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) if (triangle.PathingFlags.HasFlag(filterFlags)) foreach (var edge in triangle.Edges) foreach (var point in edge.Points) @@ -975,7 +975,7 @@ namespace MHServerEmu.Games.Navi } // Faces - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) if (triangle.PathingFlags.HasFlag(filterFlags)) { var p0 = triangle.PointCW(0); diff --git a/src/MHServerEmu.Games/Navi/NaviMesh.cs b/src/MHServerEmu.Games/Navi/NaviMesh.cs index 248f4518..47f4390d 100644 --- a/src/MHServerEmu.Games/Navi/NaviMesh.cs +++ b/src/MHServerEmu.Games/Navi/NaviMesh.cs @@ -345,7 +345,7 @@ namespace MHServerEmu.Games.Navi private void ReverseMarkupMesh() { - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) for (int edgeIndex = 0; edgeIndex < 3; edgeIndex++) { NaviEdge edge = triangle.Edges[edgeIndex]; @@ -367,7 +367,7 @@ namespace MHServerEmu.Games.Navi private void ClearMarkup() { - foreach (var triangle in TriangleList) + foreach (var triangle in TriangleList.Iterate()) { triangle.ClearFlag(NaviTriangleFlags.Markup); triangle.PathingFlags = PathFlags.None; diff --git a/src/MHServerEmu.Games/Navi/NaviTriangle.cs b/src/MHServerEmu.Games/Navi/NaviTriangle.cs index 17d2c516..69a5f1d1 100644 --- a/src/MHServerEmu.Games/Navi/NaviTriangle.cs +++ b/src/MHServerEmu.Games/Navi/NaviTriangle.cs @@ -11,22 +11,21 @@ namespace MHServerEmu.Games.Navi Markup = 1 << 1, } - public sealed class TriangleList : InvasiveList + public class TriangleList : InvasiveList { public TriangleList(int maxIterators = 1) : base(maxIterators) { } - public override ref InvasiveListNode GetInvasiveListNode(NaviTriangle element, int listId) => ref element.InvasiveListNode; + public override InvasiveListNode GetInvasiveListNode(NaviTriangle element, int listId) => element.InvasiveListNode; } public class NaviTriangle { private InlineArray3 _edges; - private InvasiveListNode _invasiveListNode; public ref InlineArray3 Edges { get => ref _edges; } public byte EdgeSideFlags { get; private set; } public NaviTriangleFlags Flags { get; private set; } public PathFlags PathingFlags { get; set; } - public ref InvasiveListNode InvasiveListNode { get => ref _invasiveListNode; } + public InvasiveListNode InvasiveListNode { get; private set; } public ContentFlagCounts ContentFlagCounts; // ContentFlagCounts needs to be a field for Clear() calls @@ -35,6 +34,7 @@ namespace MHServerEmu.Games.Navi Edges[0] = e0; Edges[1] = e1; Edges[2] = e2; + InvasiveListNode = new(); UpdateEdgeSideFlags(); Attach(); } diff --git a/src/MHServerEmu.Games/Network/MigrationUtility.cs b/src/MHServerEmu.Games/Network/MigrationUtility.cs index 2fcf3e1a..e642fd89 100644 --- a/src/MHServerEmu.Games/Network/MigrationUtility.cs +++ b/src/MHServerEmu.Games/Network/MigrationUtility.cs @@ -1,9 +1,7 @@ using Gazillion; -using MHServerEmu.Core.Memory; using MHServerEmu.Core.Serialization; using MHServerEmu.DatabaseAccess.Models; using MHServerEmu.Games.Entities; -using MHServerEmu.Games.Entities.Avatars; using MHServerEmu.Games.GameData; using MHServerEmu.Games.GameData.Prototypes; using MHServerEmu.Games.Properties; @@ -56,25 +54,7 @@ namespace MHServerEmu.Games.Network { List<(ulong, ulong)> propertyList = migrationData.GetOrCreatePropertyList(entity.DatabaseUniqueId); propertyList.Clear(); - - // HACK: Ugly property hack, remove this when we figure out an efficient way to migrate runtime-only conditions. - PropertyEnum propertyToIgnore = PropertyEnum.Invalid; - - if (entity is Avatar) - { - switch ((AvatarPrototypeId)entity.PrototypeDataRef) - { - case AvatarPrototypeId.AntMan: - propertyToIgnore = PropertyEnum.SecondaryResource; - break; - - case AvatarPrototypeId.HumanTorch: - propertyToIgnore = PropertyEnum.Endurance; - break; - } - } - - entity.Properties.GetPropertiesForMigration(propertyList, propertyToIgnore); + entity.Properties.GetPropertiesForMigration(propertyList); } private static void RestoreProperties(MigrationData migrationData, Entity entity) diff --git a/src/MHServerEmu.Games/Network/PlayerConnection.cs b/src/MHServerEmu.Games/Network/PlayerConnection.cs index 4e275c36..35a5b9e1 100644 --- a/src/MHServerEmu.Games/Network/PlayerConnection.cs +++ b/src/MHServerEmu.Games/Network/PlayerConnection.cs @@ -259,18 +259,9 @@ namespace MHServerEmu.Games.Network using (Archive archive = new(ArchiveSerializeType.Database)) { - DBPlayer dbPlayer = _dbAccount.Player; - Span oldArchiveData = dbPlayer.ArchiveData ?? Span.Empty; - // NOTE: Use Transfer() and NOT Player.Serialize() to make sure we pack the size of the player Serializer.Transfer(archive, Player); - Span newArchiveData = archive.AsSpan(); - - // No point in doing a SequenceEqual check here, it's always different in practice. - if (newArchiveData.Length == oldArchiveData.Length) - newArchiveData.CopyTo(oldArchiveData); - else - dbPlayer.ArchiveData = newArchiveData.ToArray(); + _dbAccount.Player.ArchiveData = archive.AccessAutoBuffer().ToArray(); } // Save last town as a separate database field to be able to access it without deserializing the player entity diff --git a/src/MHServerEmu.Games/Populations/SpawnSpec.cs b/src/MHServerEmu.Games/Populations/SpawnSpec.cs index dc6c611d..88255fe0 100644 --- a/src/MHServerEmu.Games/Populations/SpawnSpec.cs +++ b/src/MHServerEmu.Games/Populations/SpawnSpec.cs @@ -110,7 +110,6 @@ namespace MHServerEmu.Games.Populations int level = area.GetCharacterLevel(entityProto); settingsProperties[PropertyEnum.CharacterLevel] = level; settingsProperties[PropertyEnum.CombatLevel] = level; - settingsProperties[PropertyEnum.DifficultyTier] = region.DifficultyTierRef; if (Group != null) { settingsProperties[PropertyEnum.SpawnGroupId] = Group.Id; diff --git a/src/MHServerEmu.Games/Powers/PowerPayload.cs b/src/MHServerEmu.Games/Powers/PowerPayload.cs index f7f43536..b79a1e0a 100644 --- a/src/MHServerEmu.Games/Powers/PowerPayload.cs +++ b/src/MHServerEmu.Games/Powers/PowerPayload.cs @@ -216,10 +216,6 @@ namespace MHServerEmu.Games.Powers if (PowerPrototype is not MovementPowerPrototype movementPowerProto || movementPowerProto.ConstantMoveTime == false) Properties.CopyProperty(power.Properties, PropertyEnum.MovementSpeedOverride); - // Difficulty tier for summons (e.g. Axis raid sentinels) - if (PowerPrototype is SummonPowerPrototype) - Properties.CopyProperty(powerOwner.Properties, PropertyEnum.DifficultyTier); - // Snapshot properties from triggering power results // TODO: Do we need full power results here? We should be able to get away with just the properties diff --git a/src/MHServerEmu.Games/Properties/PropertyCollection.cs b/src/MHServerEmu.Games/Properties/PropertyCollection.cs index f2d077d4..7cb4205f 100644 --- a/src/MHServerEmu.Games/Properties/PropertyCollection.cs +++ b/src/MHServerEmu.Games/Properties/PropertyCollection.cs @@ -844,7 +844,7 @@ namespace MHServerEmu.Games.Properties return success; } - public void GetPropertiesForMigration(List<(ulong, ulong)> propertyList, PropertyEnum propertyToIgnore = PropertyEnum.Invalid) + public void GetPropertiesForMigration(List<(ulong, ulong)> propertyList) { PropertyEnum prevProperty = PropertyEnum.Invalid; PropertyInfoPrototype propInfoProto = null; @@ -860,9 +860,9 @@ namespace MHServerEmu.Games.Properties prevProperty = propertyEnum; } - // HACK: Do not migrate some properties because we don't migrate conditions, which can cause some heroes to get stuck in bad state (e.g. Human Torch, Ant-Man). + // HACK: Do not migrate mana because we don't migrate conditions, which can cause some heroes to get stuck in bad state (e.g. Human Torch). // This can be removed if we ever start migrating runtime-only conditions. - if (propertyEnum == propertyToIgnore) + if (propertyEnum == PropertyEnum.Endurance) continue; // Migrate properties that are not saved to the database, but are supposed to be replicated for transfer diff --git a/src/MHServerEmu.Games/Regions/MatchQueues/MatchQueueStatus.cs b/src/MHServerEmu.Games/Regions/MatchQueues/MatchQueueStatus.cs index 16caecad..57d830d5 100644 --- a/src/MHServerEmu.Games/Regions/MatchQueues/MatchQueueStatus.cs +++ b/src/MHServerEmu.Games/Regions/MatchQueues/MatchQueueStatus.cs @@ -214,7 +214,7 @@ namespace MHServerEmu.Games.Regions.MatchQueues /// Handles a request from a client. /// public bool TryRegionRequestCommand(PrototypeId regionRef, PrototypeId difficultyTierRef, - ulong groupId, RegionRequestQueueCommandVar command, int teamSizeOverride = -1) + ulong groupId, RegionRequestQueueCommandVar command) { if (regionRef == PrototypeId.Invalid) return Logger.WarnReturn(false, "TryRegionRequestCommand(): regionRef == PrototypeId.Invalid"); @@ -272,7 +272,7 @@ namespace MHServerEmu.Games.Regions.MatchQueues if (command == RegionRequestQueueCommandVar.eRRQC_AddToQueueBypass && regionProto.AllowsQueueBypass == false) return false; - _owner.SendRegionRequestQueueCommandToPlayerManager(regionRef, difficultyTierRef, command, groupId, 0, teamSizeOverride); + _owner.SendRegionRequestQueueCommandToPlayerManager(regionRef, difficultyTierRef, command, groupId); return true; } diff --git a/src/MHServerEmu.PlayerManagement/Auth/SessionManager.cs b/src/MHServerEmu.PlayerManagement/Auth/SessionManager.cs index bf6998e0..afe7915e 100644 --- a/src/MHServerEmu.PlayerManagement/Auth/SessionManager.cs +++ b/src/MHServerEmu.PlayerManagement/Auth/SessionManager.cs @@ -33,8 +33,6 @@ namespace MHServerEmu.PlayerManagement.Auth private CooldownTimer _updateTimer = new(TimeSpan.FromMilliseconds(1000)); - public bool WhitelistEnabled { get; private set; } - public int PendingSessionCount { get => _pendingSessionDict.Count; } public int ActiveSessionCount { get => _activeSessionDict.Count; } @@ -44,16 +42,6 @@ namespace MHServerEmu.PlayerManagement.Auth public SessionManager(PlayerManagerService playerManager) { _playerManager = playerManager; - WhitelistEnabled = playerManager.Config.UseWhitelist; - } - - public void SetWhitelistEnabled(bool enable) - { - if (WhitelistEnabled == enable) - return; - - WhitelistEnabled = enable; - Logger.Info($"Whitelist {(enable ? "enabled" : "disabled")}"); } public void Update() @@ -104,7 +92,7 @@ namespace MHServerEmu.PlayerManagement.Auth } // Verify credentials - AuthStatusCode statusCode = AccountManager.TryGetAccountByLoginDataPB(loginDataPB, WhitelistEnabled, out DBAccount account); + AuthStatusCode statusCode = AccountManager.TryGetAccountByLoginDataPB(loginDataPB, _playerManager.Config.UseWhitelist, out DBAccount account); if (statusCode != AuthStatusCode.Success) return statusCode; diff --git a/src/MHServerEmu.PlayerManagement/Matchmaking/Match.cs b/src/MHServerEmu.PlayerManagement/Matchmaking/Match.cs index fbe32b28..99c1ec0c 100644 --- a/src/MHServerEmu.PlayerManagement/Matchmaking/Match.cs +++ b/src/MHServerEmu.PlayerManagement/Matchmaking/Match.cs @@ -27,13 +27,11 @@ namespace MHServerEmu.PlayerManagement.Matchmaking QueueParams = queueParams; int[] teamLimits = Queue.Prototype.TeamLimits; - if (teamLimits.HasValue()) { for (int i = 0; i < teamLimits.Length; i++) { - int teamLimit = (queueParams.TeamSizeOverride > 0) ? queueParams.TeamSizeOverride : teamLimits[i]; - MatchTeam team = new(i, teamLimit); + MatchTeam team = new(i, teamLimits[i]); _teams.Add(team); } } diff --git a/src/MHServerEmu.PlayerManagement/Matchmaking/RegionRequestQueueCommandHandler.cs b/src/MHServerEmu.PlayerManagement/Matchmaking/RegionRequestQueueCommandHandler.cs index 232d53c4..b622c041 100644 --- a/src/MHServerEmu.PlayerManagement/Matchmaking/RegionRequestQueueCommandHandler.cs +++ b/src/MHServerEmu.PlayerManagement/Matchmaking/RegionRequestQueueCommandHandler.cs @@ -22,7 +22,7 @@ namespace MHServerEmu.PlayerManagement.Matchmaking } public void HandleCommand(PrototypeId regionRef, PrototypeId difficultyTierRef, PrototypeId metaStateRef, - RegionRequestQueueCommandVar command, ulong regionRequestGroupId, ulong targetPlayerDbId, int teamSizeOverride = -1) + RegionRequestQueueCommandVar command, ulong regionRequestGroupId, ulong targetPlayerDbId) { Logger.Trace($"HandleCommand(): command=[{command}], region=[{regionRef.GetNameFormatted()}], player=[{_player}]"); @@ -31,7 +31,7 @@ namespace MHServerEmu.PlayerManagement.Matchmaking case RegionRequestQueueCommandVar.eRRQC_AddToQueueSolo: case RegionRequestQueueCommandVar.eRRQC_AddToQueueParty: case RegionRequestQueueCommandVar.eRRQC_AddToQueueBypass: - OnAddToQueue(regionRef, difficultyTierRef, metaStateRef, command, teamSizeOverride); + OnAddToQueue(regionRef, difficultyTierRef, metaStateRef, command); break; case RegionRequestQueueCommandVar.eRRQC_RemoveFromQueue: @@ -53,8 +53,7 @@ namespace MHServerEmu.PlayerManagement.Matchmaking } } - private bool OnAddToQueue(PrototypeId regionRef, PrototypeId difficultyTierRef, PrototypeId metaStateRef, - RegionRequestQueueCommandVar command, int teamSizeOverride = -1) + private bool OnAddToQueue(PrototypeId regionRef, PrototypeId difficultyTierRef, PrototypeId metaStateRef, RegionRequestQueueCommandVar command) { RegionRequestQueue queue = PlayerManagerService.Instance.RegionRequestQueueManager.GetRegionRequestQueue(regionRef); if (queue == null) @@ -75,7 +74,7 @@ namespace MHServerEmu.PlayerManagement.Matchmaking // Create region request group MasterParty party = command == RegionRequestQueueCommandVar.eRRQC_AddToQueueParty ? _player.CurrentParty : null; - RegionRequestQueueParams queueParams = new(difficultyTierRef, metaStateRef, command == RegionRequestQueueCommandVar.eRRQC_AddToQueueBypass, teamSizeOverride); + RegionRequestQueueParams queueParams = new(difficultyTierRef, metaStateRef, command == RegionRequestQueueCommandVar.eRRQC_AddToQueueBypass); RegionRequestGroup group = RegionRequestGroup.Create(queue, queueParams, _player, party); if (group == null) diff --git a/src/MHServerEmu.PlayerManagement/Matchmaking/RegionRequestQueueParams.cs b/src/MHServerEmu.PlayerManagement/Matchmaking/RegionRequestQueueParams.cs index 5cb36768..306cae9d 100644 --- a/src/MHServerEmu.PlayerManagement/Matchmaking/RegionRequestQueueParams.cs +++ b/src/MHServerEmu.PlayerManagement/Matchmaking/RegionRequestQueueParams.cs @@ -10,24 +10,22 @@ namespace MHServerEmu.PlayerManagement.Matchmaking public readonly PrototypeId DifficultyTierRef; public readonly PrototypeId MetaStateRef; public readonly bool IsBypass; - public readonly int TeamSizeOverride; - public RegionRequestQueueParams(PrototypeId difficultyTierRef, PrototypeId metaStateRef, bool isBypass, int teamSizeOverride) + public RegionRequestQueueParams(PrototypeId difficultyTierRef, PrototypeId metaStateRef, bool isBypass) { DifficultyTierRef = difficultyTierRef; MetaStateRef = metaStateRef; IsBypass = isBypass; - TeamSizeOverride = teamSizeOverride; } public override string ToString() { - return $"difficulty={DifficultyTierRef.GetNameFormatted()}, metaState={MetaStateRef.GetNameFormatted()}, isBypass={IsBypass}, teamSizeOverride={TeamSizeOverride}"; + return $"difficulty={DifficultyTierRef.GetNameFormatted()}, metaState={MetaStateRef.GetNameFormatted()}, isBypass={IsBypass}"; } public override int GetHashCode() { - return HashCode.Combine(DifficultyTierRef, MetaStateRef, IsBypass, TeamSizeOverride); + return HashCode.Combine(DifficultyTierRef, MetaStateRef, IsBypass); } public override bool Equals(object obj) @@ -42,8 +40,7 @@ namespace MHServerEmu.PlayerManagement.Matchmaking { return DifficultyTierRef == other.DifficultyTierRef && MetaStateRef == other.MetaStateRef && - IsBypass == other.IsBypass && - TeamSizeOverride == other.TeamSizeOverride; + IsBypass == other.IsBypass; } public static bool operator ==(RegionRequestQueueParams left, RegionRequestQueueParams right) diff --git a/src/MHServerEmu.PlayerManagement/Network/PlayerManagerServiceMailbox.cs b/src/MHServerEmu.PlayerManagement/Network/PlayerManagerServiceMailbox.cs index 25754e66..ead02804 100644 --- a/src/MHServerEmu.PlayerManagement/Network/PlayerManagerServiceMailbox.cs +++ b/src/MHServerEmu.PlayerManagement/Network/PlayerManagerServiceMailbox.cs @@ -137,10 +137,6 @@ namespace MHServerEmu.PlayerManagement.Network OnAccountOperationRequest(accountOperationRequest); break; - case ServiceMessage.SetWhitelistEnabled setWhitelistEnabled: - OnSetWhitelistEnabled(setWhitelistEnabled); - break; - default: Logger.Warn($"ReceiveServiceMessage(): Unhandled service message type {message.GetType().Name}"); break; @@ -429,10 +425,9 @@ namespace MHServerEmu.PlayerManagement.Network RegionRequestQueueCommandVar command = matchRegionRequestQueueCommand.Command; ulong regionRequestGroupId = matchRegionRequestQueueCommand.RegionRequestGroupId; ulong targetPlayerDbId = matchRegionRequestQueueCommand.TargetPlayerDbId; - int teamSizeOverride = matchRegionRequestQueueCommand.TeamSizeOverride; PlayerHandle player = _playerManager.ClientManager.GetPlayer(playerDbId); - player?.ReceiveRegionRequestQueueCommand(regionRef, difficultyTierRef, metaStateRef, command, regionRequestGroupId, targetPlayerDbId, teamSizeOverride); + player?.ReceiveRegionRequestQueueCommand(regionRef, difficultyTierRef, metaStateRef, command, regionRequestGroupId, targetPlayerDbId); return true; } @@ -632,13 +627,6 @@ namespace MHServerEmu.PlayerManagement.Network return true; } - private bool OnSetWhitelistEnabled(in ServiceMessage.SetWhitelistEnabled setWhitelistEnabled) - { - _playerManager.SessionManager.SetWhitelistEnabled(setWhitelistEnabled.Enable); - - return true; - } - #endregion } } diff --git a/src/MHServerEmu.PlayerManagement/PlayerManagerConfig.cs b/src/MHServerEmu.PlayerManagement/PlayerManagerConfig.cs index 5fc140f4..edb2aa5a 100644 --- a/src/MHServerEmu.PlayerManagement/PlayerManagerConfig.cs +++ b/src/MHServerEmu.PlayerManagement/PlayerManagerConfig.cs @@ -14,7 +14,7 @@ namespace MHServerEmu.PlayerManagement public bool ShowNewsOnLogin { get; private set; } = false; public string NewsUrl { get; private set; } = "http://localhost/"; public int ServerCapacity { get; private set; } = 0; - public int MaxLoginQueueClients { get; private set; } = 8192; + public int MaxLoginQueueClients { get; private set; } = 10000; public bool EnableTownPlayerLimit { get; private set; } = false; } } diff --git a/src/MHServerEmu.PlayerManagement/PlayerManagerService.cs b/src/MHServerEmu.PlayerManagement/PlayerManagerService.cs index dca9ec71..269927ef 100644 --- a/src/MHServerEmu.PlayerManagement/PlayerManagerService.cs +++ b/src/MHServerEmu.PlayerManagement/PlayerManagerService.cs @@ -49,8 +49,6 @@ namespace MHServerEmu.PlayerManagement /// public PlayerManagerService() { - Config = ConfigManager.Instance.GetConfig(); - _serviceMailbox = new(this); SessionManager = new(this); @@ -64,6 +62,8 @@ namespace MHServerEmu.PlayerManagement RegionRequestQueueManager = new(this); EventScheduler = new(); + + Config = ConfigManager.Instance.GetConfig(); } #region IGameService Implementation diff --git a/src/MHServerEmu.PlayerManagement/Players/LoginQueueManager.cs b/src/MHServerEmu.PlayerManagement/Players/LoginQueueManager.cs index 24225974..e29f815e 100644 --- a/src/MHServerEmu.PlayerManagement/Players/LoginQueueManager.cs +++ b/src/MHServerEmu.PlayerManagement/Players/LoginQueueManager.cs @@ -1,7 +1,6 @@ using Gazillion; using Google.ProtocolBuffers; using MHServerEmu.Core.Collections; -using MHServerEmu.Core.Extensions; using MHServerEmu.Core.Logging; using MHServerEmu.Core.Network; using MHServerEmu.Core.System.Time; @@ -16,55 +15,24 @@ namespace MHServerEmu.PlayerManagement.Players private static readonly Logger Logger = LogManager.CreateLogger(); private static readonly TimeSpan PendingClientTimeout = TimeSpan.FromSeconds(15); - private static readonly TimeSpan StatusUpdateInterval = TimeSpan.FromSeconds(10); - private static readonly TimeSpan ReconnectPermissionDuration = TimeSpan.FromMinutes(10); - - private static readonly SessionEncryptionChanged SessionEncryptionChangedMessage = SessionEncryptionChanged.CreateBuilder() - .SetRandomNumberIndex(0) - .SetEncryptedRandomNumber(ByteString.Empty) - .Build(); private readonly DoubleBufferQueue _newClientQueue = new(); - - private readonly LinkedList _defaultQueue = new(); - private readonly LinkedList _reconnectQueue = new(); - private readonly Queue _highPriorityQueue = new(); - - private readonly Stack> _queueNodes; + private readonly Queue _loginQueue = new(); + private readonly Queue _highPriorityLoginQueue = new(); // Pending clients are clients that have successfully passed the login queue private readonly Dictionary _pendingClients = new(); private readonly PlayerManagerService _playerManager; - private readonly LoginQueueStatus.Builder _statusBuilder = LoginQueueStatus.CreateBuilder(); - private readonly Dictionary _statusUpdateTimes; - - private readonly Dictionary _reconnectPermissions = new(); - private CooldownTimer _reconnectPermissionPurgeTimer = new(TimeSpan.FromMinutes(1)); - - public int PlayersInLine { get => _defaultQueue.Count + _reconnectQueue.Count; } - public LoginQueueManager(PlayerManagerService playerManager) { _playerManager = playerManager; - - int maxQueueClients = playerManager.Config.MaxLoginQueueClients; - - _queueNodes = new(maxQueueClients); - for (int i = 0; i < maxQueueClients; i++) - { - LinkedListNode node = new(null); - _queueNodes.Push(node); - } - - _statusUpdateTimes = new(maxQueueClients); } public void Update() { TimeOutPendingClients(); - PurgeReconnectPermissions(); AcceptNewClients(); ProcessLoginQueue(); } @@ -104,29 +72,13 @@ namespace MHServerEmu.PlayerManagement.Players } } - private void PurgeReconnectPermissions() - { - if (_reconnectPermissionPurgeTimer.Check() == false) - return; - - TimeSpan now = Clock.UnixTime; - - foreach (var kvp in _reconnectPermissions) - { - TimeSpan duration = now - kvp.Value; - if (duration >= ReconnectPermissionDuration) - { - _reconnectPermissions.Remove(kvp.Key); - Logger.Info($"Reconnect permission expired for account 0x{kvp.Key:X}"); - } - } - } - /// /// Accepts asynchronously added clients to the login queue. /// private void AcceptNewClients() { + int maxLoginQueueClients = _playerManager.Config.MaxLoginQueueClients; + _newClientQueue.Swap(); while (_newClientQueue.CurrentCount > 0) @@ -146,30 +98,22 @@ namespace MHServerEmu.PlayerManagement.Players // High priority queue always ignores server capacity if (IsClientHighPriority(client)) { - _highPriorityQueue.Enqueue(client); + _highPriorityLoginQueue.Enqueue(client); } else { - if (_queueNodes.TryPop(out LinkedListNode queueNode) == false) + if (_loginQueue.Count >= maxLoginQueueClients) { - Logger.Warn($"AcceptNewClients(): Unable to accept client [{client}], the queue already has {PlayersInLine} clients, which is the maximum number allowed by the current server configuration"); + Logger.Warn($"AcceptNewClients(): Unable to accept client [{client}], the queue already has {maxLoginQueueClients} clients, which is the maximum number allowed by the current server configuration"); client.Disconnect(); RemoveClientSession(client); continue; } - LinkedList queueToUse = _defaultQueue; - - if (_reconnectPermissions.Remove(client.DbId)) - { - queueToUse = _reconnectQueue; - Logger.Info($"Consumed reconnect permission for client [{client}]"); - } - - queueNode.Value = client; - queueToUse.AddLast(queueNode); + _loginQueue.Enqueue(client); } + Logger.Info($"Accepted client [{client}] into the login queue"); } } @@ -183,40 +127,37 @@ namespace MHServerEmu.PlayerManagement.Players int availableCapacity = totalCapacity - _playerManager.ClientManager.PlayerCount - _pendingClients.Count; // Let clients from the high priority queue in first ignoring capacity - while (_highPriorityQueue.Count > 0) + while (_highPriorityLoginQueue.Count > 0) { - IFrontendClient client = _highPriorityQueue.Dequeue(); - ProcessQueuedClient(client, ref availableCapacity, false); + IFrontendClient client = _highPriorityLoginQueue.Dequeue(); + ProcessQueuedClient(client, ref availableCapacity); } - // Let clients from the reconnect and default queues, check available capacity if enabled - ProcessQueue(_reconnectQueue, totalCapacity, ref availableCapacity, false); - ProcessQueue(_defaultQueue, totalCapacity, ref availableCapacity, true); - - // Update status of remaining players - int playersInLine = PlayersInLine; - if (playersInLine > 0) + // Let clients from the normal login queue, check available capacity if enabled + while (_loginQueue.Count > 0 && (totalCapacity <= 0 || availableCapacity > 0)) { - _statusBuilder.SetNumberOfPlayersInLine((ulong)playersInLine); - - TimeSpan now = Clock.UnixTime; - ulong nextPlaceInLine = 1; - - UpdateQueueStatus(_reconnectQueue, ref nextPlaceInLine, now); - UpdateQueueStatus(_defaultQueue, ref nextPlaceInLine, now); + IFrontendClient client = _loginQueue.Dequeue(); + ProcessQueuedClient(client, ref availableCapacity); } + + // Send status updates to remaining players + int playersInLine = _loginQueue.Count; + if (playersInLine == 0) + return; + + LoginQueueStatus.Builder statusBuilder = LoginQueueStatus.CreateBuilder() + .SetNumberOfPlayersInLine((ulong)playersInLine); + + ulong placeInLine = 1; + + foreach (IFrontendClient client in _loginQueue) + client.SendMessage(MuxChannel, statusBuilder.SetPlaceInLine(placeInLine++).Build()); } private static bool IsClientHighPriority(IFrontendClient client) { - DBAccount account = ((IDBAccountOwner)client).Account; - - // Users with elevated privileges (moderators / admins) have high priority by default. - if (account.UserLevel > AccountUserLevel.User) - return true; - - // Accounts can be manually flagged to have high priority. - if (account.Flags.HasFlag(AccountFlags.BypassLoginQueue)) + // Users with elevated privileges (moderators / admins) have high priority + if (((IDBAccountOwner)client).Account.UserLevel > AccountUserLevel.User) return true; // Add more cases as needed @@ -224,27 +165,8 @@ namespace MHServerEmu.PlayerManagement.Players return false; } - private void ProcessQueue(LinkedList queue, int totalCapacity, ref int availableCapacity, bool allowReconnect) + private bool ProcessQueuedClient(IFrontendClient client, ref int availableCapacity) { - while (queue.Count > 0 && (totalCapacity <= 0 || availableCapacity > 0)) - { - LinkedListNode queueNode = queue.First; - IFrontendClient client = queueNode.Value; - - RemoveQueueNode(queueNode); - ProcessQueuedClient(client, ref availableCapacity, allowReconnect); - } - } - - private bool ProcessQueuedClient(IFrontendClient client, ref int availableCapacity, bool allowReconnect) - { - // Allow all clients that pass the default queue to reconnect because of the client-side afk timer bug. - if (allowReconnect) - { - _reconnectPermissions[client.DbId] = Clock.UnixTime; - Logger.Info($"Added reconnect permission for client [{client}]"); - } - if (client.IsConnected == false) { Logger.Warn($"ProcessQueuedClient(): Client [{client}] disconnected while waiting in the login queue"); @@ -256,8 +178,10 @@ namespace MHServerEmu.PlayerManagement.Players // However, if a malicious user modifies their client, it may try to skip ahead, so we need to verify this. _pendingClients.Add(client, Clock.UnixTime); - // Gazillion never finished implementing encryption, so the SessionEncryptionChanged message is just a dummy we can cache and reuse. - client.SendMessage(MuxChannel, SessionEncryptionChangedMessage); + client.SendMessage(MuxChannel, SessionEncryptionChanged.CreateBuilder() + .SetRandomNumberIndex(0) + .SetEncryptedRandomNumber(ByteString.Empty) + .Build()); Logger.Info($"Client [{client}] passed the login queue"); @@ -266,47 +190,6 @@ namespace MHServerEmu.PlayerManagement.Players return true; } - private void UpdateQueueStatus(LinkedList queue, ref ulong nextPlaceInLine, TimeSpan now) - { - LinkedListNode current = queue.First; - while (current != null) - { - IFrontendClient client = current.Value; - - if (client.IsConnected == false) - { - Logger.Warn($"UpdateQueueStatus(): Client [{client}] disconnected while waiting in the login queue"); - - LinkedListNode prev = current; - current = current.Next; - RemoveQueueNode(prev); - - RemoveClientSession(client); - - continue; - } - - ulong placeInLine = nextPlaceInLine++; - - ref TimeSpan lastUpdateTime = ref _statusUpdateTimes.GetValueRefOrAddDefault(client); - if ((now - lastUpdateTime) >= StatusUpdateInterval) - { - lastUpdateTime = now; - LoginQueueStatus status = _statusBuilder.SetPlaceInLine(placeInLine).Build(); - client.SendMessage(MuxChannel, status); - } - - current = current.Next; - } - } - - private void RemoveQueueNode(LinkedListNode node) - { - _statusUpdateTimes.Remove(node.Value); - node.Remove(); - _queueNodes.Push(node); - } - private void RemoveClientSession(IFrontendClient client) { ulong sessionId = client.Session.Id; diff --git a/src/MHServerEmu.PlayerManagement/Players/PlayerHandle.cs b/src/MHServerEmu.PlayerManagement/Players/PlayerHandle.cs index be9dc490..60f4215c 100644 --- a/src/MHServerEmu.PlayerManagement/Players/PlayerHandle.cs +++ b/src/MHServerEmu.PlayerManagement/Players/PlayerHandle.cs @@ -814,9 +814,9 @@ namespace MHServerEmu.PlayerManagement.Players } public void ReceiveRegionRequestQueueCommand(PrototypeId regionRef, PrototypeId difficultyTierRef, PrototypeId metaStateRef, - RegionRequestQueueCommandVar command, ulong regionRequestGroupId, ulong targetPlayerDbId, int teamSizeOverride) + RegionRequestQueueCommandVar command, ulong regionRequestGroupId, ulong targetPlayerDbId) { - _regionRequestQueueCommandHandler.HandleCommand(regionRef, difficultyTierRef, metaStateRef, command, regionRequestGroupId, targetPlayerDbId, teamSizeOverride); + _regionRequestQueueCommandHandler.HandleCommand(regionRef, difficultyTierRef, metaStateRef, command, regionRequestGroupId, targetPlayerDbId); } public void AddToChatRoom(ChatRoomTypes roomType, ulong roomId) diff --git a/src/MHServerEmu/Commands/Implementations/ItemCommands.cs b/src/MHServerEmu/Commands/Implementations/ItemCommands.cs index 3a2470f6..13943267 100644 --- a/src/MHServerEmu/Commands/Implementations/ItemCommands.cs +++ b/src/MHServerEmu/Commands/Implementations/ItemCommands.cs @@ -71,7 +71,7 @@ namespace MHServerEmu.Commands.Implementations LootManager lootGenerator = playerConnection.Game.LootManager; for (int i = 0; i < count; i++) - lootGenerator.GiveItem(itemProtoRef, LootContext.CashShop, player); + lootGenerator.GiveItem(itemProtoRef, LootContext.Drop, player); Logger.Debug($"GiveItem(): {itemProtoRef.GetName()}[{count}] to {player}"); return string.Empty; diff --git a/src/MHServerEmu/Commands/Implementations/PvPCommands.cs b/src/MHServerEmu/Commands/Implementations/PvPCommands.cs deleted file mode 100644 index 9ae91f98..00000000 --- a/src/MHServerEmu/Commands/Implementations/PvPCommands.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Gazillion; -using MHServerEmu.Commands.Attributes; -using MHServerEmu.Core.Network; -using MHServerEmu.DatabaseAccess.Models; -using MHServerEmu.Games.GameData; -using MHServerEmu.Games.Network; - -namespace MHServerEmu.Commands.Implementations -{ - [CommandGroup("pvp")] - [CommandGroupDescription("Commands related to PvP matchmaking.")] - [CommandGroupUserLevel(AccountUserLevel.User)] - public class PvPCommands : CommandGroup - { - private static readonly PrototypeId RegionRef = - GameDatabase.GetPrototypeRefByName("Metagame/DefenderPvP/Regions/PvPDefenderTier5Region.prototype"); - - private static readonly PrototypeId DifficultyRef = - GameDatabase.GetPrototypeRefByName("Difficulty/Tiers/Tier1Normal.prototype"); - - private string JoinQueue(int size, NetClient client) - { - PlayerConnection playerConnection = (PlayerConnection)client; - var player = playerConnection.Player; - if (player == null) return "Player not found."; - - bool isInParty = player.Party != null && player.Party.NumMembers > 1; - var command = isInParty - ? RegionRequestQueueCommandVar.eRRQC_AddToQueueParty - : RegionRequestQueueCommandVar.eRRQC_AddToQueueSolo; - - int limit = (size == 5) ? -1 : size; - player.MatchQueueStatus.TryRegionRequestCommand(RegionRef, DifficultyRef, 0, command, limit); - return $"Queued for {size}v{size} PvP! ({(isInParty ? "Party" : "Solo")})"; - } - - [Command("1v1")] - [CommandDescription("Join 1v1 PvP queue.")] - [CommandUsage("pvp 1v1")] - [CommandInvokerType(CommandInvokerType.Client)] - public string Queue1v1(string[] @params, NetClient client) => JoinQueue(1, client); - - [Command("2v2")] - [CommandDescription("Join 2v2 PvP queue.")] - [CommandUsage("pvp 2v2")] - [CommandInvokerType(CommandInvokerType.Client)] - public string Queue2v2(string[] @params, NetClient client) => JoinQueue(2, client); - - [Command("3v3")] - [CommandDescription("Join 3v3 PvP queue.")] - [CommandUsage("pvp 3v3")] - [CommandInvokerType(CommandInvokerType.Client)] - public string Queue3v3(string[] @params, NetClient client) => JoinQueue(3, client); - - [Command("4v4")] - [CommandDescription("Join 4v4 PvP queue.")] - [CommandUsage("pvp 4v4")] - [CommandInvokerType(CommandInvokerType.Client)] - public string Queue4v4(string[] @params, NetClient client) => JoinQueue(4, client); - - [Command("5v5")] - [CommandDescription("Join 5v5 PvP queue.")] - [CommandUsage("pvp 5v5")] - [CommandInvokerType(CommandInvokerType.Client)] - public string Queue5v5(string[] @params, NetClient client) => JoinQueue(5, client); - } -} diff --git a/src/MHServerEmu/Commands/Implementations/ServerCommands.cs b/src/MHServerEmu/Commands/Implementations/ServerCommands.cs index 6603f315..dfe14e0d 100644 --- a/src/MHServerEmu/Commands/Implementations/ServerCommands.cs +++ b/src/MHServerEmu/Commands/Implementations/ServerCommands.cs @@ -105,29 +105,6 @@ namespace MHServerEmu.Commands.Implementations return string.Empty; } - [Command("whitelist")] - [CommandDescription("Enables or disables account whitelist for logins.")] - [CommandParamCount(1)] - [CommandUserLevel(AccountUserLevel.Admin)] - [CommandInvokerType(CommandInvokerType.ServerConsole)] - public string Whitelist(string[] @params, NetClient client) - { - bool enable; - - if (bool.TryParse(@params[0], out enable) == false) - { - if (int.TryParse(@params[0], out int value) == false) - return "Invalid parameter. Please use boolean or integer values."; - - enable = value != 0; - } - - ServiceMessage.SetWhitelistEnabled message = new(enable); - ServerManager.Instance.SendMessageToService(GameServiceType.PlayerManager, message); - - return $"Sent a request to {(enable ? "enable" : "disable")} whitelist."; - } - [Command("shutdown")] [CommandDescription("Shuts the server down.")] [CommandUsage("server shutdown")] diff --git a/src/MHServerEmu/Config.ini b/src/MHServerEmu/Config.ini index 47affec0..14c07402 100644 --- a/src/MHServerEmu/Config.ini +++ b/src/MHServerEmu/Config.ini @@ -87,7 +87,7 @@ UseWhitelist=false ShowNewsOnLogin=false NewsUrl=http://localhost/news/index.html ServerCapacity=0 -MaxLoginQueueClients=8192 +MaxLoginQueueClients=10000 EnableTownPlayerLimit=false [SQLiteDBManager]