> [!IMPORTANT] > **Breaking Changes** > - DecodePacket and EncodePacket delegates replaced with IClientEncryption interface > - NetState.Connection (Socket) replaced with internal RingSocket management > - NetState.RecvPipe and NetState.SendPipe removed (buffers managed internally) ## Summary Upgrades the networking stack from PollGroup-based I/O to io_uring, significantly improving I/O performance on Linux. This also adds native client encryption support for encrypted UO clients. ## Major Changes io_uring Networking Architecture - Replaced PollGroup with IORingGroup for async socket I/O operations - Removed Pipe.cs (mirrored ring buffer) and TcpServer.cs in favor of RingSocketManager - Added NetState.Network.cs - centralized network infrastructure handling accept, recv, send, and disconnect completions - Added SocketHelper.cs - platform-specific socket utilities for raw socket handle operations (getpeername, getsockname) - Buffer management now handled by RingSocketManager with configurable slab allocation ### Client Encryption Support - Added full encryption stack in Network/Encryption/: - EncryptionConfig.cs - configurable encryption modes (None, Unencrypted, Encrypted, Both) - EncryptionManager.cs - encryption detection and initialization for login/game packets - LoginEncryption.cs - handles login packet encryption with version-derived keys - GameEncryption.cs - handles game server encryption using Twofish - TwofishEngine.cs - optimized Twofish block cipher implementation - LoginKeys.cs - encryption key table for client versions - IClientEncryption.cs - interface for client encryption implementations ### NetState Improvements - Replaced Socket Connection with RingSocket _socket for managed socket lifecycle - Changed from GCHandle polling to event-based completion processing - Disconnect handling now properly waits for pending sends to flush - Simplified connecting socket management using lazy queue removal ### Configuration - New settings: network.encryptionMode and network.encryptionDebug - Encryption mode flags: Unencrypted, Encrypted, or Both ### Dependencies - Replaced PollGroup NuGet package with IORingGroup - Linux requires liburing-dev / liburing-devel package ### Test plan - Verify server starts and accepts connections on Linux with io_uring - Verify server starts and accepts connections on Windows (fallback to IOCP) - Test unencrypted client connections (ClassicUO with encryption disabled) - Test encrypted client connections if available - Verify graceful disconnect flushes pending data - Confirm CI builds pass on all target platforms
89 lines
2.7 KiB
C#
89 lines
2.7 KiB
C#
using System.Collections.Generic;
|
|
using ModernUO.CodeGeneratedEvents;
|
|
using Server.Mobiles;
|
|
|
|
namespace Server.Misc
|
|
{
|
|
/*
|
|
* This system prevents the inability for server staff to
|
|
* access their server due to data overflows during login.
|
|
*
|
|
* Whenever a staff character's NetState is disposed right after
|
|
* the login process, the character is moved to and logged out
|
|
* at a "safe" alternative.
|
|
*
|
|
* The location the character was moved from will be reported
|
|
* to the player upon the next successful login.
|
|
*
|
|
* This system does not affect non-staff players.
|
|
*/
|
|
public static class PreventInaccess
|
|
{
|
|
public static readonly bool Enabled = true;
|
|
|
|
private static readonly LocationInfo[] m_Destinations =
|
|
{
|
|
new(new Point3D(5275, 1163, 0), Map.Felucca), // Jail
|
|
new(new Point3D(5275, 1163, 0), Map.Trammel),
|
|
new(new Point3D(5445, 1153, 0), Map.Felucca), // Green acres
|
|
new(new Point3D(5445, 1153, 0), Map.Trammel)
|
|
};
|
|
|
|
private static Dictionary<Mobile, LocationInfo> m_MoveHistory;
|
|
|
|
public static void Initialize()
|
|
{
|
|
m_MoveHistory = new Dictionary<Mobile, LocationInfo>();
|
|
}
|
|
|
|
[OnEvent(nameof(PlayerMobile.PlayerLoginEvent))]
|
|
public static void OnLogin(PlayerMobile from)
|
|
{
|
|
if (!Enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (from == null || from.AccessLevel < AccessLevel.Counselor)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (HasDisconnected(from))
|
|
{
|
|
if (!m_MoveHistory.ContainsKey(from))
|
|
{
|
|
m_MoveHistory[from] = new LocationInfo(from.Location, from.Map);
|
|
}
|
|
|
|
var dest = GetRandomDestination();
|
|
|
|
from.Location = dest.Location;
|
|
from.Map = dest.Map;
|
|
}
|
|
else if (m_MoveHistory.Remove(from, out var orig))
|
|
{
|
|
from.SendMessage(
|
|
$"Your character was moved from {orig.Location} ({orig.Map}) due to a detected client crash."
|
|
);
|
|
}
|
|
}
|
|
|
|
private static bool HasDisconnected(Mobile m) => m.NetState is not { IsConnected: true };
|
|
|
|
private static LocationInfo GetRandomDestination() => m_Destinations.RandomElement();
|
|
|
|
private class LocationInfo
|
|
{
|
|
public LocationInfo(Point3D loc, Map map)
|
|
{
|
|
Location = loc;
|
|
Map = map;
|
|
}
|
|
|
|
public Point3D Location { get; }
|
|
|
|
public Map Map { get; }
|
|
}
|
|
}
|
|
}
|