From 2c0d97cd3681cdba8889bd35f8881afd214f391d Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 12 Apr 2020 20:57:48 -0700 Subject: [PATCH] Switches to Kestrel (#102) --- .gitignore | 11 +- Projects/Scripts/Accounting/IPLimiter.cs | 4 +- .../IPAddressCommandImplementor.cs | 4 +- .../Implementors/OnlineCommandImplementor.cs | 4 +- Projects/Scripts/Commands/Handlers.cs | 4 +- Projects/Scripts/Engines/Help/PageQueue.cs | 2 +- Projects/Scripts/Gumps/AdminGump.cs | 8 +- Projects/Scripts/Gumps/WhoGump.cs | 2 +- Projects/Scripts/Misc/CrashGuard.cs | 2 +- Projects/Scripts/Misc/FoodDecay.cs | 2 +- Projects/Scripts/Misc/LightCycle.cs | 16 +- Projects/Scripts/Misc/LoginStats.cs | 4 +- Projects/Scripts/Misc/ServerList.cs | 2 +- Projects/Scripts/Misc/SocketOptions.cs | 4 +- Projects/Scripts/Misc/VendorGenerator.cs | 5 +- Projects/Scripts/Misc/Weather.cs | 2 +- Projects/Scripts/Misc/WebStatus.cs | 4 +- Projects/Scripts/Scripts.csproj | 4 +- .../SpecialSystems/Engines/PreventInaccess.cs | 2 +- Projects/Server/AggressorInfo.cs | 2 +- Projects/Server/ApplicationLifetime.cs | 108 ------ Projects/Server/Assemblies/libuv.dll | Bin 230912 -> 0 bytes Projects/Server/AssemblyHandler.cs | 20 +- Projects/Server/Body.cs | 2 +- .../Server/Buffers/DiagnosticPoolBlock.cs | 2 +- Projects/Server/Buffers/MemoryPoolFactory.cs | 10 +- .../Server/ContextMenus/OpenBackpackEntry.cs | 2 +- .../Server/ContextMenus/PaperdollEntry.cs | 2 +- Projects/Server/Diagnostics/BaseProfile.cs | 2 +- Projects/Server/Diagnostics/GumpProfile.cs | 2 +- Projects/Server/Diagnostics/PacketProfile.cs | 4 +- Projects/Server/Diagnostics/TargetProfile.cs | 2 +- Projects/Server/Diagnostics/TimerProfile.cs | 2 +- Projects/Server/EventSink.cs | 4 +- Projects/Server/Guild.cs | 2 +- Projects/Server/Gumps/Gump.cs | 14 +- Projects/Server/Gumps/GumpAlphaRegion.cs | 2 +- Projects/Server/Gumps/GumpBackground.cs | 2 +- Projects/Server/Gumps/GumpButton.cs | 2 +- Projects/Server/Gumps/GumpCheck.cs | 2 +- Projects/Server/Gumps/GumpGroup.cs | 2 +- Projects/Server/Gumps/GumpHtml.cs | 2 +- Projects/Server/Gumps/GumpHtmlLocalized.cs | 6 +- Projects/Server/Gumps/GumpImage.cs | 4 +- Projects/Server/Gumps/GumpImageTileButton.cs | 4 +- Projects/Server/Gumps/GumpImageTiled.cs | 2 +- Projects/Server/Gumps/GumpItem.cs | 4 +- Projects/Server/Gumps/GumpItemProperty.cs | 2 +- Projects/Server/Gumps/GumpLabel.cs | 2 +- Projects/Server/Gumps/GumpLabelCropped.cs | 2 +- Projects/Server/Gumps/GumpPage.cs | 2 +- Projects/Server/Gumps/GumpRadio.cs | 2 +- Projects/Server/Gumps/GumpTextEntry.cs | 2 +- Projects/Server/Gumps/GumpTextEntryLimited.cs | 2 +- Projects/Server/Gumps/GumpTooltip.cs | 2 +- Projects/Server/Item.cs | 6 +- Projects/Server/Items/Container.cs | 16 +- .../Server/Kestrel/CorrelationIdGenerator.cs | 43 --- .../Internal}/IAsyncDisposable.cs | 2 +- .../Transport.LIbuv/Internal}/ILibuvTrace.cs | 4 +- .../Internal}/LibuvAwaitable.cs | 11 +- .../Internal}/LibuvConnection.cs | 26 +- .../Internal}/LibuvConnectionListener.cs | 21 +- .../Internal}/LibuvConstants.cs | 53 ++- .../Internal}/LibuvOutputConsumer.cs | 6 +- .../Transport.LIbuv/Internal}/LibuvThread.cs | 40 +-- .../Transport.LIbuv/Internal}/LibuvTrace.cs | 4 +- .../Internal}/LibuvTransportContext.cs | 4 +- .../Internal/LibuvTransportFactory.cs | 64 ++++ .../Transport.LIbuv/Internal}/Listener.cs | 32 +- .../Internal}/ListenerContext.cs | 25 +- .../Internal}/ListenerPrimary.cs | 17 +- .../Internal}/ListenerSecondary.cs | 6 +- .../Internal/Networking}/LibuvFunctions.cs | 141 ++++---- .../Internal/Networking/PlatformApis.cs | 20 ++ .../Internal/Networking}/SockAddr.cs | 47 +-- .../Internal/Networking}/UvAsyncHandle.cs | 10 +- .../Internal/Networking}/UvConnectRequest.cs | 6 +- .../Internal/Networking}/UvException.cs | 4 +- .../Internal/Networking}/UvHandle.cs | 10 +- .../Internal/Networking}/UvLoopHandle.cs | 4 +- .../Internal/Networking}/UvMemory.cs | 5 +- .../Internal/Networking}/UvPipeHandle.cs | 4 +- .../Internal/Networking}/UvRequest.cs | 10 +- .../Internal/Networking}/UvStreamHandle.cs | 12 +- .../Internal/Networking}/UvTcpHandle.cs | 4 +- .../Internal/Networking}/UvTimerHandle.cs | 4 +- .../Internal/Networking}/UvWriteReq.cs | 30 +- .../Transport.LIbuv/Internal}/WriteReqPool.cs | 6 +- .../Transport.LIbuv}/LibuvTransportOptions.cs | 25 +- .../WebHostBuilderLibuvExtensions.cs | 51 +++ .../TransportConnection.FeatureCollection.cs | 44 --- .../Kestrel/TransportConnection.Generated.cs | 248 -------------- .../Server/Kestrel/TransportConnection.cs | 59 ---- Projects/Server/KeywordList.cs | 2 +- Projects/Server/Main.cs | 28 +- Projects/Server/Map.cs | 9 +- Projects/Server/Menus/ItemListMenu.cs | 2 +- Projects/Server/Menus/QuestionMenu.cs | 2 +- Projects/Server/Mobile.cs | 321 +++++------------- Projects/Server/MultiData.cs | 8 +- Projects/Server/Network/Listener.cs | 177 ---------- .../{MessagePump.cs => MessagePumpService.cs} | 20 +- Projects/Server/Network/NetState.cs | 125 ++----- Projects/Server/Network/Packet.cs | 2 +- Projects/Server/Network/PacketHandlers.cs | 27 +- Projects/Server/Network/PacketWriter.cs | 4 +- Projects/Server/Network/Packets.cs | 56 +-- Projects/Server/Network/ServerStartup.cs | 23 ++ .../Server/Network/StaticPacketHandlers.cs | 11 +- Projects/Server/Network/TcpServer.cs | 57 ++++ .../Server/Network/UOConnectionHandler.cs | 116 +++++++ Projects/Server/ObjectPropertyList.cs | 4 +- .../Server/Persistence/BinaryMemoryWriter.cs | 2 +- .../Server/Persistence/DynamicSaveStrategy.cs | 8 +- Projects/Server/Persistence/FileOperations.cs | 2 +- Projects/Server/Persistence/FileQueue.cs | 14 +- .../Persistence/ParallelSaveStrategy.cs | 18 +- .../Server/Persistence/QueuedMemoryWriter.cs | 4 +- .../Persistence/StandardSaveStrategy.cs | 2 +- Projects/Server/Point3DList.cs | 2 +- Projects/Server/Region.cs | 4 +- Projects/Server/Sector.cs | 10 +- Projects/Server/Serialization/AsyncWriter.cs | 10 +- .../Server/Serialization/BinaryFileReader.cs | 2 +- .../Server/Serialization/BinaryFileWriter.cs | 10 +- Projects/Server/Serialization/BufferWriter.cs | 6 +- Projects/Server/Server.csproj | 25 +- .../SignalR/AsyncDisposableExtensions.cs | 19 -- Projects/Server/Skills.cs | 2 +- Projects/Server/Targeting/Target.cs | 10 +- Projects/Server/TileData.cs | 2 +- Projects/Server/TileList.cs | 2 +- Projects/Server/TileMatrix.cs | 30 +- Projects/Server/TileMatrixPatch.cs | 3 +- Projects/Server/Timer.cs | 21 +- Projects/Server/Utilities/ActivatorUtil.cs | 46 +-- Projects/Server/Utilities/RefPool.cs | 21 +- Projects/Server/Utilities/Utility.cs | 8 +- Projects/Server/World.cs | 4 +- Publish-Linux.sh | 0 141 files changed, 1040 insertions(+), 1679 deletions(-) delete mode 100644 Projects/Server/ApplicationLifetime.cs delete mode 100644 Projects/Server/Assemblies/libuv.dll delete mode 100644 Projects/Server/Kestrel/CorrelationIdGenerator.cs rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/IAsyncDisposable.cs (76%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/ILibuvTrace.cs (83%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/LibuvAwaitable.cs (76%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/LibuvConnection.cs (90%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/LibuvConnectionListener.cs (89%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/LibuvConstants.cs (50%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/LibuvOutputConsumer.cs (90%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/LibuvThread.cs (88%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/LibuvTrace.cs (94%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/LibuvTransportContext.cs (74%) create mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportFactory.cs rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/Listener.cs (83%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/ListenerContext.cs (84%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/ListenerPrimary.cs (91%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/ListenerSecondary.cs (92%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/LibuvFunctions.cs (78%) create mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/PlatformApis.cs rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/SockAddr.cs (78%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvAsyncHandle.cs (86%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvConnectRequest.cs (86%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvException.cs (70%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvHandle.cs (83%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvLoopHandle.cs (84%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvMemory.cs (90%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvPipeHandle.cs (83%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvRequest.cs (65%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvStreamHandle.cs (89%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvTcpHandle.cs (90%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvTimerHandle.cs (87%) rename Projects/Server/{LibUv/Internal => Kestrel/Transport.LIbuv/Internal/Networking}/UvWriteReq.cs (84%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv/Internal}/WriteReqPool.cs (84%) rename Projects/Server/{LibUv => Kestrel/Transport.LIbuv}/LibuvTransportOptions.cs (66%) create mode 100644 Projects/Server/Kestrel/Transport.LIbuv/WebHostBuilderLibuvExtensions.cs delete mode 100644 Projects/Server/Kestrel/TransportConnection.FeatureCollection.cs delete mode 100644 Projects/Server/Kestrel/TransportConnection.Generated.cs delete mode 100644 Projects/Server/Kestrel/TransportConnection.cs delete mode 100644 Projects/Server/Network/Listener.cs rename Projects/Server/Network/{MessagePump.cs => MessagePumpService.cs} (76%) create mode 100644 Projects/Server/Network/ServerStartup.cs create mode 100644 Projects/Server/Network/TcpServer.cs create mode 100644 Projects/Server/Network/UOConnectionHandler.cs delete mode 100644 Projects/Server/SignalR/AsyncDisposableExtensions.cs mode change 100644 => 100755 Publish-Linux.sh diff --git a/.gitignore b/.gitignore index 19f99345f..e4e6742f0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /Distribution/Logs /Distribution/Backups /Distribution/Saves +/Distribution/docs /Distribution/System.IO.Pipelines.dll /Distribution/libdrng.dll /Distribution/zlib.dll @@ -12,21 +13,17 @@ /Distribution/System.Runtime.CompilerServices.Unsafe.dll # LibUv Dependencies +/Distribution/libuv.dylib /Distribution/libuv.dll +/Distribution/libuv.so /Distribution/Microsoft.AspNetCore.Connections.Abstractions.dll /Distribution/Microsoft.AspNetCore.Http.Features.dll +/Distribution/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.dll /Distribution/Microsoft.Extensions.Configuration.Abstractions.dll -/Distribution/Microsoft.Extensions.Configuration.Binder.dll -/Distribution/Microsoft.Extensions.Configuration.dll /Distribution/Microsoft.Extensions.DependencyInjection.Abstractions.dll -/Distribution/Microsoft.Extensions.DependencyInjection.dll /Distribution/Microsoft.Extensions.FileProviders.Abstractions.dll /Distribution/Microsoft.Extensions.Hosting.Abstractions.dll /Distribution/Microsoft.Extensions.Logging.Abstractions.dll -/Distribution/Microsoft.Extensions.Logging.Configuration.dll -/Distribution/Microsoft.Extensions.Logging.Console.dll -/Distribution/Microsoft.Extensions.Logging.dll -/Distribution/Microsoft.Extensions.Options.ConfigurationExtensions.dll /Distribution/Microsoft.Extensions.Options.dll /Distribution/Microsoft.Extensions.Primitives.dll diff --git a/Projects/Scripts/Accounting/IPLimiter.cs b/Projects/Scripts/Accounting/IPLimiter.cs index 072ed9148..5e0cbf7c0 100644 --- a/Projects/Scripts/Accounting/IPLimiter.cs +++ b/Projects/Scripts/Accounting/IPLimiter.cs @@ -30,7 +30,7 @@ namespace Server.Misc if (!Enabled || IsExempt(ourAddress)) return true; - List netStates = NetState.Instances; + List netStates = TcpServer.Instances; int count = 0; @@ -50,4 +50,4 @@ namespace Server.Misc return true; } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Commands/Generic/Implementors/IPAddressCommandImplementor.cs b/Projects/Scripts/Commands/Generic/Implementors/IPAddressCommandImplementor.cs index d0d6f1bbe..927867b50 100644 --- a/Projects/Scripts/Commands/Generic/Implementors/IPAddressCommandImplementor.cs +++ b/Projects/Scripts/Commands/Generic/Implementors/IPAddressCommandImplementor.cs @@ -36,7 +36,7 @@ namespace Server.Commands.Generic List list = new List(); List addresses = new List(); - List states = NetState.Instances; + List states = TcpServer.Instances; for (int i = 0; i < states.Count; ++i) { @@ -60,4 +60,4 @@ namespace Server.Commands.Generic } } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Commands/Generic/Implementors/OnlineCommandImplementor.cs b/Projects/Scripts/Commands/Generic/Implementors/OnlineCommandImplementor.cs index 4d538f36e..2f80acea7 100644 --- a/Projects/Scripts/Commands/Generic/Implementors/OnlineCommandImplementor.cs +++ b/Projects/Scripts/Commands/Generic/Implementors/OnlineCommandImplementor.cs @@ -34,7 +34,7 @@ namespace Server.Commands.Generic List list = new List(); - List states = NetState.Instances; + List states = TcpServer.Instances; for (int i = 0; i < states.Count; ++i) { @@ -61,4 +61,4 @@ namespace Server.Commands.Generic } } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Commands/Handlers.cs b/Projects/Scripts/Commands/Handlers.cs index 723dfb2eb..f1540b499 100644 --- a/Projects/Scripts/Commands/Handlers.cs +++ b/Projects/Scripts/Commands/Handlers.cs @@ -671,7 +671,7 @@ namespace Server.Commands public static void BroadcastMessage(AccessLevel ac, int hue, string message) { - foreach (NetState state in NetState.Instances) + foreach (NetState state in TcpServer.Instances) { Mobile m = state.Mobile; @@ -743,7 +743,7 @@ namespace Server.Commands [Description("View some stats about the server.")] public static void Stats_OnCommand(CommandEventArgs e) { - e.Mobile.SendMessage("Open Connections: {0}", NetState.Instances.Count); + e.Mobile.SendMessage("Open Connections: {0}", TcpServer.Instances.Count); e.Mobile.SendMessage("Mobiles: {0}", World.Mobiles.Count); e.Mobile.SendMessage("Items: {0}", World.Items.Count); } diff --git a/Projects/Scripts/Engines/Help/PageQueue.cs b/Projects/Scripts/Engines/Help/PageQueue.cs index fa934094a..bce872da8 100644 --- a/Projects/Scripts/Engines/Help/PageQueue.cs +++ b/Projects/Scripts/Engines/Help/PageQueue.cs @@ -212,7 +212,7 @@ namespace Server.Engines.Help bool isStaffOnline = false; - foreach (NetState ns in NetState.Instances) + foreach (NetState ns in TcpServer.Instances) { Mobile m = ns.Mobile; diff --git a/Projects/Scripts/Gumps/AdminGump.cs b/Projects/Scripts/Gumps/AdminGump.cs index a9d63360d..5742830db 100644 --- a/Projects/Scripts/Gumps/AdminGump.cs +++ b/Projects/Scripts/Gumps/AdminGump.cs @@ -133,7 +133,7 @@ namespace Server.Gumps AddLabel(150, 170, LabelHue, Firewall.List.Count.ToString()); AddLabel(20, 190, LabelHue, "Clients:"); - AddLabel(150, 190, LabelHue, NetState.Instances.Count.ToString()); + AddLabel(150, 190, LabelHue, TcpServer.Instances.Count.ToString()); AddLabel(20, 210, LabelHue, "Mobiles:"); AddLabel(150, 210, LabelHue, World.Mobiles.Count.ToString()); @@ -380,7 +380,7 @@ namespace Server.Gumps { if (m_List == null) { - List states = NetState.Instances.ToList(); + List states = TcpServer.Instances; states.Sort(NetStateComparer.Instance); m_List = states.ToList(); @@ -1932,7 +1932,7 @@ namespace Server.Gumps if (level > AccessLevel.Player) { - List clients = NetState.Instances; + List clients = TcpServer.Instances; int count = 0; for (int i = 0; i < clients.Count; ++i) @@ -2020,7 +2020,7 @@ namespace Server.Gumps } else { - List instances = NetState.Instances; + List instances = TcpServer.Instances; for (int i = 0; i < instances.Count; ++i) { diff --git a/Projects/Scripts/Gumps/WhoGump.cs b/Projects/Scripts/Gumps/WhoGump.cs index 85263057b..098c4f41d 100644 --- a/Projects/Scripts/Gumps/WhoGump.cs +++ b/Projects/Scripts/Gumps/WhoGump.cs @@ -112,7 +112,7 @@ namespace Server.Gumps string filter = string.IsNullOrWhiteSpace(rawFilter) ? null : rawFilter.Trim().ToLower(); List list = new List(); - List states = NetState.Instances; + List states = TcpServer.Instances; for ( int i = 0; i < states.Count; ++i ) { diff --git a/Projects/Scripts/Misc/CrashGuard.cs b/Projects/Scripts/Misc/CrashGuard.cs index 7ab2e9743..26d9e31bb 100644 --- a/Projects/Scripts/Misc/CrashGuard.cs +++ b/Projects/Scripts/Misc/CrashGuard.cs @@ -202,7 +202,7 @@ namespace Server.Misc try { - List states = NetState.Instances; + List states = TcpServer.Instances; op.WriteLine("- Count: {0}", states.Count); diff --git a/Projects/Scripts/Misc/FoodDecay.cs b/Projects/Scripts/Misc/FoodDecay.cs index 4b3d44d52..e5afa1b2b 100644 --- a/Projects/Scripts/Misc/FoodDecay.cs +++ b/Projects/Scripts/Misc/FoodDecay.cs @@ -19,7 +19,7 @@ namespace Server.Misc public static void FoodDecay() { - foreach (NetState state in NetState.Instances) + foreach (NetState state in TcpServer.Instances) { HungerDecay(state.Mobile); ThirstDecay(state.Mobile); diff --git a/Projects/Scripts/Misc/LightCycle.cs b/Projects/Scripts/Misc/LightCycle.cs index 90155d0fa..0df950d41 100644 --- a/Projects/Scripts/Misc/LightCycle.cs +++ b/Projects/Scripts/Misc/LightCycle.cs @@ -20,9 +20,9 @@ namespace Server { m_LevelOverride = value; - for (int i = 0; i < NetState.Instances.Count; ++i) + for (int i = 0; i < TcpServer.Instances.Count; ++i) { - NetState ns = NetState.Instances[i]; + NetState ns = TcpServer.Instances[i]; Mobile m = ns.Mobile; m?.CheckLightLevels(false); @@ -69,12 +69,12 @@ namespace Server Clock.GetTime(from.Map, from.X, from.Y, out int hours, out int minutes); /* OSI times: - * + * * Midnight -> 3:59 AM : Night * 4:00 AM -> 11:59 PM : Day - * + * * RunUO times: - * + * * 10:00 PM -> 11:59 PM : Scale to night * Midnight -> 3:59 AM : Night * 4:00 AM -> 5:59 AM : Scale to day @@ -102,9 +102,9 @@ namespace Server protected override void OnTick() { - for (int i = 0; i < NetState.Instances.Count; ++i) + for (int i = 0; i < TcpServer.Instances.Count; ++i) { - NetState ns = NetState.Instances[i]; + NetState ns = TcpServer.Instances[i]; Mobile m = ns.Mobile; m?.CheckLightLevels(false); @@ -130,4 +130,4 @@ namespace Server } } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Misc/LoginStats.cs b/Projects/Scripts/Misc/LoginStats.cs index 69985c921..8b6ca8614 100644 --- a/Projects/Scripts/Misc/LoginStats.cs +++ b/Projects/Scripts/Misc/LoginStats.cs @@ -12,7 +12,7 @@ namespace Server.Misc private static void EventSink_Login(LoginEventArgs args) { - int userCount = NetState.Instances.Count; + int userCount = TcpServer.Instances.Count; int itemCount = World.Items.Count; int mobileCount = World.Mobiles.Count; @@ -27,4 +27,4 @@ namespace Server.Misc mobileCount, mobileCount == 1 ? "" : "s"); } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Misc/ServerList.cs b/Projects/Scripts/Misc/ServerList.cs index 87fe8d846..e2a1c6141 100644 --- a/Projects/Scripts/Misc/ServerList.cs +++ b/Projects/Scripts/Misc/ServerList.cs @@ -9,7 +9,7 @@ using Server.Network; namespace Server.Misc { - public class ServerList + public static class ServerList { /* * The default setting for Address, a value of 'null', will use your local IP address. If all of your local IP addresses diff --git a/Projects/Scripts/Misc/SocketOptions.cs b/Projects/Scripts/Misc/SocketOptions.cs index 3a59aac2c..3aa4b1d41 100644 --- a/Projects/Scripts/Misc/SocketOptions.cs +++ b/Projects/Scripts/Misc/SocketOptions.cs @@ -1,4 +1,5 @@ using System.Net; +using Server.Network; namespace Server { @@ -15,8 +16,7 @@ namespace Server public static void RegisterListeners() { - for (int i = 0; i < m_ListenerEndPoints.Length; i++) - Core.MessagePump.AddListener(m_ListenerEndPoints[i]); + TcpServer.Listeners.AddRange(m_ListenerEndPoints); } } } diff --git a/Projects/Scripts/Misc/VendorGenerator.cs b/Projects/Scripts/Misc/VendorGenerator.cs index 3e450fe0f..b04fe173f 100644 --- a/Projects/Scripts/Misc/VendorGenerator.cs +++ b/Projects/Scripts/Misc/VendorGenerator.cs @@ -316,10 +316,7 @@ namespace Server private static void ProcessDisplayCase(Map map, StaticTile[] tiles, int x, int y) { - ShopFlags flags = ShopFlags.None; - - for (int i = 0; i < tiles.Length; ++i) - flags |= ProcessDisplayedItem(tiles[i].ID); + ShopFlags flags = tiles.Aggregate(ShopFlags.None, (current, t) => current | ProcessDisplayedItem(t.ID)); if (flags != ShopFlags.None) { diff --git a/Projects/Scripts/Misc/Weather.cs b/Projects/Scripts/Misc/Weather.cs index 58071654e..86fa57e24 100644 --- a/Projects/Scripts/Misc/Weather.cs +++ b/Projects/Scripts/Misc/Weather.cs @@ -263,7 +263,7 @@ namespace Server.Misc else type = 2; - List states = NetState.Instances; + List states = TcpServer.Instances; Packet weatherPacket = null; diff --git a/Projects/Scripts/Misc/WebStatus.cs b/Projects/Scripts/Misc/WebStatus.cs index f0ada5366..961ec3c4d 100644 --- a/Projects/Scripts/Misc/WebStatus.cs +++ b/Projects/Scripts/Misc/WebStatus.cs @@ -119,7 +119,7 @@ namespace Server.Misc int index = 0; - foreach (Mobile m in NetState.Instances.Where(state => state.Mobile != null).Select(state => state.Mobile)) + foreach (Mobile m in TcpServer.Instances.Where(state => state.Mobile != null).Select(state => state.Mobile)) { ++index; @@ -181,4 +181,4 @@ namespace Server.Misc } } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Scripts.csproj b/Projects/Scripts/Scripts.csproj index 5707e931e..670d74678 100644 --- a/Projects/Scripts/Scripts.csproj +++ b/Projects/Scripts/Scripts.csproj @@ -36,6 +36,8 @@ - + + + diff --git a/Projects/Scripts/SpecialSystems/Engines/PreventInaccess.cs b/Projects/Scripts/SpecialSystems/Engines/PreventInaccess.cs index fcde69079..9e25a137f 100644 --- a/Projects/Scripts/SpecialSystems/Engines/PreventInaccess.cs +++ b/Projects/Scripts/SpecialSystems/Engines/PreventInaccess.cs @@ -41,7 +41,7 @@ namespace Server.Misc { Mobile from = e.Mobile; - if (from?.AccessLevel < AccessLevel.Counselor) + if (from == null || from.AccessLevel < AccessLevel.Counselor) return; if (HasDisconnected(from)) diff --git a/Projects/Server/AggressorInfo.cs b/Projects/Server/AggressorInfo.cs index a8706ca85..69ce600fe 100644 --- a/Projects/Server/AggressorInfo.cs +++ b/Projects/Server/AggressorInfo.cs @@ -27,7 +27,7 @@ namespace Server { public class AggressorInfo { - private static Queue m_Pool = new Queue(); + private static readonly Queue m_Pool = new Queue(); private Mobile m_Attacker, m_Defender; private bool m_CanReportMurder; private bool m_CriminalAggression; diff --git a/Projects/Server/ApplicationLifetime.cs b/Projects/Server/ApplicationLifetime.cs deleted file mode 100644 index 314cdcdea..000000000 --- a/Projects/Server/ApplicationLifetime.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Hosting -{ - /// - /// Allows consumers to perform cleanup during a graceful shutdown. - /// - internal class ApplicationLifetime : IHostApplicationLifetime - { - private readonly CancellationTokenSource _startedSource = new CancellationTokenSource(); - private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource(); - private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource(); - private readonly ILogger _logger; - - public ApplicationLifetime(ILogger logger) => _logger = logger; - - /// - /// Triggered when the application host has fully started and is about to wait - /// for a graceful shutdown. - /// - public CancellationToken ApplicationStarted => _startedSource.Token; - - /// - /// Triggered when the application host is performing a graceful shutdown. - /// Request may still be in flight. Shutdown will block until this event completes. - /// - public CancellationToken ApplicationStopping => _stoppingSource.Token; - - /// - /// Triggered when the application host is performing a graceful shutdown. - /// All requests should be complete at this point. Shutdown will block - /// until this event completes. - /// - public CancellationToken ApplicationStopped => _stoppedSource.Token; - - /// - /// Signals the ApplicationStopping event and blocks until it completes. - /// - public void StopApplication() - { - // Lock on CTS to synchronize multiple calls to StopApplication. This guarantees that the first call - // to StopApplication and its callbacks run to completion before subsequent calls to StopApplication, - // which will no-op since the first call already requested cancellation, get a chance to execute. - lock (_stoppingSource) - { - try - { - ExecuteHandlers(_stoppingSource); - } - catch (Exception) - { - /* _logger.ApplicationError(LoggerEventIds.ApplicationStoppingException, - "An error occurred stopping the application", - ex);*/ - } - } - } - - /// - /// Signals the ApplicationStarted event and blocks until it completes. - /// - public void NotifyStarted() - { - try - { - ExecuteHandlers(_startedSource); - } - catch (Exception) - { - /* _logger.ApplicationError(LoggerEventIds.ApplicationStartupException, - "An error occurred starting the application", - ex);*/ - } - } - - /// - /// Signals the ApplicationStopped event and blocks until it completes. - /// - public void NotifyStopped() - { - try - { - ExecuteHandlers(_stoppedSource); - } - catch (Exception) - { - /* _logger.ApplicationError(LoggerEventIds.ApplicationStoppedException, - "An error occurred stopping the application", - ex);*/ - } - } - - private void ExecuteHandlers(CancellationTokenSource cancel) - { - // Noop if this is already cancelled - if (cancel.IsCancellationRequested) return; - - // Run the cancellation token callbacks - cancel.Cancel(false); - } - } -} diff --git a/Projects/Server/Assemblies/libuv.dll b/Projects/Server/Assemblies/libuv.dll deleted file mode 100644 index d744f343de78be93ae40cdcbc4406966a3a02d11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 230912 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 m_TypeCaches = new Dictionary(); + private static readonly Dictionary m_TypeCaches = new Dictionary(); private static TypeCache m_NullCache; public static Assembly[] Assemblies { get; set; } @@ -85,10 +85,7 @@ namespace Server List types = new List(); if(ignoreCase) name = name.ToLower(); - for (int i = 0; i < Assemblies.Length; i++) - { - types.AddRange(GetTypeCache(Assemblies[i])[name]); - } + for (int i = 0; i < Assemblies.Length; i++) types.AddRange(GetTypeCache(Assemblies[i])[name]); if (types.Count == 0) types.AddRange(GetTypeCache(Core.Assembly)[name]); return types; @@ -107,15 +104,12 @@ namespace Server public class TypeCache { - private Dictionary m_NameMap = new Dictionary(); - private Type[] m_Types; - public IEnumerable Types { get => m_Types; } - public IEnumerable Names { get => m_NameMap.Keys; } + private readonly Dictionary m_NameMap = new Dictionary(); + private readonly Type[] m_Types; + public IEnumerable Types => m_Types; + public IEnumerable Names => m_NameMap.Keys; - public IEnumerable this[string name] - { - get => m_NameMap.TryGetValue(name, out int[] value) ? value.Select(x => m_Types[x]) : new Type[0]; - } + public IEnumerable this[string name] => m_NameMap.TryGetValue(name, out int[] value) ? value.Select(x => m_Types[x]) : new Type[0]; public TypeCache(Assembly asm) { diff --git a/Projects/Server/Body.cs b/Projects/Server/Body.cs index 998378129..b20101b14 100644 --- a/Projects/Server/Body.cs +++ b/Projects/Server/Body.cs @@ -35,7 +35,7 @@ namespace Server public struct Body { - private static BodyType[] m_Types; + private static readonly BodyType[] m_Types; static Body() { diff --git a/Projects/Server/Buffers/DiagnosticPoolBlock.cs b/Projects/Server/Buffers/DiagnosticPoolBlock.cs index 9623596f8..62da1dc84 100644 --- a/Projects/Server/Buffers/DiagnosticPoolBlock.cs +++ b/Projects/Server/Buffers/DiagnosticPoolBlock.cs @@ -20,7 +20,7 @@ namespace System.Buffers private readonly IMemoryOwner _memoryOwner; private MemoryHandle? _memoryHandle; - private Memory _memory; + private readonly Memory _memory; private readonly object _syncObj = new object(); private bool _isDisposed; diff --git a/Projects/Server/Buffers/MemoryPoolFactory.cs b/Projects/Server/Buffers/MemoryPoolFactory.cs index f8a3f37c5..b8eb87903 100644 --- a/Projects/Server/Buffers/MemoryPoolFactory.cs +++ b/Projects/Server/Buffers/MemoryPoolFactory.cs @@ -5,15 +5,7 @@ namespace System.Buffers { public static class SlabMemoryPoolFactory { - public static MemoryPool Create() - { -#if DEBUG - return new DiagnosticMemoryPool(CreateSlabMemoryPool()); -#else - return CreateSlabMemoryPool(); -#endif - } - + public static MemoryPool Create() => CreateSlabMemoryPool(); public static MemoryPool CreateSlabMemoryPool() => new SlabMemoryPool(); } } diff --git a/Projects/Server/ContextMenus/OpenBackpackEntry.cs b/Projects/Server/ContextMenus/OpenBackpackEntry.cs index 547fabff8..7664bf43e 100644 --- a/Projects/Server/ContextMenus/OpenBackpackEntry.cs +++ b/Projects/Server/ContextMenus/OpenBackpackEntry.cs @@ -22,7 +22,7 @@ namespace Server.ContextMenus { public class OpenBackpackEntry : ContextMenuEntry { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public OpenBackpackEntry(Mobile m) : base(6145) => m_Mobile = m; diff --git a/Projects/Server/ContextMenus/PaperdollEntry.cs b/Projects/Server/ContextMenus/PaperdollEntry.cs index 12aee25ab..717c12fd5 100644 --- a/Projects/Server/ContextMenus/PaperdollEntry.cs +++ b/Projects/Server/ContextMenus/PaperdollEntry.cs @@ -22,7 +22,7 @@ namespace Server.ContextMenus { public class PaperdollEntry : ContextMenuEntry { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public PaperdollEntry(Mobile m) : base(6123, 18) => m_Mobile = m; diff --git a/Projects/Server/Diagnostics/BaseProfile.cs b/Projects/Server/Diagnostics/BaseProfile.cs index bbc7804a5..086042ade 100644 --- a/Projects/Server/Diagnostics/BaseProfile.cs +++ b/Projects/Server/Diagnostics/BaseProfile.cs @@ -27,7 +27,7 @@ namespace Server.Diagnostics { public abstract class BaseProfile { - private Stopwatch _stopwatch; + private readonly Stopwatch _stopwatch; protected BaseProfile(string name) { diff --git a/Projects/Server/Diagnostics/GumpProfile.cs b/Projects/Server/Diagnostics/GumpProfile.cs index b8ac1c7cb..190ba1327 100644 --- a/Projects/Server/Diagnostics/GumpProfile.cs +++ b/Projects/Server/Diagnostics/GumpProfile.cs @@ -25,7 +25,7 @@ namespace Server.Diagnostics { public class GumpProfile : BaseProfile { - private static Dictionary _profiles = new Dictionary(); + private static readonly Dictionary _profiles = new Dictionary(); public GumpProfile(Type type) : base(type.FullName) { diff --git a/Projects/Server/Diagnostics/PacketProfile.cs b/Projects/Server/Diagnostics/PacketProfile.cs index 54f349dcf..6f9ee132b 100644 --- a/Projects/Server/Diagnostics/PacketProfile.cs +++ b/Projects/Server/Diagnostics/PacketProfile.cs @@ -53,7 +53,7 @@ namespace Server.Diagnostics public class PacketSendProfile : BasePacketProfile { - private static Dictionary _profiles = new Dictionary(); + private static readonly Dictionary _profiles = new Dictionary(); private long _created; @@ -87,7 +87,7 @@ namespace Server.Diagnostics public class PacketReceiveProfile : BasePacketProfile { - private static Dictionary _profiles = new Dictionary(); + private static readonly Dictionary _profiles = new Dictionary(); public PacketReceiveProfile(int packetId) : base($"0x{packetId:X2}") diff --git a/Projects/Server/Diagnostics/TargetProfile.cs b/Projects/Server/Diagnostics/TargetProfile.cs index 581961a0d..d16312209 100644 --- a/Projects/Server/Diagnostics/TargetProfile.cs +++ b/Projects/Server/Diagnostics/TargetProfile.cs @@ -25,7 +25,7 @@ namespace Server.Diagnostics { public class TargetProfile : BaseProfile { - private static Dictionary _profiles = new Dictionary(); + private static readonly Dictionary _profiles = new Dictionary(); public TargetProfile(Type type) : base(type.FullName) diff --git a/Projects/Server/Diagnostics/TimerProfile.cs b/Projects/Server/Diagnostics/TimerProfile.cs index 2f6f8826c..90c336397 100644 --- a/Projects/Server/Diagnostics/TimerProfile.cs +++ b/Projects/Server/Diagnostics/TimerProfile.cs @@ -25,7 +25,7 @@ namespace Server.Diagnostics { public class TimerProfile : BaseProfile { - private static Dictionary _profiles = new Dictionary(); + private static readonly Dictionary _profiles = new Dictionary(); public TimerProfile(string name) : base(name) diff --git a/Projects/Server/EventSink.cs b/Projects/Server/EventSink.cs index 89a131305..a5a826932 100644 --- a/Projects/Server/EventSink.cs +++ b/Projects/Server/EventSink.cs @@ -196,7 +196,7 @@ namespace Server public class AggressiveActionEventArgs : EventArgs { - private static Queue m_Pool = new Queue(); + private static readonly Queue m_Pool = new Queue(); private AggressiveActionEventArgs(Mobile aggressed, Mobile aggressor, bool criminal) { @@ -512,7 +512,7 @@ namespace Server public class MovementEventArgs : EventArgs { - private static Queue m_Pool = new Queue(); + private static readonly Queue m_Pool = new Queue(); public MovementEventArgs(Mobile mobile, Direction dir) { diff --git a/Projects/Server/Guild.cs b/Projects/Server/Guild.cs index df966e444..55b33fb84 100644 --- a/Projects/Server/Guild.cs +++ b/Projects/Server/Guild.cs @@ -32,7 +32,7 @@ namespace Server.Guilds public abstract class BaseGuild : ISerializable { - private BufferWriter m_SaveBuffer; + private readonly BufferWriter m_SaveBuffer; public BufferWriter SaveBuffer => m_SaveBuffer; private static uint m_NextID = 1; diff --git a/Projects/Server/Gumps/Gump.cs b/Projects/Server/Gumps/Gump.cs index 05718b5dd..f9b94881e 100644 --- a/Projects/Server/Gumps/Gump.cs +++ b/Projects/Server/Gumps/Gump.cs @@ -29,13 +29,13 @@ namespace Server.Gumps { private static uint m_NextSerial = 1; - private static byte[] m_BeginLayout = StringToBuffer("{ "); - private static byte[] m_EndLayout = StringToBuffer(" }"); + private static readonly byte[] m_BeginLayout = StringToBuffer("{ "); + private static readonly byte[] m_EndLayout = StringToBuffer(" }"); - private static byte[] m_NoMove = StringToBuffer("{ nomove }"); - private static byte[] m_NoClose = StringToBuffer("{ noclose }"); - private static byte[] m_NoDispose = StringToBuffer("{ nodispose }"); - private static byte[] m_NoResize = StringToBuffer("{ noresize }"); + private static readonly byte[] m_NoMove = StringToBuffer("{ nomove }"); + private static readonly byte[] m_NoClose = StringToBuffer("{ noclose }"); + private static readonly byte[] m_NoDispose = StringToBuffer("{ nodispose }"); + private static readonly byte[] m_NoResize = StringToBuffer("{ noresize }"); private bool m_Closable = true; private bool m_Disposable = true; @@ -43,7 +43,7 @@ namespace Server.Gumps private bool m_Resizable = true; private uint m_Serial; - private List m_Strings; + private readonly List m_Strings; internal int m_TextEntries, m_Switches; private int m_X, m_Y; diff --git a/Projects/Server/Gumps/GumpAlphaRegion.cs b/Projects/Server/Gumps/GumpAlphaRegion.cs index 20cfefe0d..165d37f98 100644 --- a/Projects/Server/Gumps/GumpAlphaRegion.cs +++ b/Projects/Server/Gumps/GumpAlphaRegion.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpAlphaRegion : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("checkertrans"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("checkertrans"); private int m_Width, m_Height; private int m_X, m_Y; diff --git a/Projects/Server/Gumps/GumpBackground.cs b/Projects/Server/Gumps/GumpBackground.cs index 8016cfe1b..3c96f17f2 100644 --- a/Projects/Server/Gumps/GumpBackground.cs +++ b/Projects/Server/Gumps/GumpBackground.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpBackground : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("resizepic"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("resizepic"); private int m_GumpID; private int m_Width, m_Height; private int m_X, m_Y; diff --git a/Projects/Server/Gumps/GumpButton.cs b/Projects/Server/Gumps/GumpButton.cs index 9186b8e37..dd6c7ec8e 100644 --- a/Projects/Server/Gumps/GumpButton.cs +++ b/Projects/Server/Gumps/GumpButton.cs @@ -30,7 +30,7 @@ namespace Server.Gumps public class GumpButton : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("button"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("button"); private int m_ButtonID; private int m_ID1, m_ID2; private int m_Param; diff --git a/Projects/Server/Gumps/GumpCheck.cs b/Projects/Server/Gumps/GumpCheck.cs index afc3a54d0..61f47f6db 100644 --- a/Projects/Server/Gumps/GumpCheck.cs +++ b/Projects/Server/Gumps/GumpCheck.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpCheck : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("checkbox"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("checkbox"); private int m_ID1, m_ID2; private bool m_InitialState; private int m_SwitchID; diff --git a/Projects/Server/Gumps/GumpGroup.cs b/Projects/Server/Gumps/GumpGroup.cs index 93418d837..7eb0f154e 100644 --- a/Projects/Server/Gumps/GumpGroup.cs +++ b/Projects/Server/Gumps/GumpGroup.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpGroup : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("group"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("group"); private int m_Group; public GumpGroup(int group) => m_Group = group; diff --git a/Projects/Server/Gumps/GumpHtml.cs b/Projects/Server/Gumps/GumpHtml.cs index 12cd813e4..b70bee5cb 100644 --- a/Projects/Server/Gumps/GumpHtml.cs +++ b/Projects/Server/Gumps/GumpHtml.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpHtml : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("htmlgump"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("htmlgump"); private bool m_Background, m_Scrollbar; private string m_Text; private int m_Width, m_Height; diff --git a/Projects/Server/Gumps/GumpHtmlLocalized.cs b/Projects/Server/Gumps/GumpHtmlLocalized.cs index ad8bf533e..8db25a304 100644 --- a/Projects/Server/Gumps/GumpHtmlLocalized.cs +++ b/Projects/Server/Gumps/GumpHtmlLocalized.cs @@ -31,9 +31,9 @@ namespace Server.Gumps public class GumpHtmlLocalized : GumpEntry { - private static byte[] m_LayoutNamePlain = Gump.StringToBuffer("xmfhtmlgump"); - private static byte[] m_LayoutNameColor = Gump.StringToBuffer("xmfhtmlgumpcolor"); - private static byte[] m_LayoutNameArgs = Gump.StringToBuffer("xmfhtmltok"); + private static readonly byte[] m_LayoutNamePlain = Gump.StringToBuffer("xmfhtmlgump"); + private static readonly byte[] m_LayoutNameColor = Gump.StringToBuffer("xmfhtmlgumpcolor"); + private static readonly byte[] m_LayoutNameArgs = Gump.StringToBuffer("xmfhtmltok"); private string m_Args; private bool m_Background, m_Scrollbar; private int m_Color; diff --git a/Projects/Server/Gumps/GumpImage.cs b/Projects/Server/Gumps/GumpImage.cs index 26d013222..dad0341ce 100644 --- a/Projects/Server/Gumps/GumpImage.cs +++ b/Projects/Server/Gumps/GumpImage.cs @@ -24,8 +24,8 @@ namespace Server.Gumps { public class GumpImage : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("gumppic"); - private static byte[] m_HueEquals = Gump.StringToBuffer(" hue="); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("gumppic"); + private static readonly byte[] m_HueEquals = Gump.StringToBuffer(" hue="); private int m_GumpID; private int m_Hue; private int m_X, m_Y; diff --git a/Projects/Server/Gumps/GumpImageTileButton.cs b/Projects/Server/Gumps/GumpImageTileButton.cs index a7fcfc4c0..37af7c24a 100644 --- a/Projects/Server/Gumps/GumpImageTileButton.cs +++ b/Projects/Server/Gumps/GumpImageTileButton.cs @@ -24,8 +24,8 @@ namespace Server.Gumps { public class GumpImageTileButton : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("buttontileart"); - private static byte[] m_LayoutTooltip = Gump.StringToBuffer(" }{ tooltip"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("buttontileart"); + private static readonly byte[] m_LayoutTooltip = Gump.StringToBuffer(" }{ tooltip"); private int m_ButtonID; private int m_Height; private int m_Hue; diff --git a/Projects/Server/Gumps/GumpImageTiled.cs b/Projects/Server/Gumps/GumpImageTiled.cs index e090bdf3a..b3b099b82 100644 --- a/Projects/Server/Gumps/GumpImageTiled.cs +++ b/Projects/Server/Gumps/GumpImageTiled.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpImageTiled : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("gumppictiled"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("gumppictiled"); private int m_GumpID; private int m_Width, m_Height; private int m_X, m_Y; diff --git a/Projects/Server/Gumps/GumpItem.cs b/Projects/Server/Gumps/GumpItem.cs index 8420886ea..a43cc4dd0 100644 --- a/Projects/Server/Gumps/GumpItem.cs +++ b/Projects/Server/Gumps/GumpItem.cs @@ -24,8 +24,8 @@ namespace Server.Gumps { public class GumpItem : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("tilepic"); - private static byte[] m_LayoutNameHue = Gump.StringToBuffer("tilepichue"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("tilepic"); + private static readonly byte[] m_LayoutNameHue = Gump.StringToBuffer("tilepichue"); private int m_Hue; private int m_ItemID; private int m_X, m_Y; diff --git a/Projects/Server/Gumps/GumpItemProperty.cs b/Projects/Server/Gumps/GumpItemProperty.cs index 5ce65e19e..b4b43e9be 100644 --- a/Projects/Server/Gumps/GumpItemProperty.cs +++ b/Projects/Server/Gumps/GumpItemProperty.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpItemProperty : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("itemproperty"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("itemproperty"); private uint m_Serial; public GumpItemProperty(uint serial) => m_Serial = serial; diff --git a/Projects/Server/Gumps/GumpLabel.cs b/Projects/Server/Gumps/GumpLabel.cs index d8a0c3389..623363484 100644 --- a/Projects/Server/Gumps/GumpLabel.cs +++ b/Projects/Server/Gumps/GumpLabel.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpLabel : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("text"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("text"); private int m_Hue; private string m_Text; private int m_X, m_Y; diff --git a/Projects/Server/Gumps/GumpLabelCropped.cs b/Projects/Server/Gumps/GumpLabelCropped.cs index 462968311..cd988ab59 100644 --- a/Projects/Server/Gumps/GumpLabelCropped.cs +++ b/Projects/Server/Gumps/GumpLabelCropped.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpLabelCropped : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("croppedtext"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("croppedtext"); private int m_Hue; private string m_Text; private int m_Width, m_Height; diff --git a/Projects/Server/Gumps/GumpPage.cs b/Projects/Server/Gumps/GumpPage.cs index 8b5479512..7e0ebd9d2 100644 --- a/Projects/Server/Gumps/GumpPage.cs +++ b/Projects/Server/Gumps/GumpPage.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpPage : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("page"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("page"); private int m_Page; public GumpPage(int page) => m_Page = page; diff --git a/Projects/Server/Gumps/GumpRadio.cs b/Projects/Server/Gumps/GumpRadio.cs index 740c3d9b1..153d9b319 100644 --- a/Projects/Server/Gumps/GumpRadio.cs +++ b/Projects/Server/Gumps/GumpRadio.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpRadio : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("radio"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("radio"); private int m_ID1, m_ID2; private bool m_InitialState; private int m_SwitchID; diff --git a/Projects/Server/Gumps/GumpTextEntry.cs b/Projects/Server/Gumps/GumpTextEntry.cs index 8ebd49f83..e364281e2 100644 --- a/Projects/Server/Gumps/GumpTextEntry.cs +++ b/Projects/Server/Gumps/GumpTextEntry.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpTextEntry : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("textentry"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("textentry"); private int m_EntryID; private int m_Hue; private string m_InitialText; diff --git a/Projects/Server/Gumps/GumpTextEntryLimited.cs b/Projects/Server/Gumps/GumpTextEntryLimited.cs index 2b4d983aa..11538ac2b 100644 --- a/Projects/Server/Gumps/GumpTextEntryLimited.cs +++ b/Projects/Server/Gumps/GumpTextEntryLimited.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpTextEntryLimited : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("textentrylimited"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("textentrylimited"); private int m_EntryID; private int m_Hue; private string m_InitialText; diff --git a/Projects/Server/Gumps/GumpTooltip.cs b/Projects/Server/Gumps/GumpTooltip.cs index 2982ebecc..f05672bd8 100644 --- a/Projects/Server/Gumps/GumpTooltip.cs +++ b/Projects/Server/Gumps/GumpTooltip.cs @@ -24,7 +24,7 @@ namespace Server.Gumps { public class GumpTooltip : GumpEntry { - private static byte[] m_LayoutName = Gump.StringToBuffer("tooltip"); + private static readonly byte[] m_LayoutName = Gump.StringToBuffer("tooltip"); private int m_Number; public GumpTooltip(int number) => m_Number = number; diff --git a/Projects/Server/Item.cs b/Projects/Server/Item.cs index 1bf318018..8fc4db6c2 100644 --- a/Projects/Server/Item.cs +++ b/Projects/Server/Item.cs @@ -201,13 +201,13 @@ namespace Server public class Item : IHued, IComparable, ISerializable, ISpawnable, IPropertyListObject { - private BufferWriter m_SaveBuffer; - public BufferWriter SaveBuffer { get { return m_SaveBuffer; } } + private readonly BufferWriter m_SaveBuffer; + public BufferWriter SaveBuffer => m_SaveBuffer; public const int QuestItemHue = 0x4EA; // Hmmmm... "for EA"? public static readonly List EmptyItems = new List(); - private static List m_DeltaQueue = new List(); + private static readonly List m_DeltaQueue = new List(); private static bool _processing; diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index 846cea79d..5bc03a3f2 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -36,8 +36,8 @@ namespace Server.Items public class Container : Item { - private static QueuePool m_QueuePool = new QueuePool(QueueRef.Generate, preGenerateCount: 2, maxRefrenceRetention: 5); - private static List m_FindItemsList = new List(); + private static readonly QueuePool m_QueuePool = new QueuePool(QueueRef.Generate, preGenerateCount: 2, maxRefrenceRetention: 5); + private static readonly List m_FindItemsList = new List(); private ContainerData m_ContainerData; @@ -200,7 +200,7 @@ namespace Server.Items public virtual bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) { - if (m?.AccessLevel < AccessLevel.GameMaster) + if (m == null || m.AccessLevel < AccessLevel.GameMaster) { if (IsDecoContainer) { @@ -735,7 +735,7 @@ namespace Server.Items private class GroupComparer : IComparer { - private CheckItemGroup m_Grouper; + private readonly CheckItemGroup m_Grouper; public GroupComparer(CheckItemGroup grouper) => m_Grouper = grouper; @@ -754,8 +754,8 @@ namespace Server.Items private struct ItemStackEntry { - public Item m_StackItem; - public Item m_DropItem; + public readonly Item m_StackItem; + public readonly Item m_DropItem; public ItemStackEntry(Item stack, Item drop) { @@ -1536,12 +1536,10 @@ namespace Server.Items { var container = queue.Dequeue(); foreach (var item in container.Items) - { if (item is T typedItem && predicate?.Invoke(typedItem) != false) items.Add(typedItem); else if (recurse && item is Container itemContainer) queue.Enqueue(itemContainer); - } } return items; } @@ -1583,7 +1581,7 @@ namespace Server.Items public class ContainerData { - private static Dictionary m_Table; + private static readonly Dictionary m_Table; static ContainerData() { diff --git a/Projects/Server/Kestrel/CorrelationIdGenerator.cs b/Projects/Server/Kestrel/CorrelationIdGenerator.cs deleted file mode 100644 index 1be328db1..000000000 --- a/Projects/Server/Kestrel/CorrelationIdGenerator.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; - -namespace Microsoft.AspNetCore.Connections -{ - public static class CorrelationIdGenerator - { - // Base32 encoding - in ascii sort order for easy text based sorting - private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray(); - - // Seed the _lastConnectionId for this application instance with - // the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 - // for a roughly increasing _lastId over restarts - private static long _lastId = DateTime.UtcNow.Ticks; - - public static string GetNextId() => GenerateId(Interlocked.Increment(ref _lastId)); - - private static string GenerateId(long id) - { - return string.Create(13, id, (buffer, value) => - { - char[] encode32Chars = s_encode32Chars; - - buffer[12] = encode32Chars[value & 31]; - buffer[11] = encode32Chars[(value >> 5) & 31]; - buffer[10] = encode32Chars[(value >> 10) & 31]; - buffer[9] = encode32Chars[(value >> 15) & 31]; - buffer[8] = encode32Chars[(value >> 20) & 31]; - buffer[7] = encode32Chars[(value >> 25) & 31]; - buffer[6] = encode32Chars[(value >> 30) & 31]; - buffer[5] = encode32Chars[(value >> 35) & 31]; - buffer[4] = encode32Chars[(value >> 40) & 31]; - buffer[3] = encode32Chars[(value >> 45) & 31]; - buffer[2] = encode32Chars[(value >> 50) & 31]; - buffer[1] = encode32Chars[(value >> 55) & 31]; - buffer[0] = encode32Chars[(value >> 60) & 31]; - }); - } - } -} diff --git a/Projects/Server/LibUv/IAsyncDisposable.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/IAsyncDisposable.cs similarity index 76% rename from Projects/Server/LibUv/IAsyncDisposable.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/IAsyncDisposable.cs index 70f4a53a5..f46e09e7f 100644 --- a/Projects/Server/LibUv/IAsyncDisposable.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/IAsyncDisposable.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { interface IAsyncDisposable { diff --git a/Projects/Server/LibUv/ILibuvTrace.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ILibuvTrace.cs similarity index 83% rename from Projects/Server/LibUv/ILibuvTrace.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/ILibuvTrace.cs index 371018cd2..29fd48d9e 100644 --- a/Projects/Server/LibUv/ILibuvTrace.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ILibuvTrace.cs @@ -4,9 +4,9 @@ using System; using Microsoft.Extensions.Logging; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public interface ILibuvTrace : ILogger + internal interface ILibuvTrace : ILogger { void ConnectionRead(string connectionId, int count); diff --git a/Projects/Server/LibUv/LibuvAwaitable.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvAwaitable.cs similarity index 76% rename from Projects/Server/LibUv/LibuvAwaitable.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvAwaitable.cs index efdaeae29..bb8c4e7b5 100644 --- a/Projects/Server/LibUv/LibuvAwaitable.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvAwaitable.cs @@ -5,11 +5,11 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; -using Libuv.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public class LibuvAwaitable : ICriticalNotifyCompletion where TRequest : UvRequest + internal class LibuvAwaitable : ICriticalNotifyCompletion where TRequest : UvRequest { private static readonly Action _callbackCompleted = () => { }; @@ -53,8 +53,7 @@ namespace Libuv { // There should never be a race between IsCompleted and OnCompleted since both operations // should always be on the libuv thread - if (ReferenceEquals(_callback, _callbackCompleted)) - Debug.Fail($"{typeof(LibuvAwaitable)}.{nameof(OnCompleted)} raced with {nameof(IsCompleted)}, scheduling callback."); + if (ReferenceEquals(_callback, _callbackCompleted)) Debug.Fail($"{typeof(LibuvAwaitable)}.{nameof(OnCompleted)} raced with {nameof(IsCompleted)}, scheduling callback."); _callback = continuation; } @@ -65,7 +64,7 @@ namespace Libuv } } - public struct UvWriteResult + internal struct UvWriteResult { public int Status { get; } public UvException Error { get; } diff --git a/Projects/Server/LibUv/LibuvConnection.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnection.cs similarity index 90% rename from Projects/Server/LibUv/LibuvConnection.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnection.cs index 5839c4d6b..dd9dcecc8 100644 --- a/Projects/Server/LibUv/LibuvConnection.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnection.cs @@ -8,19 +8,21 @@ using System.IO.Pipelines; using System.Net; using System.Threading; using System.Threading.Tasks; -using Libuv.Internal; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public partial class LibuvConnection : TransportConnection + internal partial class LibuvConnection : TransportConnection { private static readonly int MinAllocBufferSize = SlabMemoryPool.BlockSize / 2; - private static readonly Action _readCallback = ReadCallback; + private static readonly Action _readCallback = + (handle, status, state) => ReadCallback(handle, status, state); - private static readonly Func _allocCallback = AllocCallback; + private static readonly Func _allocCallback = + (handle, suggestedSize, state) => AllocCallback(handle, suggestedSize, state); private readonly UvStreamHandle _socket; private readonly CancellationTokenSource _connectionClosedTokenSource = new CancellationTokenSource(); @@ -243,7 +245,7 @@ namespace Libuv state._waitForConnectionClosedTcs.TrySetResult(null); }, this, - false); + preferLocal: false); } private async Task ApplyBackpressureAsync(ValueTask flushTask) @@ -282,16 +284,18 @@ namespace Libuv // The operation was canceled by the server not the client. No need for additional logs. return new ConnectionAbortedException(uvError.Message, uvError); } - - if (LibuvConstants.IsConnectionReset(uvError.StatusCode)) + else if (LibuvConstants.IsConnectionReset(uvError.StatusCode)) { // Log connection resets at a lower (Debug) level. Log.ConnectionReset(ConnectionId); return new ConnectionResetException(uvError.Message, uvError); } - // This is unexpected. - Log.ConnectionError(ConnectionId, uvError); - return new IOException(uvError.Message, uvError); + else + { + // This is unexpected. + Log.ConnectionError(ConnectionId, uvError); + return new IOException(uvError.Message, uvError); + } } private void CancelConnectionClosedToken() diff --git a/Projects/Server/LibUv/LibuvConnectionListener.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnectionListener.cs similarity index 89% rename from Projects/Server/LibUv/LibuvConnectionListener.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnectionListener.cs index d04da54c6..99868a0fd 100644 --- a/Projects/Server/LibUv/LibuvConnectionListener.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnectionListener.cs @@ -7,15 +7,14 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using Libuv.Internal; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Server; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public class LibuvConnectionListener : IConnectionListener + internal class LibuvConnectionListener : IConnectionListener { private readonly List _listeners = new List(); private IAsyncEnumerator _acceptEnumerator; @@ -63,8 +62,7 @@ namespace Libuv var disposeTasks = _listeners.Select(listener => ((IAsyncDisposable)listener).DisposeAsync()).ToArray(); - if (!await WaitAsync(Task.WhenAll(disposeTasks), TimeSpan.FromSeconds(5)).ConfigureAwait(false)) - Log.LogError(0, null, "Disposing listeners failed"); + if (!await WaitAsync(Task.WhenAll(disposeTasks), TimeSpan.FromSeconds(5)).ConfigureAwait(false)) Log.LogError(0, null, "Disposing listeners failed"); } @@ -101,14 +99,9 @@ namespace Libuv } Threads.Clear(); -#if DEBUG && !INNER_LOOP - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); -#endif } - public async Task BindAsync() + internal async Task BindAsync() { // TODO: Move thread management to LibuvTransportFactory // TODO: Split endpoint management from thread management @@ -127,7 +120,7 @@ namespace Libuv } else { - var pipeName = (Core.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); + var pipeName = (Libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); var listenerPrimary = new ListenerPrimary(TransportContext); @@ -171,7 +164,7 @@ namespace Libuv while (remainingSlots > 0) { // Calling GetAwaiter().GetResult() is safe because we know the task is completed - var (connection, slot) = (await Task.WhenAny(slots)).GetAwaiter().GetResult(); + (var connection, var slot) = (await Task.WhenAny(slots)).GetAwaiter().GetResult(); // If the connection is null then the listener was closed if (connection == null) diff --git a/Projects/Server/LibUv/LibuvConstants.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConstants.cs similarity index 50% rename from Projects/Server/LibUv/LibuvConstants.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConstants.cs index bce142a81..5352d5e8d 100644 --- a/Projects/Server/LibUv/LibuvConstants.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConstants.cs @@ -2,11 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Runtime.CompilerServices; -using Server; +using System.Runtime.InteropServices; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public static class LibuvConstants + internal static class LibuvConstants { public const int EOF = -4095; public static readonly int? ECONNRESET = GetECONNRESET(); @@ -22,76 +22,69 @@ namespace Libuv private static int? GetECONNRESET() { - if (Core.IsWindows) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return -4077; - if (Core.IsLinux) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return -104; - if (Core.IsDarwin) - return -5; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -54; return null; } private static int? GetEPIPE() { - if (Core.IsWindows) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return -4047; - if (Core.IsLinux) - return -32; - if (Core.IsDarwin) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return -32; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -32; return null; } private static int? GetENOTCONN() { - if (Core.IsWindows) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return -4053; - if (Core.IsLinux) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return -107; - if (Core.IsDarwin) - return -57; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -57; return null; } private static int? GetEINVAL() { - if (Core.IsWindows) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return -4071; - if (Core.IsLinux) - return -22; - if (Core.IsDarwin) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return -22; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -22; return null; } private static int? GetEADDRINUSE() { - if (Core.IsWindows) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return -4091; - if (Core.IsLinux) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return -98; - if (Core.IsDarwin) - return -48; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -48; return null; } private static int? GetENOTSUP() { - if (Core.IsLinux) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return -95; - if (Core.IsDarwin) - return -45; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -45; return null; } private static int? GetECANCELED() { - if (Core.IsWindows) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return -4081; - if (Core.IsLinux) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return -125; - if (Core.IsDarwin) - return -89; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -89; return null; } } diff --git a/Projects/Server/LibUv/LibuvOutputConsumer.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvOutputConsumer.cs similarity index 90% rename from Projects/Server/LibUv/LibuvOutputConsumer.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvOutputConsumer.cs index e9d42ca52..bb0b95831 100644 --- a/Projects/Server/LibUv/LibuvOutputConsumer.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvOutputConsumer.cs @@ -4,11 +4,11 @@ using System; using System.IO.Pipelines; using System.Threading.Tasks; -using Libuv.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public class LibuvOutputConsumer + internal class LibuvOutputConsumer { private readonly LibuvThread _thread; private readonly UvStreamHandle _socket; diff --git a/Projects/Server/LibUv/LibuvThread.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvThread.cs similarity index 88% rename from Projects/Server/LibUv/LibuvThread.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvThread.cs index d57d7f2dc..c2a742a80 100644 --- a/Projects/Server/LibUv/LibuvThread.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvThread.cs @@ -9,13 +9,13 @@ using System.IO.Pipelines; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; -using Libuv.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public class LibuvThread : PipeScheduler + internal class LibuvThread : PipeScheduler { // maximum times the work queues swapped and are processed in a single pass // as completing a task may immediately have write data to put on the network @@ -35,8 +35,8 @@ namespace Libuv private readonly object _workSync = new object(); private readonly object _closeHandleSync = new object(); private readonly object _startSync = new object(); - private bool _stopImmediate; - private bool _initCompleted; + private bool _stopImmediate = false; + private bool _initCompleted = false; private Exception _closeError; private readonly ILibuvTrace _log; @@ -60,9 +60,9 @@ namespace Libuv #endif #if !DEBUG - // Mark the thread as being as unimportant to keeping the process alive. - // Don't do this for debug builds, so we know if the thread isn't terminating. - _thread.IsBackground = true; + // Mark the thread as being as unimportant to keeping the process alive. + // Don't do this for debug builds, so we know if the thread isn't terminating. + _thread.IsBackground = true; #endif QueueCloseHandle = PostCloseHandle; QueueCloseAsyncHandle = EnqueueCloseHandle; @@ -76,10 +76,6 @@ namespace Libuv public WriteReqPool WriteReqPool { get; } -#if DEBUG - public List Requests { get; } = new List(); -#endif - public Exception FatalError => _closeError; public Action, IntPtr> QueueCloseHandle { get; } @@ -129,18 +125,6 @@ namespace Libuv if (_closeError != null) ExceptionDispatchInfo.Capture(_closeError).Throw(); } -#if DEBUG && !INNER_LOOP - private void CheckUvReqLeaks() - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - - // Detect leaks in UvRequest objects - foreach (var request in Requests) Debug.Assert(request.Target == null, $"{request.Target?.GetType()} object is still alive."); - } -#endif - private void AllowStop() { _post.Unreference(); @@ -320,11 +304,6 @@ namespace Libuv } WriteReqPool.Dispose(); _threadTcs.SetResult(null); - -#if DEBUG && !INNER_LOOP - // Check for handle leaks after disposing everything - CheckUvReqLeaks(); -#endif } } @@ -406,8 +385,7 @@ namespace Libuv return wasWork; } - private static async Task WaitAsync(Task task, TimeSpan timeout) => - await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false) == task; + private static async Task WaitAsync(Task task, TimeSpan timeout) => await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false) == task; public override void Schedule(Action action, object state) { diff --git a/Projects/Server/LibUv/LibuvTrace.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTrace.cs similarity index 94% rename from Projects/Server/LibUv/LibuvTrace.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTrace.cs index ebc090316..1d3e2e794 100644 --- a/Projects/Server/LibUv/LibuvTrace.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTrace.cs @@ -4,9 +4,9 @@ using System; using Microsoft.Extensions.Logging; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public class LibuvTrace : ILibuvTrace + internal class LibuvTrace : ILibuvTrace { // ConnectionRead: Reserved: 3 diff --git a/Projects/Server/LibUv/LibuvTransportContext.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportContext.cs similarity index 74% rename from Projects/Server/LibUv/LibuvTransportContext.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportContext.cs index 730f31eb3..d5f68e1f5 100644 --- a/Projects/Server/LibUv/LibuvTransportContext.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportContext.cs @@ -3,9 +3,9 @@ using Microsoft.Extensions.Hosting; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public class LibuvTransportContext + internal class LibuvTransportContext { public LibuvTransportOptions Options { get; set; } diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportFactory.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportFactory.cs new file mode 100644 index 000000000..6b06c11bc --- /dev/null +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportFactory.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal +{ + internal class LibuvTransportFactory : IConnectionListenerFactory + { + private readonly LibuvTransportContext _baseTransportContext; + + public LibuvTransportFactory( + IOptions options, + IHostApplicationLifetime applicationLifetime, + ILoggerFactory loggerFactory) + { + if (options == null) throw new ArgumentNullException(nameof(options)); + if (applicationLifetime == null) throw new ArgumentNullException(nameof(applicationLifetime)); + if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory)); + + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"); + var trace = new LibuvTrace(logger); + + var threadCount = options.Value.ThreadCount; + + if (threadCount <= 0) + throw new ArgumentOutOfRangeException(nameof(threadCount), + threadCount, + "ThreadCount must be positive."); + + if (!LibuvConstants.ECONNRESET.HasValue) trace.LogWarning("Unable to determine ECONNRESET value on this platform."); + + if (!LibuvConstants.EADDRINUSE.HasValue) trace.LogWarning("Unable to determine EADDRINUSE value on this platform."); + + _baseTransportContext = new LibuvTransportContext + { + Options = options.Value, + AppLifetime = applicationLifetime, + Log = trace, + }; + } + + public async ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default) + { + var transportContext = new LibuvTransportContext + { + Options = _baseTransportContext.Options, + AppLifetime = _baseTransportContext.AppLifetime, + Log = _baseTransportContext.Log + }; + + var transport = new LibuvConnectionListener(transportContext, endpoint); + await transport.BindAsync(); + return transport; + } + } +} diff --git a/Projects/Server/LibUv/Listener.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Listener.cs similarity index 83% rename from Projects/Server/LibUv/Listener.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Listener.cs index 4ed799b78..aac5bf3f2 100644 --- a/Projects/Server/LibUv/Listener.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Listener.cs @@ -5,16 +5,16 @@ using System; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; -using Libuv.Internal; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { /// /// Base class for listeners in Kestrel. Listens for incoming connections /// - public class Listener : ListenerContext, IAsyncDisposable + internal class Listener : ListenerContext, IAsyncDisposable { // REVIEW: This needs to be bounded and we need a strategy for what to do when the queue is full private bool _closed; @@ -46,13 +46,17 @@ namespace Libuv /// private UvStreamHandle CreateListenSocket() { - return EndPoint switch + switch (EndPoint) { - IPEndPoint _ => ListenTcp(false), - UnixDomainSocketEndPoint _ => ListenPipe(false), - FileHandleEndPoint _ => ListenHandle(), - _ => throw new NotSupportedException() - }; + case IPEndPoint _: + return ListenTcp(useFileHandle: false); + case UnixDomainSocketEndPoint _: + return ListenPipe(useFileHandle: false); + case FileHandleEndPoint _: + return ListenHandle(); + default: + throw new NotSupportedException(); + } } private UvTcpHandle ListenTcp(bool useFileHandle) @@ -62,7 +66,7 @@ namespace Libuv try { socket.Init(Thread.Loop, Thread.QueueCloseHandle); - socket.NoDelay(true); + socket.NoDelay(TransportContext.Options.NoDelay); if (!useFileHandle) { @@ -117,9 +121,9 @@ namespace Libuv case FileHandleType.Auto: break; case FileHandleType.Tcp: - return ListenTcp(true); + return ListenTcp(useFileHandle: true); case FileHandleType.Pipe: - return ListenPipe(true); + return ListenPipe(useFileHandle: true); default: throw new NotSupportedException(); } @@ -127,7 +131,7 @@ namespace Libuv UvStreamHandle handle; try { - handle = ListenTcp(true); + handle = ListenTcp(useFileHandle: true); EndPoint = new FileHandleEndPoint(handleEndPoint.FileHandle, FileHandleType.Tcp); return handle; } @@ -136,7 +140,7 @@ namespace Libuv Log.LogDebug(0, exception, "Listener.ListenHandle"); } - handle = ListenPipe(true); + handle = ListenPipe(useFileHandle: true); EndPoint = new FileHandleEndPoint(handleEndPoint.FileHandle, FileHandleType.Pipe); return handle; } diff --git a/Projects/Server/LibUv/ListenerContext.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerContext.cs similarity index 84% rename from Projects/Server/LibUv/ListenerContext.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerContext.cs index 52b5b9149..32cfd42d8 100644 --- a/Projects/Server/LibUv/ListenerContext.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerContext.cs @@ -9,13 +9,13 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; -using Libuv.Internal; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public class ListenerContext + internal class ListenerContext { // Single reader, single writer queue since all writes happen from the uv thread and reads happen sequentially private readonly Channel _acceptQueue = Channel.CreateUnbounded(new UnboundedChannelOptions @@ -115,7 +115,7 @@ namespace Libuv try { socket.Init(Thread.Loop, Thread.QueueCloseHandle); - socket.NoDelay(true); + socket.NoDelay(TransportContext.Options.NoDelay); } catch { @@ -152,17 +152,14 @@ namespace Libuv { var fileHandleEndPoint = (FileHandleEndPoint)EndPoint; - switch (fileHandleEndPoint.FileHandleType) + return fileHandleEndPoint.FileHandleType switch { - case FileHandleType.Auto: - throw new InvalidOperationException("Cannot accept on a non-specific file handle, listen should be performed first."); - case FileHandleType.Tcp: - return AcceptTcp(); - case FileHandleType.Pipe: - return AcceptPipe(); - default: - throw new NotSupportedException(); - } + FileHandleType.Auto => throw new InvalidOperationException( + "Cannot accept on a non-specific file handle, listen should be performed first."), + FileHandleType.Tcp => (UvStreamHandle)AcceptTcp(), + FileHandleType.Pipe => AcceptPipe(), + _ => throw new NotSupportedException() + }; } } } diff --git a/Projects/Server/LibUv/ListenerPrimary.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerPrimary.cs similarity index 91% rename from Projects/Server/LibUv/ListenerPrimary.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerPrimary.cs index da006b7fe..e172ad175 100644 --- a/Projects/Server/LibUv/ListenerPrimary.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerPrimary.cs @@ -7,19 +7,16 @@ using System.IO; using System.Net; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Libuv.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; -using Server; -#pragma warning disable 169 - -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { /// /// A primary listener waits for incoming connections on a specified socket. Incoming /// connections may be passed to a secondary listener to handle. /// - public class ListenerPrimary : Listener + internal class ListenerPrimary : Listener { // The list of pipes that can be dispatched to (where we've confirmed the _pipeMessage) private readonly List _dispatchPipes = new List(); @@ -29,7 +26,7 @@ namespace Libuv private string _pipeName; private byte[] _pipeMessage; private IntPtr _fileCompletionInfoPtr; - private bool _tryDetachFromIOCP = Core.IsWindows; + private bool _tryDetachFromIOCP = PlatformApis.IsWindows; // this message is passed to write2 because it must be non-zero-length, // but it has no other functional significance @@ -151,15 +148,17 @@ namespace Libuv Thread.Loop.Libuv.uv_fileno(handle, ref socket); if (NtSetInformationFile(socket, out statusBlock, _fileCompletionInfoPtr, - (uint)Marshal.SizeOf(), FileReplaceCompletionInformation) == STATUS_INVALID_INFO_CLASS) + (uint)Marshal.SizeOf(), FileReplaceCompletionInformation) == STATUS_INVALID_INFO_CLASS) // Replacing IOCP information is only supported on Windows 8.1 or newer _tryDetachFromIOCP = false; } private struct IO_STATUS_BLOCK { +#pragma warning disable 169 uint status; ulong information; +#pragma warning restore 169 } private struct FILE_COMPLETION_INFORMATION @@ -219,7 +218,7 @@ namespace Libuv { // This is an unexpected immediate termination of the dispatch pipe most likely caused by an // external process scanning the pipe, so don't we don't log it too severely. - // https://github.com/aspnet/AspNetCore/issues/4741 + // https://github.com/dotnet/aspnetcore/issues/4741 dispatchPipe.Dispose(); _bufHandle.Free(); diff --git a/Projects/Server/LibUv/ListenerSecondary.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerSecondary.cs similarity index 92% rename from Projects/Server/LibUv/ListenerSecondary.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerSecondary.cs index 4377a4e9c..e741c8c0c 100644 --- a/Projects/Server/LibUv/ListenerSecondary.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerSecondary.cs @@ -6,16 +6,16 @@ using System.Net; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Libuv.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.Extensions.Logging; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { /// /// A secondary listener is delegated requests from a primary listener via a named pipe or /// UNIX domain socket. /// - public class ListenerSecondary : ListenerContext, IAsyncDisposable + internal class ListenerSecondary : ListenerContext, IAsyncDisposable { private string _pipeName; private byte[] _pipeMessage; diff --git a/Projects/Server/LibUv/Internal/LibuvFunctions.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/LibuvFunctions.cs similarity index 78% rename from Projects/Server/LibUv/Internal/LibuvFunctions.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/LibuvFunctions.cs index f215c0ae6..718bdc3cd 100644 --- a/Projects/Server/LibUv/Internal/LibuvFunctions.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/LibuvFunctions.cs @@ -4,14 +4,15 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Server; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public class LibuvFunctions + internal class LibuvFunctions { public LibuvFunctions() { + IsWindows = PlatformApis.IsWindows; + _uv_loop_init = NativeMethods.uv_loop_init; _uv_loop_close = NativeMethods.uv_loop_close; _uv_run = NativeMethods.uv_run; @@ -63,6 +64,8 @@ namespace Libuv.Internal { } + public readonly bool IsWindows; + public void ThrowIfErrored(int statusCode) { // Note: method is explicitly small so the success case is easily inlined @@ -83,47 +86,51 @@ namespace Libuv.Internal error = statusCode < 0 ? GetError(statusCode) : null; } - // Note: method marked as NoInlining so it doesn't bloat either of the two preceding functions - // Check and ThrowError and alter their jit heuristics. [MethodImpl(MethodImplOptions.NoInlining)] - private UvException GetError(int statusCode) => - new UvException($"Error {statusCode} {err_name(statusCode)} {strerror(statusCode)}", statusCode); + private UvException GetError(int statusCode) + { + // Note: method marked as NoInlining so it doesn't bloat either of the two preceding functions + // Check and ThrowError and alter their jit heuristics. + var errorName = err_name(statusCode); + var errorDescription = strerror(statusCode); + return new UvException("Error " + statusCode + " " + errorName + " " + errorDescription, statusCode); + } - public Func _uv_loop_init; + protected Func _uv_loop_init; public void loop_init(UvLoopHandle handle) { ThrowIfErrored(_uv_loop_init(handle)); } - public Func _uv_loop_close; + protected Func _uv_loop_close; public void loop_close(UvLoopHandle handle) { - handle.Validate(true); + handle.Validate(closed: true); ThrowIfErrored(_uv_loop_close(handle.InternalGetHandle())); } - public Func _uv_run; + protected Func _uv_run; public void run(UvLoopHandle handle, int mode) { handle.Validate(); ThrowIfErrored(_uv_run(handle, mode)); } - public Action _uv_stop; + protected Action _uv_stop; public void stop(UvLoopHandle handle) { handle.Validate(); _uv_stop(handle); } - public Action _uv_ref; + protected Action _uv_ref; public void @ref(UvHandle handle) { handle.Validate(); _uv_ref(handle); } - public Action _uv_unref; + protected Action _uv_unref; public void unref(UvHandle handle) { handle.Validate(); @@ -131,8 +138,8 @@ namespace Libuv.Internal } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate int uv_fileno_func(UvHandle handle, ref IntPtr socket); - public uv_fileno_func _uv_fileno; + protected delegate int uv_fileno_func(UvHandle handle, ref IntPtr socket); + protected uv_fileno_func _uv_fileno; public void uv_fileno(UvHandle handle, ref IntPtr socket) { handle.Validate(); @@ -141,10 +148,10 @@ namespace Libuv.Internal [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_close_cb(IntPtr handle); - public Action _uv_close; + protected Action _uv_close; public void close(UvHandle handle, uv_close_cb close_cb) { - handle.Validate(true); + handle.Validate(closed: true); _uv_close(handle.InternalGetHandle(), close_cb); } @@ -155,7 +162,7 @@ namespace Libuv.Internal [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_async_cb(IntPtr handle); - public Func _uv_async_init; + protected Func _uv_async_init; public void async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb) { loop.Validate(); @@ -163,19 +170,19 @@ namespace Libuv.Internal ThrowIfErrored(_uv_async_init(loop, handle, cb)); } - public Func _uv_async_send; + protected Func _uv_async_send; public void async_send(UvAsyncHandle handle) { ThrowIfErrored(_uv_async_send(handle)); } - public Func _uv_unsafe_async_send; + protected Func _uv_unsafe_async_send; public void unsafe_async_send(IntPtr handle) { ThrowIfErrored(_uv_unsafe_async_send(handle)); } - public Func _uv_tcp_init; + protected Func _uv_tcp_init; public void tcp_init(UvLoopHandle loop, UvTcpHandle handle) { loop.Validate(); @@ -183,29 +190,29 @@ namespace Libuv.Internal ThrowIfErrored(_uv_tcp_init(loop, handle)); } - public delegate int uv_tcp_bind_func(UvTcpHandle handle, ref SockAddr addr, int flags); - public uv_tcp_bind_func _uv_tcp_bind; + protected delegate int uv_tcp_bind_func(UvTcpHandle handle, ref SockAddr addr, int flags); + protected uv_tcp_bind_func _uv_tcp_bind; public void tcp_bind(UvTcpHandle handle, ref SockAddr addr, int flags) { handle.Validate(); ThrowIfErrored(_uv_tcp_bind(handle, ref addr, flags)); } - public Func _uv_tcp_open; + protected Func _uv_tcp_open; public void tcp_open(UvTcpHandle handle, IntPtr hSocket) { handle.Validate(); ThrowIfErrored(_uv_tcp_open(handle, hSocket)); } - public Func _uv_tcp_nodelay; + protected Func _uv_tcp_nodelay; public void tcp_nodelay(UvTcpHandle handle, bool enable) { handle.Validate(); ThrowIfErrored(_uv_tcp_nodelay(handle, enable ? 1 : 0)); } - public Func _uv_pipe_init; + protected Func _uv_pipe_init; public void pipe_init(UvLoopHandle loop, UvPipeHandle handle, bool ipc) { loop.Validate(); @@ -213,14 +220,14 @@ namespace Libuv.Internal ThrowIfErrored(_uv_pipe_init(loop, handle, ipc ? -1 : 0)); } - public Func _uv_pipe_bind; + protected Func _uv_pipe_bind; public void pipe_bind(UvPipeHandle handle, string name) { handle.Validate(); ThrowIfErrored(_uv_pipe_bind(handle, name)); } - public Func _uv_pipe_open; + protected Func _uv_pipe_open; public void pipe_open(UvPipeHandle handle, IntPtr hSocket) { handle.Validate(); @@ -229,14 +236,14 @@ namespace Libuv.Internal [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_connection_cb(IntPtr server, int status); - public Func _uv_listen; + protected Func _uv_listen; public void listen(UvStreamHandle handle, int backlog, uv_connection_cb cb) { handle.Validate(); ThrowIfErrored(_uv_listen(handle, backlog, cb)); } - public Func _uv_accept; + protected Func _uv_accept; public void accept(UvStreamHandle server, UvStreamHandle client) { server.Validate(); @@ -246,7 +253,7 @@ namespace Libuv.Internal [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_connect_cb(IntPtr req, int status); - public Action _uv_pipe_connect; + protected Action _uv_pipe_connect; public void pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb) { req.Validate(); @@ -254,7 +261,7 @@ namespace Libuv.Internal _uv_pipe_connect(req, handle, name, cb); } - public Func _uv_pipe_pending_count; + protected Func _uv_pipe_pending_count; public int pipe_pending_count(UvPipeHandle handle) { handle.Validate(); @@ -265,21 +272,21 @@ namespace Libuv.Internal public delegate void uv_alloc_cb(IntPtr server, int suggested_size, out uv_buf_t buf); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_read_cb(IntPtr server, int nread, ref uv_buf_t buf); - public Func _uv_read_start; + protected Func _uv_read_start; public void read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { handle.Validate(); ThrowIfErrored(_uv_read_start(handle, alloc_cb, read_cb)); } - public Func _uv_read_stop; + protected Func _uv_read_stop; public void read_stop(UvStreamHandle handle) { handle.Validate(); ThrowIfErrored(_uv_read_stop(handle)); } - public Func _uv_try_write; + protected Func _uv_try_write; public int try_write(UvStreamHandle handle, uv_buf_t[] bufs, int nbufs) { handle.Validate(); @@ -291,8 +298,8 @@ namespace Libuv.Internal [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_write_cb(IntPtr req, int status); - public unsafe delegate int uv_write_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb); - public uv_write_func _uv_write; + protected unsafe delegate int uv_write_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb); + protected uv_write_func _uv_write; public unsafe void write(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb) { req.Validate(); @@ -300,8 +307,8 @@ namespace Libuv.Internal ThrowIfErrored(_uv_write(req, handle, bufs, nbufs, cb)); } - public unsafe delegate int uv_write2_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb); - public uv_write2_func _uv_write2; + protected unsafe delegate int uv_write2_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb); + protected uv_write2_func _uv_write2; public unsafe void write2(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb) { req.Validate(); @@ -309,38 +316,38 @@ namespace Libuv.Internal ThrowIfErrored(_uv_write2(req, handle, bufs, nbufs, sendHandle, cb)); } - public Func _uv_err_name; + protected Func _uv_err_name; public string err_name(int err) { IntPtr ptr = _uv_err_name(err); return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr); } - public Func _uv_strerror; + protected Func _uv_strerror; public string strerror(int err) { IntPtr ptr = _uv_strerror(err); return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr); } - public Func _uv_loop_size; + protected Func _uv_loop_size; public int loop_size() => _uv_loop_size(); - public Func _uv_handle_size; + protected Func _uv_handle_size; public int handle_size(HandleType handleType) => _uv_handle_size(handleType); - public Func _uv_req_size; + protected Func _uv_req_size; public int req_size(RequestType reqType) => _uv_req_size(reqType); - public delegate int uv_ip4_addr_func(string ip, int port, out SockAddr addr); - public uv_ip4_addr_func _uv_ip4_addr; + protected delegate int uv_ip4_addr_func(string ip, int port, out SockAddr addr); + protected uv_ip4_addr_func _uv_ip4_addr; public void ip4_addr(string ip, int port, out SockAddr addr, out UvException error) { Check(_uv_ip4_addr(ip, port, out addr), out error); } - public delegate int uv_ip6_addr_func(string ip, int port, out SockAddr addr); - public uv_ip6_addr_func _uv_ip6_addr; + protected delegate int uv_ip6_addr_func(string ip, int port, out SockAddr addr); + protected uv_ip6_addr_func _uv_ip6_addr; public void ip6_addr(string ip, int port, out SockAddr addr, out UvException error) { Check(_uv_ip6_addr(ip, port, out addr), out error); @@ -348,15 +355,15 @@ namespace Libuv.Internal [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_walk_cb(IntPtr handle, IntPtr arg); - public Func _uv_walk; + protected Func _uv_walk; public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg) { loop.Validate(); _uv_walk(loop, walk_cb, arg); } - public Func _uv_timer_init; - public void timer_init(UvLoopHandle loop, UvTimerHandle handle) + protected Func _uv_timer_init; + public unsafe void timer_init(UvLoopHandle loop, UvTimerHandle handle) { loop.Validate(); handle.Validate(); @@ -365,29 +372,29 @@ namespace Libuv.Internal [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void uv_timer_cb(IntPtr handle); - public Func _uv_timer_start; - public void timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat) + protected Func _uv_timer_start; + public unsafe void timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat) { handle.Validate(); ThrowIfErrored(_uv_timer_start(handle, cb, timeout, repeat)); } - public Func _uv_timer_stop; - public void timer_stop(UvTimerHandle handle) + protected Func _uv_timer_stop; + public unsafe void timer_stop(UvTimerHandle handle) { handle.Validate(); ThrowIfErrored(_uv_timer_stop(handle)); } - public Func _uv_now; - public long now(UvLoopHandle loop) + protected Func _uv_now; + public unsafe long now(UvLoopHandle loop) { loop.Validate(); return _uv_now(loop); } public delegate int uv_tcp_getsockname_func(UvTcpHandle handle, out SockAddr addr, ref int namelen); - public uv_tcp_getsockname_func _uv_tcp_getsockname; + protected uv_tcp_getsockname_func _uv_tcp_getsockname; public void tcp_getsockname(UvTcpHandle handle, out SockAddr addr, ref int namelen) { handle.Validate(); @@ -395,14 +402,14 @@ namespace Libuv.Internal } public delegate int uv_tcp_getpeername_func(UvTcpHandle handle, out SockAddr addr, ref int namelen); - public uv_tcp_getpeername_func _uv_tcp_getpeername; + protected uv_tcp_getpeername_func _uv_tcp_getpeername; public void tcp_getpeername(UvTcpHandle handle, out SockAddr addr, ref int namelen) { handle.Validate(); ThrowIfErrored(_uv_tcp_getpeername(handle, out addr, ref namelen)); } - public uv_buf_t buf_init(IntPtr memory, int len) => new uv_buf_t(memory, len, Core.IsWindows); + public uv_buf_t buf_init(IntPtr memory, int len) => new uv_buf_t(memory, len, IsWindows); public struct uv_buf_t { @@ -417,9 +424,9 @@ namespace Libuv.Internal private readonly IntPtr _field0; private readonly IntPtr _field1; - public uv_buf_t(IntPtr memory, int len, bool windows) + public uv_buf_t(IntPtr memory, int len, bool IsWindows) { - if (windows) + if (IsWindows) { _field0 = (IntPtr)len; _field1 = memory; @@ -581,16 +588,16 @@ namespace Libuv.Internal public static extern int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg); [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] - public static extern int uv_timer_init(UvLoopHandle loop, UvTimerHandle handle); + public static extern unsafe int uv_timer_init(UvLoopHandle loop, UvTimerHandle handle); [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] - public static extern int uv_timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat); + public static extern unsafe int uv_timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat); [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] - public static extern int uv_timer_stop(UvTimerHandle handle); + public static extern unsafe int uv_timer_stop(UvTimerHandle handle); [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] - public static extern long uv_now(UvLoopHandle loop); + public static extern unsafe long uv_now(UvLoopHandle loop); [DllImport("WS2_32.dll", CallingConvention = CallingConvention.Winapi)] public static extern unsafe int WSAIoctl( diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/PlatformApis.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/PlatformApis.cs new file mode 100644 index 000000000..ebf59a240 --- /dev/null +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/PlatformApis.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking +{ + internal static class PlatformApis + { + public static bool IsWindows { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + public static bool IsDarwin { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long VolatileRead(ref long value) => IntPtr.Size == 8 ? Volatile.Read(ref value) : Interlocked.Read(ref value); + } +} diff --git a/Projects/Server/LibUv/Internal/SockAddr.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/SockAddr.cs similarity index 78% rename from Projects/Server/LibUv/Internal/SockAddr.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/SockAddr.cs index 09c6e6473..9a01f5be3 100644 --- a/Projects/Server/LibUv/Internal/SockAddr.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/SockAddr.cs @@ -3,20 +3,19 @@ using System.Net; using System.Runtime.InteropServices; -using Server; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { [StructLayout(LayoutKind.Sequential)] - public struct SockAddr + internal struct SockAddr { // this type represents native memory occupied by sockaddr struct // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740496(v=vs.85).aspx // although the c/c++ header defines it as a 2-byte short followed by a 14-byte array, // the simplest way to reserve the same size in c# is with four nameless long values - private long _field0; - private long _field1; - private long _field2; + private readonly long _field0; + private readonly long _field1; + private readonly long _field2; private long _field3; public SockAddr(long ignored) => _field0 = _field1 = _field2 = _field3 = 0; @@ -64,30 +63,33 @@ namespace Libuv.Internal var port = ((int)(_field0 & 0x00FF0000) >> 8) | (int)((_field0 & 0xFF000000) >> 24); int family = (int)_field0; - if (Core.IsDarwin) + if (PlatformApis.IsDarwin) // see explanation in example 4 family >>= 8; family &= 0xFF; if (family == 2) + { // AF_INET => IPv4 return new IPEndPoint(new IPAddress((_field0 >> 32) & 0xFFFFFFFF), port); - - if (IsIPv4MappedToIPv6()) + } + else if (IsIPv4MappedToIPv6()) { var ipv4bits = (_field2 >> 32) & 0x00000000FFFFFFFF; return new IPEndPoint(new IPAddress(ipv4bits), port); } - - // otherwise IPv6 - var bytes = new byte[16]; - fixed (byte* b = bytes) + else { - *(long*)b = _field1; - *(long*)(b + 8) = _field2; - } + // otherwise IPv6 + var bytes = new byte[16]; + fixed (byte* b = bytes) + { + *(long*)b = _field1; + *(long*)(b + 8) = _field2; + } - return new IPEndPoint(new IPAddress(bytes, ScopeId), port); + return new IPEndPoint(new IPAddress(bytes, ScopeId), port); + } } public uint ScopeId @@ -100,8 +102,13 @@ namespace Libuv.Internal } } - // If the IPAddress is an IPv4 mapped to IPv6, return the IPv4 representation instead. - // For example [::FFFF:127.0.0.1] will be transform to IPAddress of 127.0.0.1 - private bool IsIPv4MappedToIPv6() => _field1 == 0 && (_field2 & 0xFFFFFFFF) == 0xFFFF0000; + private bool IsIPv4MappedToIPv6() + { + // If the IPAddress is an IPv4 mapped to IPv6, return the IPv4 representation instead. + // For example [::FFFF:127.0.0.1] will be transform to IPAddress of 127.0.0.1 + if (_field1 != 0) return false; + + return (_field2 & 0xFFFFFFFF) == 0xFFFF0000; + } } } diff --git a/Projects/Server/LibUv/Internal/UvAsyncHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvAsyncHandle.cs similarity index 86% rename from Projects/Server/LibUv/Internal/UvAsyncHandle.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvAsyncHandle.cs index f27a179a8..47552037c 100644 --- a/Projects/Server/LibUv/Internal/UvAsyncHandle.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvAsyncHandle.cs @@ -5,13 +5,13 @@ using System; using System.Diagnostics; using System.Threading; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public class UvAsyncHandle : UvHandle + internal class UvAsyncHandle : UvHandle { - private static readonly LibuvFunctions.uv_close_cb _destroyMemory = handle => DestroyMemory(handle); + private static readonly LibuvFunctions.uv_close_cb _destroyMemory = (handle) => DestroyMemory(handle); - private static readonly LibuvFunctions.uv_async_cb _uv_async_cb = handle => AsyncCb(handle); + private static readonly LibuvFunctions.uv_async_cb _uv_async_cb = (handle) => AsyncCb(handle); private Action _callback; private Action, IntPtr> _queueCloseHandle; @@ -56,7 +56,7 @@ namespace Libuv.Internal { // This can be called from the finalizer. // Ensure the closure doesn't reference "this". - LibuvFunctions uv = _uv; + var uv = _uv; _queueCloseHandle(memory2 => uv.close(memory2, _destroyMemory), memory); uv.unsafe_async_send(memory); } diff --git a/Projects/Server/LibUv/Internal/UvConnectRequest.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvConnectRequest.cs similarity index 86% rename from Projects/Server/LibUv/Internal/UvConnectRequest.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvConnectRequest.cs index cc60539ac..8aa07fc8d 100644 --- a/Projects/Server/LibUv/Internal/UvConnectRequest.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvConnectRequest.cs @@ -4,14 +4,14 @@ using System; using Microsoft.Extensions.Logging; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { /// /// Summary description for UvWriteRequest /// - public class UvConnectRequest : UvRequest + internal class UvConnectRequest : UvRequest { - private static readonly LibuvFunctions.uv_connect_cb _uv_connect_cb = UvConnectCb; + private static readonly LibuvFunctions.uv_connect_cb _uv_connect_cb = (req, status) => UvConnectCb(req, status); private Action _callback; private object _state; diff --git a/Projects/Server/LibUv/Internal/UvException.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvException.cs similarity index 70% rename from Projects/Server/LibUv/Internal/UvException.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvException.cs index 3d4e492ef..bdda17954 100644 --- a/Projects/Server/LibUv/Internal/UvException.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvException.cs @@ -3,9 +3,9 @@ using System; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public class UvException : Exception + internal class UvException : Exception { public UvException(string message, int statusCode) : base(message) => StatusCode = statusCode; diff --git a/Projects/Server/LibUv/Internal/UvHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvHandle.cs similarity index 83% rename from Projects/Server/LibUv/Internal/UvHandle.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvHandle.cs index 888791bcc..4f738819e 100644 --- a/Projects/Server/LibUv/Internal/UvHandle.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvHandle.cs @@ -5,11 +5,11 @@ using System; using System.Diagnostics; using System.Threading; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public abstract class UvHandle : UvMemory + internal abstract class UvHandle : UvMemory { - private static readonly LibuvFunctions.uv_close_cb _destroyMemory = DestroyMemory; + private static readonly LibuvFunctions.uv_close_cb _destroyMemory = (handle) => DestroyMemory(handle); private Action, IntPtr> _queueCloseHandle; protected UvHandle(ILibuvTrace logger) : base (logger) @@ -28,7 +28,7 @@ namespace Libuv.Internal protected override bool ReleaseHandle() { - IntPtr memory = handle; + var memory = handle; if (memory != IntPtr.Zero) { handle = IntPtr.Zero; @@ -41,7 +41,7 @@ namespace Libuv.Internal { // This can be called from the finalizer. // Ensure the closure doesn't reference "this". - LibuvFunctions uv = _uv; + var uv = _uv; _queueCloseHandle(memory2 => uv.close(memory2, _destroyMemory), memory); } else diff --git a/Projects/Server/LibUv/Internal/UvLoopHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvLoopHandle.cs similarity index 84% rename from Projects/Server/LibUv/Internal/UvLoopHandle.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvLoopHandle.cs index c99c8896f..d24c1fac0 100644 --- a/Projects/Server/LibUv/Internal/UvLoopHandle.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvLoopHandle.cs @@ -4,9 +4,9 @@ using System; using System.Threading; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public class UvLoopHandle : UvMemory + internal class UvLoopHandle : UvMemory { public UvLoopHandle(ILibuvTrace logger) : base(logger) { diff --git a/Projects/Server/LibUv/Internal/UvMemory.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvMemory.cs similarity index 90% rename from Projects/Server/LibUv/Internal/UvMemory.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvMemory.cs index b5369ab5c..e42e420f8 100644 --- a/Projects/Server/LibUv/Internal/UvMemory.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvMemory.cs @@ -7,12 +7,12 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { /// /// Summary description for UvMemory /// - public abstract class UvMemory : SafeHandle + internal abstract class UvMemory : SafeHandle { protected LibuvFunctions _uv; protected int _threadId; @@ -66,7 +66,6 @@ namespace Libuv.Internal { Debug.Assert(closed || !IsClosed, "Handle is closed"); Debug.Assert(!IsInvalid, "Handle is invalid"); - Debug.Assert(_threadId == Thread.CurrentThread.ManagedThreadId, "ThreadId is incorrect"); } diff --git a/Projects/Server/LibUv/Internal/UvPipeHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvPipeHandle.cs similarity index 83% rename from Projects/Server/LibUv/Internal/UvPipeHandle.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvPipeHandle.cs index 188777209..0ce7aedd3 100644 --- a/Projects/Server/LibUv/Internal/UvPipeHandle.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvPipeHandle.cs @@ -3,9 +3,9 @@ using System; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public class UvPipeHandle : UvStreamHandle + internal class UvPipeHandle : UvStreamHandle { public UvPipeHandle(ILibuvTrace logger) : base(logger) { diff --git a/Projects/Server/LibUv/Internal/UvRequest.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvRequest.cs similarity index 65% rename from Projects/Server/LibUv/Internal/UvRequest.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvRequest.cs index ed7199ce2..c25a1eecd 100644 --- a/Projects/Server/LibUv/Internal/UvRequest.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvRequest.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public class UvRequest : UvMemory + internal class UvRequest : UvMemory { protected UvRequest(ILibuvTrace logger) : base(logger, GCHandleType.Normal) { @@ -14,11 +14,6 @@ namespace Libuv.Internal public virtual void Init(LibuvThread thread) { -#if DEBUG - // Store weak handles to all UvRequest objects so we can do leak detection - // while running tests - thread.Requests.Add(new WeakReference(this)); -#endif } protected override bool ReleaseHandle() @@ -29,3 +24,4 @@ namespace Libuv.Internal } } } + diff --git a/Projects/Server/LibUv/Internal/UvStreamHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvStreamHandle.cs similarity index 89% rename from Projects/Server/LibUv/Internal/UvStreamHandle.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvStreamHandle.cs index 2edf8253b..eb139c311 100644 --- a/Projects/Server/LibUv/Internal/UvStreamHandle.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvStreamHandle.cs @@ -5,16 +5,14 @@ using System; using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public abstract class UvStreamHandle : UvHandle + internal abstract class UvStreamHandle : UvHandle { - private static readonly LibuvFunctions.uv_connection_cb _uv_connection_cb = UvConnectionCb; + private static readonly LibuvFunctions.uv_connection_cb _uv_connection_cb = (handle, status) => UvConnectionCb(handle, status); // Ref and out lamda params must be explicitly typed - private static readonly LibuvFunctions.uv_alloc_cb _uv_alloc_cb = - (IntPtr handle, int suggested_size, out LibuvFunctions.uv_buf_t buf) => UvAllocCb(handle, suggested_size, out buf); - private static readonly LibuvFunctions.uv_read_cb _uv_read_cb = - (IntPtr handle, int status, ref LibuvFunctions.uv_buf_t buf) => UvReadCb(handle, status, ref buf); + private static readonly LibuvFunctions.uv_alloc_cb _uv_alloc_cb = (IntPtr handle, int suggested_size, out LibuvFunctions.uv_buf_t buf) => UvAllocCb(handle, suggested_size, out buf); + private static readonly LibuvFunctions.uv_read_cb _uv_read_cb = (IntPtr handle, int status, ref LibuvFunctions.uv_buf_t buf) => UvReadCb(handle, status, ref buf); private Action _listenCallback; private object _listenState; diff --git a/Projects/Server/LibUv/Internal/UvTcpHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTcpHandle.cs similarity index 90% rename from Projects/Server/LibUv/Internal/UvTcpHandle.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTcpHandle.cs index 745477201..548ec53a0 100644 --- a/Projects/Server/LibUv/Internal/UvTcpHandle.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTcpHandle.cs @@ -5,9 +5,9 @@ using System; using System.Net; using System.Runtime.InteropServices; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public class UvTcpHandle : UvStreamHandle + internal class UvTcpHandle : UvStreamHandle { public UvTcpHandle(ILibuvTrace logger) : base(logger) { diff --git a/Projects/Server/LibUv/Internal/UvTimerHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTimerHandle.cs similarity index 87% rename from Projects/Server/LibUv/Internal/UvTimerHandle.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTimerHandle.cs index 5fc52af17..f15c8458c 100644 --- a/Projects/Server/LibUv/Internal/UvTimerHandle.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTimerHandle.cs @@ -4,9 +4,9 @@ using System; using Microsoft.Extensions.Logging; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { - public class UvTimerHandle : UvHandle + internal class UvTimerHandle : UvHandle { private static readonly LibuvFunctions.uv_timer_cb _uv_timer_cb = UvTimerCb; diff --git a/Projects/Server/LibUv/Internal/UvWriteReq.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvWriteReq.cs similarity index 84% rename from Projects/Server/LibUv/Internal/UvWriteReq.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvWriteReq.cs index 29b8b4e9f..b3fa47ce6 100644 --- a/Projects/Server/LibUv/Internal/UvWriteReq.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvWriteReq.cs @@ -7,14 +7,14 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; -namespace Libuv.Internal +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking { /// /// Summary description for UvWriteRequest /// - public class UvWriteReq : UvRequest + internal class UvWriteReq : UvRequest { - private static readonly LibuvFunctions.uv_write_cb _uv_write_cb = (ptr, status) => UvWriteCb(ptr, status); + private static readonly LibuvFunctions.uv_write_cb _uv_write_cb = (IntPtr ptr, int status) => UvWriteCb(ptr, status); private IntPtr _bufs; @@ -22,9 +22,9 @@ namespace Libuv.Internal private object _state; private const int BUFFER_COUNT = 4; - private LibuvAwaitable _awaitable = new LibuvAwaitable(); - private List _pins = new List(BUFFER_COUNT + 1); - private List _handles = new List(BUFFER_COUNT + 1); + private readonly LibuvAwaitable _awaitable = new LibuvAwaitable(); + private readonly List _pins = new List(BUFFER_COUNT + 1); + private readonly List _handles = new List(BUFFER_COUNT + 1); public UvWriteReq(ILibuvTrace logger) : base(logger) { @@ -76,9 +76,6 @@ namespace Libuv.Internal nBuffers++; var pBuffers = (LibuvFunctions.uv_buf_t*)_bufs; - if (pBuffers == null) - throw new NullReferenceException(); - if (nBuffers > BUFFER_COUNT) { // create and pin buffer array when it's larger than the pre-allocated one @@ -86,8 +83,6 @@ namespace Libuv.Internal var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned); _pins.Add(gcHandle); pBuffers = (LibuvFunctions.uv_buf_t*)gcHandle.AddrOfPinnedObject(); - if (pBuffers == null) - throw new NullReferenceException(); } if (nBuffers == 1) @@ -137,7 +132,7 @@ namespace Libuv.Internal Action callback, object state) { - WriteArraySegmentInternal(handle, bufs, null, callback, state); + WriteArraySegmentInternal(handle, bufs, sendHandle: null, callback: callback, state: state); } public void Write2( @@ -154,15 +149,12 @@ namespace Libuv.Internal UvStreamHandle handle, ArraySegment> bufs, UvStreamHandle sendHandle, - Action callback, object state - ) + Action callback, + object state) { try { var pBuffers = (LibuvFunctions.uv_buf_t*)_bufs; - if (pBuffers == null) - throw new NullReferenceException(); - var nBuffers = bufs.Count; if (nBuffers > BUFFER_COUNT) { @@ -171,14 +163,12 @@ namespace Libuv.Internal var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned); _pins.Add(gcHandle); pBuffers = (LibuvFunctions.uv_buf_t*)gcHandle.AddrOfPinnedObject(); - if (pBuffers == null) - throw new NullReferenceException(); } for (var index = 0; index < nBuffers; index++) { // create and pin each segment being written - var buf = bufs.Array?[bufs.Offset + index] ?? throw new Exception("buffs.Array is null"); + var buf = bufs.Array[bufs.Offset + index]; var gcHandle = GCHandle.Alloc(buf.Array, GCHandleType.Pinned); _pins.Add(gcHandle); diff --git a/Projects/Server/LibUv/WriteReqPool.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/WriteReqPool.cs similarity index 84% rename from Projects/Server/LibUv/WriteReqPool.cs rename to Projects/Server/Kestrel/Transport.LIbuv/Internal/WriteReqPool.cs index 745874344..41f76afd7 100644 --- a/Projects/Server/LibUv/WriteReqPool.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/Internal/WriteReqPool.cs @@ -3,11 +3,11 @@ using System; using System.Collections.Generic; -using Libuv.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { - public class WriteReqPool + internal class WriteReqPool { private const int _maxPooledWriteReqs = 1024; diff --git a/Projects/Server/LibUv/LibuvTransportOptions.cs b/Projects/Server/Kestrel/Transport.LIbuv/LibuvTransportOptions.cs similarity index 66% rename from Projects/Server/LibUv/LibuvTransportOptions.cs rename to Projects/Server/Kestrel/Transport.LIbuv/LibuvTransportOptions.cs index 9de07cc83..7ea005700 100644 --- a/Projects/Server/LibUv/LibuvTransportOptions.cs +++ b/Projects/Server/Kestrel/Transport.LIbuv/LibuvTransportOptions.cs @@ -4,7 +4,7 @@ using System; using System.Buffers; -namespace Libuv +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv { /// /// Provides programmatic configuration of Libuv transport features. @@ -19,6 +19,14 @@ namespace Libuv /// public int ThreadCount { get; set; } = ProcessorThreadCount; + /// + /// Set to false to enable Nagle's algorithm for all connections. + /// + /// + /// Defaults to true. + /// + public bool NoDelay { get; set; } = true; + /// /// The maximum length of the pending connection queue. /// @@ -39,12 +47,19 @@ namespace Libuv { // Actual core count would be a better number // rather than logical cores which includes hyper-threaded cores. - // Divide by 2 for hyper-threading, and good defaults (still need threads for the game thread). + // Divide by 2 for hyper-threading, and good defaults (still need threads to do webserving). var threadCount = Environment.ProcessorCount >> 1; - // Receive Side Scaling RSS Processor count currently maxes out at 16 - // would be better to check the NIC's current hardware queues; but xplat... - return threadCount < 1 ? 1 : threadCount > 16 ? 16 : threadCount; + if (threadCount < 1) + // Ensure shifted value is at least one + return 1; + + if (threadCount > 16) + // Receive Side Scaling RSS Processor count currently maxes out at 16 + // would be better to check the NIC's current hardware queues; but xplat... + return 16; + + return threadCount; } } } diff --git a/Projects/Server/Kestrel/Transport.LIbuv/WebHostBuilderLibuvExtensions.cs b/Projects/Server/Kestrel/Transport.LIbuv/WebHostBuilderLibuvExtensions.cs new file mode 100644 index 000000000..242695bb3 --- /dev/null +++ b/Projects/Server/Kestrel/Transport.LIbuv/WebHostBuilderLibuvExtensions.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Hosting +{ + public static class WebHostBuilderLibuvExtensions + { + /// + /// Specify Libuv as the transport to be used by Kestrel. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. + /// + public static IWebHostBuilder UseLibuv(this IWebHostBuilder hostBuilder) + { + return hostBuilder.ConfigureServices(services => + { + services.AddSingleton(); + }); + } + + /// + /// Specify Libuv as the transport to be used by Kestrel. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. + /// + /// + /// A callback to configure Libuv options. + /// + /// + /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. + /// + public static IWebHostBuilder UseLibuv(this IWebHostBuilder hostBuilder, Action configureOptions) + { + return hostBuilder.UseLibuv().ConfigureServices(services => + { + services.Configure(configureOptions); + }); + } + } +} diff --git a/Projects/Server/Kestrel/TransportConnection.FeatureCollection.cs b/Projects/Server/Kestrel/TransportConnection.FeatureCollection.cs deleted file mode 100644 index 70033b7c9..000000000 --- a/Projects/Server/Kestrel/TransportConnection.FeatureCollection.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Buffers; -using System.Collections.Generic; -using System.IO.Pipelines; -using System.Threading; -using Microsoft.AspNetCore.Connections.Features; - -namespace Microsoft.AspNetCore.Connections -{ - public partial class TransportConnection : IConnectionIdFeature, - IConnectionTransportFeature, - IConnectionItemsFeature, - IMemoryPoolFeature, - IConnectionLifetimeFeature - { - // NOTE: When feature interfaces are added to or removed from this TransportConnection class implementation, - // then the list of `features` in the generated code project MUST also be updated. - // See also: tools/CodeGenerator/TransportConnectionFeatureCollection.cs - - MemoryPool IMemoryPoolFeature.MemoryPool => MemoryPool; - - IDuplexPipe IConnectionTransportFeature.Transport - { - get => Transport; - set => Transport = value; - } - - IDictionary IConnectionItemsFeature.Items - { - get => Items; - set => Items = value; - } - - CancellationToken IConnectionLifetimeFeature.ConnectionClosed - { - get => ConnectionClosed; - set => ConnectionClosed = value; - } - - void IConnectionLifetimeFeature.Abort() => Abort(new ConnectionAbortedException("The connection was aborted by the application via IConnectionLifetimeFeature.Abort().")); - } -} diff --git a/Projects/Server/Kestrel/TransportConnection.Generated.cs b/Projects/Server/Kestrel/TransportConnection.Generated.cs deleted file mode 100644 index eb6f2ba25..000000000 --- a/Projects/Server/Kestrel/TransportConnection.Generated.cs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections; -using System.Collections.Generic; - -using Microsoft.AspNetCore.Connections.Features; -using Microsoft.AspNetCore.Http.Features; - -namespace Microsoft.AspNetCore.Connections -{ - public partial class TransportConnection : IFeatureCollection - { - private static readonly Type IConnectionIdFeatureType = typeof(IConnectionIdFeature); - private static readonly Type IConnectionTransportFeatureType = typeof(IConnectionTransportFeature); - private static readonly Type IConnectionItemsFeatureType = typeof(IConnectionItemsFeature); - private static readonly Type IMemoryPoolFeatureType = typeof(IMemoryPoolFeature); - private static readonly Type IConnectionLifetimeFeatureType = typeof(IConnectionLifetimeFeature); - - private object _currentIConnectionIdFeature; - private object _currentIConnectionTransportFeature; - private object _currentIConnectionItemsFeature; - private object _currentIMemoryPoolFeature; - private object _currentIConnectionLifetimeFeature; - - private int _featureRevision; - - private List> MaybeExtra; - - private void FastReset() - { - _currentIConnectionIdFeature = this; - _currentIConnectionTransportFeature = this; - _currentIConnectionItemsFeature = this; - _currentIMemoryPoolFeature = this; - _currentIConnectionLifetimeFeature = this; - - } - - // Internal for testing - internal void ResetFeatureCollection() - { - FastReset(); - MaybeExtra?.Clear(); - _featureRevision++; - } - - private object ExtraFeatureGet(Type key) - { - if (MaybeExtra == null) - { - return null; - } - for (var i = 0; i < MaybeExtra.Count; i++) - { - var kv = MaybeExtra[i]; - if (kv.Key == key) - { - return kv.Value; - } - } - return null; - } - - private void ExtraFeatureSet(Type key, object value) - { - if (MaybeExtra == null) - { - MaybeExtra = new List>(2); - } - - for (var i = 0; i < MaybeExtra.Count; i++) - { - if (MaybeExtra[i].Key == key) - { - MaybeExtra[i] = new KeyValuePair(key, value); - return; - } - } - MaybeExtra.Add(new KeyValuePair(key, value)); - } - - bool IFeatureCollection.IsReadOnly => false; - - int IFeatureCollection.Revision => _featureRevision; - - object IFeatureCollection.this[Type key] - { - get - { - object feature = null; - if (key == IConnectionIdFeatureType) - { - feature = _currentIConnectionIdFeature; - } - else if (key == IConnectionTransportFeatureType) - { - feature = _currentIConnectionTransportFeature; - } - else if (key == IConnectionItemsFeatureType) - { - feature = _currentIConnectionItemsFeature; - } - else if (key == IMemoryPoolFeatureType) - { - feature = _currentIMemoryPoolFeature; - } - else if (key == IConnectionLifetimeFeatureType) - { - feature = _currentIConnectionLifetimeFeature; - } - else if (MaybeExtra != null) - { - feature = ExtraFeatureGet(key); - } - - return feature; - } - - set - { - _featureRevision++; - - if (key == IConnectionIdFeatureType) - { - _currentIConnectionIdFeature = value; - } - else if (key == IConnectionTransportFeatureType) - { - _currentIConnectionTransportFeature = value; - } - else if (key == IConnectionItemsFeatureType) - { - _currentIConnectionItemsFeature = value; - } - else if (key == IMemoryPoolFeatureType) - { - _currentIMemoryPoolFeature = value; - } - else if (key == IConnectionLifetimeFeatureType) - { - _currentIConnectionLifetimeFeature = value; - } - else - { - ExtraFeatureSet(key, value); - } - } - } - - TFeature IFeatureCollection.Get() - { - TFeature feature = default; - if (typeof(TFeature) == typeof(IConnectionIdFeature)) - { - feature = (TFeature)_currentIConnectionIdFeature; - } - else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) - { - feature = (TFeature)_currentIConnectionTransportFeature; - } - else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) - { - feature = (TFeature)_currentIConnectionItemsFeature; - } - else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) - { - feature = (TFeature)_currentIMemoryPoolFeature; - } - else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) - { - feature = (TFeature)_currentIConnectionLifetimeFeature; - } - else if (MaybeExtra != null) - { - feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); - } - - return feature; - } - - void IFeatureCollection.Set(TFeature feature) - { - _featureRevision++; - if (typeof(TFeature) == typeof(IConnectionIdFeature)) - { - _currentIConnectionIdFeature = feature; - } - else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) - { - _currentIConnectionTransportFeature = feature; - } - else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) - { - _currentIConnectionItemsFeature = feature; - } - else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) - { - _currentIMemoryPoolFeature = feature; - } - else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) - { - _currentIConnectionLifetimeFeature = feature; - } - else - { - ExtraFeatureSet(typeof(TFeature), feature); - } - } - - private IEnumerable> FastEnumerable() - { - if (_currentIConnectionIdFeature != null) - { - yield return new KeyValuePair(IConnectionIdFeatureType, _currentIConnectionIdFeature); - } - if (_currentIConnectionTransportFeature != null) - { - yield return new KeyValuePair(IConnectionTransportFeatureType, _currentIConnectionTransportFeature); - } - if (_currentIConnectionItemsFeature != null) - { - yield return new KeyValuePair(IConnectionItemsFeatureType, _currentIConnectionItemsFeature); - } - if (_currentIMemoryPoolFeature != null) - { - yield return new KeyValuePair(IMemoryPoolFeatureType, _currentIMemoryPoolFeature); - } - if (_currentIConnectionLifetimeFeature != null) - { - yield return new KeyValuePair(IConnectionLifetimeFeatureType, _currentIConnectionLifetimeFeature); - } - - if (MaybeExtra != null) - { - foreach (var item in MaybeExtra) - { - yield return item; - } - } - } - - IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator(); - } -} diff --git a/Projects/Server/Kestrel/TransportConnection.cs b/Projects/Server/Kestrel/TransportConnection.cs deleted file mode 100644 index fcd066608..000000000 --- a/Projects/Server/Kestrel/TransportConnection.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Buffers; -using System.Collections.Generic; -using System.IO.Pipelines; -using System.Net; -using System.Threading; -using Microsoft.AspNetCore.Http.Features; - -namespace Microsoft.AspNetCore.Connections -{ - public abstract partial class TransportConnection : ConnectionContext - { - private IDictionary _items; - private string _connectionId; - - public TransportConnection() - { - FastReset(); - } - - public override EndPoint LocalEndPoint { get; set; } - public override EndPoint RemoteEndPoint { get; set; } - - public override string ConnectionId - { - get => _connectionId ??= CorrelationIdGenerator.GetNextId(); - set => _connectionId = value; - } - - public override IFeatureCollection Features => this; - - public virtual MemoryPool MemoryPool { get; } - - public override IDuplexPipe Transport { get; set; } - - public IDuplexPipe Application { get; set; } - - public override IDictionary Items - { - get => _items ??= new ConnectionItems(); - set => _items = value; - } - - public override CancellationToken ConnectionClosed { get; set; } - - // DO NOT remove this override to ConnectionContext.Abort. Doing so would cause - // any TransportConnection that does not override Abort or calls base.Abort - // to stack overflow when IConnectionLifetimeFeature.Abort() is called. - // That said, all derived types should override this method should override - // this implementation of Abort because canceling pending output reads is not - // sufficient to abort the connection if there is backpressure. - public override void Abort(ConnectionAbortedException abortReason) - { - Application.Input.CancelPendingRead(); - } - } -} diff --git a/Projects/Server/KeywordList.cs b/Projects/Server/KeywordList.cs index a51a296b6..a96e6d995 100644 --- a/Projects/Server/KeywordList.cs +++ b/Projects/Server/KeywordList.cs @@ -22,7 +22,7 @@ namespace Server { public class KeywordList { - private static int[] m_EmptyInts = new int[0]; + private static readonly int[] m_EmptyInts = new int[0]; private int[] m_Keywords; public KeywordList() diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 25a9b2d16..c91fb518b 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -29,6 +29,9 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Server.Network; namespace Server @@ -56,7 +59,7 @@ namespace Server * GetTickCount and GetTickCount64 have poor resolution. * GetTickCount64 is unavailable on Windows XP and Windows Server 2003. * Stopwatch.GetTimestamp() (QueryPerformanceCounter) is high resolution, but - * somewhat expensive to call because of its defference to DateTime.Now, + * somewhat expensive to call because of its difference to DateTime.Now, * which is why Stopwatch has been used to verify HRT before calling GetTimestamp(), * enabling the usage of DateTime.UtcNow instead. */ @@ -80,8 +83,6 @@ namespace Server private static readonly Type[] m_SerialTypeArray = { typeof(Serial) }; - public static MessagePump MessagePump{ get; set; } - public static bool Profiling { get => m_Profiling; @@ -265,9 +266,7 @@ namespace Server { try { - Task.WhenAll( - MessagePump.Listeners.Select(listener => listener.Dispose()) - ).Wait(); + // Close all listeners } catch { @@ -444,9 +443,6 @@ namespace Server AssemblyHandler.Invoke("Initialize"); - // Start accepting new connections - MessagePump = new MessagePump(); - AssemblyHandler.Invoke("RegisterListeners"); timerThread.Start(); @@ -454,10 +450,18 @@ namespace Server foreach (Map m in Map.AllMaps) m.Tiles.Force(); - NetState.Initialize(); - EventSink.InvokeServerStarted(); + // Start net socket server + var host = TcpServer.CreateWebHostBuilder(new string[0]).Build(); + var life = host.Services.GetRequiredService(); + life.ApplicationStopping.Register(() => { Kill(); }); + + host.Run(); + } + + public static void RunEventLoop(IMessagePumpService messagePumpService) + { try { long last = TickCount; @@ -477,7 +481,7 @@ namespace Server ); Timer.Slice(); - MessagePump.DoWork(); + messagePumpService.DoWork(); NetState.ProcessDisposedQueue(); diff --git a/Projects/Server/Map.cs b/Projects/Server/Map.cs index ce21385e6..4751ee7f2 100644 --- a/Projects/Server/Map.cs +++ b/Projects/Server/Map.cs @@ -262,16 +262,17 @@ namespace Server private static readonly List _EmptyFixItems = new List(); private Region m_DefaultRegion; - private int m_FileIndex; + private readonly int m_FileIndex; private string m_Name; - private Sector[][] m_Sectors; + private readonly Sector[][] m_Sectors; - private int m_SectorsWidth, m_SectorsHeight; + private readonly int m_SectorsWidth; + private readonly int m_SectorsHeight; private TileMatrix m_Tiles; - private object tileLock = new object(); + private readonly object tileLock = new object(); public Map(int mapID, int mapIndex, int fileIndex, int width, int height, int season, string name, MapRules rules) { diff --git a/Projects/Server/Menus/ItemListMenu.cs b/Projects/Server/Menus/ItemListMenu.cs index 8fc57a153..fe014949e 100644 --- a/Projects/Server/Menus/ItemListMenu.cs +++ b/Projects/Server/Menus/ItemListMenu.cs @@ -41,7 +41,7 @@ namespace Server.Menus.ItemLists public class ItemListMenu : IMenu { private static int m_NextSerial; - private int m_Serial; + private readonly int m_Serial; public ItemListMenu(string question, ItemListEntry[] entries) { diff --git a/Projects/Server/Menus/QuestionMenu.cs b/Projects/Server/Menus/QuestionMenu.cs index 0fe6a5aa2..2d403f537 100644 --- a/Projects/Server/Menus/QuestionMenu.cs +++ b/Projects/Server/Menus/QuestionMenu.cs @@ -25,7 +25,7 @@ namespace Server.Menus.Questions public class QuestionMenu : IMenu { private static int m_NextSerial; - private int m_Serial; + private readonly int m_Serial; public QuestionMenu(string question, string[] answers) { diff --git a/Projects/Server/Mobile.cs b/Projects/Server/Mobile.cs index ec0a5e42a..bc75d05fc 100644 --- a/Projects/Server/Mobile.cs +++ b/Projects/Server/Mobile.cs @@ -56,7 +56,7 @@ namespace Server public class TimedSkillMod : SkillMod { - private DateTime m_Expire; + private readonly DateTime m_Expire; public TimedSkillMod(SkillName skill, bool relative, double value, TimeSpan delay) : this(skill, relative, value, DateTime.UtcNow + delay) @@ -64,21 +64,16 @@ namespace Server } public TimedSkillMod(SkillName skill, bool relative, double value, DateTime expire) - : base(skill, relative, value) - { + : base(skill, relative, value) => m_Expire = expire; - } - public override bool CheckCondition() - { - return DateTime.UtcNow < m_Expire; - } + public override bool CheckCondition() => DateTime.UtcNow < m_Expire; } public class EquippedSkillMod : SkillMod { - private Item m_Item; - private Mobile m_Mobile; + private readonly Item m_Item; + private readonly Mobile m_Mobile; public EquippedSkillMod(SkillName skill, bool relative, double value, Item item, Mobile mobile) : base(skill, relative, value) @@ -87,10 +82,7 @@ namespace Server m_Mobile = mobile; } - public override bool CheckCondition() - { - return !m_Item.Deleted && !m_Mobile.Deleted && m_Item.Parent == m_Mobile; - } + public override bool CheckCondition() => !m_Item.Deleted && !m_Mobile.Deleted && m_Item.Parent == m_Mobile; } public class DefaultSkillMod : SkillMod @@ -100,10 +92,7 @@ namespace Server { } - public override bool CheckCondition() - { - return true; - } + public override bool CheckCondition() => true; } public abstract class SkillMod @@ -265,8 +254,8 @@ namespace Server public class StatMod { - private DateTime m_Added; - private TimeSpan m_Duration; + private readonly DateTime m_Added; + private readonly TimeSpan m_Duration; public StatMod(StatType type, string name, int offset, TimeSpan duration) { @@ -296,10 +285,7 @@ namespace Server public class DamageEntry { - public DamageEntry(Mobile damager) - { - Damager = damager; - } + public DamageEntry(Mobile damager) => Damager = damager; public Mobile Damager{ get; } @@ -425,10 +411,8 @@ namespace Server public class MobileNotConnectedException : Exception { public MobileNotConnectedException(Mobile source, string message) - : base(message) - { + : base(message) => Source = source.ToString(); - } } #region Delegates @@ -460,8 +444,8 @@ namespace Server /// public class Mobile : IHued, IComparable, ISerializable, ISpawnable, IPropertyListObject { - private BufferWriter m_SaveBuffer; - public BufferWriter SaveBuffer { get { return m_SaveBuffer; } } + private readonly BufferWriter m_SaveBuffer; + public BufferWriter SaveBuffer => m_SaveBuffer; private const int WarmodeCatchCount = 4; // Allow four warmode changes in 0.5 seconds, any more will be delay for two seconds @@ -470,23 +454,23 @@ namespace Server private static readonly TimeSpan WarmodeSpamDelay = TimeSpan.FromSeconds(Core.SE ? 4.0 : 2.0); - private static Packet[][] m_MovingPacketCache = new Packet[][] + private static readonly Packet[][] m_MovingPacketCache = new Packet[][] { new Packet[8], new Packet[8] }; - private static List m_MoveList = new List(); - private static List m_MoveClientList = new List(); + private static readonly List m_MoveList = new List(); + private static readonly List m_MoveClientList = new List(); - private static object m_GhostMutateContext = new object(); + private static readonly object m_GhostMutateContext = new object(); - private static List m_Hears = new List(); - private static List m_OnSpeech = new List(); + private static readonly List m_Hears = new List(); + private static readonly List m_OnSpeech = new List(); public static bool m_DefaultShowVisibleDamage, m_DefaultCanSeeVisibleDamage; - private static string[] m_AccessLevelNames = + private static readonly string[] m_AccessLevelNames = { "a player", "a counselor", @@ -497,7 +481,7 @@ namespace Server "an owner" }; - private static int[] m_InvalidBodies = + private static readonly int[] m_InvalidBodies = { 32, 95, @@ -506,12 +490,12 @@ namespace Server 198 }; - private static Queue m_DeltaQueue = new Queue(); - private static Queue m_DeltaQueueR = new Queue(); + private static readonly Queue m_DeltaQueue = new Queue(); + private static readonly Queue m_DeltaQueueR = new Queue(); private static bool _processing; - private static string[] m_GuildTypes = + private static readonly string[] m_GuildTypes = { "", " (Chaos)", @@ -4367,10 +4351,7 @@ namespace Server /// /// /// True to continue with death, false to override it. - public virtual bool OnBeforeDeath() - { - return true; - } + public virtual bool OnBeforeDeath() => true; /// /// Overridable. Event invoked after the Mobile is killed. Primarily, this method is responsible for @@ -5089,10 +5070,7 @@ namespace Server public static Mobile GetDamagerFrom(DamageEntry de) => de?.Damager; - public Mobile FindMostRecentDamager(bool allowSelf) - { - return GetDamagerFrom(FindMostRecentDamageEntry(allowSelf)); - } + public Mobile FindMostRecentDamager(bool allowSelf) => GetDamagerFrom(FindMostRecentDamageEntry(allowSelf)); public DamageEntry FindMostRecentDamageEntry(bool allowSelf) { @@ -5199,10 +5177,7 @@ namespace Server return null; } - public virtual Mobile GetDamageMaster(Mobile damagee) - { - return null; - } + public virtual Mobile GetDamageMaster(Mobile damagee) => null; public virtual DamageEntry RegisterDamage(int amount, Mobile from) { @@ -5251,10 +5226,7 @@ namespace Server Damage(amount, null); } - public virtual bool CanBeDamaged() - { - return !m_Blessed; - } + public virtual bool CanBeDamaged() => !m_Blessed; public virtual void Damage(int amount, Mobile from) { @@ -5906,15 +5878,9 @@ namespace Server } } - public static string GetAccessLevelName(AccessLevel level) - { - return m_AccessLevelNames[(int)level]; - } + public static string GetAccessLevelName(AccessLevel level) => m_AccessLevelNames[(int)level]; - public virtual bool CanPaperdollBeOpenedBy(Mobile from) - { - return Body.IsHuman || Body.IsGhost || IsBodyMod; - } + public virtual bool CanPaperdollBeOpenedBy(Mobile from) => Body.IsHuman || Body.IsGhost || IsBodyMod; public virtual void GetChildContextMenuEntries(Mobile from, List list, Item item) { @@ -6215,10 +6181,7 @@ namespace Server eable.Free(); } - public bool Send(Packet p) - { - return Send(p, false); - } + public bool Send(Packet p) => Send(p, false); public bool Send(Packet p, bool throwOnOffline) { @@ -6254,10 +6217,7 @@ namespace Server RevealingAction(); } - public virtual bool HandlesOnSpeech(Mobile from) - { - return false; - } + public virtual bool HandlesOnSpeech(Mobile from) => false; /// /// Overridable. Virtual event invoked when the Mobile hears speech. This event will only be invoked if @@ -6337,10 +6297,7 @@ namespace Server m_Direction = dir; } - public virtual int GetSeason() - { - return m_Map?.Season ?? 1; - } + public virtual int GetSeason() => m_Map?.Season ?? 1; public virtual int GetPacketFlags() { @@ -6505,10 +6462,7 @@ namespace Server m_AccessLevel > AccessLevel.Player || m.Warmode); } - public virtual bool CanBeRenamedBy(Mobile from) - { - return from.AccessLevel >= AccessLevel.GameMaster && from.m_AccessLevel > m_AccessLevel; - } + public virtual bool CanBeRenamedBy(Mobile from) => from.AccessLevel >= AccessLevel.GameMaster && from.m_AccessLevel > m_AccessLevel; public virtual void OnGuildTitleChange(string oldTitle) { @@ -6738,10 +6692,7 @@ namespace Server public bool HasFreeHand() => FindItemOnLayer(Layer.TwoHanded) == null; - public virtual IWeapon GetDefaultWeapon() - { - return DefaultWeapon; - } + public virtual IWeapon GetDefaultWeapon() => DefaultWeapon; public BankBox FindBankNoCreate() { @@ -6827,21 +6778,13 @@ namespace Server return true; } - public virtual bool CheckLift(Mobile from, Item item, ref LRReason reject) - { - return true; - } + public virtual bool CheckLift(Mobile from, Item item, ref LRReason reject) => true; - public virtual bool CheckNonlocalLift(Mobile from, Item item) - { - return from == this || from.AccessLevel > AccessLevel && from.AccessLevel >= AccessLevel.GameMaster; - } + public virtual bool CheckNonlocalLift(Mobile from, Item item) => from == this || from.AccessLevel > AccessLevel && from.AccessLevel >= AccessLevel.GameMaster; public virtual bool CheckTrade(Mobile to, Item item, SecureTradeContainer cont, bool message, bool checkItems, - int plusItems, int plusWeight) - { - return true; - } + int plusItems, int plusWeight) => + true; public virtual bool OpenTrade(Mobile from, Item offer = null) { @@ -6927,10 +6870,7 @@ namespace Server /// return base.OnDragLift( item ); /// } /// - public virtual bool OnDragLift(Item item) - { - return true; - } + public virtual bool OnDragLift(Item item) => true; /// /// Overridable. Virtual event invoked when the Mobile attempts to drop into a @@ -6940,40 +6880,28 @@ namespace Server /// . /// /// True if the drop is allowed, false if otherwise. - public virtual bool OnDroppedItemInto(Item item, Container container, Point3D loc) - { - return true; - } + public virtual bool OnDroppedItemInto(Item item, Container container, Point3D loc) => true; /// /// Overridable. Virtual event invoked when the Mobile attempts to drop directly onto another /// , . This is the case of stacking items. /// /// True if the drop is allowed, false if otherwise. - public virtual bool OnDroppedItemOnto(Item item, Item target) - { - return true; - } + public virtual bool OnDroppedItemOnto(Item item, Item target) => true; /// /// Overridable. Virtual event invoked when the Mobile attempts to drop into another /// , . The target item is most likely a . /// /// True if the drop is allowed, false if otherwise. - public virtual bool OnDroppedItemToItem(Item item, Item target, Point3D loc) - { - return true; - } + public virtual bool OnDroppedItemToItem(Item item, Item target, Point3D loc) => true; /// /// Overridable. Virtual event invoked when the Mobile attempts to give to a Mobile ( /// ). /// /// True if the drop is allowed, false if otherwise. - public virtual bool OnDroppedItemToMobile(Item item, Mobile target) - { - return true; - } + public virtual bool OnDroppedItemToMobile(Item item, Mobile target) => true; /// /// Overridable. Virtual event invoked when the Mobile attempts to drop to the world at a @@ -6983,10 +6911,7 @@ namespace Server /// . /// /// True if the drop is allowed, false if otherwise. - public virtual bool OnDroppedItemToWorld(Item item, Point3D location) - { - return true; - } + public virtual bool OnDroppedItemToWorld(Item item, Point3D location) => true; /// /// Overridable. Virtual event when successfully uses while it's on this @@ -6997,15 +6922,9 @@ namespace Server { } - public virtual bool CheckNonlocalDrop(Mobile from, Item item, Item target) - { - return from == this || from.AccessLevel > AccessLevel && from.AccessLevel >= AccessLevel.GameMaster; - } + public virtual bool CheckNonlocalDrop(Mobile from, Item item, Item target) => from == this || from.AccessLevel > AccessLevel && from.AccessLevel >= AccessLevel.GameMaster; - public virtual bool CheckItemUse(Mobile from, Item item) - { - return true; - } + public virtual bool CheckItemUse(Mobile from, Item item) => true; /// /// Overridable. Virtual event invoked when successfully lifts from this @@ -7016,15 +6935,9 @@ namespace Server { } - public virtual bool AllowItemUse(Item item) - { - return true; - } + public virtual bool AllowItemUse(Item item) => true; - public virtual bool AllowEquipFrom(Mobile mob) - { - return mob == this || mob.AccessLevel >= AccessLevel.GameMaster && mob.AccessLevel > AccessLevel; - } + public virtual bool AllowEquipFrom(Mobile mob) => mob == this || mob.AccessLevel >= AccessLevel.GameMaster && mob.AccessLevel > AccessLevel; public virtual bool EquipItem(Item item) { @@ -7304,13 +7217,10 @@ namespace Server private class MovementRecord { - private static Queue m_InstancePool = new Queue(); + private static readonly Queue m_InstancePool = new Queue(); public long m_End; - private MovementRecord(long end) - { - m_End = end; - } + private MovementRecord(long end) => m_End = end; public static MovementRecord NewInstance(long end) { @@ -7343,7 +7253,7 @@ namespace Server private class WarmodeTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public WarmodeTimer(Mobile m, bool value) : base(WarmodeSpamDelay) @@ -7365,13 +7275,11 @@ namespace Server private class SimpleTarget : Target { - private TargetCallback m_Callback; + private readonly TargetCallback m_Callback; public SimpleTarget(int range, TargetFlags flags, bool allowGround, TargetCallback callback) - : base(range, allowGround, flags) - { + : base(range, allowGround, flags) => m_Callback = callback; - } protected override void OnTarget(Mobile from, object targeted) { @@ -7381,8 +7289,8 @@ namespace Server private class SimpleStateTarget : Target { - private TargetStateCallback m_Callback; - private T m_State; + private readonly TargetStateCallback m_Callback; + private readonly T m_State; public SimpleStateTarget(int range, TargetFlags flags, bool allowGround, TargetStateCallback callback, T state) @@ -7400,13 +7308,11 @@ namespace Server private class AutoManifestTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public AutoManifestTimer(Mobile m, TimeSpan delay) - : base(delay) - { + : base(delay) => m_Mobile = m; - } protected override void OnTick() { @@ -7419,17 +7325,11 @@ namespace Server { private static LocationComparer m_Instance; - public LocationComparer(IEntity relativeTo) - { - RelativeTo = relativeTo; - } + public LocationComparer(IEntity relativeTo) => RelativeTo = relativeTo; public IEntity RelativeTo{ get; set; } - public int Compare(IEntity x, IEntity y) - { - return GetDistance(x) - GetDistance(y); - } + public int Compare(IEntity x, IEntity y) => GetDistance(x) - GetDistance(y); public static LocationComparer GetInstance(IEntity relativeTo) { @@ -7454,15 +7354,9 @@ namespace Server } } - int IComparable.CompareTo(IEntity other) - { - return other == null ? -1 : Serial.CompareTo(other.Serial); - } + int IComparable.CompareTo(IEntity other) => other == null ? -1 : Serial.CompareTo(other.Serial); - public int CompareTo(Mobile other) - { - return other == null ? -1 : Serial.CompareTo(other.Serial); - } + public int CompareTo(Mobile other) => other == null ? -1 : Serial.CompareTo(other.Serial); #region Handlers @@ -7581,7 +7475,7 @@ namespace Server private class ManaTimer : Timer { - private Mobile m_Owner; + private readonly Mobile m_Owner; public ManaTimer(Mobile m) : base(GetManaRegenRate(m), GetManaRegenRate(m)) @@ -7601,7 +7495,7 @@ namespace Server private class HitsTimer : Timer { - private Mobile m_Owner; + private readonly Mobile m_Owner; public HitsTimer(Mobile m) : base(GetHitsRegenRate(m), GetHitsRegenRate(m)) @@ -7621,7 +7515,7 @@ namespace Server private class StamTimer : Timer { - private Mobile m_Owner; + private readonly Mobile m_Owner; public StamTimer(Mobile m) : base(GetStamRegenRate(m), GetStamRegenRate(m)) @@ -7641,7 +7535,7 @@ namespace Server private class LogoutTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public LogoutTimer(Mobile m) : base(TimeSpan.FromDays(1.0)) @@ -7666,7 +7560,7 @@ namespace Server private class ParalyzedTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public ParalyzedTimer(Mobile m, TimeSpan duration) : base(duration) @@ -7683,7 +7577,7 @@ namespace Server private class FrozenTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public FrozenTimer(Mobile m, TimeSpan duration) : base(duration) @@ -7700,7 +7594,7 @@ namespace Server private class CombatTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public CombatTimer(Mobile m) : base(TimeSpan.FromSeconds(0.0), TimeSpan.FromSeconds(0.01)) { @@ -7744,7 +7638,7 @@ namespace Server private class ExpireCombatantTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public ExpireCombatantTimer(Mobile m) : base(TimeSpan.FromMinutes(1.0)) @@ -7763,7 +7657,7 @@ namespace Server private class ExpireCriminalTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public ExpireCriminalTimer(Mobile m) : base(ExpireCriminalDelay) @@ -7780,7 +7674,7 @@ namespace Server private class ExpireAggressorsTimer : Timer { - private Mobile m_Mobile; + private readonly Mobile m_Mobile; public ExpireAggressorsTimer(Mobile m) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) @@ -7804,9 +7698,9 @@ namespace Server private class SimplePrompt : Prompt { - private PromptCallback m_Callback; - private bool m_CallbackHandlesCancel; - private PromptCallback m_CancelCallback; + private readonly PromptCallback m_Callback; + private readonly bool m_CallbackHandlesCancel; + private readonly PromptCallback m_CancelCallback; public SimplePrompt(PromptCallback callback, PromptCallback cancelCallback) { @@ -7830,9 +7724,7 @@ namespace Server if (m_CallbackHandlesCancel && m_Callback != null) m_Callback(from, ""); else - { - m_CancelCallback?.Invoke(from, ""); - } + m_CancelCallback?.Invoke(@from, ""); } } @@ -7848,10 +7740,10 @@ namespace Server private class SimpleStatePrompt : Prompt { - private PromptStateCallback m_Callback; - private PromptStateCallback m_CancelCallback; + private readonly PromptStateCallback m_Callback; + private readonly PromptStateCallback m_CancelCallback; - private T m_State; + private readonly T m_State; public SimpleStatePrompt(PromptStateCallback callback, PromptStateCallback cancelCallback, T state) { @@ -7962,10 +7854,7 @@ namespace Server #region Get*InRange - public IPooledEnumerable GetItemsInRange(int range) - { - return GetItemsInRange(range); - } + public IPooledEnumerable GetItemsInRange(int range) => GetItemsInRange(range); public IPooledEnumerable GetItemsInRange(int range) where T : Item { @@ -7987,10 +7876,7 @@ namespace Server return map.GetObjectsInRange(m_Location, range); } - public IPooledEnumerable GetMobilesInRange(int range) - { - return GetMobilesInRange(range); - } + public IPooledEnumerable GetMobilesInRange(int range) => GetMobilesInRange(range); public IPooledEnumerable GetMobilesInRange(int range) where T : Mobile { @@ -8177,10 +8063,7 @@ namespace Server return true; } - public bool HasGump() where T : Gump - { - return FindGump() != null; - } + public bool HasGump() where T : Gump => FindGump() != null; public bool SendGump(Gump g) { @@ -8204,15 +8087,9 @@ namespace Server #region Beneficial Checks/Actions - public virtual bool CanBeBeneficial(Mobile target) - { - return CanBeBeneficial(target, true, false); - } + public virtual bool CanBeBeneficial(Mobile target) => CanBeBeneficial(target, true, false); - public virtual bool CanBeBeneficial(Mobile target, bool message) - { - return CanBeBeneficial(target, message, false); - } + public virtual bool CanBeBeneficial(Mobile target, bool message) => CanBeBeneficial(target, message, false); public virtual bool CanBeBeneficial(Mobile target, bool message, bool allowDead) { @@ -9001,10 +8878,7 @@ namespace Server /// /// /// - public virtual bool CheckPoisonImmunity(Mobile from, Poison poison) - { - return false; - } + public virtual bool CheckPoisonImmunity(Mobile from, Poison poison) => false; /// /// Overridable. Called from , this method checks if the Mobile is already poisoned by some @@ -9014,10 +8888,7 @@ namespace Server /// /// /// - public virtual bool CheckHigherPoison(Mobile from, Poison poison) - { - return m_Poison != null && m_Poison.Level >= poison.Level; - } + public virtual bool CheckHigherPoison(Mobile from, Poison poison) => m_Poison != null && m_Poison.Level >= poison.Level; /// /// Overridable. Attempts to apply poison to the Mobile. Checks are made such that no @@ -9090,10 +8961,7 @@ namespace Server /// /// /// - public virtual bool CheckCure(Mobile from) - { - return true; - } + public virtual bool CheckCure(Mobile from) => true; /// /// Overridable. Virtual event invoked when a call to succeeded. @@ -9166,10 +9034,7 @@ namespace Server [CommandProperty(AccessLevel.GameMaster)] public int FacialHairItemID { - get - { - return m_FacialHair?.ItemID ?? 0; - } + get => m_FacialHair?.ItemID ?? 0; set { if (m_FacialHair == null && value > 0) @@ -9327,15 +9192,9 @@ namespace Server return ret; } - public Direction GetDirectionTo(Point2D p) - { - return GetDirectionTo(p.m_X, p.m_Y); - } + public Direction GetDirectionTo(Point2D p) => GetDirectionTo(p.m_X, p.m_Y); - public Direction GetDirectionTo(Point3D p) - { - return GetDirectionTo(p.m_X, p.m_Y); - } + public Direction GetDirectionTo(Point3D p) => GetDirectionTo(p.m_X, p.m_Y); public Direction GetDirectionTo(IPoint2D p) { diff --git a/Projects/Server/MultiData.cs b/Projects/Server/MultiData.cs index db4a68048..7dffc4cbd 100644 --- a/Projects/Server/MultiData.cs +++ b/Projects/Server/MultiData.cs @@ -25,10 +25,12 @@ namespace Server { public static class MultiData { - private static MultiComponentList[] m_Components; + private static readonly MultiComponentList[] m_Components; - private static FileStream m_Index, m_Stream; - private static BinaryReader m_IndexReader, m_StreamReader; + private static readonly FileStream m_Index; + private static readonly FileStream m_Stream; + private static readonly BinaryReader m_IndexReader; + private static readonly BinaryReader m_StreamReader; static MultiData() { diff --git a/Projects/Server/Network/Listener.cs b/Projects/Server/Network/Listener.cs deleted file mode 100644 index f0b929748..000000000 --- a/Projects/Server/Network/Listener.cs +++ /dev/null @@ -1,177 +0,0 @@ -/*************************************************************************** - * Listener.cs - * ------------------- - * begin : May 1, 2002 - * copyright : (C) The RunUO Software Team - * email : info@runuo.com - * - * $Id$ - * - ***************************************************************************/ - -/*************************************************************************** - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - ***************************************************************************/ - -using System; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Libuv; -using Libuv.Internal; -using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Logging; - -namespace Server.Network -{ - public class Listener - { - private static readonly LibuvFunctions functions = new LibuvFunctions(); - - private readonly IPEndPoint m_EndPoint; - private LibuvConnectionListener m_Listener; - - public Listener(IPEndPoint ipep) - { - m_EndPoint = ipep; - LibuvTransportContext transport = new LibuvTransportContext - { - Options = new LibuvTransportOptions(), - AppLifetime = new ApplicationLifetime( - LoggerFactory.Create(builder => { builder.AddConsole(); }).CreateLogger() - ), - Log = new LibuvTrace(LoggerFactory.Create(builder => { builder.AddConsole(); }).CreateLogger("network")) - }; - - m_Listener = new LibuvConnectionListener(functions, transport, ipep); - } - - public virtual async Task Start(MessagePump pump) - { - try - { - await m_Listener.BindAsync(); - } - catch (AddressInUseException) - { - Console.WriteLine("Listener Failed: {0}:{1} (In Use)", m_EndPoint.Address, m_EndPoint.Port); - m_Listener = null; - return; - } - catch (Exception e) - { - Console.WriteLine("Listener Exception:"); - Console.WriteLine(e); - - m_Listener = null; - return; - } - - DisplayListener(); - - while (true) - { - ConnectionContext context; - try - { - context = await m_Listener.AcceptAsync(); - } - catch (SocketException ex) - { - NetState.TraceException(ex); - continue; - } - - if (VerifySocket(context)) - _ = new NetState(context, pump); - else - Release(context); - } - } - - private void DisplayListener() - { - if (!(m_Listener.EndPoint is IPEndPoint ipep)) - return; - - if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any)) - { - NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); - foreach (NetworkInterface adapter in adapters) - { - IPInterfaceProperties properties = adapter.GetIPProperties(); - foreach (UnicastIPAddressInformation unicast in properties.UnicastAddresses) - if (ipep.AddressFamily == unicast.Address.AddressFamily) - Console.WriteLine("Listening: {0}:{1}", unicast.Address, ipep.Port); - } - } - else - Console.WriteLine("Listening: {0}:{1}", ipep.Address, ipep.Port); - } - - private static bool VerifySocket(ConnectionContext context) - { - try - { - SocketConnectEventArgs args = new SocketConnectEventArgs(context); - - EventSink.InvokeSocketConnect(args); - - return args.AllowConnection; - } - catch (Exception ex) - { - NetState.TraceException(ex); - return false; - } - } - - private static void Release(ConnectionContext context) - { - try - { - context.Abort(new ConnectionAbortedException("Failed socket verification.")); - } - catch (Exception ex) - { - NetState.TraceException(ex); - } - - try - { - // TODO: Is this needed? - context.DisposeAsync(); - } - catch (Exception ex) - { - NetState.TraceException(ex); - } - } - - public async Task Dispose() - { - LibuvConnectionListener listener = Interlocked.Exchange(ref m_Listener, null); - if (listener != null) - try - { - await listener.UnbindAsync(); - await listener.DisposeAsync(); - } - catch (Exception ex) - { - Console.WriteLine("Listener: Failed to dispose."); - Console.WriteLine(ex); - } - - GC.SuppressFinalize(this); - } - } -} diff --git a/Projects/Server/Network/MessagePump.cs b/Projects/Server/Network/MessagePumpService.cs similarity index 76% rename from Projects/Server/Network/MessagePump.cs rename to Projects/Server/Network/MessagePumpService.cs index 91b24bc92..aaaac3a0b 100644 --- a/Projects/Server/Network/MessagePump.cs +++ b/Projects/Server/Network/MessagePumpService.cs @@ -19,26 +19,20 @@ * ***************************************************************************/ -using System; using System.Buffers; using System.Collections.Concurrent; -using System.Net; namespace Server.Network { - public class MessagePump + public interface IMessagePumpService { - private ConcurrentQueue m_WorkQueue = new ConcurrentQueue(); - public Listener[] Listeners => new Listener[0]; + void QueueWork(NetState ns, IMemoryOwner memOwner, OnPacketReceive onReceive); + void DoWork(); + } - public void AddListener(IPEndPoint ipep) - { - Listener[] listeners = new Listener[Listeners.Length + 1]; - Array.Copy(Listeners, listeners, Listeners.Length); - Listener listener = new Listener(ipep); - _ = listener.Start(this); - listeners[Listeners.Length] = listener; - } + public class MessagePumpService : IMessagePumpService + { + private readonly ConcurrentQueue m_WorkQueue = new ConcurrentQueue(); public void QueueWork(NetState ns, IMemoryOwner memOwner, OnPacketReceive onReceive) { diff --git a/Projects/Server/Network/NetState.cs b/Projects/Server/Network/NetState.cs index a37902ffa..3f9d9fc10 100644 --- a/Projects/Server/Network/NetState.cs +++ b/Projects/Server/Network/NetState.cs @@ -19,7 +19,6 @@ ***************************************************************************/ using System; -using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -91,7 +90,7 @@ namespace Server.Network public class NetState : IComparable { - private string m_ToString; + private readonly string m_ToString; private ClientVersion m_Version; public DateTime ConnectedOn { get; } @@ -105,10 +104,11 @@ namespace Server.Network public IPAddress Address { get; } - private static AsyncState m_PauseState = new AsyncState(true); - private static AsyncState m_ResumeState = new AsyncState(false); + private static readonly AsyncState m_PauseState = new AsyncState(true); + private static readonly AsyncState m_ResumeState = new AsyncState(false); private static AsyncState m_AsyncState = m_ResumeState; + public static AsyncState AsyncState => m_AsyncState; public IPacketEncoder PacketEncoder { get; set; } @@ -279,9 +279,6 @@ namespace Server.Network public List Menus { get; private set; } - public PipeReader RecvPipe => Connection.Transport.Input; - public PipeWriter SendPipe => Connection.Transport.Output; - public static int GumpCap { get; set; } = 512; public static int HuePickerCap { get; set; } = 512; @@ -399,11 +396,7 @@ namespace Server.Network public override string ToString() => m_ToString; - public static List Instances { get; } = new List(); - - public void SetConnectionAlive() => m_NextCheckActivity = Core.TickCount + 60000; - - public NetState(ConnectionContext connection, MessagePump pump) + public NetState(ConnectionContext connection) { Connection = connection; Seeded = false; @@ -412,10 +405,6 @@ namespace Server.Network Menus = new List(); Trades = new List(); - SetConnectionAlive(); - - Instances.Add(this); - try { Address = Utility.Intern(((IPEndPoint)Connection.RemoteEndPoint).Address); @@ -429,11 +418,6 @@ namespace Server.Network } ConnectedOn = DateTime.UtcNow; - Console.WriteLine("Client: {0}: Connected. [{1} Online]", this, Instances.Count); - -#pragma warning disable 4014 - ProcessRecvs(pump); -#pragma warning restore 4014 CreatedCallback?.Invoke(this); } @@ -448,7 +432,7 @@ namespace Server.Network m_AsyncState = Interlocked.Exchange(ref m_AsyncState, m_ResumeState); } - public virtual void Send(Packet p) + public virtual async void Send(Packet p) { if (Connection == null || BlockAllPackets) { @@ -456,19 +440,29 @@ namespace Server.Network return; } + var outPipe = Connection.Transport.Output; + try { - ReadOnlySpan buffer = p.Compile(CompressionEnabled, out int length); + //TODO: Rented memory + ReadOnlyMemory buffer = p.Compile(CompressionEnabled, out int length); - if (buffer.Length <= 0 || length <= 0) - { - p.OnSend(); - return; - } + if (buffer.Length > 0 && length > 0) + try + { + FlushResult result = await outPipe.WriteAsync(buffer.Slice(0, length)); - buffer.Slice(0, length).CopyTo(SendPipe.GetSpan(length)); - SendPipe.Advance(length); - SendPipe.FlushAsync().GetAwaiter().GetResult(); + if (result.IsCanceled || result.IsCompleted) + { + Dispose(); + return; + } + } + catch + { + Dispose(); + // ignored + } p.OnSend(); } @@ -497,48 +491,10 @@ namespace Server.Network return false; } - private async Task ProcessRecvs(MessagePump pump) - { - while (true) - { - ReadResult result = await RecvPipe.ReadAsync(); - ReadOnlySequence seq = result.Buffer; - - if (seq.IsEmpty) - break; - - SetConnectionAlive(); - - int pos = PacketHandlers.ProcessPacket(pump, this, seq); - - if (pos <= 0) - break; - - RecvPipe.AdvanceTo(seq.Slice(0, pos).End); - - if (result.IsCompleted || result.IsCanceled) - break; - } - - RecvPipe.Complete(); - Dispose(); - } - public PacketHandler GetHandler(int packetID) => ContainerGridLines ? PacketHandlers.Get6017Handler(packetID) : PacketHandlers.GetHandler(packetID); - private long m_NextCheckActivity; - - public void CheckAlive(long curTicks) - { - if (Connection == null || m_NextCheckActivity - curTicks >= 0) - return; - - Console.WriteLine("Client: {0}: Disconnecting due to inactivity...", this); - Dispose(); - } - public static void TraceException(Exception ex) { if (!Core.Debug) @@ -586,30 +542,7 @@ namespace Server.Network m_Disposed.Enqueue(this); } - public static void Initialize() - { - Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.5), CheckAllAlive); - } - - public static void CheckAllAlive() - { - try - { - long curTicks = Core.TickCount; - - if (Instances.Count >= 1024) - Parallel.ForEach(Instances, ns => ns.CheckAlive(curTicks)); - else - for (int i = 0; i < Instances.Count; ++i) - Instances[i].CheckAlive(curTicks); - } - catch (Exception ex) - { - TraceException(ex); - } - } - - private static ConcurrentQueue m_Disposed = new ConcurrentQueue(); + private static readonly ConcurrentQueue m_Disposed = new ConcurrentQueue(); public static void ProcessDisposedQueue() { @@ -636,12 +569,10 @@ namespace Server.Network ns.ServerInfo = null; ns.CityInfo = null; - Instances.Remove(ns); - if (a != null) - ns.WriteConsole("Disconnected. [{0} Online] [{1}]", Instances.Count, a); + ns.WriteConsole("Disconnected. [{0} Online] [{1}]", TcpServer.Instances.Count, a); else - ns.WriteConsole("Disconnected. [{0} Online]", Instances.Count); + ns.WriteConsole("Disconnected. [{0} Online]", TcpServer.Instances.Count); } } diff --git a/Projects/Server/Network/Packet.cs b/Projects/Server/Network/Packet.cs index bb96362d5..6f6d54f7c 100644 --- a/Projects/Server/Network/Packet.cs +++ b/Projects/Server/Network/Packet.cs @@ -34,7 +34,7 @@ namespace Server.Network private byte[] m_CompiledBuffer; private int m_CompiledLength; - private int m_Length; + private readonly int m_Length; private State m_State; protected PacketWriter m_Stream; diff --git a/Projects/Server/Network/PacketHandlers.cs b/Projects/Server/Network/PacketHandlers.cs index 0f76afa73..a47cf99da 100644 --- a/Projects/Server/Network/PacketHandlers.cs +++ b/Projects/Server/Network/PacketHandlers.cs @@ -62,17 +62,17 @@ namespace Server.Network private const int BadUOTD = unchecked((int)0xFFCEFFCE); private const int m_AuthIDWindowSize = 128; - private static PacketHandler[] m_6017Handlers; + private static readonly PacketHandler[] m_6017Handlers; - private static PacketHandler[] m_ExtendedHandlersLow; - private static Dictionary m_ExtendedHandlersHigh; + private static readonly PacketHandler[] m_ExtendedHandlersLow; + private static readonly Dictionary m_ExtendedHandlersHigh; - private static EncodedPacketHandler[] m_EncodedHandlersLow; - private static Dictionary m_EncodedHandlersHigh; + private static readonly EncodedPacketHandler[] m_EncodedHandlersLow; + private static readonly Dictionary m_EncodedHandlersHigh; - private static int[] m_EmptyInts = new int[0]; + private static readonly int[] m_EmptyInts = new int[0]; - private static KeywordList m_KeywordList = new KeywordList(); + private static readonly KeywordList m_KeywordList = new KeywordList(); public static int[] m_ValidAnimations = { @@ -91,7 +91,7 @@ namespace Server.Network public static PlayCharCallback ThirdPartyAuthCallback = null, ThirdPartyHackedCallback = null; - private static Dictionary m_AuthIDWindow = + private static readonly Dictionary m_AuthIDWindow = new Dictionary(m_AuthIDWindowSize); static PacketHandlers() @@ -277,9 +277,9 @@ namespace Server.Network ph.ThrottleCallback = t; } - private static MemoryPool _memoryPool = SlabMemoryPoolFactory.Create(); + private static readonly MemoryPool _memoryPool = SlabMemoryPoolFactory.Create(); - public static int ProcessPacket(MessagePump pump, NetState ns, in ReadOnlySequence seq) + public static int ProcessPacket(IMessagePumpService pump, NetState ns, in ReadOnlySequence seq) { PacketReader r = new PacketReader(seq); @@ -2495,8 +2495,8 @@ namespace Server.Network private class LoginTimer : Timer { - private Mobile m_Mobile; - private NetState m_State; + private readonly Mobile m_Mobile; + private readonly NetState m_State; public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) { @@ -2507,7 +2507,10 @@ namespace Server.Network protected override void OnTick() { if (m_State == null) + { Stop(); + return; + } if (m_State.Version != null) { diff --git a/Projects/Server/Network/PacketWriter.cs b/Projects/Server/Network/PacketWriter.cs index 6587e3a7a..3419171b9 100644 --- a/Projects/Server/Network/PacketWriter.cs +++ b/Projects/Server/Network/PacketWriter.cs @@ -30,12 +30,12 @@ namespace Server.Network /// public class PacketWriter { - private static ConcurrentQueue m_Pool = new ConcurrentQueue(); + private static readonly ConcurrentQueue m_Pool = new ConcurrentQueue(); /// /// Internal format buffer. /// - private byte[] m_Buffer = new byte[4]; + private readonly byte[] m_Buffer = new byte[4]; private int m_Capacity; diff --git a/Projects/Server/Network/Packets.cs b/Projects/Server/Network/Packets.cs index 920e07326..16e20b13a 100644 --- a/Projects/Server/Network/Packets.cs +++ b/Projects/Server/Network/Packets.cs @@ -540,7 +540,7 @@ namespace Server.Network public sealed class ChangeUpdateRange : Packet { - private static ChangeUpdateRange[] m_Cache = new ChangeUpdateRange[0x100]; + private static readonly ChangeUpdateRange[] m_Cache = new ChangeUpdateRange[0x100]; public ChangeUpdateRange(int range) : base(0xC8, 2) { @@ -811,7 +811,7 @@ namespace Server.Network public sealed class GlobalLightLevel : Packet { - private static GlobalLightLevel[] m_Cache = new GlobalLightLevel[0x100]; + private static readonly GlobalLightLevel[] m_Cache = new GlobalLightLevel[0x100]; public GlobalLightLevel(int level) : base(0x4F, 2) { @@ -2105,9 +2105,9 @@ namespace Server.Network public sealed class MessageLocalized : Packet { - private static MessageLocalized[] m_Cache_IntLoc = new MessageLocalized[15000]; - private static MessageLocalized[] m_Cache_CliLoc = new MessageLocalized[100000]; - private static MessageLocalized[] m_Cache_CliLocCmp = new MessageLocalized[5000]; + private static readonly MessageLocalized[] m_Cache_IntLoc = new MessageLocalized[15000]; + private static readonly MessageLocalized[] m_Cache_CliLoc = new MessageLocalized[100000]; + private static readonly MessageLocalized[] m_Cache_CliLocCmp = new MessageLocalized[5000]; public MessageLocalized(Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, string args) : base(0xC1) @@ -2318,20 +2318,20 @@ namespace Server.Network public sealed class DisplayGumpPacked : Packet, IGumpWriter { - private static byte[] m_True = Gump.StringToBuffer(" 1"); - private static byte[] m_False = Gump.StringToBuffer(" 0"); + private static readonly byte[] m_True = Gump.StringToBuffer(" 1"); + private static readonly byte[] m_False = Gump.StringToBuffer(" 0"); - private static byte[] m_BeginTextSeparator = Gump.StringToBuffer(" @"); - private static byte[] m_EndTextSeparator = Gump.StringToBuffer("@"); + private static readonly byte[] m_BeginTextSeparator = Gump.StringToBuffer(" @"); + private static readonly byte[] m_EndTextSeparator = Gump.StringToBuffer("@"); - private static byte[] m_Buffer = new byte[48]; + private static readonly byte[] m_Buffer = new byte[48]; - private Gump m_Gump; + private readonly Gump m_Gump; - private PacketWriter m_Layout; + private readonly PacketWriter m_Layout; private int m_StringCount; - private PacketWriter m_Strings; + private readonly PacketWriter m_Strings; static DisplayGumpPacked() => m_Buffer[0] = (byte)' '; @@ -2457,13 +2457,13 @@ namespace Server.Network public sealed class DisplayGumpFast : Packet, IGumpWriter { - private static byte[] m_True = Gump.StringToBuffer(" 1"); - private static byte[] m_False = Gump.StringToBuffer(" 0"); + private static readonly byte[] m_True = Gump.StringToBuffer(" 1"); + private static readonly byte[] m_False = Gump.StringToBuffer(" 0"); - private static byte[] m_BeginTextSeparator = Gump.StringToBuffer(" @"); - private static byte[] m_EndTextSeparator = Gump.StringToBuffer("@"); + private static readonly byte[] m_BeginTextSeparator = Gump.StringToBuffer(" @"); + private static readonly byte[] m_EndTextSeparator = Gump.StringToBuffer("@"); - private byte[] m_Buffer = new byte[48]; + private readonly byte[] m_Buffer = new byte[48]; private int m_LayoutLength; public DisplayGumpFast(Gump g) : base(0xB0) @@ -2629,7 +2629,7 @@ namespace Server.Network { public static readonly Packet InvalidInstance = SetStatic(new PlayMusic(MusicName.Invalid)); - private static Packet[] m_Instances = new Packet[60]; + private static readonly Packet[] m_Instances = new Packet[60]; public PlayMusic(MusicName name) : base(0x6D, 3) { @@ -2700,7 +2700,7 @@ namespace Server.Network public sealed class SeasonChange : Packet { - private static SeasonChange[][] m_Cache = new SeasonChange[][] + private static readonly SeasonChange[][] m_Cache = new SeasonChange[][] { new SeasonChange[2], new SeasonChange[2], @@ -3250,8 +3250,8 @@ namespace Server.Network public sealed class MobileIncoming : Packet { - private static ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); - private static ThreadLocal m_VersionTL = new ThreadLocal(); + private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); + private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); public Mobile m_Beheld; @@ -3363,8 +3363,8 @@ namespace Server.Network public sealed class MobileIncomingSA : Packet { - private static ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); - private static ThreadLocal m_VersionTL = new ThreadLocal(); + private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); + private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); public Mobile m_Beheld; @@ -3485,8 +3485,8 @@ namespace Server.Network // Pre-7.0.0.0 Mobile Incoming public sealed class MobileIncomingOld : Packet { - private static ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); - private static ThreadLocal m_VersionTL = new ThreadLocal(); + private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); + private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); public Mobile m_Beheld; @@ -3654,7 +3654,7 @@ namespace Server.Network public sealed class PingAck : Packet { - private static PingAck[] m_Cache = new PingAck[0x100]; + private static readonly PingAck[] m_Cache = new PingAck[0x100]; public PingAck(byte ping) : base(0x73, 2) { @@ -3689,7 +3689,7 @@ namespace Server.Network public sealed class MovementAck : Packet { - private static MovementAck[] m_Cache = new MovementAck[8 * 256]; + private static readonly MovementAck[] m_Cache = new MovementAck[8 * 256]; private MovementAck(int seq, int noto) : base(0x22, 3) { diff --git a/Projects/Server/Network/ServerStartup.cs b/Projects/Server/Network/ServerStartup.cs new file mode 100644 index 000000000..d0f0324c5 --- /dev/null +++ b/Projects/Server/Network/ServerStartup.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Server.Network; + +namespace Server.Network +{ + public class ServerStartup + { + private readonly IMessagePumpService _messagePumpService; + public ServerStartup(IMessagePumpService messagePumpService) => _messagePumpService = messagePumpService; + + public void ConfigureServices(IServiceCollection services) + { + } + + public void Configure(IApplicationBuilder app) + { + // Run async? + Task.Run(() => Core.RunEventLoop(_messagePumpService)); + } + } +} diff --git a/Projects/Server/Network/StaticPacketHandlers.cs b/Projects/Server/Network/StaticPacketHandlers.cs index 6f8e2b2f6..259647f95 100644 --- a/Projects/Server/Network/StaticPacketHandlers.cs +++ b/Projects/Server/Network/StaticPacketHandlers.cs @@ -19,19 +19,18 @@ * along with this program. If not, see . * *************************************************************************/ -using System; using System.Collections.Concurrent; namespace Server.Network { public static class StaticPacketHandlers { - private static ConcurrentDictionary OPLInfoPackets = new ConcurrentDictionary(); - private static ConcurrentDictionary RemoveEntityPackets = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary OPLInfoPackets = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary RemoveEntityPackets = new ConcurrentDictionary(); - private static ConcurrentDictionary WorldItemPackets = new ConcurrentDictionary(); - private static ConcurrentDictionary WorldItemSAPackets = new ConcurrentDictionary(); - private static ConcurrentDictionary WorldItemHSPackets = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary WorldItemPackets = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary WorldItemSAPackets = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary WorldItemHSPackets = new ConcurrentDictionary(); public static OPLInfo GetOPLInfoPacket(IPropertyListObject obj) { diff --git a/Projects/Server/Network/TcpServer.cs b/Projects/Server/Network/TcpServer.cs new file mode 100644 index 000000000..270beec52 --- /dev/null +++ b/Projects/Server/Network/TcpServer.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.NetworkInformation; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace Server.Network +{ + public class TcpServer + { + public static List Listeners { get; } = new List(); + // Make this thread safe + public static List Instances { get; } = new List(); + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseSetting(WebHostDefaults.SuppressStatusMessagesKey, "True") + .ConfigureServices(services => + { + services.AddSingleton(new MessagePumpService()); + }) + .UseKestrel(options => + { + foreach (var ipep in Listeners) + { + options.Listen(ipep, builder => { builder.UseConnectionHandler(); }); + DisplayListener(ipep); + } + + options.ListenLocalhost(2593, builder => { builder.UseConnectionHandler(); }); + + // Webservices here + }) + .UseLibuv() + .UseStartup(); + + private static void DisplayListener(IPEndPoint ipep) + { + if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any)) + { + NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); + foreach (NetworkInterface adapter in adapters) + { + IPInterfaceProperties properties = adapter.GetIPProperties(); + foreach (UnicastIPAddressInformation unicast in properties.UnicastAddresses) + if (ipep.AddressFamily == unicast.Address.AddressFamily) + Console.WriteLine("Listening: {0}:{1}", unicast.Address, ipep.Port); + } + } + else + Console.WriteLine("Listening: {0}:{1}", ipep.Address, ipep.Port); + } + } +} diff --git a/Projects/Server/Network/UOConnectionHandler.cs b/Projects/Server/Network/UOConnectionHandler.cs new file mode 100644 index 000000000..75f24c1ef --- /dev/null +++ b/Projects/Server/Network/UOConnectionHandler.cs @@ -0,0 +1,116 @@ +using System; +using System.Buffers; +using System.IO.Pipelines; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; +using Microsoft.Extensions.Logging; + +namespace Server.Network +{ + public class ServerConnectionHandler : ConnectionHandler + { + private readonly IMessagePumpService _messagePumpService; + private readonly ILogger _logger; + + public ServerConnectionHandler( + IMessagePumpService messagePumpService, + ILogger logger + ) + { + _messagePumpService = messagePumpService; + _logger = logger; + } + + public override async Task OnConnectedAsync(ConnectionContext connection) + { + if (!VerifySocket(connection)) + { + Release(connection); + return; + } + + NetState ns = new NetState(connection); + TcpServer.Instances.Add(ns); + _logger.LogInformation($"Client: {ns}: Connected. [{TcpServer.Instances.Count} Online]"); + + connection.ConnectionClosed.Register(() => { TcpServer.Instances.Remove(ns); }); + + await ProcessIncoming(ns); + } + + private async Task ProcessIncoming(NetState ns) + { + var inPipe = ns.Connection.Transport.Input; + + while (true) + { + if (NetState.AsyncState.Paused) + continue; + + try + { + ReadResult result = await inPipe.ReadAsync(); + if (result.IsCanceled || result.IsCompleted) + return; + + ReadOnlySequence seq = result.Buffer; + + if (seq.IsEmpty) + break; + + int pos = PacketHandlers.ProcessPacket(_messagePumpService, ns, seq); + + if (pos <= 0) + break; + + inPipe.AdvanceTo(seq.Slice(0, pos).End); + } + catch + { + // ignored + } + } + + inPipe.Complete(); + } + + private static bool VerifySocket(ConnectionContext connection) + { + try + { + SocketConnectEventArgs args = new SocketConnectEventArgs(connection); + + EventSink.InvokeSocketConnect(args); + + return args.AllowConnection; + } + catch (Exception ex) + { + NetState.TraceException(ex); + return false; + } + } + + private static void Release(ConnectionContext connection) + { + try + { + connection.Abort(new ConnectionAbortedException("Failed socket verification.")); + } + catch (Exception ex) + { + NetState.TraceException(ex); + } + + try + { + // TODO: Is this needed? + connection.DisposeAsync(); + } + catch (Exception ex) + { + NetState.TraceException(ex); + } + } + } +} diff --git a/Projects/Server/ObjectPropertyList.cs b/Projects/Server/ObjectPropertyList.cs index 0f845e62a..b89da34a8 100644 --- a/Projects/Server/ObjectPropertyList.cs +++ b/Projects/Server/ObjectPropertyList.cs @@ -35,10 +35,10 @@ namespace Server public sealed class ObjectPropertyList : Packet { private static byte[] m_Buffer = new byte[1024]; - private static Encoding m_Encoding = Encoding.Unicode; + private static readonly Encoding m_Encoding = Encoding.Unicode; // Each of these are localized to "~1_NOTHING~" which allows the string argument to be used - private static int[] m_StringNumbers = + private static readonly int[] m_StringNumbers = { 1042971, 1070722 diff --git a/Projects/Server/Persistence/BinaryMemoryWriter.cs b/Projects/Server/Persistence/BinaryMemoryWriter.cs index 5cc2f0a51..04697d869 100644 --- a/Projects/Server/Persistence/BinaryMemoryWriter.cs +++ b/Projects/Server/Persistence/BinaryMemoryWriter.cs @@ -25,7 +25,7 @@ namespace Server public sealed class BinaryMemoryWriter : BinaryFileWriter { private static byte[] indexBuffer; - private MemoryStream stream; + private readonly MemoryStream stream; public BinaryMemoryWriter() : base(new MemoryStream(512), true) => diff --git a/Projects/Server/Persistence/DynamicSaveStrategy.cs b/Projects/Server/Persistence/DynamicSaveStrategy.cs index 0132c1f52..175b168f8 100644 --- a/Projects/Server/Persistence/DynamicSaveStrategy.cs +++ b/Projects/Server/Persistence/DynamicSaveStrategy.cs @@ -29,16 +29,16 @@ namespace Server { public sealed class DynamicSaveStrategy : SaveStrategy { - private ConcurrentBag _decayBag; + private readonly ConcurrentBag _decayBag; private SequentialFileWriter _guildData, _guildIndex; - private BlockingCollection _guildThreadWriters; + private readonly BlockingCollection _guildThreadWriters; private SequentialFileWriter _itemData, _itemIndex; - private BlockingCollection _itemThreadWriters; + private readonly BlockingCollection _itemThreadWriters; private SequentialFileWriter _mobileData, _mobileIndex; - private BlockingCollection _mobileThreadWriters; + private readonly BlockingCollection _mobileThreadWriters; public DynamicSaveStrategy() { diff --git a/Projects/Server/Persistence/FileOperations.cs b/Projects/Server/Persistence/FileOperations.cs index 3939c9e48..7e5eb04c8 100644 --- a/Projects/Server/Persistence/FileOperations.cs +++ b/Projects/Server/Persistence/FileOperations.cs @@ -71,7 +71,7 @@ namespace Server #if !MONO private class UnbufferedFileStream : FileStream { - private SafeFileHandle fileHandle; + private readonly SafeFileHandle fileHandle; public UnbufferedFileStream(SafeFileHandle fileHandle, FileAccess access, int bufferSize, bool isAsync) : base(fileHandle, access, bufferSize, isAsync) => diff --git a/Projects/Server/Persistence/FileQueue.cs b/Projects/Server/Persistence/FileQueue.cs index 3ec5af2f5..f3f3d2e8d 100644 --- a/Projects/Server/Persistence/FileQueue.cs +++ b/Projects/Server/Persistence/FileQueue.cs @@ -29,19 +29,19 @@ namespace Server public sealed class FileQueue : IDisposable { - private static int bufferSize; + private static readonly int bufferSize; - private Chunk[] active; + private readonly Chunk[] active; private int activeCount; private Page buffered; - private FileCommitCallback callback; + private readonly FileCommitCallback callback; private ManualResetEvent idle; - private Queue pending; + private readonly Queue pending; - private object syncRoot; + private readonly object syncRoot; static FileQueue() => bufferSize = FileOperations.BufferSize; @@ -193,8 +193,8 @@ namespace Server public sealed class Chunk { private int offset; - private FileQueue owner; - private int slot; + private readonly FileQueue owner; + private readonly int slot; public Chunk(FileQueue owner, int slot, byte[] buffer, int offset, int size) { diff --git a/Projects/Server/Persistence/ParallelSaveStrategy.cs b/Projects/Server/Persistence/ParallelSaveStrategy.cs index 80614bc3c..45f28795e 100644 --- a/Projects/Server/Persistence/ParallelSaveStrategy.cs +++ b/Projects/Server/Persistence/ParallelSaveStrategy.cs @@ -28,7 +28,7 @@ namespace Server { public sealed class ParallelSaveStrategy : SaveStrategy { - private Queue _decayQueue; + private readonly Queue _decayQueue; private Consumer[] consumers; private int cycle; @@ -40,7 +40,7 @@ namespace Server private SequentialFileWriter mobileData, mobileIndex; - private int processorCount; + private readonly int processorCount; public ParallelSaveStrategy(int processorCount) { @@ -226,9 +226,9 @@ namespace Server private sealed class Producer : IEnumerable { - private IEnumerable guilds; - private IEnumerable items; - private IEnumerable mobiles; + private readonly IEnumerable guilds; + private readonly IEnumerable items; + private readonly IEnumerable mobiles; public Producer() { @@ -257,13 +257,13 @@ namespace Server private sealed class Consumer { - public ConsumableEntry[] buffer; + public readonly ConsumableEntry[] buffer; - public ManualResetEvent completionEvent; + public readonly ManualResetEvent completionEvent; public int head, done, tail; - private ParallelSaveStrategy owner; + private readonly ParallelSaveStrategy owner; - private Thread thread; + private readonly Thread thread; public Consumer(ParallelSaveStrategy owner, int bufferSize) { diff --git a/Projects/Server/Persistence/QueuedMemoryWriter.cs b/Projects/Server/Persistence/QueuedMemoryWriter.cs index 1bcd67982..6effa9f97 100644 --- a/Projects/Server/Persistence/QueuedMemoryWriter.cs +++ b/Projects/Server/Persistence/QueuedMemoryWriter.cs @@ -25,8 +25,8 @@ namespace Server { public sealed class QueuedMemoryWriter : BinaryFileWriter { - private MemoryStream _memStream; - private List _orderedIndexInfo = new List(); + private readonly MemoryStream _memStream; + private readonly List _orderedIndexInfo = new List(); public QueuedMemoryWriter() : base(new MemoryStream(1024 * 1024), true) => diff --git a/Projects/Server/Persistence/StandardSaveStrategy.cs b/Projects/Server/Persistence/StandardSaveStrategy.cs index d65de691d..d75403754 100644 --- a/Projects/Server/Persistence/StandardSaveStrategy.cs +++ b/Projects/Server/Persistence/StandardSaveStrategy.cs @@ -35,7 +35,7 @@ namespace Server public static SaveOption SaveType = SaveOption.Normal; - private Queue _decayQueue; + private readonly Queue _decayQueue; public StandardSaveStrategy() => _decayQueue = new Queue(); diff --git a/Projects/Server/Point3DList.cs b/Projects/Server/Point3DList.cs index 635121987..6b34f72da 100644 --- a/Projects/Server/Point3DList.cs +++ b/Projects/Server/Point3DList.cs @@ -22,7 +22,7 @@ namespace Server { public class Point3DList { - private static Point3D[] m_EmptyList = new Point3D[0]; + private static readonly Point3D[] m_EmptyList = new Point3D[0]; private Point3D[] m_List; public Point3DList() diff --git a/Projects/Server/Region.cs b/Projects/Server/Region.cs index c731f7964..dcda3c477 100644 --- a/Projects/Server/Region.cs +++ b/Projects/Server/Region.cs @@ -110,8 +110,8 @@ namespace Server private Point3D m_GoLocation; - private string m_Name; - private int m_Priority; + private readonly string m_Name; + private readonly int m_Priority; public Region(string name, Map map, int priority, params Rectangle2D[] area) : this(name, map, priority, ConvertTo3D(area)) diff --git a/Projects/Server/Sector.cs b/Projects/Server/Sector.cs index ae9ef8b94..5713accbd 100644 --- a/Projects/Server/Sector.cs +++ b/Projects/Server/Sector.cs @@ -48,11 +48,11 @@ namespace Server public class Sector { // TODO: Can we avoid this? - private static List m_DefaultMobileList = new List(); - private static List m_DefaultItemList = new List(); - private static List m_DefaultClientList = new List(); - private static List m_DefaultMultiList = new List(); - private static List m_DefaultRectList = new List(); + private static readonly List m_DefaultMobileList = new List(); + private static readonly List m_DefaultItemList = new List(); + private static readonly List m_DefaultClientList = new List(); + private static readonly List m_DefaultMultiList = new List(); + private static readonly List m_DefaultRectList = new List(); private bool m_Active; private List m_Clients; private List m_Items; diff --git a/Projects/Server/Serialization/AsyncWriter.cs b/Projects/Server/Serialization/AsyncWriter.cs index c8cfd2c55..7112ea59c 100644 --- a/Projects/Server/Serialization/AsyncWriter.cs +++ b/Projects/Server/Serialization/AsyncWriter.cs @@ -29,18 +29,18 @@ namespace Server { public sealed class AsyncWriter : IGenericWriter { - private int BufferSize; + private readonly int BufferSize; private BinaryWriter m_Bin; private bool m_Closed; - private FileStream m_File; + private readonly FileStream m_File; private long m_LastPos, m_CurPos; private MemoryStream m_Mem; private Thread m_WorkerThread; - private Queue m_WriteQueue; - private bool PrefixStrings; + private readonly Queue m_WriteQueue; + private readonly bool PrefixStrings; public AsyncWriter(string filename, bool prefix) : this(filename, 1048576, prefix) //1 mb buffer @@ -565,7 +565,7 @@ namespace Server private class WorkerThread { - private AsyncWriter m_Owner; + private readonly AsyncWriter m_Owner; public WorkerThread(AsyncWriter owner) => m_Owner = owner; diff --git a/Projects/Server/Serialization/BinaryFileReader.cs b/Projects/Server/Serialization/BinaryFileReader.cs index 5d21a0faf..2abbc1d2f 100644 --- a/Projects/Server/Serialization/BinaryFileReader.cs +++ b/Projects/Server/Serialization/BinaryFileReader.cs @@ -29,7 +29,7 @@ namespace Server { public sealed class BinaryFileReader : IGenericReader { - private BinaryReader m_File; + private readonly BinaryReader m_File; public BinaryFileReader(BinaryReader br) => m_File = br; diff --git a/Projects/Server/Serialization/BinaryFileWriter.cs b/Projects/Server/Serialization/BinaryFileWriter.cs index 9cb205354..bd8bc9783 100644 --- a/Projects/Server/Serialization/BinaryFileWriter.cs +++ b/Projects/Server/Serialization/BinaryFileWriter.cs @@ -32,20 +32,20 @@ namespace Server { private const int LargeByteBufferSize = 256; - private byte[] m_Buffer; + private readonly byte[] m_Buffer; private byte[] m_CharacterBuffer; - private Encoding m_Encoding; - private Stream m_File; + private readonly Encoding m_Encoding; + private readonly Stream m_File; private int m_Index; private int m_MaxBufferChars; private long m_Position; - private char[] m_SingleCharBuffer = new char[1]; - private bool PrefixStrings; + private readonly char[] m_SingleCharBuffer = new char[1]; + private readonly bool PrefixStrings; public BinaryFileWriter(Stream strm, bool prefixStr) { diff --git a/Projects/Server/Serialization/BufferWriter.cs b/Projects/Server/Serialization/BufferWriter.cs index 0462a21a9..e2e06323c 100644 --- a/Projects/Server/Serialization/BufferWriter.cs +++ b/Projects/Server/Serialization/BufferWriter.cs @@ -35,13 +35,13 @@ namespace Server private byte[] m_CharacterBuffer; - private Encoding m_Encoding; + private readonly Encoding m_Encoding; private int m_Index; private int m_MaxBufferChars; - private char[] m_SingleCharBuffer = new char[1]; - private bool PrefixStrings; + private readonly char[] m_SingleCharBuffer = new char[1]; + private readonly bool PrefixStrings; public BufferWriter(bool prefixStr) { diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index d8fffcd3b..8b9200a2f 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -6,7 +6,7 @@ Server.Core ModernUO - 0.2.1 + 0.3.0 Kamron Batman ModernUO ModernUO Server @@ -35,21 +35,24 @@ libdrng.dll Always - - libuv.dll - Always - zlib.dll Always - - - - - - + + + + + + + + + + + + + diff --git a/Projects/Server/SignalR/AsyncDisposableExtensions.cs b/Projects/Server/SignalR/AsyncDisposableExtensions.cs deleted file mode 100644 index ac2ab7c2d..000000000 --- a/Projects/Server/SignalR/AsyncDisposableExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading.Tasks; - -namespace SignalR -{ - public static class AsyncDisposableExtensions - { - // Does a light up check to see if a type is IAsyncDisposable and calls DisposeAsync if it is - public static ValueTask DisposeAsync(this IDisposable disposable) - { - if (disposable is IAsyncDisposable asyncDisposable) return asyncDisposable.DisposeAsync(); - disposable.Dispose(); - return default; - } - } -} diff --git a/Projects/Server/Skills.cs b/Projects/Server/Skills.cs index 9088bf63f..f9b44c370 100644 --- a/Projects/Server/Skills.cs +++ b/Projects/Server/Skills.cs @@ -487,7 +487,7 @@ namespace Server public class Skills : IEnumerable { private Skill m_Highest; - private Skill[] m_Skills; + private readonly Skill[] m_Skills; public Skills(Mobile owner) { diff --git a/Projects/Server/Targeting/Target.cs b/Projects/Server/Targeting/Target.cs index 72f595a95..155e41107 100644 --- a/Projects/Server/Targeting/Target.cs +++ b/Projects/Server/Targeting/Target.cs @@ -257,11 +257,11 @@ namespace Server.Targeting private class TimeoutTimer : Timer { - private static TimeSpan ThirtySeconds = TimeSpan.FromSeconds(30.0); - private static TimeSpan TenSeconds = TimeSpan.FromSeconds(10.0); - private static TimeSpan OneSecond = TimeSpan.FromSeconds(1.0); - private Mobile m_Mobile; - private Target m_Target; + private static readonly TimeSpan ThirtySeconds = TimeSpan.FromSeconds(30.0); + private static readonly TimeSpan TenSeconds = TimeSpan.FromSeconds(10.0); + private static readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1.0); + private readonly Mobile m_Mobile; + private readonly Target m_Target; public TimeoutTimer(Target target, Mobile m, TimeSpan delay) : base(delay) { diff --git a/Projects/Server/TileData.cs b/Projects/Server/TileData.cs index ff97764af..0865840b8 100644 --- a/Projects/Server/TileData.cs +++ b/Projects/Server/TileData.cs @@ -177,7 +177,7 @@ namespace Server public static class TileData { - private static byte[] m_StringBuffer = new byte[20]; + private static readonly byte[] m_StringBuffer = new byte[20]; static TileData() { diff --git a/Projects/Server/TileList.cs b/Projects/Server/TileList.cs index 1097c3c37..1212a39ae 100644 --- a/Projects/Server/TileList.cs +++ b/Projects/Server/TileList.cs @@ -22,7 +22,7 @@ namespace Server { public class TileList { - private static StaticTile[] m_EmptyTiles = new StaticTile[0]; + private static readonly StaticTile[] m_EmptyTiles = new StaticTile[0]; private StaticTile[] m_Tiles; public TileList() diff --git a/Projects/Server/TileMatrix.cs b/Projects/Server/TileMatrix.cs index 44dc98601..d45da5ac3 100644 --- a/Projects/Server/TileMatrix.cs +++ b/Projects/Server/TileMatrix.cs @@ -29,30 +29,30 @@ namespace Server { public class TileMatrix { - private static List m_Instances = new List(); + private static readonly List m_Instances = new List(); - private int m_FileIndex; - private List m_FileShare = new List(); + private readonly int m_FileIndex; + private readonly List m_FileShare = new List(); - private LandTile[] m_InvalidLandBlock; - private int[][] m_LandPatches; - private LandTile[][][] m_LandTiles; + private readonly LandTile[] m_InvalidLandBlock; + private readonly int[][] m_LandPatches; + private readonly LandTile[][][] m_LandTiles; private TileList[][] m_Lists; - private UOPIndex m_MapIndex; + private readonly UOPIndex m_MapIndex; private DateTime m_NextLandWarning; private DateTime m_NextStaticWarning; - private Map m_Owner; + private readonly Map m_Owner; - private int[][] m_StaticPatches; - private StaticTile[][][][][] m_StaticTiles; + private readonly int[][] m_StaticPatches; + private readonly StaticTile[][][][][] m_StaticTiles; private StaticTile[] m_TileBuffer = new StaticTile[128]; - private TileList m_TilesList = new TileList(); + private readonly TileList m_TilesList = new TileList(); // private int m_Width, m_Height; public TileMatrix(Map owner, int fileIndex, int mapID, int width, int height) @@ -564,10 +564,10 @@ namespace Server public class UOPIndex { - private UOPEntry[] m_Entries; - private int m_Length; + private readonly UOPEntry[] m_Entries; + private readonly int m_Length; - private BinaryReader m_Reader; + private readonly BinaryReader m_Reader; public UOPIndex(FileStream stream) { @@ -652,7 +652,7 @@ namespace Server private class UOPEntry : IComparable { - public int m_Length; + public readonly int m_Length; public int m_Offset; public int m_Order; diff --git a/Projects/Server/TileMatrixPatch.cs b/Projects/Server/TileMatrixPatch.cs index e515fee8a..9c0bb2a0a 100644 --- a/Projects/Server/TileMatrixPatch.cs +++ b/Projects/Server/TileMatrixPatch.cs @@ -25,7 +25,8 @@ namespace Server { public class TileMatrixPatch { - private int m_LandBlocks, m_StaticBlocks; + private readonly int m_LandBlocks; + private readonly int m_StaticBlocks; private StaticTile[] m_TileBuffer = new StaticTile[128]; diff --git a/Projects/Server/Timer.cs b/Projects/Server/Timer.cs index 21b9db12b..5b4d2ef46 100644 --- a/Projects/Server/Timer.cs +++ b/Projects/Server/Timer.cs @@ -45,11 +45,12 @@ namespace Server public class Timer { - private static Queue m_Queue = new Queue(); + private static readonly Queue m_Queue = new Queue(); private static int m_QueueCountAtSlice; private long m_Delay; - private int m_Index, m_Count; + private int m_Index; + private readonly int m_Count; private long m_Interval; private List m_List; private long m_Next; @@ -232,11 +233,11 @@ namespace Server public class TimerThread { - private static Dictionary m_Changed = new Dictionary(); + private static readonly Dictionary m_Changed = new Dictionary(); - private static long[] m_NextPriorities = new long[8]; + private static readonly long[] m_NextPriorities = new long[8]; - private static long[] m_PriorityDelays = { + private static readonly long[] m_PriorityDelays = { 0, 10, 25, @@ -247,7 +248,7 @@ namespace Server 60000 }; - private static List[] m_Timers = { + private static readonly List[] m_Timers = { new List(), new List(), new List(), @@ -258,7 +259,7 @@ namespace Server new List() }; - private static AutoResetEvent m_Signal = new AutoResetEvent(false); + private static readonly AutoResetEvent m_Signal = new AutoResetEvent(false); public static void DumpInfo(TextWriter tw) { @@ -415,7 +416,7 @@ namespace Server private class TimerChangeEntry { - private static Queue m_InstancePool = new Queue(); + private static readonly Queue m_InstancePool = new Queue(); public bool m_IsAdd; public int m_NewIndex; public Timer m_Timer; @@ -528,7 +529,7 @@ namespace Server private class DelayStateCallTimer : Timer { - private T m_State; + private readonly T m_State; public DelayStateCallTimer(TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, T state) : base(delay, interval, count) @@ -555,7 +556,7 @@ namespace Server private class DelayTaskTimer : Timer { - private TaskCompletionSource m_TaskCompleter; + private readonly TaskCompletionSource m_TaskCompleter; public Task Task => m_TaskCompleter.Task; diff --git a/Projects/Server/Utilities/ActivatorUtil.cs b/Projects/Server/Utilities/ActivatorUtil.cs index 32a92a71c..945ee63c6 100644 --- a/Projects/Server/Utilities/ActivatorUtil.cs +++ b/Projects/Server/Utilities/ActivatorUtil.cs @@ -11,18 +11,12 @@ namespace Server.Utilities { ConstructorInfo emptyCtor = type.GetConstructor(Type.EmptyTypes); - if (emptyCtor != null && predicate?.Invoke(emptyCtor) != false) - { - return emptyCtor; - } + if (emptyCtor != null && predicate?.Invoke(emptyCtor) != false) return emptyCtor; ConstructorInfo optionalCtor = type.GetConstructors().SingleOrDefault(info => predicate?.Invoke(info) != false && info.GetParameters().All(x => x.IsOptional)); - if (optionalCtor != null) - { - return optionalCtor; - } + if (optionalCtor != null) return optionalCtor; throw new TypeInitializationException(type.ToString(), new Exception($"There is no empty/default constructor for {type} that matches predicate.")); @@ -38,39 +32,26 @@ namespace Server.Utilities { ctor = type.GetConstructor(args); - if (ctor != null && predicate?.Invoke(ctor) != false) - { - return ctor; - } + if (ctor != null && predicate?.Invoke(ctor) != false) return ctor; } else { ctor = type.GetConstructors().SingleOrDefault(info => { - if (predicate?.Invoke(info) == false) - { - return false; - } + if (predicate?.Invoke(info) == false) return false; List paramList = info.GetParameters().ToList(); // If more args are given than parameters, skip. - if (args.Length > paramList.Count) - { - return false; - } + if (args.Length > paramList.Count) return false; // check all given args map to params. for (int i = 0; i < args.Length; i++) - { - // if a null reference is passed, but the type is not nullable + // if a null reference is passed, but the type is not nullable if (args[i] == null && paramList[i].ParameterType.IsValueType // or if an arg is not null and is not assignable to the parameter type, skip. || !(args[i] == null || paramList[i].ParameterType.IsAssignableFrom(args[i]))) - { return false; - } - } // If there are more parameters, check if they any are not optional, if any are not, skip. // Otherwise all checks have passed. We have found a match @@ -78,10 +59,7 @@ namespace Server.Utilities .All(x => x.IsOptional); }); - if (ctor != null) - { - return ctor; - } + if (ctor != null) return ctor; } throw new Exception($"There is no empty/default constructor for {type} that matches predicate."); @@ -97,10 +75,7 @@ namespace Server.Utilities ConstructorInfo cctor = GetConstructor(type, constructorPredicate); ParameterInfo[] args = cctor.GetParameters(); - if (args.Length == 0) - { - return cctor.Invoke(Type.EmptyTypes); - } + if (args.Length == 0) return cctor.Invoke(Type.EmptyTypes); object[] argList = new object[args.Length]; Array.Fill(argList, Type.Missing); @@ -110,10 +85,7 @@ namespace Server.Utilities public static object CreateInstance(Type type, Predicate constructorPredicate = null, params object[] args) { - if (args == null || args.Length == 0) - { - return CreateInstance(type, constructorPredicate); - } + if (args == null || args.Length == 0) return CreateInstance(type, constructorPredicate); ConstructorInfo cctor = GetConstructor(type, constructorPredicate, args.Select(x => x?.GetType()).ToArray()); return cctor.Invoke(args); diff --git a/Projects/Server/Utilities/RefPool.cs b/Projects/Server/Utilities/RefPool.cs index f9f61cb12..76c0f6178 100644 --- a/Projects/Server/Utilities/RefPool.cs +++ b/Projects/Server/Utilities/RefPool.cs @@ -20,11 +20,8 @@ namespace Server.Utilities /// public abstract class BaseRef : IRef where TDerived : IRef { - private RefPool m_Pool; - public BaseRef(RefPool pool) - { - m_Pool = pool; - } + private readonly RefPool m_Pool; + public BaseRef(RefPool pool) => m_Pool = pool; protected abstract void OnDispose(); public void Dispose() { @@ -42,8 +39,8 @@ namespace Server.Utilities public delegate TRef Generator(RefPool targetPool); public const int DEFAULT_RESOURCE_RETENTION = 10; - private Stack m_Resources = new Stack(); - private Generator m_Generator; + private readonly Stack m_Resources = new Stack(); + private readonly Generator m_Generator; private int m_MaxRefrenceRetention; /// @@ -90,8 +87,9 @@ namespace Server.Utilities /// public class QueueRef : Queue, IRef { - private RefPool> m_Pool; - private QueueRef(RefPool> pool) { m_Pool = pool; } + private readonly RefPool> m_Pool; + private QueueRef(RefPool> pool) => m_Pool = pool; + /// Clears the queue and returns this resource to its parent resource pool. public void Dispose() { Clear(); m_Pool.Return(this); } /// @@ -102,8 +100,9 @@ namespace Server.Utilities /// public class StackRef : Stack, IRef { - private RefPool> m_Pool; - private StackRef(RefPool> pool) { m_Pool = pool; } + private readonly RefPool> m_Pool; + private StackRef(RefPool> pool) => m_Pool = pool; + /// Clears the stack and returns this resource to its parent resource pool. public void Dispose() { Clear(); m_Pool.Return(this); } /// diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 1e68ff305..12c841164 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -36,7 +36,7 @@ namespace Server private static Dictionary _ipAddressTable; - private static SkillName[] m_AllSkills = + private static readonly SkillName[] m_AllSkills = { SkillName.Alchemy, SkillName.Anatomy, @@ -95,7 +95,7 @@ namespace Server SkillName.Spellweaving }; - private static SkillName[] m_CombatSkills = + private static readonly SkillName[] m_CombatSkills = { SkillName.Archery, SkillName.Swords, @@ -104,7 +104,7 @@ namespace Server SkillName.Wrestling }; - private static SkillName[] m_CraftSkills = + private static readonly SkillName[] m_CraftSkills = { SkillName.Alchemy, SkillName.Blacksmith, @@ -117,7 +117,7 @@ namespace Server SkillName.Tinkering }; - private static Stack m_ConsoleColors = new Stack(); + private static readonly Stack m_ConsoleColors = new Stack(); public static Encoding UTF8 => m_UTF8 ?? (m_UTF8 = new UTF8Encoding(false, false)); public static Encoding UTF8WithEncoding => m_UTF8WithEncoding ?? (m_UTF8WithEncoding = new UTF8Encoding(true, false)); diff --git a/Projects/Server/World.cs b/Projects/Server/World.cs index 93c2fd1b6..d4c75c5ae 100644 --- a/Projects/Server/World.cs +++ b/Projects/Server/World.cs @@ -31,7 +31,7 @@ namespace Server { public static class World { - private static ManualResetEvent m_DiskWriteHandle = new ManualResetEvent(true); + private static readonly ManualResetEvent m_DiskWriteHandle = new ManualResetEvent(true); private static Queue _addQueue, _deleteQueue; @@ -98,7 +98,7 @@ namespace Server else p = new UnicodeMessage(Serial.MinusOne, -1, MessageType.Regular, hue, 3, "ENU", "System", text); - List list = NetState.Instances; + List list = TcpServer.Instances; p.Acquire(); diff --git a/Publish-Linux.sh b/Publish-Linux.sh old mode 100644 new mode 100755