fix(core): Tightens the network stack (#479)
- [X] Removes network pause/resume - [X] Adds back packet profiler - [X] Adds state machine to keep track and trace netstates - [X] Changes NetState.Running back to using an interlock exchange - [X] Adds preliminary packet throttling support ### Packet Throttling - `[GetThrottle <packetId>` to get the delay in milliseconds for that packet - `[SetThrottle <packetId> <delay>` to set the delay in milliseconds for that packet The settings are saved to `Configuration/throttles.json`
This commit is contained in:
parent
1736469ac0
commit
8903028b5f
36 changed files with 1018 additions and 405 deletions
|
|
@ -140,7 +140,7 @@ namespace Server.Network
|
|||
|
||||
if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length)
|
||||
{
|
||||
state.Disconnect();
|
||||
state.Disconnect("Invalid city selected during character creation.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -197,7 +197,7 @@ namespace Server.Network
|
|||
else
|
||||
{
|
||||
state.BlockAllPackets = false;
|
||||
state.Disconnect();
|
||||
state.Disconnect("Character creation blocked.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -256,7 +256,7 @@ namespace Server.Network
|
|||
|
||||
if (a == null || charSlot < 0 || charSlot >= a.Length)
|
||||
{
|
||||
state.Disconnect();
|
||||
state.Disconnect("Invalid character slot selected.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -277,11 +277,11 @@ namespace Server.Network
|
|||
|
||||
if (m == null)
|
||||
{
|
||||
state.Disconnect();
|
||||
state.Disconnect("Empty character slot selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
m.NetState?.Disconnect();
|
||||
m.NetState?.Disconnect("Character selected for a player already logged in.");
|
||||
|
||||
state.SendClientVersionRequest();
|
||||
|
||||
|
|
@ -380,9 +380,11 @@ namespace Server.Network
|
|||
|
||||
public static void GameLogin(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
// TODO: Connection throttling
|
||||
|
||||
if (state.SentFirstPacket)
|
||||
{
|
||||
state.Disconnect();
|
||||
state.Disconnect("Duplicate game login packet received.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -390,14 +392,16 @@ namespace Server.Network
|
|||
|
||||
var authID = reader.ReadInt32();
|
||||
|
||||
if (
|
||||
!m_AuthIDWindow.TryGetValue(authID, out var ap) ||
|
||||
state._authId != 0 && authID != state._authId ||
|
||||
state._authId == 0 && authID != state._seed
|
||||
)
|
||||
if (!m_AuthIDWindow.TryGetValue(authID, out var ap))
|
||||
{
|
||||
state.WriteConsole("Invalid client detected, disconnecting");
|
||||
state.Disconnect();
|
||||
state.WriteConsole("Invalid client detected, disconnecting...");
|
||||
state.Disconnect("Unable to find auth id.");
|
||||
}
|
||||
|
||||
if (state._authId != 0 && authID != state._authId || state._authId == 0 && authID != state._seed)
|
||||
{
|
||||
state.WriteConsole("Invalid client detected, disconnecting...");
|
||||
state.Disconnect("Invalid auth id in game login packet.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -424,7 +428,7 @@ namespace Server.Network
|
|||
}
|
||||
else
|
||||
{
|
||||
state.Disconnect();
|
||||
state.Disconnect("Login rejected by GameLogin packet handler.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -436,7 +440,7 @@ namespace Server.Network
|
|||
|
||||
if (info == null || a == null || index < 0 || index >= info.Length)
|
||||
{
|
||||
state.Disconnect();
|
||||
state.Disconnect("Invalid server selected.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -457,7 +461,7 @@ namespace Server.Network
|
|||
if (state._seed == 0)
|
||||
{
|
||||
state.WriteConsole("Invalid client detected, disconnecting");
|
||||
state.Disconnect();
|
||||
state.Disconnect("Duplicate seed sent.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -471,9 +475,11 @@ namespace Server.Network
|
|||
|
||||
public static void AccountLogin(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
// TODO: Throttle Connection
|
||||
|
||||
if (state.SentFirstPacket)
|
||||
{
|
||||
state.Disconnect();
|
||||
state.Disconnect("Duplicate account login packet sent.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -512,7 +518,7 @@ namespace Server.Network
|
|||
private static void AccountLogin_ReplyRej(this NetState state, ALRReason reason)
|
||||
{
|
||||
state.SendAccountLoginRejected(reason);
|
||||
state.Disconnect();
|
||||
state.Disconnect("Account login rejected by AccountLogin packet handler.");
|
||||
}
|
||||
|
||||
private class LoginTimer : Timer
|
||||
|
|
|
|||
|
|
@ -106,9 +106,9 @@ namespace Server.Network
|
|||
|
||||
public static void ExtendedCommand(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int packetID = reader.ReadUInt16();
|
||||
int packetId = reader.ReadUInt16();
|
||||
|
||||
var ph = GetExtendedHandler(packetID);
|
||||
var ph = GetExtendedHandler(packetId);
|
||||
|
||||
if (ph == null)
|
||||
{
|
||||
|
|
@ -122,11 +122,11 @@ namespace Server.Network
|
|||
{
|
||||
state.WriteConsole(
|
||||
"Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile",
|
||||
packetID
|
||||
packetId
|
||||
);
|
||||
}
|
||||
|
||||
state.Disconnect();
|
||||
state.Disconnect($"Sent in-game packet(0xBFx{packetId:X2}) but mobile is deleted.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
|
|
@ -82,87 +83,25 @@ namespace Server.Network
|
|||
}
|
||||
}
|
||||
|
||||
public static int ProcessPacket(this NetState ns, ArraySegment<byte>[] buffer)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsInfoPacket(byte packetId)
|
||||
{
|
||||
var reader = new CircularBufferReader(buffer);
|
||||
|
||||
var packetId = reader.ReadByte();
|
||||
|
||||
if (!ns.Seeded)
|
||||
// These packets can arrive at any time during the login process. They're just informational.
|
||||
return packetId switch
|
||||
{
|
||||
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
|
||||
{
|
||||
var seed = (packetId << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte();
|
||||
|
||||
if (seed == 0)
|
||||
{
|
||||
ns.WriteConsole("Invalid client detected, disconnecting");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ns._seed = seed;
|
||||
ns.Seeded = true;
|
||||
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (ns.CheckEncrypted(packetId))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get Handlers
|
||||
var handler = ns.GetHandler(packetId);
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
reader.Trace(ns);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We use this for failing fast where we already know the length, but may not read it entirely
|
||||
var packetLength = handler.Length;
|
||||
|
||||
if (handler.Length <= 0 && reader.Length >= 3)
|
||||
{
|
||||
packetLength = reader.ReadUInt16();
|
||||
if (packetLength < 3)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Not enough data, let's wait for more to come in
|
||||
if (reader.Length < packetLength)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (handler.Ingame && ns.Mobile?.Deleted != false)
|
||||
{
|
||||
ns.WriteConsole("Sent ingame packet (0x{1:X2}) without being attached to a valid mobile.", ns, packetId);
|
||||
return -1;
|
||||
}
|
||||
|
||||
var throttled = handler.ThrottleCallback?.Invoke(ns) ?? TimeSpan.Zero;
|
||||
|
||||
if (throttled > TimeSpan.Zero)
|
||||
{
|
||||
ns.ThrottledUntil = DateTime.UtcNow + throttled;
|
||||
}
|
||||
|
||||
// The packet length is sent in as a ref to support situations where a smaller/larger packet is read.
|
||||
// Example is DropReq to support 6.0.1.7+ where the packet is 1 byte larger
|
||||
handler.OnReceive(ns, reader, ref packetLength);
|
||||
|
||||
return packetLength;
|
||||
0x01 => true, // Disconnect
|
||||
0x73 => true, // Ping
|
||||
0xA4 => true, // SystemInfo
|
||||
0xB1 => true, // Gump Response
|
||||
0xBB => true, // Account ID
|
||||
0xBD => true, // Client Version
|
||||
0xBE => true, // Assist Version
|
||||
0xD9 => true, // Hardware Info
|
||||
0xDD => true, // Gumps (Packed)
|
||||
0xE1 => true, // Client Type
|
||||
0xF4 => true, // CrashReport
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ namespace Server.Network
|
|||
if (!buttonExists)
|
||||
{
|
||||
state.WriteConsole("Invalid gump response, disconnecting...");
|
||||
state.Disconnect();
|
||||
state.Disconnect("Invalid gump response.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -381,7 +381,7 @@ namespace Server.Network
|
|||
if (switchCount < 0 || switchCount > gump.m_Switches)
|
||||
{
|
||||
state.WriteConsole("Invalid gump response, disconnecting...");
|
||||
state.Disconnect();
|
||||
state.Disconnect("Invalid gump response.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -397,7 +397,7 @@ namespace Server.Network
|
|||
if (textCount < 0 || textCount > gump.m_TextEntries)
|
||||
{
|
||||
state.WriteConsole("Invalid gump response, disconnecting...");
|
||||
state.Disconnect();
|
||||
state.Disconnect("Invalid gump response.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -411,7 +411,7 @@ namespace Server.Network
|
|||
if (textLength > 239)
|
||||
{
|
||||
state.WriteConsole("Invalid gump response, disconnecting...");
|
||||
state.Disconnect();
|
||||
state.Disconnect("Invalid gump response.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -593,14 +593,14 @@ namespace Server.Network
|
|||
if (ph.Ingame && state.Mobile == null)
|
||||
{
|
||||
state.WriteConsole(
|
||||
"Sent ingame packet (0xD7x{0:X2}) before having been attached to a mobile",
|
||||
"Sent in-game packet (0xD7x{0:X2}) before being attached to a mobile",
|
||||
packetId
|
||||
);
|
||||
state.Disconnect();
|
||||
state.Disconnect($"Sent in-game packet (0xD7x{packetId:X2}) before being attached to a mobile.");
|
||||
}
|
||||
else if (ph.Ingame && state.Mobile.Deleted)
|
||||
{
|
||||
state.Disconnect();
|
||||
state.Disconnect($"Sent in-game packet(0xD7x{packetId:X2}) but mobile is deleted.");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue