deF+H*huGlKaYDV4W_63j=2L^x;!I9knvI9Uq1(0P}M}9V?K@Jr_`epn4
zH=ZmIVnb3swpFg9?R)iZtS6g(F)P#3Ts){O@)~;CH?1
zS!NDtK9N~5Sv2KUjdGvUy#8V>9Pt)
z+9U-7&!^>0yv?tDwC8~+vlOQ4Ly(@2TBa!W!jB6Q=dnp4l_BbRB)C`|O6)S#+2q*o
z-B#*UZd|ACFDKK@?0%*8N|`OzYQ6+S
zc#7P%1&eCQpMftXbA*t}q$m-R_n?r{NzhA(I?DPCe^lr-MzEwMlET6mHBtJ_ag8A16qojq5uE@
literal 0
HcmV?d00001
diff --git a/rdrand.so b/Projects/Server/Assemblies/rdrand.so
similarity index 100%
rename from rdrand.so
rename to Projects/Server/Assemblies/rdrand.so
diff --git a/rdrand32.dll b/Projects/Server/Assemblies/rdrand32.dll
similarity index 100%
rename from rdrand32.dll
rename to Projects/Server/Assemblies/rdrand32.dll
diff --git a/rdrand64.dll b/Projects/Server/Assemblies/rdrand64.dll
similarity index 100%
rename from rdrand64.dll
rename to Projects/Server/Assemblies/rdrand64.dll
diff --git a/zlib32.dll b/Projects/Server/Assemblies/zlib32.dll
similarity index 100%
rename from zlib32.dll
rename to Projects/Server/Assemblies/zlib32.dll
diff --git a/zlib64.dll b/Projects/Server/Assemblies/zlib64.dll
similarity index 100%
rename from zlib64.dll
rename to Projects/Server/Assemblies/zlib64.dll
diff --git a/Server/Attributes.cs b/Projects/Server/Attributes.cs
similarity index 100%
rename from Server/Attributes.cs
rename to Projects/Server/Attributes.cs
diff --git a/Server/BaseVendor.cs b/Projects/Server/BaseVendor.cs
similarity index 100%
rename from Server/BaseVendor.cs
rename to Projects/Server/BaseVendor.cs
diff --git a/Server/Body.cs b/Projects/Server/Body.cs
similarity index 100%
rename from Server/Body.cs
rename to Projects/Server/Body.cs
diff --git a/Server/ClientVersion.cs b/Projects/Server/ClientVersion.cs
similarity index 100%
rename from Server/ClientVersion.cs
rename to Projects/Server/ClientVersion.cs
diff --git a/Server/Commands.cs b/Projects/Server/Commands.cs
similarity index 100%
rename from Server/Commands.cs
rename to Projects/Server/Commands.cs
diff --git a/Server/ContextMenus/ContextMenu.cs b/Projects/Server/ContextMenus/ContextMenu.cs
similarity index 100%
rename from Server/ContextMenus/ContextMenu.cs
rename to Projects/Server/ContextMenus/ContextMenu.cs
diff --git a/Server/ContextMenus/ContextMenuEntry.cs b/Projects/Server/ContextMenus/ContextMenuEntry.cs
similarity index 100%
rename from Server/ContextMenus/ContextMenuEntry.cs
rename to Projects/Server/ContextMenus/ContextMenuEntry.cs
diff --git a/Server/ContextMenus/OpenBackpackEntry.cs b/Projects/Server/ContextMenus/OpenBackpackEntry.cs
similarity index 100%
rename from Server/ContextMenus/OpenBackpackEntry.cs
rename to Projects/Server/ContextMenus/OpenBackpackEntry.cs
diff --git a/Server/ContextMenus/PaperdollEntry.cs b/Projects/Server/ContextMenus/PaperdollEntry.cs
similarity index 100%
rename from Server/ContextMenus/PaperdollEntry.cs
rename to Projects/Server/ContextMenus/PaperdollEntry.cs
diff --git a/Server/Diagnostics/BaseProfile.cs b/Projects/Server/Diagnostics/BaseProfile.cs
similarity index 100%
rename from Server/Diagnostics/BaseProfile.cs
rename to Projects/Server/Diagnostics/BaseProfile.cs
diff --git a/Server/Diagnostics/GumpProfile.cs b/Projects/Server/Diagnostics/GumpProfile.cs
similarity index 100%
rename from Server/Diagnostics/GumpProfile.cs
rename to Projects/Server/Diagnostics/GumpProfile.cs
diff --git a/Server/Diagnostics/PacketProfile.cs b/Projects/Server/Diagnostics/PacketProfile.cs
similarity index 93%
rename from Server/Diagnostics/PacketProfile.cs
rename to Projects/Server/Diagnostics/PacketProfile.cs
index e85c7d89d..54f349dcf 100644
--- a/Server/Diagnostics/PacketProfile.cs
+++ b/Projects/Server/Diagnostics/PacketProfile.cs
@@ -28,8 +28,7 @@ namespace Server.Diagnostics
{
public abstract class BasePacketProfile : BaseProfile
{
- protected BasePacketProfile(string name)
- : base(name)
+ protected BasePacketProfile(string name) : base(name)
{
}
@@ -37,7 +36,7 @@ namespace Server.Diagnostics
public double AverageLength => (double)TotalLength / Math.Max(1, Count);
- public void Finish(int length)
+ public void Finish(long length)
{
Finish();
diff --git a/Server/Diagnostics/TargetProfile.cs b/Projects/Server/Diagnostics/TargetProfile.cs
similarity index 100%
rename from Server/Diagnostics/TargetProfile.cs
rename to Projects/Server/Diagnostics/TargetProfile.cs
diff --git a/Server/Diagnostics/TimerProfile.cs b/Projects/Server/Diagnostics/TimerProfile.cs
similarity index 100%
rename from Server/Diagnostics/TimerProfile.cs
rename to Projects/Server/Diagnostics/TimerProfile.cs
diff --git a/Server/Effects.cs b/Projects/Server/Effects.cs
similarity index 100%
rename from Server/Effects.cs
rename to Projects/Server/Effects.cs
diff --git a/Server/EventSink.cs b/Projects/Server/EventSink.cs
similarity index 95%
rename from Server/EventSink.cs
rename to Projects/Server/EventSink.cs
index ba31ba159..d447e4228 100644
--- a/Server/EventSink.cs
+++ b/Projects/Server/EventSink.cs
@@ -608,10 +608,10 @@ namespace Server
public void AddServer(string name, IPEndPoint address)
{
- AddServer(name, 0, TimeZone.CurrentTimeZone, address);
+ AddServer(name, 0, TimeZoneInfo.Local, address);
}
- public void AddServer(string name, int fullPercent, TimeZone tz, IPEndPoint address)
+ public void AddServer(string name, int fullPercent, TimeZoneInfo tz, IPEndPoint address)
{
Servers.Add(new ServerInfo(name, fullPercent, tz, address));
}
@@ -1129,4 +1129,4 @@ namespace Server
QuestGumpRequest = null;
}
}
-}
\ No newline at end of file
+}
diff --git a/Server/ExpansionInfo.cs b/Projects/Server/ExpansionInfo.cs
similarity index 100%
rename from Server/ExpansionInfo.cs
rename to Projects/Server/ExpansionInfo.cs
diff --git a/Server/Geometry.cs b/Projects/Server/Geometry.cs
similarity index 100%
rename from Server/Geometry.cs
rename to Projects/Server/Geometry.cs
diff --git a/Server/Guild.cs b/Projects/Server/Guild.cs
similarity index 100%
rename from Server/Guild.cs
rename to Projects/Server/Guild.cs
diff --git a/Server/Gumps/Gump.cs b/Projects/Server/Gumps/Gump.cs
similarity index 100%
rename from Server/Gumps/Gump.cs
rename to Projects/Server/Gumps/Gump.cs
diff --git a/Server/Gumps/GumpAlphaRegion.cs b/Projects/Server/Gumps/GumpAlphaRegion.cs
similarity index 100%
rename from Server/Gumps/GumpAlphaRegion.cs
rename to Projects/Server/Gumps/GumpAlphaRegion.cs
diff --git a/Server/Gumps/GumpBackground.cs b/Projects/Server/Gumps/GumpBackground.cs
similarity index 100%
rename from Server/Gumps/GumpBackground.cs
rename to Projects/Server/Gumps/GumpBackground.cs
diff --git a/Server/Gumps/GumpButton.cs b/Projects/Server/Gumps/GumpButton.cs
similarity index 100%
rename from Server/Gumps/GumpButton.cs
rename to Projects/Server/Gumps/GumpButton.cs
diff --git a/Server/Gumps/GumpCheck.cs b/Projects/Server/Gumps/GumpCheck.cs
similarity index 100%
rename from Server/Gumps/GumpCheck.cs
rename to Projects/Server/Gumps/GumpCheck.cs
diff --git a/Server/Gumps/GumpEntry.cs b/Projects/Server/Gumps/GumpEntry.cs
similarity index 100%
rename from Server/Gumps/GumpEntry.cs
rename to Projects/Server/Gumps/GumpEntry.cs
diff --git a/Server/Gumps/GumpGroup.cs b/Projects/Server/Gumps/GumpGroup.cs
similarity index 100%
rename from Server/Gumps/GumpGroup.cs
rename to Projects/Server/Gumps/GumpGroup.cs
diff --git a/Server/Gumps/GumpHtml.cs b/Projects/Server/Gumps/GumpHtml.cs
similarity index 100%
rename from Server/Gumps/GumpHtml.cs
rename to Projects/Server/Gumps/GumpHtml.cs
diff --git a/Server/Gumps/GumpHtmlLocalized.cs b/Projects/Server/Gumps/GumpHtmlLocalized.cs
similarity index 100%
rename from Server/Gumps/GumpHtmlLocalized.cs
rename to Projects/Server/Gumps/GumpHtmlLocalized.cs
diff --git a/Server/Gumps/GumpImage.cs b/Projects/Server/Gumps/GumpImage.cs
similarity index 100%
rename from Server/Gumps/GumpImage.cs
rename to Projects/Server/Gumps/GumpImage.cs
diff --git a/Server/Gumps/GumpImageTileButton.cs b/Projects/Server/Gumps/GumpImageTileButton.cs
similarity index 100%
rename from Server/Gumps/GumpImageTileButton.cs
rename to Projects/Server/Gumps/GumpImageTileButton.cs
diff --git a/Server/Gumps/GumpImageTiled.cs b/Projects/Server/Gumps/GumpImageTiled.cs
similarity index 100%
rename from Server/Gumps/GumpImageTiled.cs
rename to Projects/Server/Gumps/GumpImageTiled.cs
diff --git a/Server/Gumps/GumpItem.cs b/Projects/Server/Gumps/GumpItem.cs
similarity index 100%
rename from Server/Gumps/GumpItem.cs
rename to Projects/Server/Gumps/GumpItem.cs
diff --git a/Server/Gumps/GumpItemProperty.cs b/Projects/Server/Gumps/GumpItemProperty.cs
similarity index 100%
rename from Server/Gumps/GumpItemProperty.cs
rename to Projects/Server/Gumps/GumpItemProperty.cs
diff --git a/Server/Gumps/GumpLabel.cs b/Projects/Server/Gumps/GumpLabel.cs
similarity index 100%
rename from Server/Gumps/GumpLabel.cs
rename to Projects/Server/Gumps/GumpLabel.cs
diff --git a/Server/Gumps/GumpLabelCropped.cs b/Projects/Server/Gumps/GumpLabelCropped.cs
similarity index 100%
rename from Server/Gumps/GumpLabelCropped.cs
rename to Projects/Server/Gumps/GumpLabelCropped.cs
diff --git a/Server/Gumps/GumpPage.cs b/Projects/Server/Gumps/GumpPage.cs
similarity index 100%
rename from Server/Gumps/GumpPage.cs
rename to Projects/Server/Gumps/GumpPage.cs
diff --git a/Server/Gumps/GumpRadio.cs b/Projects/Server/Gumps/GumpRadio.cs
similarity index 100%
rename from Server/Gumps/GumpRadio.cs
rename to Projects/Server/Gumps/GumpRadio.cs
diff --git a/Server/Gumps/GumpTextEntry.cs b/Projects/Server/Gumps/GumpTextEntry.cs
similarity index 100%
rename from Server/Gumps/GumpTextEntry.cs
rename to Projects/Server/Gumps/GumpTextEntry.cs
diff --git a/Server/Gumps/GumpTextEntryLimited.cs b/Projects/Server/Gumps/GumpTextEntryLimited.cs
similarity index 100%
rename from Server/Gumps/GumpTextEntryLimited.cs
rename to Projects/Server/Gumps/GumpTextEntryLimited.cs
diff --git a/Server/Gumps/GumpTooltip.cs b/Projects/Server/Gumps/GumpTooltip.cs
similarity index 100%
rename from Server/Gumps/GumpTooltip.cs
rename to Projects/Server/Gumps/GumpTooltip.cs
diff --git a/Server/Gumps/RelayInfo.cs b/Projects/Server/Gumps/RelayInfo.cs
similarity index 100%
rename from Server/Gumps/RelayInfo.cs
rename to Projects/Server/Gumps/RelayInfo.cs
diff --git a/Server/HuePicker.cs b/Projects/Server/HuePicker.cs
similarity index 100%
rename from Server/HuePicker.cs
rename to Projects/Server/HuePicker.cs
diff --git a/Server/IAccount.cs b/Projects/Server/IAccount.cs
similarity index 100%
rename from Server/IAccount.cs
rename to Projects/Server/IAccount.cs
diff --git a/Server/IEntity.cs b/Projects/Server/IEntity.cs
similarity index 100%
rename from Server/IEntity.cs
rename to Projects/Server/IEntity.cs
diff --git a/Server/Insensitive.cs b/Projects/Server/Insensitive.cs
similarity index 100%
rename from Server/Insensitive.cs
rename to Projects/Server/Insensitive.cs
diff --git a/Server/Interfaces.cs b/Projects/Server/Interfaces.cs
similarity index 100%
rename from Server/Interfaces.cs
rename to Projects/Server/Interfaces.cs
diff --git a/Server/Item.cs b/Projects/Server/Item.cs
similarity index 100%
rename from Server/Item.cs
rename to Projects/Server/Item.cs
diff --git a/Server/ItemBounds.cs b/Projects/Server/ItemBounds.cs
similarity index 100%
rename from Server/ItemBounds.cs
rename to Projects/Server/ItemBounds.cs
diff --git a/Server/Items/BaseMulti.cs b/Projects/Server/Items/BaseMulti.cs
similarity index 100%
rename from Server/Items/BaseMulti.cs
rename to Projects/Server/Items/BaseMulti.cs
diff --git a/Server/Items/Container.cs b/Projects/Server/Items/Container.cs
similarity index 100%
rename from Server/Items/Container.cs
rename to Projects/Server/Items/Container.cs
diff --git a/Server/Items/Containers.cs b/Projects/Server/Items/Containers.cs
similarity index 100%
rename from Server/Items/Containers.cs
rename to Projects/Server/Items/Containers.cs
diff --git a/Server/Items/SecureTradeContainer.cs b/Projects/Server/Items/SecureTradeContainer.cs
similarity index 100%
rename from Server/Items/SecureTradeContainer.cs
rename to Projects/Server/Items/SecureTradeContainer.cs
diff --git a/Server/Items/VirtualCheck.cs b/Projects/Server/Items/VirtualCheck.cs
similarity index 100%
rename from Server/Items/VirtualCheck.cs
rename to Projects/Server/Items/VirtualCheck.cs
diff --git a/Server/Items/VirtualHair.cs b/Projects/Server/Items/VirtualHair.cs
similarity index 100%
rename from Server/Items/VirtualHair.cs
rename to Projects/Server/Items/VirtualHair.cs
diff --git a/Server/KeywordList.cs b/Projects/Server/KeywordList.cs
similarity index 100%
rename from Server/KeywordList.cs
rename to Projects/Server/KeywordList.cs
diff --git a/Server/Layer.cs b/Projects/Server/Layer.cs
similarity index 100%
rename from Server/Layer.cs
rename to Projects/Server/Layer.cs
diff --git a/Server/LightType.cs b/Projects/Server/LightType.cs
similarity index 100%
rename from Server/LightType.cs
rename to Projects/Server/LightType.cs
diff --git a/Server/Main.cs b/Projects/Server/Main.cs
similarity index 91%
rename from Server/Main.cs
rename to Projects/Server/Main.cs
index e5bf0daaa..798c523a4 100644
--- a/Server/Main.cs
+++ b/Projects/Server/Main.cs
@@ -112,12 +112,10 @@ namespace Server
public static bool Service{ get; private set; }
- public static bool Debug{ get; private set; }
+ public static bool Debug { get; private set; }
internal static bool HaltOnWarning{ get; private set; }
- internal static bool VBdotNet{ get; private set; }
-
public static List DataDirectories{ get; } = new List();
public static Assembly Assembly{ get; set; }
@@ -199,9 +197,6 @@ namespace Server
if (HaltOnWarning)
Utility.Separate(sb, "-haltonwarning", " ");
- if (VBdotNet)
- Utility.Separate(sb, "-vb", " ");
-
if (_UseHRT)
Utility.Separate(sb, "-usehrt", " ");
@@ -270,7 +265,8 @@ namespace Server
{
try
{
- foreach (Listener l in MessagePump.Listeners) l.Dispose();
+ foreach (Listener l in MessagePump.Listeners)
+ l.Dispose();
}
catch
{
@@ -322,7 +318,7 @@ namespace Server
Closing = true;
- Console.Write("Exiting...");
+ Console.WriteLine("Exiting...");
World.WaitForWriteCompletion();
@@ -355,8 +351,6 @@ namespace Server
m_Cache = false;
else if (Insensitive.Equals(a, "-haltonwarning"))
HaltOnWarning = true;
- else if (Insensitive.Equals(a, "-vb"))
- VBdotNet = true;
else if (Insensitive.Equals(a, "-usehrt"))
_UseHRT = true;
@@ -398,10 +392,14 @@ namespace Server
Version ver = Assembly.GetName().Version;
// Added to help future code support on forums, as a 'check' people can ask for to it see if they recompiled core or not
- Console.WriteLine("RunUO - [https://github.com/runuo/] Version {0}.{1}.{2}.{3}", ver.Major, ver.Minor, ver.Build,
+ Console.WriteLine("ModernUO - [https://github.com/kamronbatman/ModernUO] Version {0}.{1}.{2}.{3}", ver.Major, ver.Minor, ver.Build,
ver.Revision);
+#if NETCORE
+ Console.WriteLine("Core: Running on {0}", RuntimeInformation.FrameworkDescription);
+#else
Console.WriteLine("Core: Running on .NET Framework Version {0}.{1}.{2}", Environment.Version.Major,
Environment.Version.Minor, Environment.Version.Build);
+#endif
string s = Arguments;
@@ -440,18 +438,8 @@ namespace Server
Console.WriteLine("RandomImpl: {0} ({1})", RandomImpl.Type.Name,
RandomImpl.IsHardwareRNG ? "Hardware" : "Software");
- while (!ScriptCompiler.Compile(Debug, m_Cache))
- {
- Console.WriteLine("Scripts: One or more scripts failed to compile or no script files were found.");
-
- if (Service)
- return;
-
- Console.WriteLine(" - Press return to exit, or R to try again.");
-
- if (Console.ReadKey(true).Key != ConsoleKey.R)
- return;
- }
+ // Load Assembly Scripts.CS.dll
+ ScriptCompiler.LoadScripts();
ScriptCompiler.Invoke("Configure");
@@ -460,7 +448,10 @@ namespace Server
ScriptCompiler.Invoke("Initialize");
- MessagePump messagePump = MessagePump = new MessagePump();
+ // Start accepting new connections
+ MessagePump = new MessagePump();
+
+ ScriptCompiler.Invoke("RegisterListeners");
timerThread.Start();
@@ -488,16 +479,15 @@ namespace Server
Task.Run(() => Mobile.ProcessDeltaQueue()),
Task.Run(() => Item.ProcessDeltaQueue()),
Task.Run(() => Timer.Slice()),
- Task.Run(() => messagePump.Slice())
+ Task.Run(() => MessagePump.DoWork())
);
-
- NetState.FlushAll();
NetState.ProcessDisposedQueue();
Slice?.Invoke();
- if (sample++ % sampleInterval != 0) continue;
+ if (sample++ % sampleInterval != 0)
+ continue;
long now = TickCount;
m_CyclesPerSecond[m_CycleIndex++ % m_CyclesPerSecond.Length] = ticksPerSecond / (now - last);
@@ -596,7 +586,7 @@ namespace Server
internal static extern bool SetConsoleCtrlHandler(ConsoleEventHandler callback, bool add);
}
- #region Expansions
+#region Expansions
public static Expansion Expansion{ get; set; }
@@ -620,7 +610,7 @@ namespace Server
public static bool TOL => Expansion >= Expansion.TOL;
- #endregion
+#endregion
}
public class FileLogger : TextWriter
diff --git a/Server/Map.cs b/Projects/Server/Map.cs
similarity index 100%
rename from Server/Map.cs
rename to Projects/Server/Map.cs
diff --git a/Server/Menus/IMenu.cs b/Projects/Server/Menus/IMenu.cs
similarity index 100%
rename from Server/Menus/IMenu.cs
rename to Projects/Server/Menus/IMenu.cs
diff --git a/Server/Menus/ItemListMenu.cs b/Projects/Server/Menus/ItemListMenu.cs
similarity index 100%
rename from Server/Menus/ItemListMenu.cs
rename to Projects/Server/Menus/ItemListMenu.cs
diff --git a/Server/Menus/QuestionMenu.cs b/Projects/Server/Menus/QuestionMenu.cs
similarity index 100%
rename from Server/Menus/QuestionMenu.cs
rename to Projects/Server/Menus/QuestionMenu.cs
diff --git a/Server/Mobile.cs b/Projects/Server/Mobile.cs
similarity index 96%
rename from Server/Mobile.cs
rename to Projects/Server/Mobile.cs
index 87aae8bc3..4b06f948b 100644
--- a/Server/Mobile.cs
+++ b/Projects/Server/Mobile.cs
@@ -1448,13 +1448,7 @@ namespace Server
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Owner)]
public NetState NetState
{
- get
- {
- if (m_NetState != null && m_NetState.Socket == null && !m_NetState.IsDisposing)
- NetState = null;
-
- return m_NetState;
- }
+ get => m_NetState?.Socket != null && !m_NetState.IsDisposing ? m_NetState : null;
set
{
if (m_NetState != value)
diff --git a/Server/Movement.cs b/Projects/Server/Movement.cs
similarity index 100%
rename from Server/Movement.cs
rename to Projects/Server/Movement.cs
diff --git a/Server/MultiData.cs b/Projects/Server/MultiData.cs
similarity index 100%
rename from Server/MultiData.cs
rename to Projects/Server/MultiData.cs
diff --git a/Server/NativeReader.cs b/Projects/Server/NativeReader.cs
similarity index 100%
rename from Server/NativeReader.cs
rename to Projects/Server/NativeReader.cs
diff --git a/Server/Network/Compression.cs b/Projects/Server/Network/Compression.cs
similarity index 96%
rename from Server/Network/Compression.cs
rename to Projects/Server/Network/Compression.cs
index 5d0803004..8f2ba1ca7 100644
--- a/Server/Network/Compression.cs
+++ b/Projects/Server/Network/Compression.cs
@@ -47,8 +47,7 @@ namespace Server.Network
// If our input exceeds this length, we may potentially overflow the buffer
private const int PossibleOverflow = (BufferSize * 8 - TerminalCodeLength) / MaximalCodeLength;
- private static int[] _huffmanTable = new int[514]
- {
+ private static int[] _huffmanTable = {
0x2, 0x000, 0x5, 0x01F, 0x6, 0x022, 0x7, 0x034, 0x7, 0x075, 0x6, 0x028, 0x6, 0x03B, 0x7, 0x032,
0x8, 0x0E0, 0x8, 0x062, 0x7, 0x056, 0x8, 0x079, 0x9, 0x19D, 0x8, 0x097, 0x6, 0x02A, 0x7, 0x057,
0x8, 0x071, 0x8, 0x05B, 0x9, 0x1CC, 0x8, 0x0A7, 0x7, 0x025, 0x7, 0x04F, 0x8, 0x066, 0x8, 0x07D,
@@ -107,10 +106,10 @@ namespace Server.Network
public static unsafe void Compress(byte[] input, int offset, int count, byte[] output, ref int length)
{
- if (input == null) throw new ArgumentNullException("input");
+ if (input == null) throw new ArgumentNullException(nameof(input));
- if (offset < 0 || offset >= input.Length) throw new ArgumentOutOfRangeException("offset");
- if (count < 0 || count > input.Length) throw new ArgumentOutOfRangeException("count");
+ if (offset < 0 || offset >= input.Length) throw new ArgumentOutOfRangeException(nameof(offset));
+ if (count < 0 || count > input.Length) throw new ArgumentOutOfRangeException(nameof(count));
if (input.Length - offset < count) throw new ArgumentException();
length = 0;
diff --git a/Server/Network/EncodedPacketHandler.cs b/Projects/Server/Network/EncodedPacketHandler.cs
similarity index 96%
rename from Server/Network/EncodedPacketHandler.cs
rename to Projects/Server/Network/EncodedPacketHandler.cs
index 533cc7643..c400b5bbe 100644
--- a/Server/Network/EncodedPacketHandler.cs
+++ b/Projects/Server/Network/EncodedPacketHandler.cs
@@ -37,4 +37,4 @@ namespace Server.Network
public bool Ingame{ get; }
}
-}
\ No newline at end of file
+}
diff --git a/Server/Network/EncodedReader.cs b/Projects/Server/Network/EncodedReader.cs
similarity index 54%
rename from Server/Network/EncodedReader.cs
rename to Projects/Server/Network/EncodedReader.cs
index 5caf631e5..cbca5ea66 100644
--- a/Server/Network/EncodedReader.cs
+++ b/Projects/Server/Network/EncodedReader.cs
@@ -20,7 +20,7 @@
namespace Server.Network
{
- public class EncodedReader
+ public ref struct EncodedReader
{
private PacketReader m_Reader;
@@ -29,47 +29,20 @@ namespace Server.Network
m_Reader = reader;
}
- public byte[] Buffer => m_Reader.Buffer;
-
public void Trace(NetState state)
{
m_Reader.Trace(state);
}
- public int ReadInt32()
- {
- if (m_Reader.ReadByte() != 0)
- return 0;
+ public int ReadInt32() => m_Reader.ReadByte() != 0 ? 0 : m_Reader.ReadInt32();
- return m_Reader.ReadInt32();
- }
+ public Point3D ReadPoint3D() => m_Reader.ReadByte() != 3 ? Point3D.Zero :
+ new Point3D(m_Reader.ReadInt16(), m_Reader.ReadInt16(), m_Reader.ReadByte());
- public Point3D ReadPoint3D()
- {
- if (m_Reader.ReadByte() != 3)
- return Point3D.Zero;
+ public string ReadUnicodeStringSafe() => m_Reader.ReadByte() != 2 ? string.Empty :
+ m_Reader.ReadUnicodeStringSafe(m_Reader.ReadUInt16());
- return new Point3D(m_Reader.ReadInt16(), m_Reader.ReadInt16(), m_Reader.ReadByte());
- }
-
- public string ReadUnicodeStringSafe()
- {
- if (m_Reader.ReadByte() != 2)
- return "";
-
- int length = m_Reader.ReadUInt16();
-
- return m_Reader.ReadUnicodeStringSafe(length);
- }
-
- public string ReadUnicodeString()
- {
- if (m_Reader.ReadByte() != 2)
- return "";
-
- int length = m_Reader.ReadUInt16();
-
- return m_Reader.ReadUnicodeString(length);
- }
+ public string ReadUnicodeString() => m_Reader.ReadByte() != 2 ? string.Empty :
+ m_Reader.ReadUnicodeString(m_Reader.ReadUInt16());
}
-}
\ No newline at end of file
+}
diff --git a/Server/Network/Listener.cs b/Projects/Server/Network/Listener.cs
similarity index 53%
rename from Server/Network/Listener.cs
rename to Projects/Server/Network/Listener.cs
index 13a9afef9..93077fca2 100644
--- a/Server/Network/Listener.cs
+++ b/Projects/Server/Network/Listener.cs
@@ -19,58 +19,35 @@
***************************************************************************/
using System;
-using System.Collections;
-using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
+using System.Threading.Tasks;
namespace Server.Network
{
- public class Listener : IDisposable
+ public class Listener
{
- private Socket m_Listener;
-
- private Queue m_Accepted;
- private object m_AcceptedSyncRoot;
-
- private SocketAsyncEventArgs m_EventArgs;
-
- private static Socket[] m_EmptySockets = new Socket[0];
-
- public static IPEndPoint[] EndPoints{ get; set; }
-
+ private Socket m_Socket;
+ private IPEndPoint m_EndPoint;
public Listener(IPEndPoint ipep)
{
- m_Accepted = new Queue();
- m_AcceptedSyncRoot = ((ICollection)m_Accepted).SyncRoot;
+#pragma warning disable IDE0068 // Use recommended dispose pattern
+ m_Socket = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+#pragma warning restore IDE0068 // Use recommended dispose pattern
- m_Listener = Bind(ipep);
-
- if (m_Listener == null)
- return;
-
- DisplayListener();
-
- m_EventArgs = new SocketAsyncEventArgs();
- m_EventArgs.Completed += Accept_Completion;
- Accept_Start();
+ m_Socket.LingerState.Enabled = false;
+ m_Socket.ExclusiveAddressUse = false;
+ m_EndPoint = ipep;
}
- private Socket Bind(IPEndPoint ipep)
+ public virtual async Task Start(MessagePump pump)
{
- Socket s = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-
try
{
- s.LingerState.Enabled = false;
- s.ExclusiveAddressUse = false;
-
- s.Bind(ipep);
- s.Listen(8);
-
- return s;
+ m_Socket.Bind(m_EndPoint);
+ m_Socket.Listen(8);
}
catch (Exception e)
{
@@ -79,12 +56,12 @@ namespace Server.Network
if (se.ErrorCode == 10048)
{
// WSAEADDRINUSE
- Console.WriteLine("Listener Failed: {0}:{1} (In Use)", ipep.Address, ipep.Port);
+ Console.WriteLine("Listener Failed: {0}:{1} (In Use)", m_EndPoint.Address, m_EndPoint.Port);
}
else if (se.ErrorCode == 10049)
{
// WSAEADDRNOTAVAIL
- Console.WriteLine("Listener Failed: {0}:{1} (Unavailable)", ipep.Address, ipep.Port);
+ Console.WriteLine("Listener Failed: {0}:{1} (Unavailable)", m_EndPoint.Address, m_EndPoint.Port);
}
else
{
@@ -93,13 +70,35 @@ namespace Server.Network
}
}
- return null;
+ m_Socket = null;
+ return;
+ }
+
+ DisplayListener();
+
+ while (true)
+ {
+ Socket s;
+ try
+ {
+ s = await m_Socket.AcceptAsync().ConfigureAwait(false);
+ }
+ catch (SocketException ex)
+ {
+ NetState.TraceException(ex);
+ continue;
+ }
+
+ if (VerifySocket(s))
+ _ = new NetState(s, pump);
+ else
+ Release(s);
}
}
private void DisplayListener()
{
- if (!(m_Listener.LocalEndPoint is IPEndPoint ipep))
+ if (!(m_Socket.LocalEndPoint is IPEndPoint ipep))
return;
if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any))
@@ -112,17 +111,6 @@ namespace Server.Network
if (ipep.AddressFamily == unicast.Address.AddressFamily)
Console.WriteLine("Listening: {0}:{1}", unicast.Address, ipep.Port);
}
-
- /*
- try {
- Console.WriteLine( "Listening: {0}:{1}", IPAddress.Loopback, ipep.Port );
- IPHostEntry iphe = Dns.GetHostEntry( Dns.GetHostName() );
- IPAddress[] ip = iphe.AddressList;
- for ( int i = 0; i < ip.Length; ++i )
- Console.WriteLine( "Listening: {0}:{1}", ip[i], ipep.Port );
- }
- catch { }
- */
}
else
{
@@ -130,49 +118,6 @@ namespace Server.Network
}
}
- private void Accept_Start()
- {
- bool result;
-
- do
- {
- try
- {
- result = !m_Listener.AcceptAsync(m_EventArgs);
- }
- catch (SocketException ex)
- {
- NetState.TraceException(ex);
- break;
- }
- catch (ObjectDisposedException)
- {
- break;
- }
-
- if (result)
- Accept_Process(m_EventArgs);
- } while (result);
- }
-
- private void Accept_Completion(object sender, SocketAsyncEventArgs e)
- {
- Accept_Process(e);
-
- Accept_Start();
- }
-
- private void Accept_Process(SocketAsyncEventArgs e)
- {
- if (e.SocketError == SocketError.Success && VerifySocket(e.AcceptSocket))
- Enqueue(e.AcceptSocket);
- else
- Release(e.AcceptSocket);
-
- e.AcceptSocket = null;
- }
-
-
private bool VerifySocket(Socket socket)
{
try
@@ -191,16 +136,6 @@ namespace Server.Network
}
}
- private void Enqueue(Socket socket)
- {
- lock (m_AcceptedSyncRoot)
- {
- m_Accepted.Enqueue(socket);
- }
-
- Core.Set();
- }
-
private void Release(Socket socket)
{
try
@@ -220,37 +155,20 @@ namespace Server.Network
{
NetState.TraceException(ex);
}
- }
- public Socket[] Slice()
- {
- Socket[] array;
-
- lock (m_AcceptedSyncRoot)
+ try
{
- if (m_Accepted.Count == 0)
- return m_EmptySockets;
-
- array = m_Accepted.ToArray();
- m_Accepted.Clear();
+ socket.Dispose();
}
-
- return array;
- }
-
- public void Dispose(bool disposing)
- {
- if (disposing)
+ catch (SocketException ex)
{
- Socket socket = Interlocked.Exchange(ref m_Listener, null);
-
- socket?.Close();
+ NetState.TraceException(ex);
}
}
public void Dispose()
{
- Dispose(true);
+ Interlocked.Exchange(ref m_Socket, null)?.Close();
GC.SuppressFinalize(this);
}
}
diff --git a/Projects/Server/Network/MessagePump.cs b/Projects/Server/Network/MessagePump.cs
new file mode 100644
index 000000000..f9c735fdf
--- /dev/null
+++ b/Projects/Server/Network/MessagePump.cs
@@ -0,0 +1,77 @@
+
+/***************************************************************************
+ * MessagePump.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.Buffers;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Net;
+
+namespace Server.Network
+{
+ public class MessagePump
+ {
+ private ConcurrentQueue m_WorkQueue = new ConcurrentQueue();
+ public Listener[] Listeners => new Listener[0];
+
+ 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 void QueueWork(NetState ns, in ReadOnlySequence seq, OnPacketReceive onReceive)
+ {
+ m_WorkQueue.Enqueue(new Work(ns, seq, onReceive));
+ Core.Set();
+ }
+
+ public void DoWork()
+ {
+ int count = m_WorkQueue.Count;
+ while (count-- > 0)
+ {
+ if (!m_WorkQueue.TryDequeue(out Work work))
+ break;
+
+ work.OnReceive(work.State, new PacketReader(work.Sequence));
+ }
+ }
+
+ // TODO: Optimize this with a pool
+ private class Work
+ {
+ public NetState State;
+ public ReadOnlySequence Sequence;
+ public OnPacketReceive OnReceive;
+
+ public Work(NetState ns, in ReadOnlySequence seq, OnPacketReceive onReceive)
+ {
+ State = ns;
+ Sequence = seq;
+ OnReceive = onReceive;
+ }
+ }
+ }
+}
diff --git a/Projects/Server/Network/NetState.cs b/Projects/Server/Network/NetState.cs
new file mode 100644
index 000000000..51f4cbf8f
--- /dev/null
+++ b/Projects/Server/Network/NetState.cs
@@ -0,0 +1,764 @@
+/***************************************************************************
+ * NetState.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.Buffers;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Pipelines;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
+using Server.Accounting;
+using Server.Diagnostics;
+using Server.Gumps;
+using Server.HuePickers;
+using Server.Items;
+using Server.Menus;
+
+namespace Server.Network
+{
+ public interface IPacketEncoder
+ {
+ void EncodeOutgoingPacket(NetState to, ref Memory seq);
+ void DecodeIncomingPacket(NetState from, ref Memory seq);
+ }
+
+ public delegate void NetStateCreatedCallback(NetState ns);
+
+ [Flags]
+ public enum ProtocolChanges
+ {
+ NewSpellbook = 0x00000001,
+ DamagePacket = 0x00000002,
+ Unpack = 0x00000004,
+ BuffIcon = 0x00000008,
+ NewHaven = 0x00000010,
+ ContainerGridLines = 0x00000020,
+ ExtendedSupportedFeatures = 0x00000040,
+ StygianAbyss = 0x00000080,
+ HighSeas = 0x00000100,
+ NewCharacterList = 0x00000200,
+ NewCharacterCreation = 0x00000400,
+ ExtendedStatus = 0x00000800,
+ NewMobileIncoming = 0x00001000,
+ NewSecureTrading = 0x00002000,
+ UltimaStore = 0x00004000,
+ EndlessJourney = 0x00008000,
+
+ Version400a = NewSpellbook,
+ Version407a = Version400a | DamagePacket,
+ Version500a = Version407a | Unpack,
+ Version502b = Version500a | BuffIcon,
+ Version6000 = Version502b | NewHaven,
+ Version6017 = Version6000 | ContainerGridLines,
+ Version60142 = Version6017 | ExtendedSupportedFeatures,
+ Version7000 = Version60142 | StygianAbyss,
+ Version7090 = Version7000 | HighSeas,
+ Version70130 = Version7090 | NewCharacterList,
+ Version70160 = Version70130 | NewCharacterCreation,
+ Version70300 = Version70160 | ExtendedStatus,
+ Version70331 = Version70300 | NewMobileIncoming,
+ Version704565 = Version70331 | NewSecureTrading,
+ Version70500 = Version704565 | UltimaStore,
+ Version70610 = Version70500 | EndlessJourney
+ }
+
+ public class AsyncState
+ {
+ public bool Paused { get; set; }
+ public AsyncState(bool paused)
+ {
+ Paused = paused;
+ }
+ }
+
+ public class NetState : IComparable
+ {
+ private string m_ToString;
+ private ClientVersion m_Version;
+ private SendQueue m_SendQueue = new SendQueue();
+
+ public DateTime ConnectedOn { get; }
+
+ public TimeSpan ConnectedFor => DateTime.UtcNow - ConnectedOn;
+
+ public DateTime ThrottledUntil { get; set; }
+
+ internal int m_Seed;
+ internal int m_AuthID;
+
+ public IPAddress Address { get; }
+
+ private static AsyncState m_PauseState = new AsyncState(true);
+ private static AsyncState m_ResumeState = new AsyncState(false);
+
+ private static AsyncState m_AsyncState = m_ResumeState;
+
+ public IPacketEncoder PacketEncoder { get; set; }
+
+ public static NetStateCreatedCallback CreatedCallback { get; set; }
+
+ public bool SentFirstPacket { get; set; }
+
+ public bool BlockAllPackets { get; set; }
+
+ public ClientFlags Flags { get; set; }
+
+ public ClientVersion Version
+ {
+ get => m_Version;
+ set
+ {
+ m_Version = value;
+
+ if (value >= m_Version70610)
+ ProtocolChanges = ProtocolChanges.Version70610;
+ if (value >= m_Version70500)
+ ProtocolChanges = ProtocolChanges.Version70500;
+ if (value >= m_Version704565)
+ ProtocolChanges = ProtocolChanges.Version704565;
+ else if (value >= m_Version70331)
+ ProtocolChanges = ProtocolChanges.Version70331;
+ else if (value >= m_Version70300)
+ ProtocolChanges = ProtocolChanges.Version70300;
+ else if (value >= m_Version70160)
+ ProtocolChanges = ProtocolChanges.Version70160;
+ else if (value >= m_Version70130)
+ ProtocolChanges = ProtocolChanges.Version70130;
+ else if (value >= m_Version7090)
+ ProtocolChanges = ProtocolChanges.Version7090;
+ else if (value >= m_Version7000)
+ ProtocolChanges = ProtocolChanges.Version7000;
+ else if (value >= m_Version60142)
+ ProtocolChanges = ProtocolChanges.Version60142;
+ else if (value >= m_Version6017)
+ ProtocolChanges = ProtocolChanges.Version6017;
+ else if (value >= m_Version6000)
+ ProtocolChanges = ProtocolChanges.Version6000;
+ else if (value >= m_Version502b)
+ ProtocolChanges = ProtocolChanges.Version502b;
+ else if (value >= m_Version500a)
+ ProtocolChanges = ProtocolChanges.Version500a;
+ else if (value >= m_Version407a)
+ ProtocolChanges = ProtocolChanges.Version407a;
+ else if (value >= m_Version400a) ProtocolChanges = ProtocolChanges.Version400a;
+ }
+ }
+
+ private static readonly ClientVersion m_Version400a = new ClientVersion("4.0.0a");
+ private static readonly ClientVersion m_Version407a = new ClientVersion("4.0.7a");
+ private static readonly ClientVersion m_Version500a = new ClientVersion("5.0.0a");
+ private static readonly ClientVersion m_Version502b = new ClientVersion("5.0.2b");
+ private static readonly ClientVersion m_Version6000 = new ClientVersion("6.0.0.0");
+ private static readonly ClientVersion m_Version6017 = new ClientVersion("6.0.1.7");
+ private static readonly ClientVersion m_Version60142 = new ClientVersion("6.0.14.2");
+ private static readonly ClientVersion m_Version7000 = new ClientVersion("7.0.0.0");
+ private static readonly ClientVersion m_Version7090 = new ClientVersion("7.0.9.0");
+ private static readonly ClientVersion m_Version70130 = new ClientVersion("7.0.13.0");
+ private static readonly ClientVersion m_Version70160 = new ClientVersion("7.0.16.0");
+ private static readonly ClientVersion m_Version70300 = new ClientVersion("7.0.30.0");
+ private static readonly ClientVersion m_Version70331 = new ClientVersion("7.0.33.1");
+ private static readonly ClientVersion m_Version704565 = new ClientVersion("7.0.45.65");
+ private static readonly ClientVersion m_Version70500 = new ClientVersion("7.0.50.0");
+ private static readonly ClientVersion m_Version70610 = new ClientVersion("7.0.61.0");
+
+ public bool NewSpellbook => (ProtocolChanges & ProtocolChanges.NewSpellbook) != 0;
+ public bool DamagePacket => (ProtocolChanges & ProtocolChanges.DamagePacket) != 0;
+ public bool Unpack => (ProtocolChanges & ProtocolChanges.Unpack) != 0;
+ public bool BuffIcon => (ProtocolChanges & ProtocolChanges.BuffIcon) != 0;
+ public bool NewHaven => (ProtocolChanges & ProtocolChanges.NewHaven) != 0;
+ public bool ContainerGridLines => (ProtocolChanges & ProtocolChanges.ContainerGridLines) != 0;
+ public bool ExtendedSupportedFeatures => (ProtocolChanges & ProtocolChanges.ExtendedSupportedFeatures) != 0;
+ public bool StygianAbyss => (ProtocolChanges & ProtocolChanges.StygianAbyss) != 0;
+ public bool HighSeas => (ProtocolChanges & ProtocolChanges.HighSeas) != 0;
+ public bool NewCharacterList => (ProtocolChanges & ProtocolChanges.NewCharacterList) != 0;
+ public bool NewCharacterCreation => (ProtocolChanges & ProtocolChanges.NewCharacterCreation) != 0;
+ public bool ExtendedStatus => (ProtocolChanges & ProtocolChanges.ExtendedStatus) != 0;
+ public bool NewMobileIncoming => (ProtocolChanges & ProtocolChanges.NewMobileIncoming) != 0;
+ public bool NewSecureTrading => (ProtocolChanges & ProtocolChanges.NewSecureTrading) != 0;
+
+ public bool IsUOTDClient =>
+ (Flags & ClientFlags.UOTD) != 0 || m_Version?.Type == ClientType.UOTD;
+
+ public bool IsSAClient => m_Version?.Type == ClientType.SA;
+
+ public List Trades { get; }
+
+ public void ValidateAllTrades()
+ {
+ for (int i = Trades.Count - 1; i >= 0; --i)
+ {
+ if (i >= Trades.Count) continue;
+
+ SecureTrade trade = Trades[i];
+
+ if (trade.From.Mobile.Deleted || trade.To.Mobile.Deleted || !trade.From.Mobile.Alive ||
+ !trade.To.Mobile.Alive || !trade.From.Mobile.InRange(trade.To.Mobile, 2) ||
+ trade.From.Mobile.Map != trade.To.Mobile.Map) trade.Cancel();
+ }
+ }
+
+ public void CancelAllTrades()
+ {
+ for (int i = Trades.Count - 1; i >= 0; --i)
+ if (i < Trades.Count)
+ Trades[i].Cancel();
+ }
+
+ public void RemoveTrade(SecureTrade trade)
+ {
+ Trades.Remove(trade);
+ }
+
+ public SecureTrade FindTrade(Mobile m)
+ {
+ for (int i = 0; i < Trades.Count; ++i)
+ {
+ SecureTrade trade = Trades[i];
+
+ if (trade.From.Mobile == m || trade.To.Mobile == m) return trade;
+ }
+
+ return null;
+ }
+
+ public SecureTradeContainer FindTradeContainer(Mobile m)
+ {
+ for (int i = 0; i < Trades.Count; ++i)
+ {
+ SecureTrade trade = Trades[i];
+
+ SecureTradeInfo from = trade.From;
+ SecureTradeInfo to = trade.To;
+
+ if (from.Mobile == Mobile && to.Mobile == m) return from.Container;
+
+ if (from.Mobile == m && to.Mobile == Mobile) return to.Container;
+ }
+
+ return null;
+ }
+
+ public SecureTradeContainer AddTrade(NetState state)
+ {
+ SecureTrade newTrade = new SecureTrade(Mobile, state.Mobile);
+
+ Trades.Add(newTrade);
+ state.Trades.Add(newTrade);
+
+ return newTrade.From.Container;
+ }
+
+ public bool Running { get; private set; }
+
+ public bool Seeded { get; set; }
+
+ public Socket Socket { get; private set; }
+
+ public bool CompressionEnabled { get; set; }
+
+ public int Sequence { get; set; }
+
+ public List Gumps { get; private set; }
+
+ public List HuePickers { get; private set; }
+
+ public List Menus { get; private set; }
+
+ public static int GumpCap { get; set; } = 512;
+
+ public static int HuePickerCap { get; set; } = 512;
+
+ public static int MenuCap { get; set; } = 512;
+
+ public void WriteConsole(string text)
+ {
+ Console.WriteLine("Client: {0}: {1}", this, text);
+ }
+
+ public void WriteConsole(string format, params object[] args)
+ {
+ WriteConsole(string.Format(format, args));
+ }
+
+ public void AddMenu(IMenu menu)
+ {
+ if (Menus == null)
+ Menus = new List();
+
+ if (Menus.Count < MenuCap)
+ Menus.Add(menu);
+ else
+ {
+ WriteConsole("Exceeded menu cap, disconnecting...");
+ Dispose();
+ }
+ }
+
+ public void RemoveMenu(IMenu menu)
+ {
+ Menus?.Remove(menu);
+ }
+
+ public void RemoveMenu(int index)
+ {
+ Menus?.RemoveAt(index);
+ }
+
+ public void ClearMenus()
+ {
+ Menus?.Clear();
+ }
+
+ public void AddHuePicker(HuePicker huePicker)
+ {
+ if (HuePickers == null)
+ HuePickers = new List();
+
+ if (HuePickers.Count < HuePickerCap)
+ HuePickers.Add(huePicker);
+ else
+ {
+ WriteConsole("Exceeded hue picker cap, disconnecting...");
+ Dispose();
+ }
+ }
+
+ public void RemoveHuePicker(HuePicker huePicker)
+ {
+ HuePickers?.Remove(huePicker);
+ }
+
+ public void RemoveHuePicker(int index)
+ {
+ HuePickers?.RemoveAt(index);
+ }
+
+ public void ClearHuePickers()
+ {
+ HuePickers?.Clear();
+ }
+
+ public void AddGump(Gump gump)
+ {
+ if (Gumps == null)
+ Gumps = new List();
+
+ if (Gumps.Count < GumpCap)
+ Gumps.Add(gump);
+ else
+ {
+ WriteConsole("Exceeded gump cap, disconnecting...");
+ Dispose();
+ }
+ }
+
+ public void RemoveGump(Gump gump)
+ {
+ Gumps?.Remove(gump);
+ }
+
+ public void RemoveGump(int index)
+ {
+ Gumps?.RemoveAt(index);
+ }
+
+ public void ClearGumps()
+ {
+ Gumps?.Clear();
+ }
+
+ public void LaunchBrowser(string url)
+ {
+ Send(new MessageLocalized(Serial.MinusOne, -1, MessageType.Label, 0x35, 3, 501231, "", ""));
+ Send(new LaunchBrowser(url));
+ }
+
+ public CityInfo[] CityInfo { get; set; }
+
+ public Mobile Mobile { get; set; }
+
+ public ServerInfo[] ServerInfo { get; set; }
+
+ public IAccount Account { get; set; }
+
+ public override string ToString()
+ {
+ return m_ToString;
+ }
+
+ public static List Instances { get; } = new List();
+
+ public NetState(Socket socket, MessagePump pump)
+ {
+ Socket = socket;
+ Seeded = false;
+ Running = false;
+ Gumps = new List();
+ HuePickers = new List();
+ Menus = new List();
+ Trades = new List();
+
+ m_NextCheckActivity = Core.TickCount + 30000;
+
+ Instances.Add(this);
+
+ try
+ {
+ Address = Utility.Intern(((IPEndPoint)Socket.RemoteEndPoint).Address);
+ m_ToString = Address.ToString();
+ }
+ catch (Exception ex)
+ {
+ TraceException(ex);
+ Address = IPAddress.None;
+ m_ToString = "(error)";
+ }
+
+ ConnectedOn = DateTime.UtcNow;
+ _ = Start(pump);
+
+ CreatedCallback?.Invoke(this);
+ Console.WriteLine("Client: {0}: Connected. [{1} Online]", this, NetState.Instances.Count);
+ }
+
+ public static void Pause()
+ {
+ m_AsyncState = Interlocked.Exchange(ref m_AsyncState, m_PauseState);
+ }
+
+ public static void Resume()
+ {
+ m_AsyncState = Interlocked.Exchange(ref m_AsyncState, m_ResumeState);
+ }
+
+ public virtual void Send(Packet p)
+ {
+ if (Socket == null || BlockAllPackets)
+ {
+ p.OnSend();
+ return;
+ }
+
+ m_SendQueue.Enqueue(p);
+ }
+
+ public bool CheckEncrypted(int packetID)
+ {
+ if (!SentFirstPacket && packetID != 0xF0 && packetID != 0xF1 && packetID != 0xCF && packetID != 0x80 &&
+ packetID != 0x91 && packetID != 0xA4 && packetID != 0xEF)
+ {
+ Console.WriteLine("Client: {0}: Encrypted client detected, disconnecting", this);
+ Dispose();
+ return true;
+ }
+
+ return false;
+ }
+
+ private Pipe m_RecvdPipe;
+
+ private async Task Start(MessagePump pump)
+ {
+ m_RecvdPipe = new Pipe();
+ Running = true;
+
+ await Task.WhenAll(ProcessSends(), ProcessRecvs(), HandlePackets(pump)).ConfigureAwait(false);
+ }
+
+ private async Task ProcessRecvs()
+ {
+ PipeWriter w = m_RecvdPipe.Writer;
+
+ while (true)
+ {
+ if (m_AsyncState.Paused)
+ {
+ await Timer.Pause(50).ConfigureAwait(false);
+ continue;
+ }
+
+ try
+ {
+ Memory memory = w.GetMemory();
+ int bytesRead = await Socket.ReceiveAsync(memory, SocketFlags.None).ConfigureAwait(false);
+ if (bytesRead == 0)
+ break;
+
+ Interlocked.Exchange(ref m_NextCheckActivity, Core.TickCount + 90000);
+
+ w.Advance(bytesRead);
+
+ FlushResult result = await w.FlushAsync().ConfigureAwait(false);
+ if (result.IsCompleted || result.IsCanceled)
+ break;
+ }
+ catch
+ {
+ break;
+ }
+ }
+
+ w.Complete();
+ Dispose();
+ }
+
+ private async Task ProcessSends()
+ {
+ while (true)
+ {
+ try
+ {
+ Packet p = await m_SendQueue?.DequeueAsync() ?? null;
+ if (p == null)
+ break;
+
+ ReadOnlyMemory buffer = p.Compile(CompressionEnabled, out int length);
+
+ if (buffer.Length <= 0 || length <= 0)
+ {
+ p.OnSend();
+ return;
+ }
+
+ buffer = buffer.Slice(0, length);
+
+ PacketSendProfile prof = null;
+
+ if (Core.Profiling)
+ prof = PacketSendProfile.Acquire(p.GetType());
+
+ prof?.Start();
+
+ await Socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false);
+
+ p.OnSend();
+
+ prof?.Finish(length);
+ }
+ catch (SocketException ex)
+ {
+ Console.WriteLine(ex);
+ TraceException(ex);
+ Dispose();
+ break;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ }
+ }
+ }
+
+ private async Task HandlePackets(MessagePump pump)
+ {
+ PipeReader pr = m_RecvdPipe.Reader;
+
+ while (true)
+ {
+ ReadResult result = await pr.ReadAsync();
+ ReadOnlySequence seq = result.Buffer;
+ if (seq.Length == 0)
+ break;
+
+ long pos = PacketHandlers.ProcessPacket(pump, this, seq);
+
+ if (pos <= 0)
+ break;
+
+ pr.AdvanceTo(seq.GetPosition(pos, seq.Start));
+
+ if (result.IsCompleted || result.IsCanceled)
+ break;
+ }
+
+ pr.Complete();
+ }
+
+ public PacketHandler GetHandler(int packetID)
+ {
+ return ContainerGridLines ? PacketHandlers.Get6017Handler(packetID) :
+ PacketHandlers.GetHandler(packetID);
+ }
+
+ private long m_NextCheckActivity;
+
+ public void CheckAlive(long curTicks)
+ {
+ if (Socket == 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)
+ return;
+
+ try
+ {
+ using (StreamWriter op = new StreamWriter("network-errors.log", true))
+ {
+ op.WriteLine("# {0}", DateTime.UtcNow);
+
+ op.WriteLine(ex);
+
+ op.WriteLine();
+ op.WriteLine();
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+
+ Console.WriteLine(ex);
+ }
+
+ private int m_Disposing;
+
+ public bool IsDisposing { get { return m_Disposing != 0; } private set { m_Disposing = value ? 1 : 0; } }
+
+ public virtual void Dispose()
+ {
+ int disposing = Interlocked.Exchange(ref m_Disposing, 1);
+ if (disposing == 1)
+ return;
+
+ Task.Run(async () =>
+ {
+ m_RecvdPipe.Reader.CancelPendingRead();
+ m_RecvdPipe.Reader.Complete();
+ await m_RecvdPipe.Writer.FlushAsync().ConfigureAwait(false);
+ m_RecvdPipe.Writer.Complete();
+
+ try { Socket.Shutdown(SocketShutdown.Both); } catch (Exception ex) { TraceException(ex); }
+ try { Socket.Close(); } catch (Exception ex) { TraceException(ex); }
+
+ Socket = null;
+ m_RecvdPipe = null;
+ m_SendQueue = null;
+ 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();
+
+ public static void ProcessDisposedQueue()
+ {
+ int breakout = 0;
+
+ while (breakout++ < 200)
+ {
+ if (!m_Disposed.TryDequeue(out NetState ns))
+ break;
+
+ Mobile m = ns.Mobile;
+ IAccount a = ns.Account;
+
+ if (m != null)
+ {
+ m.NetState = null;
+ ns.Mobile = null;
+ }
+
+ ns.Gumps.Clear();
+ ns.Menus.Clear();
+ ns.HuePickers.Clear();
+ ns.Account = null;
+ ns.ServerInfo = null;
+ ns.CityInfo = null;
+
+ Instances.Remove(ns);
+
+ if (a != null)
+ ns.WriteConsole("Disconnected. [{0} Online] [{1}]", Instances.Count, a);
+ else
+ ns.WriteConsole("Disconnected. [{0} Online]", Instances.Count);
+ }
+ }
+
+ public ExpansionInfo ExpansionInfo
+ {
+ get
+ {
+ for (int i = ExpansionInfo.Table.Length - 1; i >= 0; i--)
+ {
+ ExpansionInfo info = ExpansionInfo.Table[i];
+
+ if (info.RequiredClient != null && Version >= info.RequiredClient || (Flags & info.ClientFlags) != 0)
+ return info;
+ }
+
+ return ExpansionInfo.GetInfo(Expansion.None);
+ }
+ }
+
+ public Expansion Expansion => (Expansion)ExpansionInfo.ID;
+
+ public ProtocolChanges ProtocolChanges { get; set; }
+
+ public bool SupportsExpansion(ExpansionInfo info, bool checkCoreExpansion = true)
+ {
+ if (info == null || checkCoreExpansion && (int)Core.Expansion < info.ID)
+ return false;
+
+ return info.RequiredClient != null ? Version >= info.RequiredClient : (Flags & info.ClientFlags) != 0;
+ }
+
+ public bool SupportsExpansion(Expansion ex, bool checkCoreExpansion = true)
+ {
+ return SupportsExpansion(ExpansionInfo.GetInfo(ex), checkCoreExpansion);
+ }
+
+ public int CompareTo(NetState other)
+ {
+ return other == null ? 1 : m_ToString.CompareTo(other.m_ToString);
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packet.cs b/Projects/Server/Network/Packet.cs
new file mode 100644
index 000000000..0bfbb39fe
--- /dev/null
+++ b/Projects/Server/Network/Packet.cs
@@ -0,0 +1,273 @@
+/***************************************************************************
+ * Packet.cs
+ * -------------------
+ * begin : August 2, 2019
+ * 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 Server.Diagnostics;
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+
+namespace Server.Network
+{
+ public abstract class Packet
+ {
+ private const int CompressorBufferSize = 0x10000;
+
+ private const int BufferSize = 4096;
+
+ private byte[] m_CompiledBuffer;
+ private int m_CompiledLength;
+ private int m_Length;
+ private State m_State;
+
+ protected PacketWriter m_Stream;
+
+ protected Packet(int packetID)
+ {
+ PacketID = packetID;
+
+ if (Core.Profiling)
+ {
+ PacketSendProfile prof = PacketSendProfile.Acquire(GetType());
+ prof.Increment();
+ }
+ }
+
+ protected Packet(int packetID, int length)
+ {
+ PacketID = packetID;
+ m_Length = length;
+
+ m_Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
+ m_Stream.Write((byte)packetID);
+
+ if (Core.Profiling)
+ {
+ PacketSendProfile prof = PacketSendProfile.Acquire(GetType());
+ prof.Increment();
+ }
+ }
+
+ public int PacketID { get; }
+
+ public PacketWriter UnderlyingStream => m_Stream;
+
+ public void EnsureCapacity(int length)
+ {
+ m_Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
+ m_Stream.Write((byte)PacketID);
+ m_Stream.Write((short)0);
+ }
+
+ public static Packet SetStatic(Packet p)
+ {
+ p.SetStatic();
+ return p;
+ }
+
+ public static Packet Acquire(Packet p)
+ {
+ p.Acquire();
+ return p;
+ }
+
+ public static void Release(ref Packet p)
+ {
+ p?.Release();
+ p = null;
+ }
+
+ public static void Release(Packet p)
+ {
+ p?.Release();
+ }
+
+ public void SetStatic()
+ {
+ m_State |= State.Static | State.Acquired;
+ }
+
+ public void Acquire()
+ {
+ m_State |= State.Acquired;
+ }
+
+ public void OnSend()
+ {
+ Core.Set(); // Is this still needed if this is done async?
+
+ if ((m_State & (State.Acquired | State.Static)) == 0)
+ Free();
+ }
+
+ private void Free()
+ {
+ if (m_CompiledBuffer == null)
+ return;
+
+ if ((m_State & State.Buffered) != 0)
+ ArrayPool.Shared.Return(m_CompiledBuffer);
+
+ m_State &= ~(State.Static | State.Acquired | State.Buffered);
+
+ m_CompiledBuffer = null;
+ }
+
+ public void Release()
+ {
+ if ((m_State & State.Acquired) != 0)
+ Free();
+ }
+
+ public byte[] Compile(bool compress, out int length)
+ {
+ lock (this)
+ {
+ if (m_CompiledBuffer == null)
+ {
+ if ((m_State & State.Accessed) == 0)
+ {
+ m_State |= State.Accessed;
+ }
+ else
+ {
+ if ((m_State & State.Warned) == 0)
+ {
+ m_State |= State.Warned;
+
+ try
+ {
+ using (StreamWriter op = new StreamWriter("net_opt.log", true))
+ {
+ op.WriteLine("Redundant compile for packet {0}, use Acquire() and Release()", GetType());
+ op.WriteLine(new StackTrace());
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ m_CompiledBuffer = new byte[0];
+ m_CompiledLength = 0;
+
+ length = m_CompiledLength;
+ return m_CompiledBuffer;
+ }
+
+ InternalCompile(compress);
+ }
+
+ length = m_CompiledLength;
+ return m_CompiledBuffer;
+ }
+ }
+
+ private void InternalCompile(bool compress)
+ {
+ if (m_Length == 0)
+ {
+ long streamLen = m_Stream.Length;
+
+ m_Stream.Seek(1, SeekOrigin.Begin);
+ m_Stream.Write((ushort)streamLen);
+ }
+ else if (m_Stream.Length != m_Length)
+ {
+ int diff = (int)m_Stream.Length - m_Length;
+
+ Console.WriteLine("Packet: 0x{0:X2}: Bad packet length! ({1}{2} bytes)", PacketID, diff >= 0 ? "+" : "",
+ diff);
+ }
+
+ MemoryStream ms = m_Stream.UnderlyingStream;
+
+ m_CompiledBuffer = ms.GetBuffer();
+ int length = (int)ms.Length;
+
+ if (compress)
+ {
+ byte[] buffer = ArrayPool.Shared.Rent(CompressorBufferSize);
+
+ Compression.Compress(m_CompiledBuffer, 0, length, buffer, ref length);
+
+ if (length <= 0)
+ {
+ Console.WriteLine("Warning: Compression buffer overflowed on packet 0x{0:X2} ('{1}') (length={2})",
+ PacketID, GetType().Name, length);
+ using (StreamWriter op = new StreamWriter("compression_overflow.log", true))
+ {
+ op.WriteLine("{0} Warning: Compression buffer overflowed on packet 0x{1:X2} ('{2}') (length={3})",
+ DateTime.UtcNow, PacketID, GetType().Name, length);
+ op.WriteLine(new StackTrace());
+ }
+ }
+ else
+ {
+ m_CompiledLength = length;
+
+ if ((m_State & State.Static) != 0)
+ {
+ m_CompiledBuffer = new byte[length];
+ Buffer.BlockCopy(buffer, 0, m_CompiledBuffer, 0, length);
+ ArrayPool.Shared.Return(buffer);
+ }
+ else
+ {
+ m_CompiledBuffer = buffer;
+ m_State |= State.Buffered;
+ }
+ }
+ }
+ else if (length > 0)
+ {
+ byte[] old = m_CompiledBuffer;
+ m_CompiledLength = length;
+
+ if ((m_State & State.Static) != 0)
+ m_CompiledBuffer = new byte[length];
+ else
+ {
+ m_CompiledBuffer = ArrayPool.Shared.Rent(length);
+ m_State |= State.Buffered;
+ }
+
+ Buffer.BlockCopy(old, 0, m_CompiledBuffer, 0, length);
+ }
+
+ PacketWriter.ReleaseInstance(m_Stream);
+ m_Stream = null;
+ }
+
+ [Flags]
+ private enum State
+ {
+ Inactive = 0x00,
+ Static = 0x01,
+ Acquired = 0x02,
+ Accessed = 0x04,
+ Buffered = 0x08,
+ Warned = 0x10
+ }
+ }
+}
diff --git a/Server/Network/PacketHandler.cs b/Projects/Server/Network/PacketHandler.cs
similarity index 83%
rename from Server/Network/PacketHandler.cs
rename to Projects/Server/Network/PacketHandler.cs
index f2268a65e..022405fd5 100644
--- a/Server/Network/PacketHandler.cs
+++ b/Projects/Server/Network/PacketHandler.cs
@@ -18,15 +18,17 @@
*
***************************************************************************/
+using System;
+
namespace Server.Network
{
public delegate void OnPacketReceive(NetState state, PacketReader pvSrc);
- public delegate bool ThrottlePacketCallback(NetState state);
+ public delegate TimeSpan ThrottlePacketCallback(NetState state);
public class PacketHandler
{
- public PacketHandler(int packetID, int length, bool ingame, OnPacketReceive onReceive)
+ public PacketHandler(int packetID, long length, bool ingame, OnPacketReceive onReceive)
{
PacketID = packetID;
Length = length;
@@ -36,7 +38,7 @@ namespace Server.Network
public int PacketID{ get; }
- public int Length{ get; }
+ public long Length{ get; }
public OnPacketReceive OnReceive{ get; }
@@ -44,4 +46,4 @@ namespace Server.Network
public bool Ingame{ get; }
}
-}
\ No newline at end of file
+}
diff --git a/Server/Network/PacketHandlers.cs b/Projects/Server/Network/PacketHandlers.cs
similarity index 92%
rename from Server/Network/PacketHandlers.cs
rename to Projects/Server/Network/PacketHandlers.cs
index b0b62e192..b7acefa3e 100644
--- a/Server/Network/PacketHandlers.cs
+++ b/Projects/Server/Network/PacketHandlers.cs
@@ -19,8 +19,10 @@
***************************************************************************/
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.IO;
+using System.IO.Pipelines;
using Server.Accounting;
using Server.ContextMenus;
using Server.Diagnostics;
@@ -304,6 +306,102 @@ namespace Server.Network
ph.ThrottleCallback = t;
}
+ public static long ProcessPacket(MessagePump pump, NetState ns, in ReadOnlySequence seq)
+ {
+ PacketReader r = new PacketReader(seq);
+
+ if (!r.TryReadByte(out byte packetId))
+ {
+ ns.Dispose();
+ return -1;
+ }
+
+ if (!ns.Seeded)
+ {
+ if (packetId == 0xEF)
+ {
+ // new packet in client 6.0.5.0 replaces the traditional seed method with a seed packet
+ // 0xEF = 239 = multicast IP, so this should never appear in a normal seed. So this is backwards compatible with older clients.
+ ns.Seeded = true;
+ }
+ else
+ {
+ int seed = (packetId << 24) | (r.ReadByte() << 16) | (r.ReadByte() << 8) | r.ReadByte();
+
+ if (seed == 0)
+ {
+ Console.WriteLine("Login: {0}: Invalid client detected, disconnecting", ns);
+ ns.Dispose();
+ return -1;
+ }
+
+ ns.m_Seed = seed;
+ ns.Seeded = true;
+
+ return 4;
+ }
+ }
+
+ if (ns.CheckEncrypted(packetId))
+ {
+ ns.Dispose();
+ return -1;
+ }
+
+ // Get Handlers
+ PacketHandler handler = ns.GetHandler(packetId);
+
+ if (handler == null)
+ {
+ r.Trace(ns);
+ return -1;
+ }
+
+ long packetLength = handler.Length;
+ if (handler.Length <= 0 && r.Length >= 3)
+ {
+ packetLength = r.ReadUInt16();
+ if (packetLength < 3)
+ {
+ ns.Dispose();
+ return -1;
+ }
+ }
+
+ if (r.Length < packetLength)
+ return 0;
+
+ if (handler.Ingame && ns.Mobile?.Deleted != false)
+ {
+ Console.WriteLine(
+ "Client: {0}: Sent ingame packet (0x{1:X2}) without being attached to a valid mobile.",
+ ns,
+ packetId
+ );
+ ns.Dispose();
+ return -1;
+ }
+
+ ThrottlePacketCallback throttler = handler.ThrottleCallback;
+ TimeSpan throttled = handler.ThrottleCallback?.Invoke(ns) ?? TimeSpan.Zero;
+
+ if (throttled > TimeSpan.Zero)
+ ns.ThrottledUntil = DateTime.UtcNow + throttled;
+
+ PacketReceiveProfile prof = null;
+
+ if (Core.Profiling)
+ prof = PacketReceiveProfile.Acquire(packetId);
+
+ prof?.Start();
+
+ pump.QueueWork(ns, seq.Slice(r.Position), handler.OnReceive);
+
+ prof?.Finish(packetLength);
+
+ return packetLength;
+ }
+
private static void UnhandledBF(NetState state, PacketReader pvSrc)
{
}
@@ -450,7 +548,8 @@ namespace Server.Network
Mobile vendor = World.FindMobile(pvSrc.ReadUInt32());
byte flag = pvSrc.ReadByte();
- if (vendor == null) return;
+ if (vendor == null)
+ return;
if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
{
@@ -499,7 +598,7 @@ namespace Server.Network
}
int count = pvSrc.ReadUInt16();
- if (count < 100 && pvSrc.Size == 1 + 2 + 4 + 2 + count * 6)
+ if (count < 100 && pvSrc.Length == 1 + 2 + 4 + 2 + count * 6)
{
List sellList = new List(count);
@@ -888,7 +987,8 @@ namespace Server.Network
public static void GodModeRequest(NetState state, PacketReader pvSrc)
{
- if (VerifyGC(state)) state.Send(new GodModeReply(pvSrc.ReadBoolean()));
+ if (VerifyGC(state))
+ state.Send(new GodModeReply(pvSrc.ReadBoolean()));
}
public static void AsciiPromptResponse(NetState state, PacketReader pvSrc)
@@ -1603,6 +1703,7 @@ namespace Server.Network
{
int packetID = pvSrc.ReadUInt16();
+ Console.WriteLine("Extended Packet: {0:X}", packetID);
PacketHandler ph = GetExtendedHandler(packetID);
if (ph == null)
@@ -1681,12 +1782,12 @@ namespace Server.Network
Mobile from = state.Mobile;
- int length = pvSrc.Size - 3;
+ long length = pvSrc.Length - 3;
if (length < 0 || length % 4 != 0)
return;
- int count = length / 4;
+ long count = length / 4;
for (int i = 0; i < count; ++i)
{
@@ -2010,7 +2111,7 @@ namespace Server.Network
pvSrc.Seek(2, SeekOrigin.Current);
int flags = pvSrc.ReadInt32();
- if (FeatureProtection.DisabledFeatures != 0 && ThirdPartyAuthCallback != null)
+/* if (FeatureProtection.DisabledFeatures != 0 && ThirdPartyAuthCallback != null)
{
bool authOK = false;
@@ -2031,18 +2132,18 @@ namespace Server.Network
}
ThirdPartyAuthCallback(state, authOK);
- }
- else
- {
+ }*/
+/* else
+ {*/
pvSrc.Seek(24, SeekOrigin.Current);
- }
+/* }*/
- if (ThirdPartyHackedCallback != null)
+/* if (ThirdPartyHackedCallback != null)
{
pvSrc.Seek(-2, SeekOrigin.Current);
if (pvSrc.ReadUInt16() == 0xDEAD)
ThirdPartyHackedCallback(state, true);
- }
+ }*/
if (!state.Running)
return;
@@ -2053,9 +2154,7 @@ namespace Server.Network
IAccount a = state.Account;
if (a == null || charSlot < 0 || charSlot >= a.Length)
- {
state.Dispose();
- }
else
{
Mobile m = a[charSlot];
@@ -2079,7 +2178,6 @@ namespace Server.Network
return;
}
-
m.NetState?.Dispose();
// TODO: Make this wait one tick so we don't have to call it unnecessarily
@@ -2206,8 +2304,6 @@ namespace Server.Network
int prof = pvSrc.ReadByte();
pvSrc.Seek(15, SeekOrigin.Current);
- //bool female = pvSrc.ReadBoolean();
-
int genderRace = pvSrc.ReadByte();
int str = pvSrc.ReadByte();
@@ -2645,8 +2741,7 @@ namespace Server.Network
if (e.Rejected)
{
state.Account = null;
- state.Send(new AccountLoginRej(ALRReason.BadComm));
- state.Dispose();
+ AccountLogin_ReplyRej(state, ALRReason.BadComm);
}
else
{
diff --git a/Projects/Server/Network/PacketReader.cs b/Projects/Server/Network/PacketReader.cs
new file mode 100644
index 000000000..d5facc4ef
--- /dev/null
+++ b/Projects/Server/Network/PacketReader.cs
@@ -0,0 +1,315 @@
+/***************************************************************************
+ * PacketReader.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.Buffers;
+using System.IO;
+using System.IO.Pipelines;
+using System.Text;
+
+namespace Server.Network
+{
+ public ref struct PacketReader
+ {
+ private SequenceReader m_Reader;
+
+ public SequencePosition Position => m_Reader.Position;
+ public long Length => m_Reader.Length;
+ public long Consumed => m_Reader.Consumed;
+ public long Remaining => m_Reader.Remaining;
+
+ public PacketReader(ReadOnlySequence seq)
+ {
+ m_Reader = new SequenceReader(seq);
+ }
+
+ public byte Peek() => m_Reader.TryPeek(out byte value) ? value : (byte)0;
+
+ public void Trace(NetState state)
+ {
+ try
+ {
+ using (StreamWriter sw = new StreamWriter("Packets.log", true))
+ {
+ byte[] buffer = m_Reader.Sequence.ToArray();
+
+ if (buffer.Length > 0)
+ sw.WriteLine("Client: {0}: Unhandled packet 0x{1:X2}", state, buffer[0]);
+
+ using (MemoryStream ms = new MemoryStream(buffer))
+ {
+ Utility.FormatBuffer(sw, ms, buffer.Length);
+ }
+
+ sw.WriteLine();
+ sw.WriteLine();
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ public SequencePosition Seek(long offset, SeekOrigin origin)
+ {
+ switch (origin)
+ {
+ case SeekOrigin.Begin:
+ if (offset < m_Reader.Consumed)
+ m_Reader.Rewind(m_Reader.Consumed - Math.Max(offset, 0L));
+ else
+ m_Reader.Advance(offset - m_Reader.Consumed);
+ break;
+ case SeekOrigin.Current:
+ if (offset < 0)
+ m_Reader.Rewind(Math.Min(m_Reader.Consumed, offset * -1));
+ else
+ m_Reader.Advance(Math.Min(m_Reader.Remaining, offset));
+ break;
+ case SeekOrigin.End:
+ long count = m_Reader.Remaining - offset;
+ if (count < 0)
+ m_Reader.Rewind(count * -1);
+ else if (count > 0)
+ m_Reader.Advance(count);
+ break;
+ }
+
+ return m_Reader.Position;
+ }
+
+ public bool TryReadByte(out byte value) => m_Reader.TryRead(out value);
+
+ public int ReadInt32() => m_Reader.TryReadBigEndian(out int value) ? value : 0;
+
+ public short ReadInt16() => m_Reader.TryReadBigEndian(out short value) ? value : (short)0;
+
+ public byte ReadByte() => m_Reader.TryRead(out byte value) ? value : (byte)0;
+
+ public uint ReadUInt32() => (uint)ReadInt32();
+
+ public ushort ReadUInt16() => (ushort)ReadInt16();
+
+ public sbyte ReadSByte() => (sbyte)ReadByte();
+
+ public bool ReadBoolean() => ReadByte() > 0;
+
+ public string ReadUnicodeStringLE()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (m_Reader.TryReadLittleEndian(out short c) && c != 0)
+ sb.Append((char)c);
+
+ return sb.ToString();
+ }
+
+ public string ReadUnicodeStringLE(int fixedLength)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (fixedLength-- > 0 && m_Reader.TryReadLittleEndian(out short c) && c != 0)
+ sb.Append((char)c);
+
+ if (fixedLength > 0)
+ m_Reader.Advance(fixedLength);
+
+ return sb.ToString();
+ }
+
+ public string ReadUnicodeStringLESafe(int fixedLength)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (fixedLength-- > 0 && m_Reader.TryReadLittleEndian(out short c) && c != 0)
+ if (IsSafeChar(c))
+ sb.Append((char)c);
+
+ if (fixedLength > 0)
+ m_Reader.Advance(fixedLength * 2);
+
+ return sb.ToString();
+ }
+
+ public string ReadUnicodeStringLESafe()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (m_Reader.TryReadLittleEndian(out short c) && c != 0)
+ if (IsSafeChar(c))
+ sb.Append((char)c);
+
+ return sb.ToString();
+ }
+
+ public string ReadUnicodeStringSafe()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (m_Reader.TryReadBigEndian(out short c) && c != 0)
+ if (IsSafeChar(c))
+ sb.Append((char)c);
+
+ return sb.ToString();
+ }
+
+ public string ReadUnicodeString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (m_Reader.TryReadBigEndian(out short c) && c != 0)
+ sb.Append((char)c);
+
+ return sb.ToString();
+ }
+
+ public bool IsSafeChar(int c)
+ {
+ return c >= 0x20 && c < 0xFFFE;
+ }
+
+ public string ReadUTF8StringSafe(int fixedLength)
+ {
+ string s;
+
+ if (m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0', true))
+ s = Utility.UTF8.GetString(span.Length > fixedLength ? span.Slice(0, fixedLength) : span);
+ else
+ {
+ long size = Math.Min(m_Reader.Remaining, fixedLength);
+ s = Utility.UTF8.GetString(m_Reader.Sequence.Slice(m_Reader.Position, size).ToArray());
+ m_Reader.Advance(size);
+ }
+
+ StringBuilder sb = new StringBuilder(s.Length);
+
+ for (int i = 0; i < s.Length; ++i)
+ if (IsSafeChar(s[i]))
+ sb.Append(s[i]);
+
+ return sb.ToString();
+ }
+
+ public string ReadUTF8StringSafe()
+ {
+ string s;
+
+ if (m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0', true))
+ s = Utility.UTF8.GetString(span);
+ else
+ {
+ s = Utility.UTF8.GetString(m_Reader.Sequence.Slice(m_Reader.Position, m_Reader.Remaining).ToArray());
+ m_Reader.Advance(m_Reader.Remaining);
+ }
+
+ StringBuilder sb = new StringBuilder(s.Length);
+
+ for (int i = 0; i < s.Length; ++i)
+ if (IsSafeChar(s[i]))
+ sb.Append(s[i]);
+
+ return sb.ToString();
+ }
+
+ public string ReadUTF8String()
+ {
+ return Utility.UTF8.GetString(
+ m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0', true) ? span :
+ m_Reader.Sequence.Slice(m_Reader.Position, m_Reader.Remaining).ToArray()
+ );
+ }
+
+ public string ReadString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (m_Reader.TryRead(out byte c))
+ sb.Append((char)c);
+
+ return sb.ToString();
+ }
+
+ public string ReadStringSafe()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (m_Reader.TryRead(out byte c))
+ if (IsSafeChar(c))
+ sb.Append((char)c);
+
+ return sb.ToString();
+ }
+
+ public string ReadUnicodeStringSafe(int fixedLength)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (fixedLength-- > 0 && m_Reader.TryReadBigEndian(out short c) && c != 0)
+ if (IsSafeChar(c))
+ sb.Append((char)c);
+
+ if (fixedLength > 0)
+ m_Reader.Advance(fixedLength * 2);
+
+ return sb.ToString();
+ }
+
+ public string ReadUnicodeString(int fixedLength)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (fixedLength-- > 0 && m_Reader.TryReadBigEndian(out short c) && c != 0)
+ sb.Append((char)c);
+
+ if (fixedLength > 0)
+ m_Reader.Advance(fixedLength * 2);
+
+ return sb.ToString();
+ }
+
+ public string ReadStringSafe(int fixedLength)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (fixedLength-- > 0 && m_Reader.TryRead(out byte c) && c != 0)
+ if (IsSafeChar(c))
+ sb.Append((char)c);
+
+ if (fixedLength > 0)
+ m_Reader.Advance(fixedLength);
+
+ return sb.ToString();
+ }
+
+ public string ReadString(int fixedLength)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ while (fixedLength-- > 0 && m_Reader.TryRead(out byte c) && c != 0)
+ sb.Append((char)c);
+
+ if (fixedLength > 0)
+ m_Reader.Advance(fixedLength);
+
+ return sb.ToString();
+ }
+ }
+}
diff --git a/Server/Network/PacketWriter.cs b/Projects/Server/Network/PacketWriter.cs
similarity index 55%
rename from Server/Network/PacketWriter.cs
rename to Projects/Server/Network/PacketWriter.cs
index e45a1e12b..c90b85fb8 100644
--- a/Server/Network/PacketWriter.cs
+++ b/Projects/Server/Network/PacketWriter.cs
@@ -19,7 +19,7 @@
***************************************************************************/
using System;
-using System.Collections.Generic;
+using System.Collections.Concurrent;
using System.IO;
using System.Text;
@@ -30,7 +30,7 @@ namespace Server.Network
///
public class PacketWriter
{
- private static Stack m_Pool = new Stack();
+ private static ConcurrentQueue m_Pool = new ConcurrentQueue();
///
/// Internal format buffer.
@@ -39,85 +39,50 @@ namespace Server.Network
private int m_Capacity;
- ///
- /// Internal stream which holds the entire packet.
- ///
- private MemoryStream m_Stream;
-
///
/// Instantiates a new PacketWriter instance with a given capacity.
///
/// Initial capacity for the internal stream.
public PacketWriter(int capacity = 32)
{
- m_Stream = new MemoryStream(capacity);
+ UnderlyingStream = new MemoryStream(capacity);
m_Capacity = capacity;
}
///
/// Gets the total stream length.
///
- public long Length => m_Stream.Length;
+ public long Length => UnderlyingStream.Length;
///
/// Gets or sets the current stream position.
///
public long Position
{
- get => m_Stream.Position;
- set => m_Stream.Position = value;
+ get => UnderlyingStream.Position;
+ set => UnderlyingStream.Position = value;
}
///
/// The internal stream used by this PacketWriter instance.
///
- public MemoryStream UnderlyingStream => m_Stream;
+ public MemoryStream UnderlyingStream { get; private set; }
- public static PacketWriter CreateInstance()
+ public static PacketWriter CreateInstance(int capacity = 32)
{
- return CreateInstance(32);
- }
-
- public static PacketWriter CreateInstance(int capacity)
- {
- PacketWriter pw = null;
-
- lock (m_Pool)
+ if (m_Pool.TryDequeue(out PacketWriter pw))
{
- if (m_Pool.Count > 0)
- {
- pw = m_Pool.Pop();
-
- if (pw != null)
- {
- pw.m_Capacity = capacity;
- pw.m_Stream.SetLength(0);
- }
- }
+ pw.m_Capacity = capacity;
+ pw.UnderlyingStream.SetLength(0);
+ return pw;
}
- return pw ?? new PacketWriter(capacity);
+ return new PacketWriter(capacity);
}
public static void ReleaseInstance(PacketWriter pw)
{
- lock (m_Pool)
- {
- if (!m_Pool.Contains(pw))
- m_Pool.Push(pw);
- else
- try
- {
- using (StreamWriter op = new StreamWriter("neterr.log"))
- {
- op.WriteLine("{0}\tInstance pool contains writer", DateTime.UtcNow);
- }
- }
- catch
- {
- Console.WriteLine("net error");
- }
- }
+ m_Pool.Enqueue(pw);
}
///
@@ -125,7 +90,7 @@ namespace Server.Network
///
public void Write(bool value)
{
- m_Stream.WriteByte((byte)(value ? 1 : 0));
+ UnderlyingStream.WriteByte((byte)(value ? 1 : 0));
}
///
@@ -133,7 +98,7 @@ namespace Server.Network
///
public void Write(byte value)
{
- m_Stream.WriteByte(value);
+ UnderlyingStream.WriteByte(value);
}
///
@@ -141,7 +106,7 @@ namespace Server.Network
///
public void Write(sbyte value)
{
- m_Stream.WriteByte((byte)value);
+ UnderlyingStream.WriteByte((byte)value);
}
///
@@ -152,7 +117,7 @@ namespace Server.Network
m_Buffer[0] = (byte)(value >> 8);
m_Buffer[1] = (byte)value;
- m_Stream.Write(m_Buffer, 0, 2);
+ UnderlyingStream.Write(m_Buffer, 0, 2);
}
///
@@ -163,7 +128,7 @@ namespace Server.Network
m_Buffer[0] = (byte)(value >> 8);
m_Buffer[1] = (byte)value;
- m_Stream.Write(m_Buffer, 0, 2);
+ UnderlyingStream.Write(m_Buffer, 0, 2);
}
///
@@ -176,7 +141,7 @@ namespace Server.Network
m_Buffer[2] = (byte)(value >> 8);
m_Buffer[3] = (byte)value;
- m_Stream.Write(m_Buffer, 0, 4);
+ UnderlyingStream.Write(m_Buffer, 0, 4);
}
///
@@ -189,7 +154,7 @@ namespace Server.Network
m_Buffer[2] = (byte)(value >> 8);
m_Buffer[3] = (byte)value;
- m_Stream.Write(m_Buffer, 0, 4);
+ UnderlyingStream.Write(m_Buffer, 0, 4);
}
///
@@ -197,7 +162,7 @@ namespace Server.Network
///
public void Write(byte[] buffer, int offset, int size)
{
- m_Stream.Write(buffer, offset, size);
+ UnderlyingStream.Write(buffer, offset, size);
}
///
@@ -214,29 +179,18 @@ namespace Server.Network
int length = value.Length;
- m_Stream.SetLength(m_Stream.Length + size);
+ UnderlyingStream.SetLength(UnderlyingStream.Length + size);
if (length >= size)
{
- m_Stream.Position += Encoding.ASCII.GetBytes(value, 0, size, m_Stream.GetBuffer(), (int)m_Stream.Position);
+ UnderlyingStream.Position +=
+ Encoding.ASCII.GetBytes(value, 0, size, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
- Encoding.ASCII.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
- m_Stream.Position += size;
+ Encoding.ASCII.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
+ UnderlyingStream.Position += size;
}
-
- /*byte[] buffer = Encoding.ASCII.GetBytes( value );
-
- if ( buffer.Length >= size )
- {
- m_Stream.Write( buffer, 0, size );
- }
- else
- {
- m_Stream.Write( buffer, 0, buffer.Length );
- Fill( size - buffer.Length );
- }*/
}
///
@@ -252,15 +206,10 @@ namespace Server.Network
int length = value.Length;
- m_Stream.SetLength(m_Stream.Length + length + 1);
+ UnderlyingStream.SetLength(UnderlyingStream.Length + length + 1);
- Encoding.ASCII.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
- m_Stream.Position += length + 1;
-
- /*byte[] buffer = Encoding.ASCII.GetBytes( value );
-
- m_Stream.Write( buffer, 0, buffer.Length );
- m_Stream.WriteByte( 0 );*/
+ Encoding.ASCII.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
+ UnderlyingStream.Position += length + 1;
}
///
@@ -276,18 +225,10 @@ namespace Server.Network
int length = value.Length;
- m_Stream.SetLength(m_Stream.Length + (length + 1) * 2);
+ UnderlyingStream.SetLength(UnderlyingStream.Length + (length + 1) * 2);
- m_Stream.Position += Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
- m_Stream.Position += 2;
-
- /*byte[] buffer = Encoding.Unicode.GetBytes( value );
-
- m_Stream.Write( buffer, 0, buffer.Length );
-
- m_Buffer[0] = 0;
- m_Buffer[1] = 0;
- m_Stream.Write( m_Buffer, 0, 2 );*/
+ UnderlyingStream.Position += Encoding.Unicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
+ UnderlyingStream.Position += 2;
}
///
@@ -302,36 +243,21 @@ namespace Server.Network
value = string.Empty;
}
+ int length = value.Length;
size *= 2;
- int length = value.Length;
-
- m_Stream.SetLength(m_Stream.Length + size);
+ UnderlyingStream.SetLength(UnderlyingStream.Length + size);
if (length * 2 >= size)
{
- m_Stream.Position +=
- Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
+ UnderlyingStream.Position +=
+ Encoding.Unicode.GetBytes(value, 0, size, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
- Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
- m_Stream.Position += size;
+ Encoding.Unicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
+ UnderlyingStream.Position += size;
}
-
- /*size *= 2;
-
- byte[] buffer = Encoding.Unicode.GetBytes( value );
-
- if ( buffer.Length >= size )
- {
- m_Stream.Write( buffer, 0, size );
- }
- else
- {
- m_Stream.Write( buffer, 0, buffer.Length );
- Fill( size - buffer.Length );
- }*/
}
///
@@ -347,19 +273,11 @@ namespace Server.Network
int length = value.Length;
- m_Stream.SetLength(m_Stream.Length + (length + 1) * 2);
+ UnderlyingStream.SetLength(UnderlyingStream.Length + (length + 1) * 2);
- m_Stream.Position +=
- Encoding.BigEndianUnicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
- m_Stream.Position += 2;
-
- /*byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value );
-
- m_Stream.Write( buffer, 0, buffer.Length );
-
- m_Buffer[0] = 0;
- m_Buffer[1] = 0;
- m_Stream.Write( m_Buffer, 0, 2 );*/
+ UnderlyingStream.Position +=
+ Encoding.BigEndianUnicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
+ UnderlyingStream.Position += 2;
}
///
@@ -374,36 +292,21 @@ namespace Server.Network
value = string.Empty;
}
+ int length = value.Length;
size *= 2;
- int length = value.Length;
+ UnderlyingStream.SetLength(UnderlyingStream.Length + size);
- m_Stream.SetLength(m_Stream.Length + size);
-
- if (length * 2 >= size)
+ if (length * 2 >= size )
{
- m_Stream.Position +=
- Encoding.BigEndianUnicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
+ UnderlyingStream.Position +=
+ Encoding.BigEndianUnicode.GetBytes(value, 0, size, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
- Encoding.BigEndianUnicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position);
- m_Stream.Position += size;
+ Encoding.BigEndianUnicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
+ UnderlyingStream.Position += size;
}
-
- /*size *= 2;
-
- byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value );
-
- if ( buffer.Length >= size )
- {
- m_Stream.Write( buffer, 0, size );
- }
- else
- {
- m_Stream.Write( buffer, 0, buffer.Length );
- Fill( size - buffer.Length );
- }*/
}
///
@@ -411,39 +314,33 @@ namespace Server.Network
///
public void Fill()
{
- Fill((int)(m_Capacity - m_Stream.Length));
+ Fill(m_Capacity - UnderlyingStream.Length);
}
///
/// Writes a number of 0x00 byte values to the underlying stream.
///
- public void Fill(int length)
+ public void Fill(long length)
{
- if (m_Stream.Position == m_Stream.Length)
+ if (UnderlyingStream.Position == UnderlyingStream.Length)
{
- m_Stream.SetLength(m_Stream.Length + length);
- m_Stream.Seek(0, SeekOrigin.End);
+ UnderlyingStream.SetLength(UnderlyingStream.Length + length);
+ UnderlyingStream.Seek(0, SeekOrigin.End);
}
else
{
- m_Stream.Write(new byte[length], 0, length);
+ UnderlyingStream.Write(new byte[length], 0, (int)length);
}
}
///
/// Offsets the current position from an origin.
///
- public long Seek(long offset, SeekOrigin origin)
- {
- return m_Stream.Seek(offset, origin);
- }
+ public long Seek(long offset, SeekOrigin origin) => UnderlyingStream.Seek(offset, origin);
///
/// Gets the entire stream content as a byte array.
///
- public byte[] ToArray()
- {
- return m_Stream.ToArray();
- }
+ public byte[] ToArray() => UnderlyingStream.ToArray();
}
}
diff --git a/Server/Network/Packets.cs b/Projects/Server/Network/Packets.cs
similarity index 90%
rename from Server/Network/Packets.cs
rename to Projects/Server/Network/Packets.cs
index a08633c70..4dabd698d 100644
--- a/Server/Network/Packets.cs
+++ b/Projects/Server/Network/Packets.cs
@@ -19,6 +19,7 @@
***************************************************************************/
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -2336,8 +2337,6 @@ namespace Server.Network
public sealed class DisplayGumpPacked : Packet, IGumpWriter
{
- private const int GumpBufferSize = 0x5000;
-
private static byte[] m_True = Gump.StringToBuffer(" 1");
private static byte[] m_False = Gump.StringToBuffer(" 0");
@@ -2345,7 +2344,6 @@ namespace Server.Network
private static byte[] m_EndTextSeparator = Gump.StringToBuffer("@");
private static byte[] m_Buffer = new byte[48];
- private static BufferPool m_PackBuffers = new BufferPool("Gump", 4, GumpBufferSize);
private Gump m_Gump;
@@ -2465,30 +2463,17 @@ namespace Server.Network
wantLength += 4095;
wantLength &= ~4095;
- byte[] m_PackBuffer;
- lock (m_PackBuffers)
- {
- m_PackBuffer = m_PackBuffers.AcquireBuffer();
- }
+ byte[] packBuffer = ArrayPool.Shared.Rent(wantLength);
- if (m_PackBuffer.Length < wantLength)
- {
- Console.WriteLine("Notice: DisplayGumpPacked creating new {0} byte buffer", wantLength);
- m_PackBuffer = new byte[wantLength];
- }
+ int packLength = wantLength;
- int packLength = m_PackBuffer.Length;
-
- Compression.Pack(m_PackBuffer, ref packLength, buffer, length, ZLibQuality.Default);
+ Compression.Pack(packBuffer, ref packLength, buffer, length, ZLibQuality.Default);
m_Stream.Write(4 + packLength);
m_Stream.Write(length);
- m_Stream.Write(m_PackBuffer, 0, packLength);
+ m_Stream.Write(packBuffer, 0, packLength);
- lock (m_PackBuffers)
- {
- m_PackBuffers.ReleaseBuffer(m_PackBuffer);
- }
+ ArrayPool.Shared.Return(packBuffer);
}
}
@@ -3954,7 +3939,7 @@ namespace Server.Network
public sealed class CharacterList : Packet
{
- private static MD5CryptoServiceProvider m_MD5Provider;
+ //private static MD5CryptoServiceProvider m_MD5Provider;
public CharacterList(IAccount a, CityInfo[] info) : base(0xA9)
{
@@ -4013,7 +3998,7 @@ namespace Server.Network
m_Stream.Write((short)-1);
- ThirdPartyFeature disabled = FeatureProtection.DisabledFeatures;
+ /*ThirdPartyFeature disabled = FeatureProtection.DisabledFeatures;
if (disabled != 0)
{
@@ -4040,7 +4025,7 @@ namespace Server.Network
m_Stream.Seek(95, SeekOrigin.Begin);
m_Stream.Write(hashCode, 0, hashCode.Length);
- }
+ }*/
}
public static CharacterListFlags AdditionalFlags{ get; set; }
@@ -4048,7 +4033,7 @@ namespace Server.Network
public sealed class CharacterListOld : Packet
{
- private static MD5CryptoServiceProvider m_MD5Provider;
+ // private static MD5CryptoServiceProvider m_MD5Provider;
public CharacterListOld(IAccount a, CityInfo[] info) : base(0xA9)
{
@@ -4099,7 +4084,7 @@ namespace Server.Network
m_Stream.Write((int)(flags | CharacterList.AdditionalFlags)); // Additional Flags
- ThirdPartyFeature disabled = FeatureProtection.DisabledFeatures;
+/* ThirdPartyFeature disabled = FeatureProtection.DisabledFeatures;
if (disabled != 0)
{
@@ -4126,7 +4111,7 @@ namespace Server.Network
m_Stream.Seek(95, SeekOrigin.Begin);
m_Stream.Write(hashCode, 0, hashCode.Length);
- }
+ }*/
}
}
@@ -4197,7 +4182,7 @@ namespace Server.Network
public sealed class ServerInfo
{
- public ServerInfo(string name, int fullPercent, TimeZone tz, IPEndPoint address)
+ public ServerInfo(string name, int fullPercent, TimeZoneInfo tz, IPEndPoint address)
{
Name = name;
FullPercent = fullPercent;
@@ -4289,271 +4274,4 @@ namespace Server.Network
m_Stream.Write(m_AuthID);
}
}
-
- public abstract class Packet
- {
- private const int CompressorBufferSize = 0x10000;
-
- private const int BufferSize = 4096;
- private static BufferPool m_CompressorBuffers = new BufferPool("Compressor", 4, CompressorBufferSize);
- private static BufferPool m_Buffers = new BufferPool("Compressed", 16, BufferSize);
-
- private byte[] m_CompiledBuffer;
- private int m_CompiledLength;
- private int m_Length;
- private State m_State;
-
- protected PacketWriter m_Stream;
-
- protected Packet(int packetID)
- {
- PacketID = packetID;
-
- if (Core.Profiling)
- {
- PacketSendProfile prof = PacketSendProfile.Acquire(GetType());
- prof.Increment();
- }
- }
-
- protected Packet(int packetID, int length)
- {
- PacketID = packetID;
- m_Length = length;
-
- m_Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
- m_Stream.Write((byte)packetID);
-
- if (Core.Profiling)
- {
- PacketSendProfile prof = PacketSendProfile.Acquire(GetType());
- prof.Increment();
- }
- }
-
- public int PacketID{ get; }
-
- public PacketWriter UnderlyingStream => m_Stream;
-
- public void EnsureCapacity(int length)
- {
- m_Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
- m_Stream.Write((byte)PacketID);
- m_Stream.Write((short)0);
- }
-
- public static Packet SetStatic(Packet p)
- {
- p.SetStatic();
- return p;
- }
-
- public static Packet Acquire(Packet p)
- {
- p.Acquire();
- return p;
- }
-
- public static void Release(ref Packet p)
- {
- p?.Release();
- p = null;
- }
-
- public static void Release(Packet p)
- {
- p?.Release();
- }
-
- public void SetStatic()
- {
- m_State |= State.Static | State.Acquired;
- }
-
- public void Acquire()
- {
- m_State |= State.Acquired;
- }
-
- public void OnSend()
- {
- Core.Set();
-
- lock (this)
- {
- if ((m_State & (State.Acquired | State.Static)) == 0)
- Free();
- }
- }
-
- private void Free()
- {
- if (m_CompiledBuffer == null)
- return;
-
- if ((m_State & State.Buffered) != 0)
- m_Buffers.ReleaseBuffer(m_CompiledBuffer);
-
- m_State &= ~(State.Static | State.Acquired | State.Buffered);
-
- m_CompiledBuffer = null;
- }
-
- public void Release()
- {
- if ((m_State & State.Acquired) != 0)
- Free();
- }
-
- public byte[] Compile(bool compress, out int length)
- {
- lock (this)
- {
- if (m_CompiledBuffer == null)
- {
- if ((m_State & State.Accessed) == 0)
- {
- m_State |= State.Accessed;
- }
- else
- {
- if ((m_State & State.Warned) == 0)
- {
- m_State |= State.Warned;
-
- try
- {
- using (StreamWriter op = new StreamWriter("net_opt.log", true))
- {
- op.WriteLine("Redundant compile for packet {0}, use Acquire() and Release()", GetType());
- op.WriteLine(new StackTrace());
- }
- }
- catch
- {
- // ignored
- }
- }
-
- m_CompiledBuffer = new byte[0];
- m_CompiledLength = 0;
-
- length = m_CompiledLength;
- return m_CompiledBuffer;
- }
-
- InternalCompile(compress);
- }
-
- length = m_CompiledLength;
- return m_CompiledBuffer;
- }
- }
-
- private void InternalCompile(bool compress)
- {
- if (m_Length == 0)
- {
- long streamLen = m_Stream.Length;
-
- m_Stream.Seek(1, SeekOrigin.Begin);
- m_Stream.Write((ushort)streamLen);
- }
- else if (m_Stream.Length != m_Length)
- {
- int diff = (int)m_Stream.Length - m_Length;
-
- Console.WriteLine("Packet: 0x{0:X2}: Bad packet length! ({1}{2} bytes)", PacketID, diff >= 0 ? "+" : "",
- diff);
- }
-
- MemoryStream ms = m_Stream.UnderlyingStream;
-
- m_CompiledBuffer = ms.GetBuffer();
- int length = (int)ms.Length;
-
- if (compress)
- {
- byte[] buffer;
- lock (m_CompressorBuffers)
- {
- buffer = m_CompressorBuffers.AcquireBuffer();
- }
-
- Compression.Compress(m_CompiledBuffer, 0, length, buffer, ref length);
-
- if (length <= 0)
- {
- Console.WriteLine("Warning: Compression buffer overflowed on packet 0x{0:X2} ('{1}') (length={2})",
- PacketID, GetType().Name, length);
- using (StreamWriter op = new StreamWriter("compression_overflow.log", true))
- {
- op.WriteLine("{0} Warning: Compression buffer overflowed on packet 0x{1:X2} ('{2}') (length={3})",
- DateTime.UtcNow, PacketID, GetType().Name, length);
- op.WriteLine(new StackTrace());
- }
- }
- else
- {
- m_CompiledLength = length;
-
- if (length > BufferSize || (m_State & State.Static) != 0)
- {
- m_CompiledBuffer = new byte[length];
- }
- else
- {
- lock (m_Buffers)
- {
- m_CompiledBuffer = m_Buffers.AcquireBuffer();
- }
-
- m_State |= State.Buffered;
- }
-
- Buffer.BlockCopy(buffer, 0, m_CompiledBuffer, 0, length);
-
- lock (m_CompressorBuffers)
- {
- m_CompressorBuffers.ReleaseBuffer(buffer);
- }
- }
- }
- else if (length > 0)
- {
- byte[] old = m_CompiledBuffer;
- m_CompiledLength = length;
-
- if (length > BufferSize || (m_State & State.Static) != 0)
- {
- m_CompiledBuffer = new byte[length];
- }
- else
- {
- lock (m_Buffers)
- {
- m_CompiledBuffer = m_Buffers.AcquireBuffer();
- }
-
- m_State |= State.Buffered;
- }
-
- Buffer.BlockCopy(old, 0, m_CompiledBuffer, 0, length);
- }
-
- PacketWriter.ReleaseInstance(m_Stream);
- m_Stream = null;
- }
-
- [Flags]
- private enum State
- {
- Inactive = 0x00,
- Static = 0x01,
- Acquired = 0x02,
- Accessed = 0x04,
- Buffered = 0x08,
- Warned = 0x10
- }
- }
}
diff --git a/Projects/Server/Network/SendQueue.cs b/Projects/Server/Network/SendQueue.cs
new file mode 100644
index 000000000..2b0364e0e
--- /dev/null
+++ b/Projects/Server/Network/SendQueue.cs
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * SendQueue.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.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace Server.Network
+{
+ public class SendQueue
+ {
+ private BlockingCollection m_Queue = new BlockingCollection(new ConcurrentQueue());
+
+ public SendQueue()
+ {
+ }
+
+ public void Enqueue(T t)
+ {
+ m_Queue.Add(t);
+ }
+
+ public Task DequeueAsync()
+ {
+ TaskCompletionSource taskCompletion = new TaskCompletionSource();
+ Task.Run(() => taskCompletion.SetResult(Dequeue()));
+ return taskCompletion.Task;
+ }
+
+ public T Dequeue()
+ {
+ return m_Queue.Take();
+ }
+ }
+}
diff --git a/Projects/Server/Network/SocketExtensions.cs b/Projects/Server/Network/SocketExtensions.cs
new file mode 100644
index 000000000..4eaf373d6
--- /dev/null
+++ b/Projects/Server/Network/SocketExtensions.cs
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * SocketExtensions.cs
+ * -------------------
+ * begin : August 2, 2019
+ * copyright : (C) The ModernUO Team
+ * email : hi@modernuo.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.Buffers;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace Server.Network
+{
+ public static class SocketExtensions
+ {
+ public static Task ReceiveAsync(this Socket socket, Memory memory, SocketFlags socketFlags)
+ {
+ return SocketTaskExtensions.ReceiveAsync(socket, GetArray(memory), socketFlags);
+ }
+
+ public static ArraySegment GetArray(this Memory memory)
+ {
+ return ((ReadOnlyMemory)memory).GetArray();
+ }
+
+ public static ArraySegment GetArray(this ReadOnlyMemory memory)
+ {
+ if (MemoryMarshal.TryGetArray(memory, out var result))
+ return result;
+
+ throw new InvalidOperationException("Buffer backed by array was expected");
+ }
+
+ public static ArraySegment GetArray(this ReadOnlySequence memory)
+ {
+ if (SequenceMarshal.TryGetArray(memory, out var result))
+ return result;
+
+ throw new InvalidOperationException("Buffer backed by array was expected");
+ }
+ }
+}
diff --git a/Server/Network/StaticPacketHandlers.cs b/Projects/Server/Network/StaticPacketHandlers.cs
similarity index 78%
rename from Server/Network/StaticPacketHandlers.cs
rename to Projects/Server/Network/StaticPacketHandlers.cs
index ff09ffd50..b2c710581 100644
--- a/Server/Network/StaticPacketHandlers.cs
+++ b/Projects/Server/Network/StaticPacketHandlers.cs
@@ -1,3 +1,23 @@
+/***************************************************************************
+ * StaticPacketHandlers.cs
+ * -------------------
+ * begin : March 15, 2019
+ * copyright : (C) The ModernUO Team
+ * email : hi@modernuo.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.Collections.Concurrent;
namespace Server.Network
diff --git a/Server/Notoriety.cs b/Projects/Server/Notoriety.cs
similarity index 100%
rename from Server/Notoriety.cs
rename to Projects/Server/Notoriety.cs
diff --git a/Server/ObjectPropertyList.cs b/Projects/Server/ObjectPropertyList.cs
similarity index 100%
rename from Server/ObjectPropertyList.cs
rename to Projects/Server/ObjectPropertyList.cs
diff --git a/Server/Party.cs b/Projects/Server/Party.cs
similarity index 100%
rename from Server/Party.cs
rename to Projects/Server/Party.cs
diff --git a/Server/Persistence/BinaryMemoryWriter.cs b/Projects/Server/Persistence/BinaryMemoryWriter.cs
similarity index 100%
rename from Server/Persistence/BinaryMemoryWriter.cs
rename to Projects/Server/Persistence/BinaryMemoryWriter.cs
diff --git a/Server/Persistence/DualSaveStrategy.cs b/Projects/Server/Persistence/DualSaveStrategy.cs
similarity index 84%
rename from Server/Persistence/DualSaveStrategy.cs
rename to Projects/Server/Persistence/DualSaveStrategy.cs
index cd93949f3..8e9ce9785 100644
--- a/Server/Persistence/DualSaveStrategy.cs
+++ b/Projects/Server/Persistence/DualSaveStrategy.cs
@@ -26,17 +26,17 @@ namespace Server
{
public override string Name => "Dual";
- public override void Save(SaveMetrics metrics, bool permitBackgroundWrite)
+ public override void Save(bool permitBackgroundWrite)
{
PermitBackgroundWrite = permitBackgroundWrite;
- Thread saveThread = new Thread(delegate() { SaveItems(metrics); });
+ Thread saveThread = new Thread(delegate() { SaveItems(); });
saveThread.Name = "Item Save Subset";
saveThread.Start();
- SaveMobiles(metrics);
- SaveGuilds(metrics);
+ SaveMobiles();
+ SaveGuilds();
saveThread.Join();
diff --git a/Server/Persistence/DynamicSaveStrategy.cs b/Projects/Server/Persistence/DynamicSaveStrategy.cs
similarity index 90%
rename from Server/Persistence/DynamicSaveStrategy.cs
rename to Projects/Server/Persistence/DynamicSaveStrategy.cs
index 384d6f506..0132c1f52 100644
--- a/Server/Persistence/DynamicSaveStrategy.cs
+++ b/Projects/Server/Persistence/DynamicSaveStrategy.cs
@@ -37,7 +37,6 @@ namespace Server
private BlockingCollection _itemThreadWriters;
- private SaveMetrics _metrics;
private SequentialFileWriter _mobileData, _mobileIndex;
private BlockingCollection _mobileThreadWriters;
@@ -51,10 +50,8 @@ namespace Server
public override string Name => "Dynamic";
- public override void Save(SaveMetrics metrics, bool permitBackgroundWrite)
+ public override void Save(bool permitBackgroundWrite)
{
- _metrics = metrics;
-
OpenFiles();
Task[] saveTasks = new Task[3];
@@ -130,8 +127,6 @@ namespace Server
if (item.Decays && item.Parent == null && item.Map != Map.Internal &&
DateTime.UtcNow > item.LastMoved + item.DecayTime) _decayBag.Add(item);
- _metrics?.OnItemSaved(size);
-
return writer;
},
writer =>
@@ -165,8 +160,6 @@ namespace Server
writer.QueueForIndex(mobile, size);
- _metrics?.OnMobileSaved(size);
-
return writer;
},
writer =>
@@ -201,8 +194,6 @@ namespace Server
writer.QueueForIndex(guild, size);
- _metrics?.OnGuildSaved(size);
-
return writer;
},
writer =>
@@ -226,14 +217,14 @@ namespace Server
private void OpenFiles()
{
- _itemData = new SequentialFileWriter(World.ItemDataPath, _metrics);
- _itemIndex = new SequentialFileWriter(World.ItemIndexPath, _metrics);
+ _itemData = new SequentialFileWriter(World.ItemDataPath);
+ _itemIndex = new SequentialFileWriter(World.ItemIndexPath);
- _mobileData = new SequentialFileWriter(World.MobileDataPath, _metrics);
- _mobileIndex = new SequentialFileWriter(World.MobileIndexPath, _metrics);
+ _mobileData = new SequentialFileWriter(World.MobileDataPath);
+ _mobileIndex = new SequentialFileWriter(World.MobileIndexPath);
- _guildData = new SequentialFileWriter(World.GuildDataPath, _metrics);
- _guildIndex = new SequentialFileWriter(World.GuildIndexPath, _metrics);
+ _guildData = new SequentialFileWriter(World.GuildDataPath);
+ _guildIndex = new SequentialFileWriter(World.GuildIndexPath);
WriteCount(_itemIndex, World.Items.Count);
WriteCount(_mobileIndex, World.Mobiles.Count);
diff --git a/Server/Persistence/FileOperations.cs b/Projects/Server/Persistence/FileOperations.cs
similarity index 100%
rename from Server/Persistence/FileOperations.cs
rename to Projects/Server/Persistence/FileOperations.cs
diff --git a/Server/Persistence/FileQueue.cs b/Projects/Server/Persistence/FileQueue.cs
similarity index 88%
rename from Server/Persistence/FileQueue.cs
rename to Projects/Server/Persistence/FileQueue.cs
index 21c44eda5..52e670cf4 100644
--- a/Server/Persistence/FileQueue.cs
+++ b/Projects/Server/Persistence/FileQueue.cs
@@ -19,9 +19,9 @@
***************************************************************************/
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Threading;
-using Server.Network;
namespace Server
{
@@ -30,7 +30,6 @@ namespace Server
public sealed class FileQueue : IDisposable
{
private static int bufferSize;
- private static BufferPool bufferPool;
private Chunk[] active;
private int activeCount;
@@ -47,15 +46,14 @@ namespace Server
static FileQueue()
{
bufferSize = FileOperations.BufferSize;
- bufferPool = new BufferPool("File Buffers", 64, bufferSize);
}
public FileQueue(int concurrentWrites, FileCommitCallback callback)
{
if (concurrentWrites < 1) throw new ArgumentOutOfRangeException("concurrentWrites");
- if (bufferSize < 1) throw new ArgumentOutOfRangeException("bufferSize");
- if (callback == null) throw new ArgumentNullException("callback");
+ if (bufferSize < 1)
+ throw new ArgumentOutOfRangeException("bufferSize");
syncRoot = new object();
@@ -139,7 +137,7 @@ namespace Server
{
if (active[slot] != chunk) throw new ArgumentException();
- bufferPool.ReleaseBuffer(chunk.Buffer);
+ ArrayPool.Shared.Return(chunk.Buffer);
if (pending.Count > 0)
{
@@ -172,7 +170,8 @@ namespace Server
while (size > 0)
{
- if (buffered.buffer == null) buffered.buffer = bufferPool.AcquireBuffer();
+ if (buffered.buffer == null)
+ buffered.buffer = ArrayPool.Shared.Rent(bufferSize);
byte[] page = buffered.buffer; // buffer page
int pageSpace = page.Length - buffered.length; // available bytes in page
@@ -229,4 +228,4 @@ namespace Server
public int length;
}
}
-}
\ No newline at end of file
+}
diff --git a/Server/Persistence/ParallelSaveStrategy.cs b/Projects/Server/Persistence/ParallelSaveStrategy.cs
similarity index 87%
rename from Server/Persistence/ParallelSaveStrategy.cs
rename to Projects/Server/Persistence/ParallelSaveStrategy.cs
index 02132c602..0e47fd454 100644
--- a/Server/Persistence/ParallelSaveStrategy.cs
+++ b/Projects/Server/Persistence/ParallelSaveStrategy.cs
@@ -38,7 +38,6 @@ namespace Server
private SequentialFileWriter itemData, itemIndex;
- private SaveMetrics metrics;
private SequentialFileWriter mobileData, mobileIndex;
private int processorCount;
@@ -57,10 +56,8 @@ namespace Server
return processorCount - 1;
}
- public override void Save(SaveMetrics metrics, bool permitBackgroundWrite)
+ public override void Save(bool permitBackgroundWrite)
{
- this.metrics = metrics;
-
OpenFiles();
consumers = new Consumer[GetThreadCount()];
@@ -121,14 +118,14 @@ namespace Server
private void OpenFiles()
{
- itemData = new SequentialFileWriter(World.ItemDataPath, metrics);
- itemIndex = new SequentialFileWriter(World.ItemIndexPath, metrics);
+ itemData = new SequentialFileWriter(World.ItemDataPath);
+ itemIndex = new SequentialFileWriter(World.ItemIndexPath);
- mobileData = new SequentialFileWriter(World.MobileDataPath, metrics);
- mobileIndex = new SequentialFileWriter(World.MobileIndexPath, metrics);
+ mobileData = new SequentialFileWriter(World.MobileDataPath);
+ mobileIndex = new SequentialFileWriter(World.MobileIndexPath);
- guildData = new SequentialFileWriter(World.GuildDataPath, metrics);
- guildIndex = new SequentialFileWriter(World.GuildIndexPath, metrics);
+ guildData = new SequentialFileWriter(World.GuildDataPath);
+ guildIndex = new SequentialFileWriter(World.GuildIndexPath);
WriteCount(itemIndex, World.Items.Count);
WriteCount(mobileIndex, World.Mobiles.Count);
@@ -176,9 +173,7 @@ namespace Server
private void Save(Item item, BinaryMemoryWriter writer)
{
- int length = writer.CommitTo(itemData, itemIndex, item.m_TypeRef, item.Serial);
-
- metrics?.OnItemSaved(length);
+ writer.CommitTo(itemData, itemIndex, item.m_TypeRef, item.Serial);
if (item.Decays && item.Parent == null && item.Map != Map.Internal &&
DateTime.UtcNow > item.LastMoved + item.DecayTime) _decayQueue.Enqueue(item);
@@ -186,16 +181,12 @@ namespace Server
private void Save(Mobile mob, BinaryMemoryWriter writer)
{
- int length = writer.CommitTo(mobileData, mobileIndex, mob.m_TypeRef, mob.Serial);
-
- metrics?.OnMobileSaved(length);
+ writer.CommitTo(mobileData, mobileIndex, mob.m_TypeRef, mob.Serial);
}
private void Save(BaseGuild guild, BinaryMemoryWriter writer)
{
- int length = writer.CommitTo(guildData, guildIndex, 0, guild.Id);
-
- metrics?.OnGuildSaved(length);
+ writer.CommitTo(guildData, guildIndex, 0, guild.Id);
}
private bool Enqueue(ISerializable value)
diff --git a/Server/Persistence/Persistence.cs b/Projects/Server/Persistence/Persistence.cs
similarity index 100%
rename from Server/Persistence/Persistence.cs
rename to Projects/Server/Persistence/Persistence.cs
diff --git a/Server/Persistence/QueuedMemoryWriter.cs b/Projects/Server/Persistence/QueuedMemoryWriter.cs
similarity index 100%
rename from Server/Persistence/QueuedMemoryWriter.cs
rename to Projects/Server/Persistence/QueuedMemoryWriter.cs
diff --git a/Server/Persistence/SaveStrategy.cs b/Projects/Server/Persistence/SaveStrategy.cs
similarity index 91%
rename from Server/Persistence/SaveStrategy.cs
rename to Projects/Server/Persistence/SaveStrategy.cs
index 9da97f2e5..dbf7f6938 100644
--- a/Server/Persistence/SaveStrategy.cs
+++ b/Projects/Server/Persistence/SaveStrategy.cs
@@ -40,7 +40,7 @@ namespace Server
return new StandardSaveStrategy();
}
- public abstract void Save(SaveMetrics metrics, bool permitBackgroundWrite);
+ public abstract void Save(bool permitBackgroundWrite);
public abstract void ProcessDecay();
}
diff --git a/Server/Persistence/SequentialFileWriter.cs b/Projects/Server/Persistence/SequentialFileWriter.cs
similarity index 89%
rename from Server/Persistence/SequentialFileWriter.cs
rename to Projects/Server/Persistence/SequentialFileWriter.cs
index be4b569e9..24af4c618 100644
--- a/Server/Persistence/SequentialFileWriter.cs
+++ b/Projects/Server/Persistence/SequentialFileWriter.cs
@@ -28,16 +28,12 @@ namespace Server
private FileQueue fileQueue;
private FileStream fileStream;
- private SaveMetrics metrics;
-
private AsyncCallback writeCallback;
- public SequentialFileWriter(string path, SaveMetrics metrics)
+ public SequentialFileWriter(string path)
{
if (path == null) throw new ArgumentNullException("path");
- this.metrics = metrics;
-
fileStream = FileOperations.OpenSequentialStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
fileQueue = new FileQueue(
@@ -66,8 +62,6 @@ namespace Server
{
fileStream.Write(chunk.Buffer, chunk.Offset, chunk.Size);
- metrics?.OnFileWritten(chunk.Size);
-
chunk.Commit();
}
else
@@ -84,8 +78,6 @@ namespace Server
fileStream.EndWrite(asyncResult);
- metrics?.OnFileWritten(chunk.Size);
-
chunk.Commit();
}
diff --git a/Server/Persistence/StandardSaveStrategy.cs b/Projects/Server/Persistence/StandardSaveStrategy.cs
similarity index 87%
rename from Server/Persistence/StandardSaveStrategy.cs
rename to Projects/Server/Persistence/StandardSaveStrategy.cs
index 0894434a7..14df9b994 100644
--- a/Server/Persistence/StandardSaveStrategy.cs
+++ b/Projects/Server/Persistence/StandardSaveStrategy.cs
@@ -47,20 +47,20 @@ namespace Server
protected bool UseSequentialWriters => SaveType == SaveOption.Normal || !PermitBackgroundWrite;
- public override void Save(SaveMetrics metrics, bool permitBackgroundWrite)
+ public override void Save(bool permitBackgroundWrite)
{
PermitBackgroundWrite = permitBackgroundWrite;
- SaveMobiles(metrics);
- SaveItems(metrics);
- SaveGuilds(metrics);
+ SaveMobiles();
+ SaveItems();
+ SaveGuilds();
if (permitBackgroundWrite && UseSequentialWriters
) //If we're permitted to write in the background, but we don't anyways, then notify.
World.NotifyDiskWriteComplete();
}
- protected void SaveMobiles(SaveMetrics metrics)
+ protected void SaveMobiles()
{
Dictionary mobiles = World.Mobiles;
@@ -92,8 +92,6 @@ namespace Server
m.Serialize(bin);
- metrics?.OnMobileSaved((int)(bin.Position - start));
-
idx.Write((int)(bin.Position - start));
m.FreeCache();
@@ -109,7 +107,7 @@ namespace Server
bin.Close();
}
- protected void SaveItems(SaveMetrics metrics)
+ protected void SaveItems()
{
Dictionary items = World.Items;
@@ -147,8 +145,6 @@ namespace Server
item.Serialize(bin);
- metrics?.OnItemSaved((int)(bin.Position - start));
-
idx.Write((int)(bin.Position - start));
item.FreeCache();
@@ -163,7 +159,7 @@ namespace Server
bin.Close();
}
- protected void SaveGuilds(SaveMetrics metrics)
+ protected void SaveGuilds()
{
GenericWriter idx;
GenericWriter bin;
@@ -190,8 +186,6 @@ namespace Server
guild.Serialize(bin);
- metrics?.OnGuildSaved((int)(bin.Position - start));
-
idx.Write((int)(bin.Position - start));
}
diff --git a/Server/Point3DList.cs b/Projects/Server/Point3DList.cs
similarity index 100%
rename from Server/Point3DList.cs
rename to Projects/Server/Point3DList.cs
diff --git a/Server/Poison.cs b/Projects/Server/Poison.cs
similarity index 100%
rename from Server/Poison.cs
rename to Projects/Server/Poison.cs
diff --git a/Server/Prompt.cs b/Projects/Server/Prompt.cs
similarity index 100%
rename from Server/Prompt.cs
rename to Projects/Server/Prompt.cs
diff --git a/Projects/Server/Properties/PublishProfiles/Linux-SelfContained.pubxml b/Projects/Server/Properties/PublishProfiles/Linux-SelfContained.pubxml
new file mode 100644
index 000000000..52201225c
--- /dev/null
+++ b/Projects/Server/Properties/PublishProfiles/Linux-SelfContained.pubxml
@@ -0,0 +1,16 @@
+
+
+
+
+ FileSystem
+ Release
+ Any CPU
+ netcoreapp3.0
+ ../
+ linux-x64
+ true
+ <_IsPortable>false
+
+
diff --git a/Projects/Server/Properties/PublishProfiles/MacOSX-SelfContained.pubxml b/Projects/Server/Properties/PublishProfiles/MacOSX-SelfContained.pubxml
new file mode 100644
index 000000000..dbafc0569
--- /dev/null
+++ b/Projects/Server/Properties/PublishProfiles/MacOSX-SelfContained.pubxml
@@ -0,0 +1,16 @@
+
+
+
+
+ FileSystem
+ Release
+ Any CPU
+ netcoreapp3.0
+ ../
+ osx-x64
+ true
+ <_IsPortable>false
+
+
diff --git a/Projects/Server/Properties/PublishProfiles/Unix-Portable.pubxml b/Projects/Server/Properties/PublishProfiles/Unix-Portable.pubxml
new file mode 100644
index 000000000..06a48d905
--- /dev/null
+++ b/Projects/Server/Properties/PublishProfiles/Unix-Portable.pubxml
@@ -0,0 +1,16 @@
+
+
+
+
+ FileSystem
+ Release
+ Any CPU
+ netcoreapp3.0
+ ../
+ linux-x64
+ false
+ <_IsPortable>true
+
+
diff --git a/Projects/Server/Properties/PublishProfiles/Windows.pubxml b/Projects/Server/Properties/PublishProfiles/Windows.pubxml
new file mode 100644
index 000000000..c74181459
--- /dev/null
+++ b/Projects/Server/Properties/PublishProfiles/Windows.pubxml
@@ -0,0 +1,16 @@
+
+
+
+
+ FileSystem
+ Release
+ Any CPU
+ netcoreapp3.0
+ ../
+ win-x64
+ false
+ <_IsPortable>true
+
+
diff --git a/Server/QuestArrow.cs b/Projects/Server/QuestArrow.cs
similarity index 100%
rename from Server/QuestArrow.cs
rename to Projects/Server/QuestArrow.cs
diff --git a/Server/Race.cs b/Projects/Server/Race.cs
similarity index 100%
rename from Server/Race.cs
rename to Projects/Server/Race.cs
diff --git a/Server/Random.cs b/Projects/Server/Random.cs
similarity index 100%
rename from Server/Random.cs
rename to Projects/Server/Random.cs
diff --git a/Server/Region.cs b/Projects/Server/Region.cs
similarity index 100%
rename from Server/Region.cs
rename to Projects/Server/Region.cs
diff --git a/Projects/Server/ScriptCompiler.cs b/Projects/Server/ScriptCompiler.cs
new file mode 100644
index 000000000..c17ec1822
--- /dev/null
+++ b/Projects/Server/ScriptCompiler.cs
@@ -0,0 +1,215 @@
+/***************************************************************************
+ * ScriptCompiler.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.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Linq;
+#if NETCORE
+using System.Runtime.Loader;
+#endif
+
+namespace Server
+{
+ public static class ScriptCompiler
+ {
+ private static Dictionary m_TypeCaches = new Dictionary();
+ private static TypeCache m_NullCache;
+ public static Assembly[] Assemblies { get; set; }
+
+ public static string ScriptsPath = EnsureDirectory("Assemblies");
+
+ public static bool LoadScripts(string path = null)
+ {
+ string[] files = Directory.GetFiles(path ?? ScriptsPath, "*.dll");
+
+ List assemblies = new List();
+
+ AssemblyName[] names = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
+
+ int loaded = 0;
+
+ for (int i = 0; i < files.Length; i++)
+ {
+#if NETCORE
+ assemblies.Add(AssemblyLoadContext.Default.LoadFromAssemblyPath(files[i]));
+ loaded++;
+#else
+ Assemblies[i] = Assembly.LoadFrom(files[i]);
+#endif
+ }
+
+ Assemblies = assemblies.ToArray();
+
+ return loaded > 0;
+ }
+
+ public static void Invoke(string method)
+ {
+ List invoke = new List