]> git.deb.at Git - rhonda/impostor.git/commitdiff
Pooling
authorAeonLucid <aeonlucid@outlook.com>
Sat, 31 Oct 2020 22:23:26 +0000 (23:23 +0100)
committerAeonLucid <aeonlucid@outlook.com>
Sat, 31 Oct 2020 22:23:26 +0000 (23:23 +0100)
22 files changed:
src/Impostor.Api/Net/Messages/IMessageReader.cs
src/Impostor.Benchmarks/Tests/MessageReaderBenchmark.cs
src/Impostor.Client.App/Program.cs
src/Impostor.Hazel/Extensions/ServiceProviderExtensions.cs [new file with mode: 0644]
src/Impostor.Hazel/Impostor.Hazel.csproj
src/Impostor.Hazel/MessageReader.cs
src/Impostor.Hazel/MessageReaderPolicy.cs [new file with mode: 0644]
src/Impostor.Hazel/MessageWriter.cs
src/Impostor.Hazel/ObjectPool.cs [deleted file]
src/Impostor.Hazel/ObjectPoolCustom.cs [new file with mode: 0644]
src/Impostor.Hazel/Udp/UdpClientConnection.cs
src/Impostor.Hazel/Udp/UdpConnection.KeepAlive.cs
src/Impostor.Hazel/Udp/UdpConnection.Reliable.cs
src/Impostor.Hazel/Udp/UdpConnection.cs
src/Impostor.Hazel/Udp/UdpConnectionListener.cs
src/Impostor.Hazel/Udp/UdpServerConnection.cs
src/Impostor.Server/Net/Matchmaker.cs
src/Impostor.Server/Program.cs
src/Impostor.Tests/Hazel/MessageReaderTests.cs
src/Impostor.Tools.Proxy/Impostor.Tools.Proxy.csproj
src/Impostor.Tools.Proxy/Program.cs
src/Impostor.Tools.ServerReplay/Program.cs

index 99e2e4d1eaf3a0b05ba905b82d66bb0f0ce252bb..3a99946762836fe728cdeaf0916619ea8b6b22e4 100644 (file)
@@ -2,7 +2,7 @@
 
 namespace Impostor.Api.Net.Messages
 {
-    public interface IMessageReader
+    public interface IMessageReader : IDisposable
     {
         /// <summary>
         ///     Gets the tag of the message.
@@ -58,4 +58,4 @@ namespace Impostor.Api.Net.Messages
 
         IMessageReader Slice(int start, int length);
     }
-}
\ No newline at end of file
+}
index c64ace9f58698f9de75bc91f5d2dc887e21216d2..c8ad2287228c6f1656f34e74a67737d3239363a5 100644 (file)
@@ -62,20 +62,20 @@ namespace Impostor.Benchmarks.Tests
             }
         }
 
-        [Benchmark]
-        public void Normal_Run_1_000_000()
-        {
-            for (var i = 0; i < 1_000_000; i++)
-            {
-                var reader = new MessageReader(_data);
-                var inner = reader.ReadMessage();
-
-                _ = inner.ReadUInt16();
-                _ = inner.ReadByte();
-                _ = inner.ReadInt32();
-                // inner.ReadPackedInt32();
-            }
-        }
+        // [Benchmark]
+        // public void Normal_Run_1_000_000()
+        // {
+        //     for (var i = 0; i < 1_000_000; i++)
+        //     {
+        //         var reader = new MessageReader(_data);
+        //         var inner = reader.ReadMessage();
+        //
+        //         _ = inner.ReadUInt16();
+        //         _ = inner.ReadByte();
+        //         _ = inner.ReadInt32();
+        //         // inner.ReadPackedInt32();
+        //     }
+        // }
 
         [Benchmark]
         public void Bytes_Run_1_000_000()
index cce1d29796d24025801e65e106abfc284f55077e..aa8866adec43427b5c8756a7cf3dbb9b4f03cb61 100644 (file)
@@ -34,7 +34,8 @@ namespace Impostor.Client.App
                 NumImpostors = 2
             });
 
-            using (var connection = new UdpClientConnection(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 22023)))
+            // TODO: ObjectPool for MessageReaders
+            using (var connection = new UdpClientConnection(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 22023), null))
             {
                 var e = new ManualResetEvent(false);
 
@@ -71,4 +72,4 @@ namespace Impostor.Client.App
             return default;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/Impostor.Hazel/Extensions/ServiceProviderExtensions.cs b/src/Impostor.Hazel/Extensions/ServiceProviderExtensions.cs
new file mode 100644 (file)
index 0000000..56c7380
--- /dev/null
@@ -0,0 +1,21 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.ObjectPool;
+
+namespace Impostor.Hazel.Extensions
+{
+    public static class ServiceProviderExtensions
+    {
+        public static void AddHazel(this IServiceCollection services)
+        {
+            services.TryAddSingleton<ObjectPoolProvider>(new DefaultObjectPoolProvider());
+
+            services.AddSingleton(serviceProvider =>
+            {
+                var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
+                var policy = ActivatorUtilities.CreateInstance<MessageReaderPolicy>(serviceProvider);
+                return provider.Create(policy);
+            });
+        }
+    }
+}
index 97673046bd5df7b5a3f4937078a2b3534050f815..53ed881b921b272979d2c07977ef0c0af9bb43a6 100644 (file)
@@ -8,6 +8,7 @@
     </PropertyGroup>
 
     <ItemGroup>
+        <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="5.0.0-rc.2.20475.17" />
         <PackageReference Include="Serilog" Version="2.10.0" />
     </ItemGroup>
 
index f81c482551d48e707d6246ee1527616c4e5958b5..df6d014d8da293b49a3abd692973e50f71d469ab 100644 (file)
@@ -3,26 +3,41 @@ using System.Buffers.Binary;
 using System.Runtime.CompilerServices;
 using System.Text;
 using Impostor.Api.Net.Messages;
+using Microsoft.Extensions.ObjectPool;
 
 namespace Impostor.Hazel
 {
     public class MessageReader : IMessageReader
     {
-        public byte Tag { get; }
-        public ReadOnlyMemory<byte> Buffer { get; }
+        private readonly ObjectPool<MessageReader> _pool;
+
+        public byte Tag { get; private set; }
+        public ReadOnlyMemory<byte> Buffer { get; private set; }
         public int Position { get; set; }
         public int Length => Buffer.Length;
 
-        public MessageReader(ReadOnlyMemory<byte> buffer)
+        internal MessageReader(ObjectPool<MessageReader> pool)
         {
-            Tag = byte.MaxValue;
-            Buffer = buffer;
+            _pool = pool;
         }
 
-        public MessageReader(byte tag, ReadOnlyMemory<byte> buffer)
+        public void Update(ReadOnlyMemory<byte> buffer)
+        {
+            Update(byte.MaxValue, buffer);
+        }
+
+        public void Update(byte tag, ReadOnlyMemory<byte> buffer)
         {
             Tag = tag;
             Buffer = buffer;
+            Position = 0;
+        }
+
+        internal void Reset()
+        {
+            Tag = byte.MaxValue;
+            Buffer = null;
+            Position = 0;
         }
 
         public IMessageReader ReadMessage()
@@ -33,7 +48,9 @@ namespace Impostor.Hazel
 
             Position += length;
 
-            return new MessageReader(tag, Buffer.Slice(pos, length));
+            var reader = _pool.Get();
+            reader.Update(tag, Buffer.Slice(pos, length));
+            return reader;
         }
 
         public bool ReadBoolean()
@@ -148,12 +165,16 @@ namespace Impostor.Hazel
 
         public IMessageReader Slice(int start)
         {
-            return new MessageReader(Tag, Buffer.Slice(start));
+            var reader = _pool.Get();
+            reader.Update(Tag, Buffer.Slice(start));
+            return reader;
         }
 
         public IMessageReader Slice(int start, int length)
         {
-            return new MessageReader(Tag, Buffer.Slice(start, length));
+            var reader = _pool.Get();
+            reader.Update(Tag, Buffer.Slice(start, length));
+            return reader;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -161,5 +182,10 @@ namespace Impostor.Hazel
         {
             return Buffer.Span[Position++];
         }
+
+        public void Dispose()
+        {
+            _pool.Return(this);
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/src/Impostor.Hazel/MessageReaderPolicy.cs b/src/Impostor.Hazel/MessageReaderPolicy.cs
new file mode 100644 (file)
index 0000000..995ad78
--- /dev/null
@@ -0,0 +1,27 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
+
+namespace Impostor.Hazel
+{
+    public class MessageReaderPolicy : IPooledObjectPolicy<MessageReader>
+    {
+        private readonly IServiceProvider _serviceProvider;
+
+        public MessageReaderPolicy(IServiceProvider serviceProvider)
+        {
+            _serviceProvider = serviceProvider;
+        }
+
+        public MessageReader Create()
+        {
+            return new MessageReader(_serviceProvider.GetRequiredService<Microsoft.Extensions.ObjectPool.ObjectPool<MessageReader>>());
+        }
+
+        public bool Return(MessageReader obj)
+        {
+            obj.Reset();
+            return true;
+        }
+    }
+}
index d12c7c94da07c8f57986e4d8589412df25855f2c..5b7342a31b879396aa0217acf17567e51e84c381 100644 (file)
@@ -11,7 +11,7 @@ namespace Impostor.Hazel
     public class MessageWriter : IMessageWriter, IRecyclable, IDisposable
     {
         private static int BufferSize = 64000;
-        private static readonly ObjectPool<MessageWriter> WriterPool = new ObjectPool<MessageWriter>(() => new MessageWriter(BufferSize));
+        private static readonly ObjectPoolCustom<MessageWriter> WriterPool = new ObjectPoolCustom<MessageWriter>(() => new MessageWriter(BufferSize));
 
         public MessageType SendOption { get; private set; }
 
diff --git a/src/Impostor.Hazel/ObjectPool.cs b/src/Impostor.Hazel/ObjectPool.cs
deleted file mode 100644 (file)
index 6d6ce6e..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Threading;
-
-namespace Impostor.Hazel
-{
-    /// <summary>
-    ///     A fairly simple object pool for items that will be created a lot.
-    /// </summary>
-    /// <typeparam name="T">The type that is pooled.</typeparam>
-    /// <threadsafety static="true" instance="true"/>
-    public sealed class ObjectPool<T> where T : IRecyclable
-    {
-        private int numberCreated;
-        public int NumberCreated { get { return numberCreated; } }
-
-        public int NumberInUse { get { return this.inuse.Count; } }
-        public int NumberNotInUse { get { return this.pool.Count; } }
-        public int Size { get { return this.NumberInUse + this.NumberNotInUse; } }
-
-#if HAZEL_BAG
-        private readonly ConcurrentBag<T> pool = new ConcurrentBag<T>();
-#else
-        private readonly List<T> pool = new List<T>();
-#endif
-
-        // Unavailable objects
-        private readonly ConcurrentDictionary<T, bool> inuse = new ConcurrentDictionary<T, bool>();
-
-        /// <summary>
-        ///     The generator for creating new objects.
-        /// </summary>
-        /// <returns></returns>
-        private readonly Func<T> objectFactory;
-        
-        /// <summary>
-        ///     Internal constructor for our ObjectPool.
-        /// </summary>
-        internal ObjectPool(Func<T> objectFactory)
-        {
-            this.objectFactory = objectFactory;
-        }
-
-        /// <summary>
-        ///     Returns a pooled object of type T, if none are available another is created.
-        /// </summary>
-        /// <returns>An instance of T.</returns>
-        internal T GetObject()
-        {
-#if HAZEL_BAG
-            if (!pool.TryTake(out T item))
-            {
-                Interlocked.Increment(ref numberCreated);
-                item = objectFactory.Invoke();
-            }
-#else
-            T item;
-            lock (this.pool)
-            {
-                if (this.pool.Count > 0)
-                {
-                    var idx = this.pool.Count - 1;
-                    item = this.pool[idx];
-                    this.pool.RemoveAt(idx);
-                }
-                else
-                {
-                    Interlocked.Increment(ref numberCreated);
-                    item = objectFactory.Invoke();
-                }
-            }
-#endif
-
-            if (!inuse.TryAdd(item, true))
-            {
-                throw new Exception("Duplicate pull " + typeof(T).Name);
-            }
-
-            return item;
-        }
-
-        /// <summary>
-        ///     Returns an object to the pool.
-        /// </summary>
-        /// <param name="item">The item to return.</param>
-        internal void PutObject(T item)
-        {
-            if (inuse.TryRemove(item, out bool b))
-            {
-#if HAZEL_BAG
-                pool.Add(item);
-#else
-                lock (this.pool)
-                {
-                    pool.Add(item);
-                }
-#endif
-            }
-            else
-            {
-#if DEBUG
-                throw new Exception("Duplicate add " + typeof(T).Name);
-#endif
-            }
-        }
-    }
-}
diff --git a/src/Impostor.Hazel/ObjectPoolCustom.cs b/src/Impostor.Hazel/ObjectPoolCustom.cs
new file mode 100644 (file)
index 0000000..5c9ef9b
--- /dev/null
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Concurrent;
+using System.Threading;
+
+namespace Impostor.Hazel
+{
+    /// <summary>
+    ///     A fairly simple object pool for items that will be created a lot.
+    /// </summary>
+    /// <typeparam name="T">The type that is pooled.</typeparam>
+    /// <threadsafety static="true" instance="true"/>
+    public sealed class ObjectPoolCustom<T> where T : IRecyclable
+    {
+        private int numberCreated;
+        public int NumberCreated { get { return numberCreated; } }
+
+        public int NumberInUse { get { return this.inuse.Count; } }
+        public int NumberNotInUse { get { return this.pool.Count; } }
+        public int Size { get { return this.NumberInUse + this.NumberNotInUse; } }
+
+#if HAZEL_BAG
+        private readonly ConcurrentBag<T> pool = new ConcurrentBag<T>();
+#else
+        private readonly List<T> pool = new List<T>();
+#endif
+
+        // Unavailable objects
+        private readonly ConcurrentDictionary<T, bool> inuse = new ConcurrentDictionary<T, bool>();
+
+        /// <summary>
+        ///     The generator for creating new objects.
+        /// </summary>
+        /// <returns></returns>
+        private readonly Func<T> objectFactory;
+        
+        /// <summary>
+        ///     Internal constructor for our ObjectPool.
+        /// </summary>
+        internal ObjectPoolCustom(Func<T> objectFactory)
+        {
+            this.objectFactory = objectFactory;
+        }
+
+        /// <summary>
+        ///     Returns a pooled object of type T, if none are available another is created.
+        /// </summary>
+        /// <returns>An instance of T.</returns>
+        internal T GetObject()
+        {
+#if HAZEL_BAG
+            if (!pool.TryTake(out T item))
+            {
+                Interlocked.Increment(ref numberCreated);
+                item = objectFactory.Invoke();
+            }
+#else
+            T item;
+            lock (this.pool)
+            {
+                if (this.pool.Count > 0)
+                {
+                    var idx = this.pool.Count - 1;
+                    item = this.pool[idx];
+                    this.pool.RemoveAt(idx);
+                }
+                else
+                {
+                    Interlocked.Increment(ref numberCreated);
+                    item = objectFactory.Invoke();
+                }
+            }
+#endif
+
+            if (!inuse.TryAdd(item, true))
+            {
+                throw new Exception("Duplicate pull " + typeof(T).Name);
+            }
+
+            return item;
+        }
+
+        /// <summary>
+        ///     Returns an object to the pool.
+        /// </summary>
+        /// <param name="item">The item to return.</param>
+        internal void PutObject(T item)
+        {
+            if (inuse.TryRemove(item, out bool b))
+            {
+#if HAZEL_BAG
+                pool.Add(item);
+#else
+                lock (this.pool)
+                {
+                    pool.Add(item);
+                }
+#endif
+            }
+            else
+            {
+#if DEBUG
+                throw new Exception("Duplicate add " + typeof(T).Name);
+#endif
+            }
+        }
+    }
+}
index 4f8117787c4c035eb3cd8f0cceb3f48db4504427..d2f5e59f4fb7d42692e68575d83bc59f5e81b213 100644 (file)
@@ -6,6 +6,7 @@ using System.Threading;
 using System.Threading.Channels;
 using System.Threading.Tasks;
 using Impostor.Api.Net.Messages;
+using Microsoft.Extensions.ObjectPool;
 using Serilog;
 
 namespace Impostor.Hazel.Udp
@@ -34,7 +35,7 @@ namespace Impostor.Hazel.Udp
         ///     Creates a new UdpClientConnection.
         /// </summary>
         /// <param name="remoteEndPoint">A <see cref="NetworkEndPoint"/> to connect to.</param>
-        public UdpClientConnection(IPEndPoint remoteEndPoint, IPMode ipMode = IPMode.IPv4) : base(null)
+        public UdpClientConnection(IPEndPoint remoteEndPoint, ObjectPool<MessageReader> readerPool, IPMode ipMode = IPMode.IPv4) : base(null, readerPool)
         {
             EndPoint = remoteEndPoint;
             RemoteEndPoint = remoteEndPoint;
@@ -249,4 +250,4 @@ namespace Impostor.Hazel.Udp
             base.Dispose(disposing);
         }
     }
-}
\ No newline at end of file
+}
index 81cffabeb185a6a58cebdd48553517169eadd18c..a73291bc4e1b68d9b764763fe60656822ecd145e 100644 (file)
@@ -14,7 +14,7 @@ namespace Impostor.Hazel.Udp
         /// </summary>
         public class PingPacket : IRecyclable
         {
-            private static readonly ObjectPool<PingPacket> PacketPool = new ObjectPool<PingPacket>(() => new PingPacket());
+            private static readonly ObjectPoolCustom<PingPacket> PacketPool = new ObjectPoolCustom<PingPacket>(() => new PingPacket());
 
             public readonly Stopwatch Stopwatch = new Stopwatch();
 
index 4d925ffaf20d6c79e92e7bfcc4dcfb9ff2a4216e..25c3633407742500ca460ed4f10c692ac9198c1b 100644 (file)
@@ -87,7 +87,7 @@ namespace Impostor.Hazel.Udp
             /// <summary>
             ///     Object pool for this event.
             /// </summary>
-            public static readonly ObjectPool<Packet> PacketPool = new ObjectPool<Packet>(() => new Packet());
+            public static readonly ObjectPoolCustom<Packet> PacketPool = new ObjectPoolCustom<Packet>(() => new Packet());
 
             /// <summary>
             ///     Returns an instance of this object from the pool.
index 4990469df5eaa3e96a1944b462166cd999053902..4fffb305a917ecaadfafe5770b44d33222a59377 100644 (file)
@@ -17,15 +17,17 @@ namespace Impostor.Hazel.Udp
 
         private static readonly ILogger Logger = Log.ForContext<UdpConnection>();
         private readonly ConnectionListener _listener;
+        private readonly Microsoft.Extensions.ObjectPool.ObjectPool<MessageReader> _readerPool;
         private readonly CancellationTokenSource _stoppingCts;
 
         private bool _isDisposing;
         private bool _isFirst = true;
         private Task _executingTask;
 
-        protected UdpConnection(ConnectionListener listener)
+        protected UdpConnection(ConnectionListener listener, Microsoft.Extensions.ObjectPool.ObjectPool<MessageReader> readerPool)
         {
             _listener = listener;
+            _readerPool = readerPool;
             _stoppingCts = new CancellationTokenSource();
 
             Pipeline = Channel.CreateUnbounded<MessageData>(new UnboundedChannelOptions
@@ -89,7 +91,11 @@ namespace Impostor.Hazel.Udp
 
                 try
                 {
-                    await HandleReceive(new MessageReader(result.Buffer));
+                    using (var reader = _readerPool.Get())
+                    {
+                        reader.Update(byte.MaxValue, result.Buffer);
+                        await HandleReceive(reader);
+                    }
                 }
                 catch (Exception e)
                 {
index f00f98fbc82be3f229e3449b1b440b9c6353b62e..b0f7cefedff648df7cb2dc224ab974d418bc32a7 100644 (file)
@@ -6,6 +6,7 @@ using System.Net.Sockets;
 using System.Threading;
 using System.Threading.Channels;
 using System.Threading.Tasks;
+using Microsoft.Extensions.ObjectPool;
 using Serilog;
 
 namespace Impostor.Hazel.Udp
@@ -27,6 +28,7 @@ namespace Impostor.Hazel.Udp
         public delegate bool AcceptConnectionCheck(IPEndPoint endPoint, byte[] input, out byte[] response);
 
         private readonly UdpClient _socket;
+        private readonly ObjectPool<MessageReader> _readerPool;
         private readonly MemoryPool<byte> _pool;
         private readonly Timer _reliablePacketTimer;
         private readonly ConcurrentDictionary<EndPoint, UdpServerConnection> _allConnections;
@@ -39,11 +41,12 @@ namespace Impostor.Hazel.Udp
         /// </summary>
         /// <param name="endPoint">The endpoint to listen on.</param>
         /// <param name="ipMode"></param>
-        public UdpConnectionListener(IPEndPoint endPoint, IPMode ipMode = IPMode.IPv4)
+        public UdpConnectionListener(IPEndPoint endPoint, ObjectPool<MessageReader> readerPool, IPMode ipMode = IPMode.IPv4)
         {
             EndPoint = endPoint;
             IPMode = ipMode;
 
+            _readerPool = readerPool;
             _pool = MemoryPool<byte>.Shared;
             _socket = new UdpClient(endPoint)
             {
@@ -165,7 +168,7 @@ namespace Impostor.Hazel.Udp
                         }
 
                         // Create new client
-                        client = new UdpServerConnection(this, data.RemoteEndPoint, IPMode);
+                        client = new UdpServerConnection(this, data.RemoteEndPoint, IPMode, _readerPool);
 
                         // Store the client
                         if (!_allConnections.TryAdd(data.RemoteEndPoint, client))
index 08034968b9b90aa03b8f15fe8f17aa8f489b4934..22eed988f295413923f4f332cb2b6724d4feafbe 100644 (file)
@@ -2,6 +2,7 @@
 using System.Net;
 using System.Threading.Tasks;
 using Impostor.Api.Net.Messages;
+using Microsoft.Extensions.ObjectPool;
 
 namespace Impostor.Hazel.Udp
 {
@@ -26,7 +27,7 @@ namespace Impostor.Hazel.Udp
         /// <param name="listener">The listener that created this connection.</param>
         /// <param name="endPoint">The endpoint that we are connected to.</param>
         /// <param name="IPMode">The IPMode we are connected using.</param>
-        internal UdpServerConnection(UdpConnectionListener listener, IPEndPoint endPoint, IPMode IPMode) : base(listener)
+        internal UdpServerConnection(UdpConnectionListener listener, IPEndPoint endPoint, IPMode IPMode, ObjectPool<MessageReader> readerPool) : base(listener, readerPool)
         {
             this.Listener = listener;
             this.RemoteEndPoint = endPoint;
index 1c1bcf39f0be4fe7082e3fa234eee0d1d901f40a..64ece55f0abc3bd622440dba6ed5a2ea01471a2c 100644 (file)
@@ -7,23 +7,27 @@ using Impostor.Hazel.Udp;
 using Impostor.Server.Net.Hazel;
 using Impostor.Server.Net.Manager;
 using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.ObjectPool;
 
 namespace Impostor.Server.Net
 {
     internal class Matchmaker
     {
         private readonly ClientManager _clientManager;
+        private readonly ObjectPool<MessageReader> _readerPool;
         private readonly ILogger<Matchmaker> _logger;
-        private readonly ILogger<Net.Hazel.HazelConnection> _connectionLogger;
+        private readonly ILogger<HazelConnection> _connectionLogger;
         private UdpConnectionListener _connection;
 
         public Matchmaker(
             ILogger<Matchmaker> logger,
             ClientManager clientManager,
+            ObjectPool<MessageReader> readerPool,
             ILogger<HazelConnection> connectionLogger)
         {
             _logger = logger;
             _clientManager = clientManager;
+            _readerPool = readerPool;
             _connectionLogger = connectionLogger;
         }
 
@@ -36,7 +40,7 @@ namespace Impostor.Server.Net
                 _ => throw new InvalidOperationException()
             };
 
-            _connection = new UdpConnectionListener(ipEndPoint, mode);
+            _connection = new UdpConnectionListener(ipEndPoint, _readerPool, mode);
             _connection.NewConnection = OnNewConnection;
 
             await _connection.StartAsync();
@@ -59,4 +63,4 @@ namespace Impostor.Server.Net
             await _clientManager.RegisterConnectionAsync(connection, name, clientVersion);
         }
     }
-}
\ No newline at end of file
+}
index a646267c9ec01531782bff84429bc6aa19c3030b..234ee01306d68db26de0449cd44ce64ba29c58ef 100644 (file)
@@ -4,6 +4,7 @@ using Impostor.Api.Games;
 using Impostor.Api.Games.Managers;
 using Impostor.Api.Net.Manager;
 using Impostor.Api.Net.Messages;
+using Impostor.Hazel.Extensions;
 using Impostor.Server.Config;
 using Impostor.Server.Events;
 using Impostor.Server.Net;
@@ -171,6 +172,7 @@ namespace Impostor.Server
                         services.AddSingleton<IGameManager>(p => p.GetRequiredService<GameManager>());
                     }
 
+                    services.AddHazel();
                     services.AddSingleton<IMessageWriterProvider, MessageWriterProvider>();
                     services.AddSingleton<IGameCodeFactory, GameCodeFactory>();
                     services.AddSingleton<IEventManager, EventManager>();
index 0d1f6135880ddd8e878f8c51454f8eac06f35cc1..2658e295a2c9ba2ae2b821e2cb126dbb6b4e5a3b 100644 (file)
@@ -1,10 +1,21 @@
-using Impostor.Hazel;
+using System;
+using Impostor.Hazel;
+using Impostor.Hazel.Extensions;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using Xunit;
 
 namespace Impostor.Tests.Hazel
 {
     public class BufferMessageReaderTests
     {
+        private ObjectPool<MessageReader> CreateReaderPool()
+        {
+            var services = new ServiceCollection();
+            services.AddHazel();
+            return services.BuildServiceProvider().GetRequiredService<ObjectPool<MessageReader>>();
+        }
+
         [Fact]
         public void ReadProperInt()
         {
@@ -20,7 +31,9 @@ namespace Impostor.Tests.Hazel
             Assert.Equal(11, msg.Length);
             Assert.Equal(msg.Length, msg.Position);
 
-            var reader = new MessageReader(msg.Buffer);
+            var readerPool = CreateReaderPool();
+            var reader = readerPool.Get();
+            reader.Update(msg.Buffer);
             Assert.Equal(byte.MaxValue, reader.Tag);
             var message = reader.ReadMessage();
             Assert.Equal(1, message.Tag);
@@ -43,7 +56,9 @@ namespace Impostor.Tests.Hazel
             Assert.Equal(5, msg.Length);
             Assert.Equal(msg.Length, msg.Position);
 
-            var reader = new MessageReader(msg.Buffer);
+            var readerPool = CreateReaderPool();
+            var reader = readerPool.Get();
+            reader.Update(msg.Buffer);
             Assert.Equal(byte.MaxValue, reader.Tag);
             var message = reader.ReadMessage();
             Assert.Equal(1, message.Tag);
@@ -65,7 +80,9 @@ namespace Impostor.Tests.Hazel
 
             Assert.Equal(msg.Length, msg.Position);
 
-            var reader = new MessageReader(msg.Buffer);
+            var readerPool = CreateReaderPool();
+            var reader = readerPool.Get();
+            reader.Update(msg.Buffer);
             Assert.Equal(byte.MaxValue, reader.Tag);
             var message = reader.ReadMessage();
             Assert.Equal(1, message.Tag);
@@ -87,7 +104,9 @@ namespace Impostor.Tests.Hazel
             Assert.Equal(7, msg.Length);
             Assert.Equal(msg.Length, msg.Position);
 
-            var reader = new MessageReader(msg.Buffer);
+            var readerPool = CreateReaderPool();
+            var reader = readerPool.Get();
+            reader.Update(msg.Buffer);
             Assert.Equal(byte.MaxValue, reader.Tag);
             var message = reader.ReadMessage();
             Assert.Equal(1, message.Tag);
@@ -110,7 +129,9 @@ namespace Impostor.Tests.Hazel
 
             msg.EndMessage();
 
-            var handleReader = new MessageReader(msg.Buffer);
+            var readerPool = CreateReaderPool();
+            var handleReader = readerPool.Get();
+            handleReader.Update(msg.Buffer);
             var handleMessage = handleReader.ReadMessage();
             Assert.Equal(1, handleMessage.Tag);
 
@@ -141,7 +162,9 @@ namespace Impostor.Tests.Hazel
 
             Assert.Equal(msg.Length, msg.Position);
 
-            var reader = new MessageReader(msg.Buffer);
+            var readerPool = CreateReaderPool();
+            var reader = readerPool.Get();
+            reader.Update(msg.Buffer);
             Assert.Equal(byte.MaxValue, reader.Tag);
             var message = reader.ReadMessage();
             Assert.Equal(1, message.Tag);
@@ -189,7 +212,9 @@ namespace Impostor.Tests.Hazel
             messageWriter.EndMessage();
 
             // Do the magic.
-            var reader = new MessageReader(messageWriter.Buffer);
+            var readerPool = CreateReaderPool();
+            var reader = readerPool.Get();
+            reader.Update(messageWriter.Buffer);
             var inner = reader.ReadMessage();
 
             while (inner.Position < inner.Length)
index f1118719d1fe0c912102533aef4072c92e8ac432..9118c83f36cba700530667036342e0c5ab7fee0c 100644 (file)
@@ -6,6 +6,7 @@
     </PropertyGroup>
 
     <ItemGroup>
+      <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.0-rc.2.20475.5" />
       <PackageReference Include="Pcap.Net.x64" Version="1.0.4.1" />
     </ItemGroup>
 
index fdfce507fffa2cf11f0fcf18923f967dce8635fe..d891bdff5a35ff77dcef036423120fe9d0d73567 100644 (file)
@@ -3,7 +3,10 @@ using System.Collections.Generic;
 using System.Linq;
 using Impostor.Api.Net.Messages;
 using Impostor.Hazel;
+using Impostor.Hazel.Extensions;
 using Impostor.Hazel.Udp;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.ObjectPool;
 using PcapDotNet.Core;
 using PcapDotNet.Packets;
 
@@ -32,9 +35,18 @@ namespace Impostor.Tools.Proxy
             {14, "ReselectServer"},
             {16, "GetGameListV2"}
         };
-        
+
+        private static IServiceProvider _serviceProvider;
+        private static ObjectPool<MessageReader> _readerPool;
+
         private static void Main(string[] args)
         {
+            var services = new ServiceCollection();
+            services.AddHazel();
+
+            _serviceProvider = services.BuildServiceProvider();
+            _readerPool = _serviceProvider.GetRequiredService<ObjectPool<MessageReader>>();
+            
             var devices = LivePacketDevice.AllLocalMachine;
             if (devices.Count == 0)
             {
@@ -69,11 +81,14 @@ namespace Impostor.Tools.Proxy
             // True if this is our own packet.
             using (var stream = udp.Payload.ToMemoryStream())
             {
-                var reader = (IMessageReader) new MessageReader(stream.ToArray());
+                using var reader = _readerPool.Get();
+
+                reader.Update(stream.ToArray());
+
                 var option = reader.Buffer.Span[0];
                 if (option == (byte) MessageType.Reliable)
                 {
-                    reader = reader.Slice(3);
+                    reader.Position += 3;
                 }
                 else if (option == (byte) UdpSendOption.Acknowledgement ||
                          option == (byte) UdpSendOption.Ping ||
@@ -84,7 +99,7 @@ namespace Impostor.Tools.Proxy
                 }
                 else
                 {
-                    reader = reader.Slice(1);
+                    reader.Position += 1;
                 }
                 
                 var isSent = ipSrc.StartsWith("192.");
index 8ae8bfa2b911749751b8fd961c8853b87d224d37..3fe40e65f82c51a1ba4f1eb89d8bfbbc14682a67 100644 (file)
@@ -21,6 +21,7 @@ using Impostor.Server.Recorder;
 using Impostor.Tools.ServerReplay.Mocks;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.ObjectPool;
 using Serilog;
 using ILogger = Serilog.ILogger;
 
@@ -36,6 +37,7 @@ namespace Impostor.Tools.ServerReplay
 
         private static ServiceProvider _serviceProvider;
 
+        private static ObjectPool<MessageReader> _readerPool;
         private static MockGameCodeFactory _gameCodeFactory;
         private static ClientManager _clientManager;
         private static GameManager _gameManager;
@@ -54,6 +56,7 @@ namespace Impostor.Tools.ServerReplay
             _serviceProvider = BuildServices();
 
             // Create required instances.
+            _readerPool = _serviceProvider.GetRequiredService<ObjectPool<MessageReader>>();
             _gameCodeFactory = _serviceProvider.GetRequiredService<MockGameCodeFactory>();
             _clientManager = _serviceProvider.GetRequiredService<ClientManager>();
             _gameManager = _serviceProvider.GetRequiredService<GameManager>();
@@ -143,11 +146,14 @@ namespace Impostor.Tools.ServerReplay
                     break;
 
                 case RecordedPacketType.Message:
-                    var messageType = (MessageType) reader.ReadByte();
+                {
+                    var messageType = (MessageType)reader.ReadByte();
                     var tag = reader.ReadByte();
                     var length = reader.ReadInt32();
                     var buffer = reader.ReadBytes(length);
-                    var message = new MessageReader(tag, buffer);
+                    using var message = _readerPool.Get();
+
+                    message.Update(tag, buffer);
 
                     if (tag == MessageFlags.HostGame)
                     {
@@ -157,7 +163,9 @@ namespace Impostor.Tools.ServerReplay
                     {
                         await client.Client!.HandleMessageAsync(message, messageType);
                     }
+
                     break;
+                }
 
                 case RecordedPacketType.GameCreated:
                     _gameCodeFactory.Result = GameCode.From(reader.ReadString());
@@ -172,4 +180,4 @@ namespace Impostor.Tools.ServerReplay
             }
         }
     }
-}
\ No newline at end of file
+}