From a8d486a969552bf7e367fcf92a308949a966dfc6 Mon Sep 17 00:00:00 2001
From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com>
Date: Fri, 30 Oct 2020 22:49:00 -0700
Subject: [PATCH] Rearranges packet code (#295)
---
.../Old/Outgoing/AccountPacketTests.cs | 26 +-
Projects/Server/Mobiles/Mobile.cs | 8 +-
Projects/Server/Network/NetState/NetState.cs | 4 +-
Projects/Server/Network/PacketHandlers.cs | 2773 -----------------
.../Packets/{Old Packets => }/ArrowPackets.cs | 0
.../{Old Packets => }/AttributeNormalizer.cs | 0
.../{Old Packets => }/CombatPackets.cs | 0
.../{Old Packets => }/DamagePackets.cs | 0
.../{Old Packets => }/DisplayHuePicker.cs | 0
.../{Old Packets => }/EffectPackets.cs | 0
.../{Old Packets => }/EquipmentPackets.cs | 0
.../Packets/{Old Packets => }/GumpPackets.cs | 0
.../Network/Packets/IncomingAccountPackets.cs | 604 ++++
.../Network/Packets/IncomingEntityPackets.cs | 194 ++
.../Packets/IncomingExtendedCommandPackets.cs | 532 ++++
.../Network/Packets/IncomingHousePackets.cs | 30 +
.../Network/Packets/IncomingItemPackets.cs | 167 +
.../Network/Packets/IncomingMessagePackets.cs | 160 +
.../Network/Packets/IncomingMobilePackets.cs | 166 +
.../Server/Network/Packets/IncomingPackets.cs | 178 ++
.../Network/Packets/IncomingPlayerPackets.cs | 658 ++++
.../Packets/IncomingTargetingPackets.cs | 144 +
.../Network/Packets/IncomingVendorPackets.cs | 117 +
.../Packets/{Old Packets => }/ItemPackets.cs | 0
.../Packets/{Old Packets => }/LightPackets.cs | 0
.../Packets/{Old Packets => }/MapPackets.cs | 0
.../Packets/{Old Packets => }/MenuPackets.cs | 0
.../{Old Packets => }/MessagePackets.cs | 0
.../{Old Packets => }/MobilePackets.cs | 0
.../{Old Packets => }/MovementPackets.cs | 0
.../ObjectHelpResponsePackets.cs | 0
...ntPackets.cs => OutgoingAccountPackets.cs} | 28 +-
.../{Old Packets => }/PlayerPackets.cs | 0
.../{Old Packets => }/SecureTradePackets.cs | 0
.../{Old Packets => }/TargetPackets.cs | 0
.../{Old Packets => }/UnicodePromptPackets.cs | 0
.../{Old Packets => }/VendorBuyPackets.cs | 0
.../{Old Packets => }/VendorSellPackets.cs | 0
Projects/Server/Race.cs | 4 +-
.../Accounting/AccountAttackLimiter.cs | 6 +-
.../UOContent/Accounting/AccountHandler.cs | 6 +-
.../Configuration/ExpansionConfiguration.cs | 2 +-
Projects/UOContent/Engines/Chat/Chat.cs | 4 +-
.../Engines/ConPVP/Games/BombingRun.cs | 6 +-
.../Engines/MLQuests/Gumps/RaceChangeGump.cs | 2 +-
Projects/UOContent/Items/Books/BaseBook.cs | 6 +-
.../Games/Mahjong/MahjongPacketHandlers.cs | 2 +-
Projects/UOContent/Items/Maps/MapItem.cs | 2 +-
.../UOContent/Items/Misc/BulletinBoards.cs | 2 +-
.../UOContent/Items/Shields/BaseShield.cs | 4 +-
Projects/UOContent/Misc/HardwareInfo.cs | 2 +-
Projects/UOContent/Misc/ProtocolExtensions.cs | 2 +-
Projects/UOContent/Mobiles/PlayerMobile.cs | 2 +-
Projects/UOContent/Multis/HouseFoundation.cs | 28 +-
54 files changed, 3023 insertions(+), 2846 deletions(-)
delete mode 100644 Projects/Server/Network/PacketHandlers.cs
rename Projects/Server/Network/Packets/{Old Packets => }/ArrowPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/AttributeNormalizer.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/CombatPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/DamagePackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/DisplayHuePicker.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/EffectPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/EquipmentPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/GumpPackets.cs (100%)
create mode 100644 Projects/Server/Network/Packets/IncomingAccountPackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingEntityPackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingHousePackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingItemPackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingMessagePackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingMobilePackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingPackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingPlayerPackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingTargetingPackets.cs
create mode 100644 Projects/Server/Network/Packets/IncomingVendorPackets.cs
rename Projects/Server/Network/Packets/{Old Packets => }/ItemPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/LightPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/MapPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/MenuPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/MessagePackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/MobilePackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/MovementPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/ObjectHelpResponsePackets.cs (100%)
rename Projects/Server/Network/Packets/{AccountPackets.cs => OutgoingAccountPackets.cs} (93%)
rename Projects/Server/Network/Packets/{Old Packets => }/PlayerPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/SecureTradePackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/TargetPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/UnicodePromptPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/VendorBuyPackets.cs (100%)
rename Projects/Server/Network/Packets/{Old Packets => }/VendorSellPackets.cs (100%)
diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs
index eb18742ad..e16833999 100644
--- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs
+++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs
@@ -22,7 +22,7 @@ namespace Server.Tests.Network
var expected = new ChangeCharacter(account).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendChangeCharacter(ns, account);
+ ns.SendChangeCharacter(account);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -35,7 +35,7 @@ namespace Server.Tests.Network
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendClientVersionRequest(ns);
+ ns.SendClientVersionRequest();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -47,7 +47,7 @@ namespace Server.Tests.Network
var expected = new DeleteResult(DeleteResultType.BadRequest).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendCharacterDeleteResult(ns, DeleteResultType.BadRequest);
+ ns.SendCharacterDeleteResult(DeleteResultType.BadRequest);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -59,7 +59,7 @@ namespace Server.Tests.Network
var expected = new PopupMessage(PMMessage.LoginSyncError).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendPopupMessage(ns, PMMessage.LoginSyncError);
+ ns.SendPopupMessage(PMMessage.LoginSyncError);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -81,7 +81,7 @@ namespace Server.Tests.Network
ns.ProtocolChanges = protocolChanges;
var expected = new SupportedFeatures(ns).Compile();
- Packets.SendSupportedFeature(ns);
+ ns.SendSupportedFeature();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -102,7 +102,7 @@ namespace Server.Tests.Network
var expected = new LoginConfirm(m).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendLoginConfirmation(ns, m);
+ ns.SendLoginConfirmation(m);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -114,7 +114,7 @@ namespace Server.Tests.Network
var expected = new LoginComplete().Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendLoginComplete(ns);
+ ns.SendLoginComplete();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -132,7 +132,7 @@ namespace Server.Tests.Network
var expected = new CharacterListUpdate(acct).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendCharacterListUpdate(ns, acct);
+ ns.SendCharacterListUpdate(acct);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -158,7 +158,7 @@ namespace Server.Tests.Network
ns.Account = acct;
ns.ProtocolChanges = ProtocolChanges.Version70130;
- Packets.SendCharacterList(ns);
+ ns.SendCharacterList();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -183,7 +183,7 @@ namespace Server.Tests.Network
ns.CityInfo = info;
ns.Account = acct;
- Packets.SendCharacterList(ns);
+ ns.SendCharacterList();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -196,7 +196,7 @@ namespace Server.Tests.Network
var expected = new AccountLoginRej(reason).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendAccountLoginRejected(ns, reason);
+ ns.SendAccountLoginRejected(reason);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -215,7 +215,7 @@ namespace Server.Tests.Network
using var ns = PacketTestUtilities.CreateTestNetState();
ns.ServerInfo = info;
- Packets.SendAccountLoginAck(ns);
+ ns.SendAccountLoginAck();
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
@@ -231,7 +231,7 @@ namespace Server.Tests.Network
using var ns = PacketTestUtilities.CreateTestNetState();
- Packets.SendPlayServerAck(ns, si, authId);
+ ns.SendPlayServerAck(si, authId);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs
index 8eb2e75e5..7a8b766a9 100644
--- a/Projects/Server/Mobiles/Mobile.cs
+++ b/Projects/Server/Mobiles/Mobile.cs
@@ -2940,13 +2940,13 @@ namespace Server
if (ns.StygianAbyss)
{
- Packets.SendSupportedFeature(ns);
+ ns.SendSupportedFeature();
ns.Send(new MobileUpdate(this));
ns.Send(new MobileAttributes(this));
}
else
{
- Packets.SendSupportedFeature(ns);
+ ns.SendSupportedFeature();
ns.Send(new MobileUpdateOld(this));
ns.Send(new MobileAttributes(this));
}
@@ -3084,13 +3084,13 @@ namespace Server
if (ns.StygianAbyss)
{
- Packets.SendSupportedFeature(ns);
+ ns.SendSupportedFeature();
ns.Send(new MobileUpdate(this));
ns.Send(new MobileAttributes(this));
}
else
{
- Packets.SendSupportedFeature(ns);
+ ns.SendSupportedFeature();
ns.Send(new MobileUpdateOld(this));
ns.Send(new MobileAttributes(this));
}
diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs
index fccc7b95c..8e33c3ac1 100644
--- a/Projects/Server/Network/NetState/NetState.cs
+++ b/Projects/Server/Network/NetState/NetState.cs
@@ -598,7 +598,7 @@ namespace Server.Network
return;
}
- var bytesProcessed = PacketHandlers.ProcessPacket(this, result.Buffer);
+ var bytesProcessed = this.ProcessPacket(result.Buffer);
if (bytesProcessed <= 0)
{
@@ -685,7 +685,7 @@ namespace Server.Network
}
public PacketHandler GetHandler(int packetID) =>
- ContainerGridLines ? PacketHandlers.Get6017Handler(packetID) : PacketHandlers.GetHandler(packetID);
+ ContainerGridLines ? IncomingPackets.Get6017Handler(packetID) : IncomingPackets.GetHandler(packetID);
public static void TraceException(Exception ex)
{
diff --git a/Projects/Server/Network/PacketHandlers.cs b/Projects/Server/Network/PacketHandlers.cs
deleted file mode 100644
index 2ed9b13d7..000000000
--- a/Projects/Server/Network/PacketHandlers.cs
+++ /dev/null
@@ -1,2773 +0,0 @@
-/*************************************************************************
- * ModernUO *
- * Copyright 2019-2020 - ModernUO Development Team *
- * Email: hi@modernuo.com *
- * File: PacketHandlers.cs *
- * *
- * 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 3 of the License, or *
- * (at your option) any later version. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program. If not, see . *
- *************************************************************************/
-
-using System;
-using System.Buffers;
-using System.Collections.Generic;
-using System.IO;
-using Server.ContextMenus;
-using Server.Diagnostics;
-using Server.Gumps;
-using Server.Items;
-using Server.Targeting;
-using CV = Server.ClientVersion;
-
-namespace Server.Network
-{
- [Flags]
- public enum MessageType
- {
- Regular = 0x00,
- System = 0x01,
- Emote = 0x02,
- Label = 0x06,
- Focus = 0x07,
- Whisper = 0x08,
- Yell = 0x09,
- Spell = 0x0A,
-
- Guild = 0x0D,
- Alliance = 0x0E,
- Command = 0x0F,
-
- Encoded = 0xC0
- }
-
- public static class PacketHandlers
- {
- private const int m_AuthIDWindowSize = 128;
- private static readonly PacketHandler[] m_6017Handlers = new PacketHandler[0x100];
-
- private static readonly PacketHandler[] m_ExtendedHandlersLow = new PacketHandler[0x100];
- private static readonly Dictionary m_ExtendedHandlersHigh = new Dictionary();
-
- private static readonly EncodedPacketHandler[] m_EncodedHandlersLow = new EncodedPacketHandler[0x100];
-
- private static readonly Dictionary m_EncodedHandlersHigh =
- new Dictionary();
-
- private static readonly int[] m_EmptyInts = Array.Empty();
-
- private static readonly KeywordList m_KeywordList = new KeywordList();
-
- private static readonly Dictionary m_AuthIDWindow =
- new Dictionary(m_AuthIDWindowSize);
-
- static PacketHandlers()
- {
- Register(0x00, 104, false, CreateCharacter);
- Register(0x01, 5, false, Disconnect);
- Register(0x02, 7, true, MovementReq);
- Register(0x03, 0, true, AsciiSpeech);
- Register(0x05, 5, true, AttackReq);
- Register(0x06, 5, true, UseReq);
- Register(0x07, 7, true, LiftReq);
- Register(0x08, 14, true, DropReq);
- Register(0x09, 5, true, LookReq);
- Register(0x12, 0, true, TextCommand);
- Register(0x13, 10, true, EquipReq);
- Register(0x22, 3, true, Resynchronize);
- Register(0x2C, 2, true, DeathStatusResponse);
- Register(0x34, 10, true, MobileQuery);
- Register(0x3A, 0, true, ChangeSkillLock);
- Register(0x3B, 0, true, VendorBuyReply);
- Register(0x5D, 73, false, PlayCharacter);
- Register(0x6C, 19, true, TargetResponse);
- Register(0x6F, 0, true, SecureTrade);
- Register(0x72, 5, true, SetWarMode);
- Register(0x73, 2, false, PingReq);
- Register(0x75, 35, true, RenameRequest);
- Register(0x7D, 13, true, MenuResponse);
- Register(0x80, 62, false, AccountLogin);
- Register(0x83, 39, false, DeleteCharacter);
- Register(0x91, 65, false, GameLogin);
- Register(0x95, 9, true, HuePickerResponse);
- Register(0x98, 0, true, MobileNameRequest);
- Register(0x9A, 0, true, AsciiPromptResponse);
- Register(0x9B, 258, true, HelpRequest);
- Register(0x9F, 0, true, VendorSellReply);
- Register(0xA0, 3, false, PlayServer);
- Register(0xA4, 149, false, SystemInfo);
- Register(0xA7, 4, true, RequestScrollWindow);
- Register(0xAD, 0, true, UnicodeSpeech);
- Register(0xB1, 0, true, DisplayGumpResponse);
- Register(0xB6, 9, true, ObjectHelpRequest);
- Register(0xB8, 0, true, ProfileReq);
- Register(0xBB, 9, false, AccountID);
- Register(0xBD, 0, false, ClientVersion);
- Register(0xBE, 0, true, AssistVersion);
- Register(0xBF, 0, true, ExtendedCommand);
- Register(0xC2, 0, true, UnicodePromptResponse);
- Register(0xC8, 2, true, SetUpdateRange);
- Register(0xCF, 0, false, AccountLogin);
- Register(0xD0, 0, true, ConfigurationFile);
- Register(0xD1, 2, true, LogoutReq);
- Register(0xD6, 0, true, BatchQueryProperties);
- Register(0xD7, 0, true, EncodedCommand);
- Register(0xE1, 0, false, ClientType);
- Register(0xEF, 21, false, LoginServerSeed);
- Register(0xEC, 0, false, EquipMacro);
- Register(0xED, 0, false, UnequipMacro);
- Register(0xF4, 0, false, CrashReport);
- Register(0xF8, 106, false, CreateCharacter70160);
- Register(0xFB, 2, false, ShowPublicHouseContent);
-
- Register6017(0x08, 15, true, DropReq6017);
-
- RegisterExtended(0x05, false, ScreenSize);
- RegisterExtended(0x06, true, PartyMessage);
- RegisterExtended(0x07, true, QuestArrow);
- RegisterExtended(0x09, true, DisarmRequest);
- RegisterExtended(0x0A, true, StunRequest);
- RegisterExtended(0x0B, false, Language);
- RegisterExtended(0x0C, true, CloseStatus);
- RegisterExtended(0x0E, true, Animate);
- RegisterExtended(0x0F, false, Empty); // What's this?
- RegisterExtended(0x10, true, QueryProperties);
- RegisterExtended(0x13, true, ContextMenuRequest);
- RegisterExtended(0x15, true, ContextMenuResponse);
- RegisterExtended(0x1A, true, StatLockChange);
- RegisterExtended(0x1C, true, CastSpell);
- RegisterExtended(0x24, false, UnhandledBF);
- RegisterExtended(0x2C, true, BandageTarget);
- RegisterExtended(0x2D, true, TargetedSpell);
- RegisterExtended(0x2E, true, TargetedSkillUse);
- RegisterExtended(0x30, true, TargetByResourceMacro);
- RegisterExtended(0x32, true, ToggleFlying);
-
- RegisterEncoded(0x19, true, SetAbility);
- RegisterEncoded(0x28, true, GuildGumpRequest);
-
- RegisterEncoded(0x32, true, QuestGumpRequest);
- }
-
- public static PacketHandler[] Handlers { get; } = new PacketHandler[0x100];
-
- public static bool SingleClickProps { get; set; }
-
- // TODO: Change to outside configuration
- public static int[] ValidAnimations { get; set; } =
- {
- 6, 21, 32, 33,
- 100, 101, 102,
- 103, 104, 105,
- 106, 107, 108,
- 109, 110, 111,
- 112, 113, 114,
- 115, 116, 117,
- 118, 119, 120,
- 121, 123, 124,
- 125, 126, 127,
- 128
- };
-
- public static bool ClientVerification { get; set; } = true;
-
- public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive)
- {
- Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
- m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive);
- }
-
- public static PacketHandler GetHandler(int packetID) => Handlers[packetID];
-
- public static void Register6017(int packetID, int length, bool ingame, OnPacketReceive onReceive)
- {
- m_6017Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
- }
-
- public static PacketHandler Get6017Handler(int packetID) => m_6017Handlers[packetID];
-
- public static void RegisterExtended(int packetID, bool ingame, OnPacketReceive onReceive)
- {
- if (packetID >= 0 && packetID < 0x100)
- {
- m_ExtendedHandlersLow[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
- }
- else
- {
- m_ExtendedHandlersHigh[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
- }
- }
-
- public static PacketHandler GetExtendedHandler(int packetID)
- {
- if (packetID >= 0 && packetID < 0x100)
- {
- return m_ExtendedHandlersLow[packetID];
- }
-
- m_ExtendedHandlersHigh.TryGetValue(packetID, out var handler);
- return handler;
- }
-
- public static void RemoveExtendedHandler(int packetID)
- {
- if (packetID >= 0 && packetID < 0x100)
- {
- m_ExtendedHandlersLow[packetID] = null;
- }
- else
- {
- m_ExtendedHandlersHigh.Remove(packetID);
- }
- }
-
- public static void RegisterEncoded(int packetID, bool ingame, OnEncodedPacketReceive onReceive)
- {
- if (packetID >= 0 && packetID < 0x100)
- {
- m_EncodedHandlersLow[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
- }
- else
- {
- m_EncodedHandlersHigh[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
- }
- }
-
- public static EncodedPacketHandler GetEncodedHandler(int packetID)
- {
- if (packetID >= 0 && packetID < 0x100)
- {
- return m_EncodedHandlersLow[packetID];
- }
-
- m_EncodedHandlersHigh.TryGetValue(packetID, out var handler);
- return handler;
- }
-
- public static void RemoveEncodedHandler(int packetID)
- {
- if (packetID >= 0 && packetID < 0x100)
- {
- m_EncodedHandlersLow[packetID] = null;
- }
- else
- {
- m_EncodedHandlersHigh.Remove(packetID);
- }
- }
-
- public static void RegisterThrottler(int packetID, ThrottlePacketCallback t)
- {
- var ph = GetHandler(packetID);
-
- if (ph != null)
- {
- ph.ThrottleCallback = t;
- }
-
- ph = Get6017Handler(packetID);
-
- if (ph != null)
- {
- ph.ThrottleCallback = t;
- }
- }
-
- public static int ProcessPacket(NetState ns, ArraySegment[] buffer)
- {
- var reader = new CircularBufferReader(buffer);
-
- var packetId = reader.ReadByte();
-
- 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
- {
- var seed = (packetId << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte();
-
- if (seed == 0)
- {
- ns.WriteConsole("Invalid client detected, disconnecting");
- return -1;
- }
-
- ns.m_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;
- }
-
- 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;
- }
-
- handler.OnReceive(ns, reader);
-
- return packetLength;
- }
-
- private static void UnhandledBF(NetState state, CircularBufferReader reader)
- {
- }
-
- public static void Empty(NetState state, CircularBufferReader reader)
- {
- }
-
- public static void SetAbility(NetState state, IEntity e, EncodedReader reader)
- {
- EventSink.InvokeSetAbility(state.Mobile, reader.ReadInt32());
- }
-
- public static void GuildGumpRequest(NetState state, IEntity e, EncodedReader reader)
- {
- EventSink.InvokeGuildGumpRequest(state.Mobile);
- }
-
- public static void QuestGumpRequest(NetState state, IEntity e, EncodedReader reader)
- {
- EventSink.InvokeQuestGumpRequest(state.Mobile);
- }
-
- public static void EncodedCommand(NetState state, CircularBufferReader reader)
- {
- var e = World.FindEntity(reader.ReadUInt32());
- int packetId = reader.ReadUInt16();
-
- var ph = GetEncodedHandler(packetId);
-
- if (ph != null)
- {
- if (ph.Ingame && state.Mobile == null)
- {
- state.WriteConsole(
- "Sent ingame packet (0xD7x{0:X2}) before having been attached to a mobile",
- packetId
- );
- state.Dispose();
- }
- else if (ph.Ingame && state.Mobile.Deleted)
- {
- state.Dispose();
- }
- else
- {
- ph.OnReceive(state, e, new EncodedReader(reader));
- }
- }
- else
- {
- reader.Trace(state);
- }
- }
-
- public static void RenameRequest(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
- var targ = World.FindMobile(reader.ReadUInt32());
-
- if (targ != null)
- {
- EventSink.InvokeRenameRequest(from, targ, reader.ReadAsciiSafe());
- }
- }
-
- public static void SecureTrade(NetState state, CircularBufferReader reader)
- {
- switch (reader.ReadByte())
- {
- case 1: // Cancel
- {
- Serial serial = reader.ReadUInt32();
-
- if (World.FindItem(serial) is SecureTradeContainer cont && cont.Trade != null &&
- (cont.Trade.From.Mobile == state.Mobile || cont.Trade.To.Mobile == state.Mobile))
- {
- cont.Trade.Cancel();
- }
-
- break;
- }
- case 2: // Check
- {
- Serial serial = reader.ReadUInt32();
-
- if (World.FindItem(serial) is SecureTradeContainer cont)
- {
- var trade = cont.Trade;
-
- var value = reader.ReadInt32() != 0;
-
- if (trade != null && trade.From.Mobile == state.Mobile)
- {
- trade.From.Accepted = value;
- trade.Update();
- }
- else if (trade != null && trade.To.Mobile == state.Mobile)
- {
- trade.To.Accepted = value;
- trade.Update();
- }
- }
-
- break;
- }
- case 3: // Update Gold
- {
- Serial serial = reader.ReadUInt32();
-
- if (World.FindItem(serial) is SecureTradeContainer cont)
- {
- var gold = reader.ReadInt32();
- var plat = reader.ReadInt32();
-
- var trade = cont.Trade;
-
- if (trade != null)
- {
- if (trade.From.Mobile == state.Mobile)
- {
- trade.From.Gold = gold;
- trade.From.Plat = plat;
- trade.UpdateFromCurrency();
- }
- else if (trade.To.Mobile == state.Mobile)
- {
- trade.To.Gold = gold;
- trade.To.Plat = plat;
- trade.UpdateToCurrency();
- }
- }
- }
- }
- break;
- }
- }
-
- public static void VendorBuyReply(NetState state, CircularBufferReader reader)
- {
- var vendor = World.FindMobile(reader.ReadUInt32());
- var flag = reader.ReadByte();
-
- if (vendor == null)
- {
- return;
- }
-
- if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
- {
- state.Send(new EndVendorBuy(vendor));
- return;
- }
-
- if (flag == 0x02)
- {
- var msgSize = reader.Remaining;
-
- if (msgSize / 7 > 100)
- {
- return;
- }
-
- var buyList = new List(msgSize / 7);
- while (msgSize > 0)
- {
- var layer = reader.ReadByte();
- Serial serial = reader.ReadUInt32();
- int amount = reader.ReadInt16();
-
- buyList.Add(new BuyItemResponse(serial, amount));
- msgSize -= 7;
- }
-
- if (buyList.Count > 0 && vendor is IVendor v && v.OnBuyItems(state.Mobile, buyList))
- {
- state.Send(new EndVendorBuy(vendor));
- }
- }
- else
- {
- state.Send(new EndVendorBuy(vendor));
- }
- }
-
- public static void VendorSellReply(NetState state, CircularBufferReader reader)
- {
- Serial serial = reader.ReadUInt32();
- var vendor = World.FindMobile(serial);
-
- if (vendor == null)
- {
- return;
- }
-
- if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
- {
- state.Send(new EndVendorSell(vendor));
- return;
- }
-
- int count = reader.ReadUInt16();
-
- if (count >= 100 || reader.Remaining != count * 6)
- {
- return;
- }
-
- var sellList = new List(count);
-
- for (var i = 0; i < count; i++)
- {
- var item = World.FindItem(reader.ReadUInt32());
- int amount = reader.ReadInt16();
-
- if (item != null && amount > 0)
- {
- sellList.Add(new SellItemResponse(item, amount));
- }
- }
-
- if (sellList.Count > 0 && vendor is IVendor v && v.OnSellItems(state.Mobile, sellList))
- {
- state.Send(new EndVendorSell(vendor));
- }
- }
-
- public static void DeleteCharacter(NetState state, CircularBufferReader reader)
- {
- reader.Seek(30, SeekOrigin.Current);
- var index = reader.ReadInt32();
-
- EventSink.InvokeDeleteRequest(state, index);
- }
-
- public static void DeathStatusResponse(NetState state, CircularBufferReader reader)
- {
- // Ignored
- }
-
- public static void ObjectHelpRequest(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- Serial serial = reader.ReadUInt32();
- int unk = reader.ReadByte();
- var lang = reader.ReadAscii(3);
-
- if (serial.IsItem)
- {
- var item = World.FindItem(serial);
-
- if (item != null && from.Map == item.Map && Utility.InUpdateRange(item.GetWorldLocation(), from.Location) &&
- from.CanSee(item))
- {
- item.OnHelpRequest(from);
- }
- }
- else if (serial.IsMobile)
- {
- var m = World.FindMobile(serial);
-
- if (m != null && from.Map == m.Map && Utility.InUpdateRange(m.Location, from.Location) && from.CanSee(m))
- {
- m.OnHelpRequest(m);
- }
- }
- }
-
- public static void MobileNameRequest(NetState state, CircularBufferReader reader)
- {
- var m = World.FindMobile(reader.ReadUInt32());
-
- if (m != null && Utility.InUpdateRange(state.Mobile, m) && state.Mobile.CanSee(m))
- {
- state.Send(new MobileName(m));
- }
- }
-
- public static void RequestScrollWindow(NetState state, CircularBufferReader reader)
- {
- int lastTip = reader.ReadInt16();
- int type = reader.ReadByte();
- }
-
- public static void AttackReq(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
- var m = World.FindMobile(reader.ReadUInt32());
-
- if (m != null)
- {
- from.Attack(m);
- }
- }
-
- public static void HuePickerResponse(NetState state, CircularBufferReader reader)
- {
- var serial = reader.ReadUInt32();
- _ = reader.ReadInt16(); // Item ID
- var hue = reader.ReadInt16() & 0x3FFF;
-
- hue = Utility.ClipDyedHue(hue);
-
- foreach (var huePicker in state.HuePickers)
- {
- if (huePicker.Serial == serial)
- {
- state.RemoveHuePicker(huePicker);
-
- huePicker.OnResponse(hue);
-
- break;
- }
- }
- }
-
- public static void SystemInfo(NetState state, CircularBufferReader reader)
- {
- int v1 = reader.ReadByte();
- int v2 = reader.ReadUInt16();
- int v3 = reader.ReadByte();
- var s1 = reader.ReadAscii(32);
- var s2 = reader.ReadAscii(32);
- var s3 = reader.ReadAscii(32);
- var s4 = reader.ReadAscii(32);
- int v4 = reader.ReadUInt16();
- int v5 = reader.ReadUInt16();
- var v6 = reader.ReadInt32();
- var v7 = reader.ReadInt32();
- var v8 = reader.ReadInt32();
- }
-
- public static void AccountID(NetState state, CircularBufferReader reader)
- {
- }
-
- public static void TextCommand(NetState state, CircularBufferReader reader)
- {
- int type = reader.ReadByte();
- var command = reader.ReadAscii();
-
- var m = state.Mobile;
-
- switch (type)
- {
- case 0xC7: // Animate
- {
- EventSink.InvokeAnimateRequest(m, command);
-
- break;
- }
- case 0x24: // Use skill
- {
- if (!int.TryParse(command.Split(' ')[0], out var skillIndex))
- {
- break;
- }
-
- Skills.UseSkill(m, skillIndex);
-
- break;
- }
- case 0x43: // Open spellbook
- {
- if (!int.TryParse(command, out var booktype))
- {
- booktype = 1;
- }
-
- EventSink.InvokeOpenSpellbookRequest(m, booktype);
-
- break;
- }
- case 0x27: // Cast spell from book
- {
- var split = command.Split(' ');
-
- if (split.Length > 0)
- {
- var spellID = Utility.ToInt32(split[0]) - 1;
- var serial = split.Length > 1 ? Utility.ToUInt32(split[1]) : (uint)Serial.MinusOne;
-
- EventSink.InvokeCastSpellRequest(m, spellID, World.FindItem(serial));
- }
-
- break;
- }
- case 0x58: // Open door
- {
- EventSink.InvokeOpenDoorMacroUsed(m);
-
- break;
- }
- case 0x56: // Cast spell from macro
- {
- var spellID = Utility.ToInt32(command) - 1;
-
- EventSink.InvokeCastSpellRequest(m, spellID, null);
-
- break;
- }
- case 0xF4: // Invoke virtues from macro
- {
- var virtueID = Utility.ToInt32(command) - 1;
-
- EventSink.InvokeVirtueMacroRequest(m, virtueID);
-
- break;
- }
- case 0x2F: // Old scroll double click
- {
- /*
- * This command is still sent for items 0xEF3 - 0xEF9
- *
- * Command is one of three, depending on the item ID of the scroll:
- * - [scroll serial]
- * - [scroll serial] [target serial]
- * - [scroll serial] [x] [y] [z]
- */
- break;
- }
- default:
- {
- state.WriteConsole("Unknown text-command type 0x{0:X2}: {1}", state, type, command);
- break;
- }
- }
- }
-
- public static void AsciiPromptResponse(NetState state, CircularBufferReader reader)
- {
- var serial = reader.ReadUInt32();
- var prompt = reader.ReadInt32();
- var type = reader.ReadInt32();
- var text = reader.ReadAsciiSafe();
-
- if (text.Length > 128)
- {
- return;
- }
-
- var from = state.Mobile;
- var p = from.Prompt;
-
- if (p != null && p.Serial == serial && p.Serial == prompt)
- {
- from.Prompt = null;
-
- if (type == 0)
- {
- p.OnCancel(from);
- }
- else
- {
- p.OnResponse(from, text);
- }
- }
- }
-
- public static void UnicodePromptResponse(NetState state, CircularBufferReader reader)
- {
- var serial = reader.ReadUInt32();
- var prompt = reader.ReadInt32();
- var type = reader.ReadInt32();
- var lang = reader.ReadAscii(4);
- var text = reader.ReadLittleUniSafe();
-
- if (text.Length > 128)
- {
- return;
- }
-
- var from = state.Mobile;
- var p = from.Prompt;
-
- if (p != null && p.Serial == serial && p.Serial == prompt)
- {
- from.Prompt = null;
-
- if (type == 0)
- {
- p.OnCancel(from);
- }
- else
- {
- p.OnResponse(from, text);
- }
- }
- }
-
- public static void MenuResponse(NetState state, CircularBufferReader reader)
- {
- var serial = reader.ReadUInt32();
- int menuID = reader.ReadInt16(); // unused in our implementation
- int index = reader.ReadInt16();
- int itemID = reader.ReadInt16();
- int hue = reader.ReadInt16();
-
- index -= 1; // convert from 1-based to 0-based
-
- foreach (var menu in state.Menus)
- {
- if (menu.Serial == serial)
- {
- state.RemoveMenu(menu);
-
- if (index >= 0 && index < menu.EntryLength)
- {
- menu.OnResponse(state, index);
- }
- else
- {
- menu.OnCancel(state);
- }
-
- break;
- }
- }
- }
-
- public static void ProfileReq(NetState state, CircularBufferReader reader)
- {
- int type = reader.ReadByte();
- Serial serial = reader.ReadUInt32();
-
- var beholder = state.Mobile;
- var beheld = World.FindMobile(serial);
-
- if (beheld == null)
- {
- return;
- }
-
- switch (type)
- {
- case 0x00: // display request
- {
- EventSink.InvokeProfileRequest(beholder, beheld);
-
- break;
- }
- case 0x01: // edit request
- {
- reader.ReadInt16(); // Skip
- int length = reader.ReadUInt16();
-
- if (length > 511)
- {
- return;
- }
-
- var text = reader.ReadBigUni(length);
-
- EventSink.InvokeChangeProfileRequest(beholder, beheld, text);
-
- break;
- }
- }
- }
-
- public static void Disconnect(NetState state, CircularBufferReader reader)
- {
- var minusOne = reader.ReadInt32();
- }
-
- public static void LiftReq(NetState state, CircularBufferReader reader)
- {
- Serial serial = reader.ReadUInt32();
- int amount = reader.ReadUInt16();
- var item = World.FindItem(serial);
-
- state.Mobile.Lift(item, amount, out var rejected, out var reject);
- }
-
- public static void EquipReq(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
- var item = from.Holding;
-
- var valid = item != null && item.HeldBy == from && item.Map == Map.Internal;
-
- from.Holding = null;
-
- if (!valid)
- {
- return;
- }
-
- reader.Seek(5, SeekOrigin.Current);
- var to = World.FindMobile(reader.ReadUInt32()) ?? from;
-
- if (!to.AllowEquipFrom(from) || !to.EquipItem(item))
- {
- item.Bounce(from);
- }
-
- item.ClearBounce();
- }
-
- public static void DropReq(NetState state, CircularBufferReader reader)
- {
- reader.ReadInt32(); // serial, ignored
- int x = reader.ReadInt16();
- int y = reader.ReadInt16();
- int z = reader.ReadSByte();
- Serial dest = reader.ReadUInt32();
-
- var loc = new Point3D(x, y, z);
-
- var from = state.Mobile;
-
- if (dest.IsMobile)
- {
- from.Drop(World.FindMobile(dest), loc);
- }
- else if (dest.IsItem)
- {
- var item = World.FindItem(dest);
-
- if (item is BaseMulti multi && multi.AllowsRelativeDrop)
- {
- loc.m_X += multi.X;
- loc.m_Y += multi.Y;
- from.Drop(loc);
- }
- else
- {
- from.Drop(item, loc);
- }
- }
- else
- {
- from.Drop(loc);
- }
- }
-
- public static void DropReq6017(NetState state, CircularBufferReader reader)
- {
- reader.ReadInt32(); // serial, ignored
- int x = reader.ReadInt16();
- int y = reader.ReadInt16();
- int z = reader.ReadSByte();
- reader.ReadByte(); // Grid Location?
- Serial dest = reader.ReadUInt32();
-
- var loc = new Point3D(x, y, z);
-
- var from = state.Mobile;
-
- if (dest.IsMobile)
- {
- from.Drop(World.FindMobile(dest), loc);
- }
- else if (dest.IsItem)
- {
- var item = World.FindItem(dest);
-
- if (item is BaseMulti multi && multi.AllowsRelativeDrop)
- {
- loc.m_X += multi.X;
- loc.m_Y += multi.Y;
- from.Drop(loc);
- }
- else
- {
- from.Drop(item, loc);
- }
- }
- else
- {
- from.Drop(loc);
- }
- }
-
- public static void ConfigurationFile(NetState state, CircularBufferReader reader)
- {
- }
-
- public static void LogoutReq(NetState state, CircularBufferReader reader)
- {
- state.Send(new LogoutAck());
- }
-
- public static void ChangeSkillLock(NetState state, CircularBufferReader reader)
- {
- var s = state.Mobile.Skills[reader.ReadInt16()];
-
- s?.SetLockNoRelay((SkillLock)reader.ReadByte());
- }
-
- public static void HelpRequest(NetState state, CircularBufferReader reader)
- {
- EventSink.InvokeHelpRequest(state.Mobile);
- }
-
- public static void TargetResponse(NetState state, CircularBufferReader reader)
- {
- int type = reader.ReadByte();
- var targetID = reader.ReadInt32();
- int flags = reader.ReadByte();
- Serial serial = reader.ReadUInt32();
- int x = reader.ReadInt16(), y = reader.ReadInt16(), z = reader.ReadInt16();
- int graphic = reader.ReadUInt16();
-
- if (targetID == unchecked((int)0xDEADBEEF))
- {
- return;
- }
-
- var from = state.Mobile;
-
- var t = from.Target;
-
- if (t == null)
- {
- return;
- }
-
- var prof = TargetProfile.Acquire(t.GetType());
- prof?.Start();
-
- try
- {
- if (x == -1 && y == -1 && !serial.IsValid)
- {
- // User pressed escape
- t.Cancel(from, TargetCancelType.Canceled);
- }
- else if (t.TargetID != targetID)
- {
- // Sanity, prevent fake target
- }
- else
- {
- object toTarget;
-
- if (type == 1)
- {
- if (graphic == 0)
- {
- toTarget = new LandTarget(new Point3D(x, y, z), from.Map);
- }
- else
- {
- var map = from.Map;
-
- if (map == null || map == Map.Internal)
- {
- t.Cancel(from, TargetCancelType.Canceled);
- return;
- }
- else
- {
- var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis);
-
- var valid = false;
-
- if (state.HighSeas)
- {
- var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
- if (id.Surface)
- {
- z -= id.Height;
- }
- }
-
- for (var i = 0; !valid && i < tiles.Length; ++i)
- {
- if (tiles[i].Z == z && tiles[i].ID == graphic)
- {
- valid = true;
- }
- }
-
- if (!valid)
- {
- t.Cancel(from, TargetCancelType.Canceled);
- return;
- }
- else
- {
- toTarget = new StaticTarget(new Point3D(x, y, z), graphic);
- }
- }
- }
- }
- else if (serial.IsMobile)
- {
- toTarget = World.FindMobile(serial);
- }
- else if (serial.IsItem)
- {
- toTarget = World.FindItem(serial);
- }
- else
- {
- t.Cancel(from, TargetCancelType.Canceled);
- return;
- }
-
- t.Invoke(from, toTarget);
- }
- }
- finally
- {
- prof?.Finish();
- }
- }
-
- public static void DisplayGumpResponse(NetState state, CircularBufferReader reader)
- {
- var serial = reader.ReadUInt32();
- var typeID = reader.ReadInt32();
- var buttonID = reader.ReadInt32();
-
- foreach (var gump in state.Gumps)
- {
- if (gump.Serial != serial || gump.TypeID != typeID)
- {
- continue;
- }
-
- var buttonExists = buttonID == 0; // 0 is always 'close'
-
- if (!buttonExists)
- {
- foreach (var e in gump.Entries)
- {
- if (e is GumpButton button && button.ButtonID == buttonID)
- {
- buttonExists = true;
- break;
- }
-
- if (e is GumpImageTileButton tileButton && tileButton.ButtonID == buttonID)
- {
- buttonExists = true;
- break;
- }
- }
- }
-
- if (!buttonExists)
- {
- state.WriteConsole("Invalid gump response, disconnecting...");
- state.Dispose();
- return;
- }
-
- var switchCount = reader.ReadInt32();
-
- if (switchCount < 0 || switchCount > gump.m_Switches)
- {
- state.WriteConsole("Invalid gump response, disconnecting...");
- state.Dispose();
- return;
- }
-
- var switches = new int[switchCount];
-
- for (var j = 0; j < switches.Length; ++j)
- {
- switches[j] = reader.ReadInt32();
- }
-
- var textCount = reader.ReadInt32();
-
- if (textCount < 0 || textCount > gump.m_TextEntries)
- {
- state.WriteConsole("Invalid gump response, disconnecting...");
- state.Dispose();
- return;
- }
-
- var textEntries = new TextRelay[textCount];
-
- for (var j = 0; j < textEntries.Length; ++j)
- {
- int entryID = reader.ReadUInt16();
- int textLength = reader.ReadUInt16();
-
- if (textLength > 239)
- {
- state.WriteConsole("Invalid gump response, disconnecting...");
- state.Dispose();
- return;
- }
-
- var text = reader.ReadBigUniSafe(textLength);
- textEntries[j] = new TextRelay(entryID, text);
- }
-
- state.RemoveGump(gump);
-
- var prof = GumpProfile.Acquire(gump.GetType());
-
- prof?.Start();
-
- gump.OnResponse(state, new RelayInfo(buttonID, switches, textEntries));
-
- prof?.Finish();
-
- return;
- }
-
- if (typeID == 461)
- {
- // Virtue gump
- var switchCount = reader.ReadInt32();
-
- if (buttonID == 1 && switchCount > 0)
- {
- var beheld = World.FindMobile(reader.ReadUInt32());
-
- if (beheld != null)
- {
- EventSink.InvokeVirtueGumpRequest(state.Mobile, beheld);
- }
- }
- else
- {
- var beheld = World.FindMobile(serial);
-
- if (beheld != null)
- {
- EventSink.InvokeVirtueItemRequest(state.Mobile, beheld, buttonID);
- }
- }
- }
- }
-
- public static void SetWarMode(NetState state, CircularBufferReader reader)
- {
- state.Mobile.DelayChangeWarmode(reader.ReadBoolean());
- }
-
- public static void Resynchronize(NetState state, CircularBufferReader reader)
- {
- var m = state.Mobile;
-
- if (state.StygianAbyss)
- {
- state.Send(new MobileUpdate(m));
- }
- else
- {
- state.Send(new MobileUpdateOld(m));
- }
-
- state.Send(MobileIncoming.Create(state, m, m));
-
- m.SendEverything();
-
- state.Sequence = 0;
-
- m.ClearFastwalkStack();
- }
-
- public static void AsciiSpeech(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- var type = (MessageType)reader.ReadByte();
- int hue = reader.ReadInt16();
- reader.ReadInt16(); // font
- var text = reader.ReadAsciiSafe().Trim();
-
- if (text.Length <= 0 || text.Length > 128)
- {
- return;
- }
-
- if (!Enum.IsDefined(typeof(MessageType), type))
- {
- type = MessageType.Regular;
- }
-
- from.DoSpeech(text, m_EmptyInts, type, Utility.ClipDyedHue(hue));
- }
-
- public static void UnicodeSpeech(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- var type = (MessageType)reader.ReadByte();
- int hue = reader.ReadInt16();
- reader.ReadInt16(); // font
- var lang = reader.ReadAscii(4);
- string text;
-
- var isEncoded = (type & MessageType.Encoded) != 0;
- int[] keywords;
-
- if (isEncoded)
- {
- int value = reader.ReadInt16();
- var count = (value & 0xFFF0) >> 4;
- var hold = value & 0xF;
-
- if (count < 0 || count > 50)
- {
- return;
- }
-
- var keyList = m_KeywordList;
-
- for (var i = 0; i < count; ++i)
- {
- int speechID;
-
- if ((i & 1) == 0)
- {
- hold <<= 8;
- hold |= reader.ReadByte();
- speechID = hold;
- hold = 0;
- }
- else
- {
- value = reader.ReadInt16();
- speechID = (value & 0xFFF0) >> 4;
- hold = value & 0xF;
- }
-
- if (!keyList.Contains(speechID))
- {
- keyList.Add(speechID);
- }
- }
-
- text = reader.ReadUTF8Safe();
-
- keywords = keyList.ToArray();
- }
- else
- {
- text = reader.ReadBigUniSafe();
-
- keywords = m_EmptyInts;
- }
-
- text = text.Trim();
-
- if (text.Length <= 0 || text.Length > 128)
- {
- return;
- }
-
- type &= ~MessageType.Encoded;
-
- if (!Enum.IsDefined(typeof(MessageType), type))
- {
- type = MessageType.Regular;
- }
-
- from.Language = lang;
- from.DoSpeech(text, keywords, type, Utility.ClipDyedHue(hue));
- }
-
- public static void UseReq(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0)
- {
- var value = reader.ReadUInt32();
-
- if ((value & ~0x7FFFFFFF) != 0)
- {
- from.OnPaperdollRequest();
- }
- else
- {
- Serial s = value;
-
- if (s.IsMobile)
- {
- var m = World.FindMobile(s);
-
- if (m?.Deleted == false)
- {
- from.Use(m);
- }
- }
- else if (s.IsItem)
- {
- var item = World.FindItem(s);
-
- if (item?.Deleted == false)
- {
- from.Use(item);
- }
- }
- }
-
- from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
- }
- else
- {
- from.SendActionMessage();
- }
- }
-
- public static void LookReq(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- Serial s = reader.ReadUInt32();
-
- if (s.IsMobile)
- {
- var m = World.FindMobile(s);
-
- if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
- {
- if (SingleClickProps)
- {
- m.OnAosSingleClick(from);
- }
- else
- {
- if (from.Region.OnSingleClick(from, m))
- {
- m.OnSingleClick(from);
- }
- }
- }
- }
- else if (s.IsItem)
- {
- var item = World.FindItem(s);
-
- if (item?.Deleted == false && from.CanSee(item) &&
- Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
- {
- if (SingleClickProps)
- {
- item.OnAosSingleClick(from);
- }
- else if (from.Region.OnSingleClick(from, item))
- {
- if (item.Parent is Item parentItem)
- {
- parentItem.OnSingleClickContained(from, item);
- }
-
- item.OnSingleClick(from);
- }
- }
- }
- }
-
- public static void PingReq(NetState state, CircularBufferReader reader)
- {
- state.Send(PingAck.Instantiate(reader.ReadByte()));
- }
-
- public static void SetUpdateRange(NetState state, CircularBufferReader reader)
- {
- state.Send(ChangeUpdateRange.Instantiate(18));
- }
-
- public static void MovementReq(NetState state, CircularBufferReader reader)
- {
- var dir = (Direction)reader.ReadByte();
- int seq = reader.ReadByte();
- var key = reader.ReadInt32();
-
- var m = state.Mobile;
-
- if (state.Sequence == 0 && seq != 0 || !m.Move(dir))
- {
- state.Send(new MovementRej(seq, m));
- state.Sequence = 0;
-
- m.ClearFastwalkStack();
- }
- else
- {
- ++seq;
-
- if (seq == 256)
- {
- seq = 1;
- }
-
- state.Sequence = seq;
- }
- }
-
- public static void Animate(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
- var action = reader.ReadInt32();
-
- var ok = false;
-
- for (var i = 0; !ok && i < ValidAnimations.Length; ++i)
- {
- ok = action == ValidAnimations[i];
- }
-
- if (from != null && ok && from.Alive && from.Body.IsHuman && !from.Mounted)
- {
- from.Animate(action, 7, 1, true, false, 0);
- }
- }
-
- public static void QuestArrow(NetState state, CircularBufferReader reader)
- {
- var rightClick = reader.ReadBoolean();
- var from = state.Mobile;
-
- from?.QuestArrow?.OnClick(rightClick);
- }
-
- public static void ExtendedCommand(NetState state, CircularBufferReader reader)
- {
- int packetID = reader.ReadUInt16();
-
- var ph = GetExtendedHandler(packetID);
-
- if (ph == null)
- {
- reader.Trace(state);
- return;
- }
-
- if (ph.Ingame && state.Mobile?.Deleted != false)
- {
- if (state.Mobile == null)
- {
- state.WriteConsole(
- "Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile",
- packetID
- );
- }
-
- state.Dispose();
- }
- else
- {
- ph.OnReceive(state, reader);
- }
- }
-
- public static void CastSpell(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- if (from == null)
- {
- return;
- }
-
- Item spellbook = null;
-
- if (reader.ReadInt16() == 1)
- {
- spellbook = World.FindItem(reader.ReadUInt32());
- }
-
- var spellID = reader.ReadInt16() - 1;
-
- EventSink.InvokeCastSpellRequest(from, spellID, spellbook);
- }
-
- public static void BandageTarget(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- if (from == null)
- {
- return;
- }
-
- if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0)
- {
- var bandage = World.FindItem(reader.ReadUInt32());
-
- if (bandage == null)
- {
- return;
- }
-
- var target = World.FindMobile(reader.ReadUInt32());
-
- if (target == null)
- {
- return;
- }
-
- EventSink.InvokeBandageTargetRequest(from, bandage, target);
-
- from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
- }
- else
- {
- from.SendActionMessage();
- }
- }
-
- public static void ToggleFlying(NetState state, CircularBufferReader reader)
- {
- state.Mobile.ToggleFlying();
- }
-
- public static void BatchQueryProperties(NetState state, CircularBufferReader reader)
- {
- if (!ObjectPropertyList.Enabled)
- {
- return;
- }
-
- var from = state.Mobile;
-
- var length = reader.Remaining;
-
- if (length % 4 != 0)
- {
- return;
- }
-
- while (reader.Remaining > 0)
- {
- Serial s = reader.ReadUInt32();
-
- if (s.IsMobile)
- {
- var m = World.FindMobile(s);
-
- if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
- {
- m.SendPropertiesTo(from);
- }
- }
- else if (s.IsItem)
- {
- var item = World.FindItem(s);
-
- if (item?.Deleted == false && from.CanSee(item) &&
- Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
- {
- item.SendPropertiesTo(from);
- }
- }
- }
- }
-
- public static void QueryProperties(NetState state, CircularBufferReader reader)
- {
- if (!ObjectPropertyList.Enabled)
- {
- return;
- }
-
- var from = state.Mobile;
-
- Serial s = reader.ReadUInt32();
-
- if (s.IsMobile)
- {
- var m = World.FindMobile(s);
-
- if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
- {
- m.SendPropertiesTo(from);
- }
- }
- else if (s.IsItem)
- {
- var item = World.FindItem(s);
-
- if (item?.Deleted == false && from.CanSee(item) &&
- Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
- {
- item.SendPropertiesTo(from);
- }
- }
- }
-
- public static void PartyMessage(NetState state, CircularBufferReader reader)
- {
- if (state.Mobile == null)
- {
- return;
- }
-
- switch (reader.ReadByte())
- {
- case 0x01:
- PartyMessage_AddMember(state, reader);
- break;
- case 0x02:
- PartyMessage_RemoveMember(state, reader);
- break;
- case 0x03:
- PartyMessage_PrivateMessage(state, reader);
- break;
- case 0x04:
- PartyMessage_PublicMessage(state, reader);
- break;
- case 0x06:
- PartyMessage_SetCanLoot(state, reader);
- break;
- case 0x08:
- PartyMessage_Accept(state, reader);
- break;
- case 0x09:
- PartyMessage_Decline(state, reader);
- break;
- default:
- reader.Trace(state);
- break;
- }
- }
-
- public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader)
- {
- PartyCommands.Handler?.OnAdd(state.Mobile);
- }
-
- public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader)
- {
- PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile(reader.ReadUInt32()));
- }
-
- public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader)
- {
- PartyCommands.Handler?.OnPrivateMessage(
- state.Mobile,
- World.FindMobile(reader.ReadUInt32()),
- reader.ReadBigUniSafe()
- );
- }
-
- public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader)
- {
- PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe());
- }
-
- public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader)
- {
- PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean());
- }
-
- public static void PartyMessage_Accept(NetState state, CircularBufferReader reader)
- {
- PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile(reader.ReadUInt32()));
- }
-
- public static void PartyMessage_Decline(NetState state, CircularBufferReader reader)
- {
- PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile(reader.ReadUInt32()));
- }
-
- public static void StunRequest(NetState state, CircularBufferReader reader)
- {
- EventSink.InvokeStunRequest(state.Mobile);
- }
-
- public static void DisarmRequest(NetState state, CircularBufferReader reader)
- {
- EventSink.InvokeDisarmRequest(state.Mobile);
- }
-
- public static void StatLockChange(NetState state, CircularBufferReader reader)
- {
- int stat = reader.ReadByte();
- int lockValue = reader.ReadByte();
-
- if (lockValue > 2)
- {
- lockValue = 0;
- }
-
- var m = state.Mobile;
-
- if (m != null)
- {
- switch (stat)
- {
- case 0:
- m.StrLock = (StatLockType)lockValue;
- break;
- case 1:
- m.DexLock = (StatLockType)lockValue;
- break;
- case 2:
- m.IntLock = (StatLockType)lockValue;
- break;
- }
- }
- }
-
- public static void ScreenSize(NetState state, CircularBufferReader reader)
- {
- var width = reader.ReadInt32();
- var unk = reader.ReadInt32();
- }
-
- public static void ContextMenuResponse(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- if (from == null)
- {
- return;
- }
-
- var menu = from.ContextMenu;
-
- from.ContextMenu = null;
-
- if (menu != null && from == menu.From)
- {
- var entity = World.FindEntity(reader.ReadUInt32());
-
- if (entity != null && entity == menu.Target && from.CanSee(entity))
- {
- Point3D p;
-
- if (entity is Mobile)
- {
- p = entity.Location;
- }
- else if (entity is Item item)
- {
- p = item.GetWorldLocation();
- }
- else
- {
- return;
- }
-
- int index = reader.ReadUInt16();
-
- if (index >= 0 && index < menu.Entries.Length)
- {
- var e = menu.Entries[index];
-
- var range = e.Range;
-
- if (range == -1)
- {
- range = 18;
- }
-
- if (e.Enabled && from.InRange(p, range))
- {
- e.OnClick();
- }
- }
- }
- }
- }
-
- public static void ContextMenuRequest(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
- var target = World.FindEntity(reader.ReadUInt32());
-
- if (from != null && target != null && from.Map == target.Map && from.CanSee(target))
- {
- if (target is Mobile && !Utility.InUpdateRange(from.Location, target.Location))
- {
- return;
- }
-
- var item = target as Item;
-
- if (item != null && !Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
- {
- return;
- }
-
- if (!from.CheckContextMenuDisplay(target))
- {
- return;
- }
-
- var c = new ContextMenu(from, target);
-
- if (c.Entries.Length > 0)
- {
- if (item?.RootParent is Mobile mobile && mobile != from && mobile.AccessLevel >= from.AccessLevel)
- {
- for (var i = 0; i < c.Entries.Length; ++i)
- {
- if (!c.Entries[i].NonLocalUse)
- {
- c.Entries[i].Enabled = false;
- }
- }
- }
-
- from.ContextMenu = c;
- }
- }
- }
-
- public static void CloseStatus(NetState state, CircularBufferReader reader)
- {
- Serial serial = reader.ReadUInt32();
- }
-
- public static void Language(NetState state, CircularBufferReader reader)
- {
- var lang = reader.ReadAscii(4);
-
- if (state.Mobile != null)
- {
- state.Mobile.Language = lang;
- }
- }
-
- public static void AssistVersion(NetState state, CircularBufferReader reader)
- {
- var unk = reader.ReadInt32();
- var av = reader.ReadAscii();
- }
-
- public static void ClientVersion(NetState state, CircularBufferReader reader)
- {
- var version = state.Version = new CV(reader.ReadAscii());
-
- EventSink.InvokeClientVersionReceived(state, version);
- }
-
- public static void ClientType(NetState state, CircularBufferReader reader)
- {
- reader.ReadUInt16();
-
- int type = reader.ReadUInt16();
- var version = state.Version = new CV(reader.ReadAscii());
-
- EventSink.InvokeClientVersionReceived(state, version);
- }
-
- public static void MobileQuery(NetState state, CircularBufferReader reader)
- {
- var from = state.Mobile;
-
- reader.ReadInt32(); // 0xEDEDEDED
- int type = reader.ReadByte();
- var m = World.FindMobile(reader.ReadUInt32());
-
- if (m != null)
- {
- switch (type)
- {
- case 0x04: // Stats
- {
- m.OnStatsQuery(from);
- break;
- }
- case 0x05:
- {
- m.OnSkillsQuery(from);
- break;
- }
- default:
- {
- reader.Trace(state);
- break;
- }
- }
- }
- }
-
- public static void PlayCharacter(NetState state, CircularBufferReader reader)
- {
- reader.ReadInt32(); // 0xEDEDEDED
-
- var name = reader.ReadAscii(30);
-
- reader.Seek(2, SeekOrigin.Current);
-
- var flags = reader.ReadInt32();
-
- reader.Seek(24, SeekOrigin.Current);
-
- var charSlot = reader.ReadInt32();
- var clientIP = reader.ReadInt32();
-
- var a = state.Account;
-
- if (a == null || charSlot < 0 || charSlot >= a.Length)
- {
- state.Dispose();
- }
- else
- {
- var m = a[charSlot];
-
- // Check if anyone is using this account
- for (var i = 0; i < a.Length; ++i)
- {
- var check = a[i];
-
- if (check != null && check.Map != Map.Internal && check != m)
- {
- state.WriteConsole("Account in use");
- Packets.SendPopupMessage(state, PMMessage.CharInWorld);
- return;
- }
- }
-
- if (m == null)
- {
- state.Dispose();
- return;
- }
-
- m.NetState?.Dispose();
-
- // TODO: Make this wait one tick so we don't have to call it unnecessarily
- NetState.ProcessDisposedQueue();
-
- Packets.SendClientVersionRequest(state);
-
- state.BlockAllPackets = true;
-
- state.Flags = (ClientFlags)flags;
-
- state.Mobile = m;
- m.NetState = state;
-
- new LoginTimer(state, m).Start();
- }
- }
-
- public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader)
- {
- var showPublicHouseContent = reader.ReadBoolean();
- }
-
- public static void DoLogin(NetState state, Mobile m)
- {
- Packets.SendLoginConfirmation(state, m);
-
- if (m.Map != null)
- {
- state.Send(new MapChange(m.Map));
- }
-
- if (!Core.SE && state.ProtocolChanges < ProtocolChanges.Version6000)
- {
- state.Send(new MapPatches());
- }
-
- state.Send(SeasonChange.Instantiate(m.GetSeason(), true));
-
- Packets.SendSupportedFeature(state);
-
- state.Sequence = 0;
-
- if (state.NewMobileIncoming)
- {
- state.Send(new MobileUpdate(m));
- state.Send(new MobileUpdate(m));
-
- m.CheckLightLevels(true);
-
- state.Send(new MobileUpdate(m));
-
- state.Send(new MobileIncoming(m, m));
- // state.Send( new MobileAttributes( m ) );
- state.Send(new MobileStatus(m, m));
- state.Send(Network.SetWarMode.Instantiate(m.Warmode));
-
- m.SendEverything();
-
- Packets.SendSupportedFeature(state);
- state.Send(new MobileUpdate(m));
- // state.Send( new MobileAttributes( m ) );
- state.Send(new MobileStatus(m, m));
- state.Send(Network.SetWarMode.Instantiate(m.Warmode));
- state.Send(new MobileIncoming(m, m));
- }
- else if (state.StygianAbyss)
- {
- state.Send(new MobileUpdate(m));
- state.Send(new MobileUpdate(m));
-
- m.CheckLightLevels(true);
-
- state.Send(new MobileUpdate(m));
-
- state.Send(new MobileIncomingSA(m, m));
- // state.Send( new MobileAttributes( m ) );
- state.Send(new MobileStatus(m, m));
- state.Send(Network.SetWarMode.Instantiate(m.Warmode));
-
- m.SendEverything();
-
- Packets.SendSupportedFeature(state);
- state.Send(new MobileUpdate(m));
- // state.Send( new MobileAttributes( m ) );
- state.Send(new MobileStatus(m, m));
- state.Send(Network.SetWarMode.Instantiate(m.Warmode));
- state.Send(new MobileIncomingSA(m, m));
- }
- else
- {
- state.Send(new MobileUpdateOld(m));
- state.Send(new MobileUpdateOld(m));
-
- m.CheckLightLevels(true);
-
- state.Send(new MobileUpdateOld(m));
-
- state.Send(new MobileIncomingOld(m, m));
- // state.Send( new MobileAttributes( m ) );
- state.Send(new MobileStatus(m, m));
- state.Send(Network.SetWarMode.Instantiate(m.Warmode));
-
- m.SendEverything();
-
- Packets.SendSupportedFeature(state);
- state.Send(new MobileUpdateOld(m));
- // state.Send( new MobileAttributes( m ) );
- state.Send(new MobileStatus(m, m));
- state.Send(Network.SetWarMode.Instantiate(m.Warmode));
- state.Send(new MobileIncomingOld(m, m));
- }
-
- Packets.SendLoginComplete(state);
- state.Send(new CurrentTime());
- state.Send(SeasonChange.Instantiate(m.GetSeason(), true));
- if (m.Map != null)
- {
- state.Send(new MapChange(m.Map));
- }
-
- EventSink.InvokeLogin(m);
-
- m.ClearFastwalkStack();
- }
-
- public static void CreateCharacter(NetState state, CircularBufferReader reader)
- {
- var unk1 = reader.ReadInt32();
- var unk2 = reader.ReadInt32();
- int unk3 = reader.ReadByte();
- var name = reader.ReadAscii(30);
-
- reader.Seek(2, SeekOrigin.Current);
- var flags = reader.ReadInt32();
- reader.Seek(8, SeekOrigin.Current);
- int prof = reader.ReadByte();
- reader.Seek(15, SeekOrigin.Current);
-
- int genderRace = reader.ReadByte();
-
- int str = reader.ReadByte();
- int dex = reader.ReadByte();
- int intl = reader.ReadByte();
- int is1 = reader.ReadByte();
- int vs1 = reader.ReadByte();
- int is2 = reader.ReadByte();
- int vs2 = reader.ReadByte();
- int is3 = reader.ReadByte();
- int vs3 = reader.ReadByte();
- int hue = reader.ReadUInt16();
- int hairVal = reader.ReadInt16();
- int hairHue = reader.ReadInt16();
- int hairValf = reader.ReadInt16();
- int hairHuef = reader.ReadInt16();
- reader.ReadByte();
- int cityIndex = reader.ReadByte();
- var charSlot = reader.ReadInt32();
- var clientIP = reader.ReadInt32();
- int shirtHue = reader.ReadInt16();
- int pantsHue = reader.ReadInt16();
-
- /*
- Pre-7.0.0.0:
- 0x00, 0x01 -> Human Male, Human Female
- 0x02, 0x03 -> Elf Male, Elf Female
-
- Post-7.0.0.0:
- 0x00, 0x01
- 0x02, 0x03 -> Human Male, Human Female
- 0x04, 0x05 -> Elf Male, Elf Female
- 0x05, 0x06 -> Gargoyle Male, Gargoyle Female
- */
-
- var female = genderRace % 2 != 0;
-
- Race race;
-
- if (state.StygianAbyss)
- {
- var raceID = (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1);
- race = Race.Races[raceID];
- }
- else
- {
- race = Race.Races[(byte)(genderRace / 2)];
- }
-
- race ??= Race.DefaultRace;
-
- var info = state.CityInfo;
- var a = state.Account;
-
- if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length)
- {
- state.Dispose();
- }
- else
- {
- // Check if anyone is using this account
- for (var i = 0; i < a.Length; ++i)
- {
- var check = a[i];
-
- if (check != null && check.Map != Map.Internal)
- {
- state.WriteConsole("Account in use");
- Packets.SendPopupMessage(state, PMMessage.CharInWorld);
- return;
- }
- }
-
- state.Flags = (ClientFlags)flags;
-
- var args = new CharacterCreatedEventArgs(
- state,
- a,
- name,
- female,
- hue,
- str,
- dex,
- intl,
- info[cityIndex],
- new[]
- {
- new SkillNameValue((SkillName)is1, vs1),
- new SkillNameValue((SkillName)is2, vs2),
- new SkillNameValue((SkillName)is3, vs3)
- },
- shirtHue,
- pantsHue,
- hairVal,
- hairHue,
- hairValf,
- hairHuef,
- prof,
- race
- );
-
- Packets.SendClientVersionRequest(state);
-
- state.BlockAllPackets = true;
-
- EventSink.InvokeCharacterCreated(args);
-
- var m = args.Mobile;
-
- if (m != null)
- {
- state.Mobile = m;
- m.NetState = state;
- new LoginTimer(state, m).Start();
- }
- else
- {
- state.BlockAllPackets = false;
- state.Dispose();
- }
- }
- }
-
- public static void CreateCharacter70160(NetState state, CircularBufferReader reader)
- {
- var unk1 = reader.ReadInt32();
- var unk2 = reader.ReadInt32();
- int unk3 = reader.ReadByte();
- var name = reader.ReadAscii(30);
-
- reader.Seek(2, SeekOrigin.Current);
- var flags = reader.ReadInt32();
- reader.Seek(8, SeekOrigin.Current);
- int prof = reader.ReadByte();
- reader.Seek(15, SeekOrigin.Current);
-
- int genderRace = reader.ReadByte();
-
- int str = reader.ReadByte();
- int dex = reader.ReadByte();
- int intl = reader.ReadByte();
- int is1 = reader.ReadByte();
- int vs1 = reader.ReadByte();
- int is2 = reader.ReadByte();
- int vs2 = reader.ReadByte();
- int is3 = reader.ReadByte();
- int vs3 = reader.ReadByte();
- int is4 = reader.ReadByte();
- int vs4 = reader.ReadByte();
-
- int hue = reader.ReadUInt16();
- int hairVal = reader.ReadInt16();
- int hairHue = reader.ReadInt16();
- int hairValf = reader.ReadInt16();
- int hairHuef = reader.ReadInt16();
- reader.ReadByte();
- int cityIndex = reader.ReadByte();
- var charSlot = reader.ReadInt32();
- var clientIP = reader.ReadInt32();
- int shirtHue = reader.ReadInt16();
- int pantsHue = reader.ReadInt16();
-
- /*
- 0x00, 0x01
- 0x02, 0x03 -> Human Male, Human Female
- 0x04, 0x05 -> Elf Male, Elf Female
- 0x05, 0x06 -> Gargoyle Male, Gargoyle Female
- */
-
- var female = genderRace % 2 != 0;
-
- Race race;
-
- var raceID = (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1);
- race = Race.Races[raceID] ?? Race.DefaultRace;
-
- var info = state.CityInfo;
- var a = state.Account;
-
- if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length)
- {
- state.Dispose();
- }
- else
- {
- // Check if anyone is using this account
- for (var i = 0; i < a.Length; ++i)
- {
- var check = a[i];
-
- if (check != null && check.Map != Map.Internal)
- {
- state.WriteConsole("Account in use");
- Packets.SendPopupMessage(state, PMMessage.CharInWorld);
- return;
- }
- }
-
- state.Flags = (ClientFlags)flags;
-
- var args = new CharacterCreatedEventArgs(
- state,
- a,
- name,
- female,
- hue,
- str,
- dex,
- intl,
- info[cityIndex],
- new[]
- {
- new SkillNameValue((SkillName)is1, vs1),
- new SkillNameValue((SkillName)is2, vs2),
- new SkillNameValue((SkillName)is3, vs3),
- new SkillNameValue((SkillName)is4, vs4)
- },
- shirtHue,
- pantsHue,
- hairVal,
- hairHue,
- hairValf,
- hairHuef,
- prof,
- race
- );
-
- Packets.SendClientVersionRequest(state);
-
- state.BlockAllPackets = true;
-
- EventSink.InvokeCharacterCreated(args);
-
- var m = args.Mobile;
-
- if (m != null)
- {
- state.Mobile = m;
- m.NetState = state;
- new LoginTimer(state, m).Start();
- }
- else
- {
- state.BlockAllPackets = false;
- state.Dispose();
- }
- }
- }
-
- private static int GenerateAuthID(NetState state)
- {
- if (m_AuthIDWindow.Count == m_AuthIDWindowSize)
- {
- var oldestID = 0;
- var oldest = DateTime.MaxValue;
-
- foreach (var kvp in m_AuthIDWindow)
- {
- if (kvp.Value.Age < oldest)
- {
- oldestID = kvp.Key;
- oldest = kvp.Value.Age;
- }
- }
-
- m_AuthIDWindow.Remove(oldestID);
- }
-
- int authID;
-
- do
- {
- authID = Utility.Random(1, int.MaxValue - 1);
-
- if (Utility.RandomBool())
- {
- authID |= 1 << 31;
- }
- } while (m_AuthIDWindow.ContainsKey(authID));
-
- m_AuthIDWindow[authID] = new AuthIDPersistence(state.Version);
-
- return authID;
- }
-
- public static void GameLogin(NetState state, CircularBufferReader reader)
- {
- if (state.SentFirstPacket)
- {
- state.Dispose();
- return;
- }
-
- state.SentFirstPacket = true;
-
- var authID = reader.ReadInt32();
-
- if (m_AuthIDWindow.TryGetValue(authID, out var ap))
- {
- m_AuthIDWindow.Remove(authID);
-
- state.Version = ap.Version;
- }
- else if (ClientVerification)
- {
- state.WriteConsole("Invalid client detected, disconnecting");
- state.Dispose();
- return;
- }
-
- if (state.m_AuthID != 0 && authID != state.m_AuthID)
- {
- state.WriteConsole("Invalid client detected, disconnecting");
- state.Dispose();
- return;
- }
-
- if (state.m_AuthID == 0 && authID != state.m_Seed)
- {
- state.WriteConsole("Invalid client detected, disconnecting");
- state.Dispose();
- return;
- }
-
- var username = reader.ReadAscii(30);
- var password = reader.ReadAscii(30);
-
- var e = new GameLoginEventArgs(state, username, password);
-
- EventSink.InvokeGameLogin(e);
-
- if (e.Accepted)
- {
- state.CityInfo = e.CityInfo;
- state.CompressionEnabled = true;
- state.PacketEncoder = NetworkCompression.Compress;
-
- Packets.SendSupportedFeature(state);
- Packets.SendCharacterList(state);
- }
- else
- {
- state.Dispose();
- }
- }
-
- public static void PlayServer(NetState state, CircularBufferReader reader)
- {
- int index = reader.ReadInt16();
- var info = state.ServerInfo;
- var a = state.Account;
-
- if (info == null || a == null || index < 0 || index >= info.Length)
- {
- state.Dispose();
- }
- else
- {
- var si = info[index];
-
- state.m_AuthID = GenerateAuthID(state);
-
- state.SentFirstPacket = false;
- Packets.SendPlayServerAck(state, si, state.m_AuthID);
- }
- }
-
- public static void LoginServerSeed(NetState state, CircularBufferReader reader)
- {
- state.m_Seed = reader.ReadInt32();
- state.Seeded = true;
-
- if (state.m_Seed == 0)
- {
- state.WriteConsole("Invalid client detected, disconnecting");
- state.Dispose();
- return;
- }
-
- var clientMaj = reader.ReadInt32();
- var clientMin = reader.ReadInt32();
- var clientRev = reader.ReadInt32();
- var clientPat = reader.ReadInt32();
-
- state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat);
- }
-
- public static void CrashReport(NetState state, CircularBufferReader reader)
- {
- var clientMaj = reader.ReadByte();
- var clientMin = reader.ReadByte();
- var clientRev = reader.ReadByte();
- var clientPat = reader.ReadByte();
-
- var x = reader.ReadUInt16();
- var y = reader.ReadUInt16();
- var z = reader.ReadSByte();
- var map = reader.ReadByte();
-
- var account = reader.ReadAscii(32);
- var character = reader.ReadAscii(32);
- var ip = reader.ReadAscii(15);
-
- var unk1 = reader.ReadInt32();
- var exception = reader.ReadInt32();
-
- var process = reader.ReadAscii(100);
- var report = reader.ReadAscii(100);
-
- reader.ReadByte(); // 0x00
-
- var offset = reader.ReadInt32();
-
- int count = reader.ReadByte();
-
- for (var i = 0; i < count; i++)
- {
- var address = reader.ReadInt32();
- }
- }
-
- public static void AccountLogin(NetState state, CircularBufferReader reader)
- {
- if (state.SentFirstPacket)
- {
- state.Dispose();
- return;
- }
-
- state.SentFirstPacket = true;
-
- var username = reader.ReadAscii(30);
- var password = reader.ReadAscii(30);
-
- var e = new AccountLoginEventArgs(state, username, password);
-
- EventSink.InvokeAccountLogin(e);
-
- if (e.Accepted)
- {
- AccountLogin_ReplyAck(state);
- }
- else
- {
- AccountLogin_ReplyRej(state, e.RejectReason);
- }
- }
-
- public static void AccountLogin_ReplyAck(NetState state)
- {
- var e = new ServerListEventArgs(state, state.Account);
-
- EventSink.InvokeServerList(e);
-
- if (e.Rejected)
- {
- state.Account = null;
- AccountLogin_ReplyRej(state, ALRReason.BadComm);
- }
- else
- {
- state.ServerInfo = e.Servers.ToArray();
- Packets.SendAccountLoginAck(state);
- }
- }
-
- public static void AccountLogin_ReplyRej(NetState state, ALRReason reason)
- {
- Packets.SendAccountLoginRejected(state, reason);
- state.Dispose();
- }
-
- public static void EquipMacro(NetState state, CircularBufferReader reader)
- {
- int count = reader.ReadByte();
- var serialList = new List(count);
- for (var i = 0; i < count; ++i)
- {
- serialList.Add(reader.ReadUInt32());
- }
-
- EventSink.InvokeEquipMacro(state.Mobile, serialList);
- }
-
- public static void UnequipMacro(NetState state, CircularBufferReader reader)
- {
- int count = reader.ReadByte();
- var layers = new List(count);
- for (var i = 0; i < count; ++i)
- {
- layers.Add((Layer)reader.ReadUInt16());
- }
-
- EventSink.InvokeUnequipMacro(state.Mobile, layers);
- }
-
- public static void TargetedSpell(NetState state, CircularBufferReader reader)
- {
- var spellId = (short)(reader.ReadInt16() - 1); // zero based;
-
- EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity(reader.ReadUInt32()), spellId);
- }
-
- public static void TargetedSkillUse(NetState state, CircularBufferReader reader)
- {
- var skillId = reader.ReadInt16();
-
- EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity(reader.ReadUInt32()), skillId);
- }
-
- public static void TargetByResourceMacro(NetState state, CircularBufferReader reader)
- {
- Serial serial = reader.ReadUInt32();
-
- if (serial.IsItem)
- {
- EventSink.InvokeTargetByResourceMacro(state.Mobile, World.FindItem(serial), reader.ReadInt16());
- }
- }
-
- private class LoginTimer : Timer
- {
- private readonly Mobile m_Mobile;
- private readonly NetState m_State;
-
- public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
- {
- m_State = state;
- m_Mobile = m;
- }
-
- protected override void OnTick()
- {
- if (m_State == null)
- {
- Stop();
- return;
- }
-
- if (m_State.Version != null)
- {
- m_State.BlockAllPackets = false;
- DoLogin(m_State, m_Mobile);
- Stop();
- }
- }
- }
-
- internal struct AuthIDPersistence
- {
- public DateTime Age;
- public ClientVersion Version;
-
- public AuthIDPersistence(ClientVersion v)
- {
- Age = DateTime.UtcNow;
- Version = v;
- }
- }
- }
-}
diff --git a/Projects/Server/Network/Packets/Old Packets/ArrowPackets.cs b/Projects/Server/Network/Packets/ArrowPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/ArrowPackets.cs
rename to Projects/Server/Network/Packets/ArrowPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/AttributeNormalizer.cs b/Projects/Server/Network/Packets/AttributeNormalizer.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/AttributeNormalizer.cs
rename to Projects/Server/Network/Packets/AttributeNormalizer.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/CombatPackets.cs b/Projects/Server/Network/Packets/CombatPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/CombatPackets.cs
rename to Projects/Server/Network/Packets/CombatPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/DamagePackets.cs b/Projects/Server/Network/Packets/DamagePackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/DamagePackets.cs
rename to Projects/Server/Network/Packets/DamagePackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/DisplayHuePicker.cs b/Projects/Server/Network/Packets/DisplayHuePicker.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/DisplayHuePicker.cs
rename to Projects/Server/Network/Packets/DisplayHuePicker.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/EffectPackets.cs b/Projects/Server/Network/Packets/EffectPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/EffectPackets.cs
rename to Projects/Server/Network/Packets/EffectPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/EquipmentPackets.cs b/Projects/Server/Network/Packets/EquipmentPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/EquipmentPackets.cs
rename to Projects/Server/Network/Packets/EquipmentPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/GumpPackets.cs b/Projects/Server/Network/Packets/GumpPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/GumpPackets.cs
rename to Projects/Server/Network/Packets/GumpPackets.cs
diff --git a/Projects/Server/Network/Packets/IncomingAccountPackets.cs b/Projects/Server/Network/Packets/IncomingAccountPackets.cs
new file mode 100644
index 000000000..82d55f793
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingAccountPackets.cs
@@ -0,0 +1,604 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingAccountPackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using CV = Server.ClientVersion;
+
+namespace Server.Network
+{
+ public static class IncomingAccountPackets
+ {
+ private const int m_AuthIDWindowSize = 128;
+ private static readonly Dictionary m_AuthIDWindow =
+ new Dictionary(m_AuthIDWindowSize);
+
+ internal struct AuthIDPersistence
+ {
+ public DateTime Age;
+ public readonly ClientVersion Version;
+
+ public AuthIDPersistence(ClientVersion v)
+ {
+ Age = DateTime.UtcNow;
+ Version = v;
+ }
+ }
+
+ public static void Configure()
+ {
+ IncomingPackets.Register(0x00, 104, false, CreateCharacter);
+ IncomingPackets.Register(0x5D, 73, false, PlayCharacter);
+ IncomingPackets.Register(0x80, 62, false, AccountLogin);
+ IncomingPackets.Register(0x83, 39, false, DeleteCharacter);
+ IncomingPackets.Register(0x91, 65, false, GameLogin);
+ IncomingPackets.Register(0xA0, 3, false, PlayServer);
+ IncomingPackets.Register(0xBB, 9, false, AccountID);
+ IncomingPackets.Register(0xBD, 0, false, ClientVersion);
+ IncomingPackets.Register(0xBE, 0, true, AssistVersion);
+ IncomingPackets.Register(0xCF, 0, false, AccountLogin);
+ IncomingPackets.Register(0xE1, 0, false, ClientType);
+ IncomingPackets.Register(0xEF, 21, false, LoginServerSeed);
+ IncomingPackets.Register(0xF8, 106, false, CreateCharacter);
+ }
+
+ public static void CreateCharacter(NetState state, CircularBufferReader reader)
+ {
+ var unk1 = reader.ReadInt32();
+ var unk2 = reader.ReadInt32();
+ int unk3 = reader.ReadByte();
+ var name = reader.ReadAscii(30);
+
+ reader.Seek(2, SeekOrigin.Current);
+ var flags = reader.ReadInt32();
+ reader.Seek(8, SeekOrigin.Current);
+ int prof = reader.ReadByte();
+ reader.Seek(15, SeekOrigin.Current);
+
+ int genderRace = reader.ReadByte();
+
+ int str = reader.ReadByte();
+ int dex = reader.ReadByte();
+ int intl = reader.ReadByte();
+ int is1 = reader.ReadByte();
+ int vs1 = reader.ReadByte();
+ int is2 = reader.ReadByte();
+ int vs2 = reader.ReadByte();
+ int is3 = reader.ReadByte();
+ int vs3 = reader.ReadByte();
+
+ SkillNameValue[] skills;
+
+ // We have protocol changes by now, so this is ok
+ if (state.NewCharacterCreation)
+ {
+ int is4 = reader.ReadByte();
+ int vs4 = reader.ReadByte();
+
+ skills = new[]
+ {
+ new SkillNameValue((SkillName)is1, vs1),
+ new SkillNameValue((SkillName)is2, vs2),
+ new SkillNameValue((SkillName)is3, vs3),
+ new SkillNameValue((SkillName)is4, vs4)
+ };
+ }
+ else
+ {
+ skills = new[]
+ {
+ new SkillNameValue((SkillName)is1, vs1),
+ new SkillNameValue((SkillName)is2, vs2),
+ new SkillNameValue((SkillName)is3, vs3)
+ };
+ }
+
+ int hue = reader.ReadUInt16();
+ int hairVal = reader.ReadInt16();
+ int hairHue = reader.ReadInt16();
+ int hairValf = reader.ReadInt16();
+ int hairHuef = reader.ReadInt16();
+ reader.ReadByte();
+ int cityIndex = reader.ReadByte();
+ var charSlot = reader.ReadInt32();
+ var clientIP = reader.ReadInt32();
+ int shirtHue = reader.ReadInt16();
+ int pantsHue = reader.ReadInt16();
+
+ /*
+ Pre-7.0.0.0:
+ 0x00, 0x01 -> Human Male, Human Female
+ 0x02, 0x03 -> Elf Male, Elf Female
+
+ Post-7.0.0.0:
+ 0x00, 0x01
+ 0x02, 0x03 -> Human Male, Human Female
+ 0x04, 0x05 -> Elf Male, Elf Female
+ 0x05, 0x06 -> Gargoyle Male, Gargoyle Female
+ */
+
+ var female = genderRace % 2 != 0;
+
+ var raceID = state.StygianAbyss ? (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1) : (byte)(genderRace / 2);
+ Race race = Race.Races[raceID] ?? Race.DefaultRace;
+
+ var info = state.CityInfo;
+ var a = state.Account;
+
+ if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length)
+ {
+ state.Dispose();
+ }
+ else
+ {
+ // Check if anyone is using this account
+ for (var i = 0; i < a.Length; ++i)
+ {
+ var check = a[i];
+
+ if (check != null && check.Map != Map.Internal)
+ {
+ state.WriteConsole("Account in use");
+ state.SendPopupMessage(PMMessage.CharInWorld);
+ return;
+ }
+ }
+
+ state.Flags = (ClientFlags)flags;
+
+ var args = new CharacterCreatedEventArgs(
+ state,
+ a,
+ name,
+ female,
+ hue,
+ str,
+ dex,
+ intl,
+ info[cityIndex],
+ skills,
+ shirtHue,
+ pantsHue,
+ hairVal,
+ hairHue,
+ hairValf,
+ hairHuef,
+ prof,
+ race
+ );
+
+ state.SendClientVersionRequest();
+
+ state.BlockAllPackets = true;
+
+ EventSink.InvokeCharacterCreated(args);
+
+ var m = args.Mobile;
+
+ if (m != null)
+ {
+ state.Mobile = m;
+ m.NetState = state;
+ new LoginTimer(state, m).Start();
+ }
+ else
+ {
+ state.BlockAllPackets = false;
+ state.Dispose();
+ }
+ }
+ }
+
+ public static void DeleteCharacter(NetState state, CircularBufferReader reader)
+ {
+ reader.Seek(30, SeekOrigin.Current);
+ var index = reader.ReadInt32();
+
+ EventSink.InvokeDeleteRequest(state, index);
+ }
+
+ public static void AccountID(NetState state, CircularBufferReader reader)
+ {
+ }
+
+ public static void AssistVersion(NetState state, CircularBufferReader reader)
+ {
+ var unk = reader.ReadInt32();
+ var av = reader.ReadAscii();
+ }
+
+ public static void ClientVersion(NetState state, CircularBufferReader reader)
+ {
+ var version = state.Version = new CV(reader.ReadAscii());
+
+ EventSink.InvokeClientVersionReceived(state, version);
+ }
+
+ public static void ClientType(NetState state, CircularBufferReader reader)
+ {
+ reader.ReadUInt16();
+
+ int type = reader.ReadUInt16();
+ var version = state.Version = new CV(reader.ReadAscii());
+
+ EventSink.InvokeClientVersionReceived(state, version);
+ }
+
+ public static void PlayCharacter(NetState state, CircularBufferReader reader)
+ {
+ reader.ReadInt32(); // 0xEDEDEDED
+
+ var name = reader.ReadAscii(30);
+
+ reader.Seek(2, SeekOrigin.Current);
+
+ var flags = reader.ReadInt32();
+
+ reader.Seek(24, SeekOrigin.Current);
+
+ var charSlot = reader.ReadInt32();
+ var clientIP = reader.ReadInt32();
+
+ var a = state.Account;
+
+ if (a == null || charSlot < 0 || charSlot >= a.Length)
+ {
+ state.Dispose();
+ }
+ else
+ {
+ var m = a[charSlot];
+
+ // Check if anyone is using this account
+ for (var i = 0; i < a.Length; ++i)
+ {
+ var check = a[i];
+
+ if (check != null && check.Map != Map.Internal && check != m)
+ {
+ state.WriteConsole("Account in use");
+ state.SendPopupMessage(PMMessage.CharInWorld);
+ return;
+ }
+ }
+
+ if (m == null)
+ {
+ state.Dispose();
+ return;
+ }
+
+ m.NetState?.Dispose();
+
+ // TODO: Make this wait one tick so we don't have to call it unnecessarily
+ NetState.ProcessDisposedQueue();
+
+ state.SendClientVersionRequest();
+
+ state.BlockAllPackets = true;
+
+ state.Flags = (ClientFlags)flags;
+
+ state.Mobile = m;
+ m.NetState = state;
+
+ new LoginTimer(state, m).Start();
+ }
+ }
+
+ public static void DoLogin(this NetState state, Mobile m)
+ {
+ state.SendLoginConfirmation(m);
+
+ if (m.Map != null)
+ {
+ state.Send(new MapChange(m.Map));
+ }
+
+ if (!Core.SE && state.ProtocolChanges < ProtocolChanges.Version6000)
+ {
+ state.Send(new MapPatches());
+ }
+
+ state.Send(SeasonChange.Instantiate(m.GetSeason(), true));
+
+ state.SendSupportedFeature();
+
+ state.Sequence = 0;
+
+ if (state.NewMobileIncoming)
+ {
+ state.Send(new MobileUpdate(m));
+ state.Send(new MobileUpdate(m));
+
+ m.CheckLightLevels(true);
+
+ state.Send(new MobileUpdate(m));
+
+ state.Send(new MobileIncoming(m, m));
+ // state.Send( new MobileAttributes( m ) );
+ state.Send(new MobileStatus(m, m));
+ state.Send(SetWarMode.Instantiate(m.Warmode));
+
+ m.SendEverything();
+
+ state.SendSupportedFeature();
+ state.Send(new MobileUpdate(m));
+ // state.Send( new MobileAttributes( m ) );
+ state.Send(new MobileStatus(m, m));
+ state.Send(SetWarMode.Instantiate(m.Warmode));
+ state.Send(new MobileIncoming(m, m));
+ }
+ else if (state.StygianAbyss)
+ {
+ state.Send(new MobileUpdate(m));
+ state.Send(new MobileUpdate(m));
+
+ m.CheckLightLevels(true);
+
+ state.Send(new MobileUpdate(m));
+
+ state.Send(new MobileIncomingSA(m, m));
+ // state.Send( new MobileAttributes( m ) );
+ state.Send(new MobileStatus(m, m));
+ state.Send(SetWarMode.Instantiate(m.Warmode));
+
+ m.SendEverything();
+
+ state.SendSupportedFeature();
+ state.Send(new MobileUpdate(m));
+ // state.Send( new MobileAttributes( m ) );
+ state.Send(new MobileStatus(m, m));
+ state.Send(SetWarMode.Instantiate(m.Warmode));
+ state.Send(new MobileIncomingSA(m, m));
+ }
+ else
+ {
+ state.Send(new MobileUpdateOld(m));
+ state.Send(new MobileUpdateOld(m));
+
+ m.CheckLightLevels(true);
+
+ state.Send(new MobileUpdateOld(m));
+
+ state.Send(new MobileIncomingOld(m, m));
+ // state.Send( new MobileAttributes( m ) );
+ state.Send(new MobileStatus(m, m));
+ state.Send(SetWarMode.Instantiate(m.Warmode));
+
+ m.SendEverything();
+
+ state.SendSupportedFeature();
+ state.Send(new MobileUpdateOld(m));
+ // state.Send( new MobileAttributes( m ) );
+ state.Send(new MobileStatus(m, m));
+ state.Send(SetWarMode.Instantiate(m.Warmode));
+ state.Send(new MobileIncomingOld(m, m));
+ }
+
+ state.SendLoginComplete();
+ state.Send(new CurrentTime());
+ state.Send(SeasonChange.Instantiate(m.GetSeason(), true));
+ if (m.Map != null)
+ {
+ state.Send(new MapChange(m.Map));
+ }
+
+ EventSink.InvokeLogin(m);
+
+ m.ClearFastwalkStack();
+ }
+
+ private static int GenerateAuthID(this NetState state)
+ {
+ if (m_AuthIDWindow.Count == m_AuthIDWindowSize)
+ {
+ var oldestID = 0;
+ var oldest = DateTime.MaxValue;
+
+ foreach (var kvp in m_AuthIDWindow)
+ {
+ if (kvp.Value.Age < oldest)
+ {
+ oldestID = kvp.Key;
+ oldest = kvp.Value.Age;
+ }
+ }
+
+ m_AuthIDWindow.Remove(oldestID);
+ }
+
+ int authID;
+
+ do
+ {
+ authID = Utility.Random(1, int.MaxValue - 1);
+
+ if (Utility.RandomBool())
+ {
+ authID |= 1 << 31;
+ }
+ } while (m_AuthIDWindow.ContainsKey(authID));
+
+ m_AuthIDWindow[authID] = new AuthIDPersistence(state.Version);
+
+ return authID;
+ }
+
+ public static void GameLogin(NetState state, CircularBufferReader reader)
+ {
+ if (state.SentFirstPacket)
+ {
+ state.Dispose();
+ return;
+ }
+
+ state.SentFirstPacket = true;
+
+ var authID = reader.ReadInt32();
+
+ if (
+ !m_AuthIDWindow.TryGetValue(authID, out var ap) ||
+ state.m_AuthID != 0 && authID != state.m_AuthID ||
+ state.m_AuthID == 0 && authID != state.m_Seed
+ )
+ {
+ state.WriteConsole("Invalid client detected, disconnecting");
+ state.Dispose();
+ return;
+ }
+
+ m_AuthIDWindow.Remove(authID);
+ state.Version = ap.Version;
+
+ var username = reader.ReadAscii(30);
+ var password = reader.ReadAscii(30);
+
+ var e = new GameLoginEventArgs(state, username, password);
+
+ EventSink.InvokeGameLogin(e);
+
+ if (e.Accepted)
+ {
+ state.CityInfo = e.CityInfo;
+ state.CompressionEnabled = true;
+ state.PacketEncoder = NetworkCompression.Compress;
+
+ state.SendSupportedFeature();
+ state.SendCharacterList();
+ }
+ else
+ {
+ state.Dispose();
+ }
+ }
+
+ public static void PlayServer(NetState state, CircularBufferReader reader)
+ {
+ int index = reader.ReadInt16();
+ var info = state.ServerInfo;
+ var a = state.Account;
+
+ if (info == null || a == null || index < 0 || index >= info.Length)
+ {
+ state.Dispose();
+ }
+ else
+ {
+ var si = info[index];
+
+ state.m_AuthID = GenerateAuthID(state);
+
+ state.SentFirstPacket = false;
+ state.SendPlayServerAck(si, state.m_AuthID);
+ }
+ }
+
+ public static void LoginServerSeed(NetState state, CircularBufferReader reader)
+ {
+ state.m_Seed = reader.ReadInt32();
+ state.Seeded = true;
+
+ if (state.m_Seed == 0)
+ {
+ state.WriteConsole("Invalid client detected, disconnecting");
+ state.Dispose();
+ return;
+ }
+
+ var clientMaj = reader.ReadInt32();
+ var clientMin = reader.ReadInt32();
+ var clientRev = reader.ReadInt32();
+ var clientPat = reader.ReadInt32();
+
+ state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat);
+ }
+
+ public static void AccountLogin(NetState state, CircularBufferReader reader)
+ {
+ if (state.SentFirstPacket)
+ {
+ state.Dispose();
+ return;
+ }
+
+ state.SentFirstPacket = true;
+
+ var username = reader.ReadAscii(30);
+ var password = reader.ReadAscii(30);
+
+ var accountLoginEventArgs = new AccountLoginEventArgs(state, username, password);
+
+ EventSink.InvokeAccountLogin(accountLoginEventArgs);
+
+ if (accountLoginEventArgs.Accepted)
+ {
+ var serverListEventArgs = new ServerListEventArgs(state, state.Account);
+
+ EventSink.InvokeServerList(serverListEventArgs);
+
+ if (serverListEventArgs.Rejected)
+ {
+ state.Account = null;
+ AccountLogin_ReplyRej(state, ALRReason.BadComm);
+ }
+ else
+ {
+ state.ServerInfo = serverListEventArgs.Servers.ToArray();
+ state.SendAccountLoginAck();
+ }
+ }
+ else
+ {
+ AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason);
+ }
+ }
+
+ private static void AccountLogin_ReplyRej(this NetState state, ALRReason reason)
+ {
+ state.SendAccountLoginRejected(reason);
+ state.Dispose();
+ }
+
+ private class LoginTimer : Timer
+ {
+ private readonly Mobile m_Mobile;
+ private readonly NetState m_State;
+
+ public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
+ {
+ m_State = state;
+ m_Mobile = m;
+ }
+
+ protected override void OnTick()
+ {
+ if (m_State == null)
+ {
+ Stop();
+ return;
+ }
+
+ if (m_State.Version != null)
+ {
+ m_State.BlockAllPackets = false;
+ DoLogin(m_State, m_Mobile);
+ Stop();
+ }
+ }
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingEntityPackets.cs b/Projects/Server/Network/Packets/IncomingEntityPackets.cs
new file mode 100644
index 000000000..ab629a83f
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingEntityPackets.cs
@@ -0,0 +1,194 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingEntityPackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+namespace Server.Network
+{
+ public static class IncomingEntityPackets
+ {
+ public static bool SingleClickProps { get; set; }
+
+ public static void Configure()
+ {
+ IncomingPackets.Register(0x06, 5, true, UseReq);
+ IncomingPackets.Register(0x09, 5, true, LookReq);
+ IncomingPackets.Register(0xB6, 9, true, ObjectHelpRequest);
+ IncomingPackets.Register(0xD6, 0, true, BatchQueryProperties);
+ }
+
+ public static void ObjectHelpRequest(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ Serial serial = reader.ReadUInt32();
+ int unk = reader.ReadByte();
+ var lang = reader.ReadAscii(3);
+
+ if (serial.IsItem)
+ {
+ var item = World.FindItem(serial);
+
+ if (item != null && from.Map == item.Map && Utility.InUpdateRange(item.GetWorldLocation(), from.Location) &&
+ from.CanSee(item))
+ {
+ item.OnHelpRequest(from);
+ }
+ }
+ else if (serial.IsMobile)
+ {
+ var m = World.FindMobile(serial);
+
+ if (m != null && from.Map == m.Map && Utility.InUpdateRange(m.Location, from.Location) && from.CanSee(m))
+ {
+ m.OnHelpRequest(m);
+ }
+ }
+ }
+
+ public static void UseReq(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0)
+ {
+ var value = reader.ReadUInt32();
+
+ if ((value & ~0x7FFFFFFF) != 0)
+ {
+ from.OnPaperdollRequest();
+ }
+ else
+ {
+ Serial s = value;
+
+ if (s.IsMobile)
+ {
+ var m = World.FindMobile(s);
+
+ if (m?.Deleted == false)
+ {
+ from.Use(m);
+ }
+ }
+ else if (s.IsItem)
+ {
+ var item = World.FindItem(s);
+
+ if (item?.Deleted == false)
+ {
+ from.Use(item);
+ }
+ }
+ }
+
+ from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
+ }
+ else
+ {
+ from.SendActionMessage();
+ }
+ }
+
+ public static void LookReq(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ Serial s = reader.ReadUInt32();
+
+ if (s.IsMobile)
+ {
+ var m = World.FindMobile(s);
+
+ if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
+ {
+ if (SingleClickProps)
+ {
+ m.OnAosSingleClick(from);
+ }
+ else
+ {
+ if (from.Region.OnSingleClick(from, m))
+ {
+ m.OnSingleClick(from);
+ }
+ }
+ }
+ }
+ else if (s.IsItem)
+ {
+ var item = World.FindItem(s);
+
+ if (item?.Deleted == false && from.CanSee(item) &&
+ Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
+ {
+ if (SingleClickProps)
+ {
+ item.OnAosSingleClick(from);
+ }
+ else if (from.Region.OnSingleClick(from, item))
+ {
+ if (item.Parent is Item parentItem)
+ {
+ parentItem.OnSingleClickContained(from, item);
+ }
+
+ item.OnSingleClick(from);
+ }
+ }
+ }
+ }
+
+ public static void BatchQueryProperties(NetState state, CircularBufferReader reader)
+ {
+ if (!ObjectPropertyList.Enabled)
+ {
+ return;
+ }
+
+ var from = state.Mobile;
+
+ var length = reader.Remaining;
+
+ if (length % 4 != 0)
+ {
+ return;
+ }
+
+ while (reader.Remaining > 0)
+ {
+ Serial s = reader.ReadUInt32();
+
+ if (s.IsMobile)
+ {
+ var m = World.FindMobile(s);
+
+ if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
+ {
+ m.SendPropertiesTo(from);
+ }
+ }
+ else if (s.IsItem)
+ {
+ var item = World.FindItem(s);
+
+ if (item?.Deleted == false && from.CanSee(item) &&
+ Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
+ {
+ item.SendPropertiesTo(from);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs b/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs
new file mode 100644
index 000000000..003a69b3f
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs
@@ -0,0 +1,532 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingExtendedCommandPackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using System.Collections.Generic;
+using Server.ContextMenus;
+
+namespace Server.Network
+{
+ public static class IncomingExtendedCommandPackets
+ {
+ private static readonly PacketHandler[] m_ExtendedHandlersLow = new PacketHandler[0x100];
+ private static readonly Dictionary m_ExtendedHandlersHigh = new Dictionary();
+
+ // TODO: Change to outside configuration
+ public static int[] ValidAnimations { get; set; } =
+ {
+ 6, 21, 32, 33,
+ 100, 101, 102, 103,
+ 104, 105, 106, 107,
+ 108, 109, 110, 111,
+ 112, 113, 114, 115,
+ 116, 117, 118, 119,
+ 120, 121, 123, 124,
+ 125, 126, 127, 128
+ };
+
+ public static void Configure()
+ {
+ IncomingPackets.Register(0xBF, 0, true, ExtendedCommand);
+
+ RegisterExtended(0x05, false, ScreenSize);
+ RegisterExtended(0x06, true, PartyMessage);
+ RegisterExtended(0x07, true, QuestArrow);
+ RegisterExtended(0x09, true, DisarmRequest);
+ RegisterExtended(0x0A, true, StunRequest);
+ RegisterExtended(0x0B, false, Language);
+ RegisterExtended(0x0C, true, CloseStatus);
+ RegisterExtended(0x0E, true, Animate);
+ RegisterExtended(0x0F, false, Empty); // What's this?
+ RegisterExtended(0x10, true, QueryProperties);
+ RegisterExtended(0x13, true, ContextMenuRequest);
+ RegisterExtended(0x15, true, ContextMenuResponse);
+ RegisterExtended(0x1A, true, StatLockChange);
+ RegisterExtended(0x1C, true, CastSpell);
+ RegisterExtended(0x24, false, UnhandledBF);
+ RegisterExtended(0x2C, true, BandageTarget);
+ RegisterExtended(0x2D, true, TargetedSpell);
+ RegisterExtended(0x2E, true, TargetedSkillUse);
+ RegisterExtended(0x30, true, TargetByResourceMacro);
+ RegisterExtended(0x32, true, ToggleFlying);
+ }
+
+ private static void UnhandledBF(NetState state, CircularBufferReader reader)
+ {
+ }
+
+ public static void Empty(NetState state, CircularBufferReader reader)
+ {
+ }
+
+ public static void RegisterExtended(int packetID, bool ingame, OnPacketReceive onReceive)
+ {
+ if (packetID >= 0 && packetID < 0x100)
+ {
+ m_ExtendedHandlersLow[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
+ }
+ else
+ {
+ m_ExtendedHandlersHigh[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
+ }
+ }
+
+ public static PacketHandler GetExtendedHandler(int packetID)
+ {
+ if (packetID >= 0 && packetID < 0x100)
+ {
+ return m_ExtendedHandlersLow[packetID];
+ }
+
+ m_ExtendedHandlersHigh.TryGetValue(packetID, out var handler);
+ return handler;
+ }
+
+ public static void RemoveExtendedHandler(int packetID)
+ {
+ if (packetID >= 0 && packetID < 0x100)
+ {
+ m_ExtendedHandlersLow[packetID] = null;
+ }
+ else
+ {
+ m_ExtendedHandlersHigh.Remove(packetID);
+ }
+ }
+
+ public static void ExtendedCommand(NetState state, CircularBufferReader reader)
+ {
+ int packetID = reader.ReadUInt16();
+
+ var ph = GetExtendedHandler(packetID);
+
+ if (ph == null)
+ {
+ reader.Trace(state);
+ return;
+ }
+
+ if (ph.Ingame && state.Mobile?.Deleted != false)
+ {
+ if (state.Mobile == null)
+ {
+ state.WriteConsole(
+ "Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile",
+ packetID
+ );
+ }
+
+ state.Dispose();
+ }
+ else
+ {
+ ph.OnReceive(state, reader);
+ }
+ }
+
+ public static void ScreenSize(NetState state, CircularBufferReader reader)
+ {
+ var width = reader.ReadInt32();
+ var unk = reader.ReadInt32();
+ }
+
+ // TODO: Move out of the core
+ public static void PartyMessage(NetState state, CircularBufferReader reader)
+ {
+ if (state.Mobile == null)
+ {
+ return;
+ }
+
+ switch (reader.ReadByte())
+ {
+ case 0x01:
+ PartyMessage_AddMember(state, reader);
+ break;
+ case 0x02:
+ PartyMessage_RemoveMember(state, reader);
+ break;
+ case 0x03:
+ PartyMessage_PrivateMessage(state, reader);
+ break;
+ case 0x04:
+ PartyMessage_PublicMessage(state, reader);
+ break;
+ case 0x06:
+ PartyMessage_SetCanLoot(state, reader);
+ break;
+ case 0x08:
+ PartyMessage_Accept(state, reader);
+ break;
+ case 0x09:
+ PartyMessage_Decline(state, reader);
+ break;
+ default:
+ reader.Trace(state);
+ break;
+ }
+ }
+
+ public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader)
+ {
+ PartyCommands.Handler?.OnAdd(state.Mobile);
+ }
+
+ public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader)
+ {
+ PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile(reader.ReadUInt32()));
+ }
+
+ public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader)
+ {
+ PartyCommands.Handler?.OnPrivateMessage(
+ state.Mobile,
+ World.FindMobile(reader.ReadUInt32()),
+ reader.ReadBigUniSafe()
+ );
+ }
+
+ public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader)
+ {
+ PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe());
+ }
+
+ public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader)
+ {
+ PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean());
+ }
+
+ public static void PartyMessage_Accept(NetState state, CircularBufferReader reader)
+ {
+ PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile(reader.ReadUInt32()));
+ }
+
+ public static void PartyMessage_Decline(NetState state, CircularBufferReader reader)
+ {
+ PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile(reader.ReadUInt32()));
+ }
+
+ public static void QuestArrow(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var rightClick = reader.ReadBoolean();
+
+ from.QuestArrow?.OnClick(rightClick);
+ }
+
+ public static void Animate(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var action = reader.ReadInt32();
+
+ var ok = false;
+
+ for (var i = 0; !ok && i < ValidAnimations.Length; ++i)
+ {
+ ok = action == ValidAnimations[i];
+ }
+
+ if (ok && from.Alive && from.Body.IsHuman && !from.Mounted)
+ {
+ from.Animate(action, 7, 1, true, false, 0);
+ }
+ }
+
+ public static void CastSpell(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ Item spellbook = reader.ReadInt16() == 1 ? World.FindItem(reader.ReadUInt32()) : null;
+
+ var spellID = reader.ReadInt16() - 1;
+ EventSink.InvokeCastSpellRequest(from, spellID, spellbook);
+ }
+
+ public static void ToggleFlying(NetState state, CircularBufferReader reader)
+ {
+ state.Mobile?.ToggleFlying();
+ }
+
+ public static void StunRequest(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ EventSink.InvokeStunRequest(from);
+ }
+
+ public static void DisarmRequest(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ EventSink.InvokeDisarmRequest(from);
+ }
+
+ public static void StatLockChange(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ int stat = reader.ReadByte();
+ int lockValue = reader.ReadByte();
+
+ if (lockValue > 2)
+ {
+ lockValue = 0;
+ }
+
+ switch (stat)
+ {
+ case 0:
+ from.StrLock = (StatLockType)lockValue;
+ break;
+ case 1:
+ from.DexLock = (StatLockType)lockValue;
+ break;
+ case 2:
+ from.IntLock = (StatLockType)lockValue;
+ break;
+ }
+ }
+
+ public static void CloseStatus(NetState state, CircularBufferReader reader)
+ {
+ Serial serial = reader.ReadUInt32();
+ }
+
+ public static void Language(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ from.Language = reader.ReadAscii(4);
+ }
+
+ public static void QueryProperties(NetState state, CircularBufferReader reader)
+ {
+ if (!ObjectPropertyList.Enabled)
+ {
+ return;
+ }
+
+ var from = state.Mobile;
+
+ Serial s = reader.ReadUInt32();
+
+ if (s.IsMobile)
+ {
+ var m = World.FindMobile(s);
+
+ if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
+ {
+ m.SendPropertiesTo(from);
+ }
+ }
+ else if (s.IsItem)
+ {
+ var item = World.FindItem(s);
+
+ if (item?.Deleted == false && from.CanSee(item) &&
+ Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
+ {
+ item.SendPropertiesTo(from);
+ }
+ }
+ }
+
+ public static void ContextMenuResponse(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var menu = from.ContextMenu;
+
+ from.ContextMenu = null;
+
+ if (menu != null && from == menu.From)
+ {
+ var entity = World.FindEntity(reader.ReadUInt32());
+
+ if (entity != null && entity == menu.Target && from.CanSee(entity))
+ {
+ Point3D p;
+
+ if (entity is Mobile)
+ {
+ p = entity.Location;
+ }
+ else if (entity is Item item)
+ {
+ p = item.GetWorldLocation();
+ }
+ else
+ {
+ return;
+ }
+
+ int index = reader.ReadUInt16();
+
+ if (index >= 0 && index < menu.Entries.Length)
+ {
+ var e = menu.Entries[index];
+
+ var range = e.Range;
+
+ if (range == -1)
+ {
+ range = 18;
+ }
+
+ if (e.Enabled && from.InRange(p, range))
+ {
+ e.OnClick();
+ }
+ }
+ }
+ }
+ }
+
+ public static void ContextMenuRequest(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+ var target = World.FindEntity(reader.ReadUInt32());
+
+ if (from != null && target != null && from.Map == target.Map && from.CanSee(target))
+ {
+ var item = target as Item;
+
+ var checkLocation = item?.GetWorldLocation() ?? target.Location;
+ if (!(Utility.InUpdateRange(from.Location, checkLocation) && from.CheckContextMenuDisplay(target)))
+ {
+ return;
+ }
+
+ var c = new ContextMenu(from, target);
+
+ if (c.Entries.Length > 0)
+ {
+ if (item?.RootParent is Mobile mobile && mobile != from && mobile.AccessLevel >= from.AccessLevel)
+ {
+ for (var i = 0; i < c.Entries.Length; ++i)
+ {
+ if (!c.Entries[i].NonLocalUse)
+ {
+ c.Entries[i].Enabled = false;
+ }
+ }
+ }
+
+ from.ContextMenu = c;
+ }
+ }
+ }
+
+ public static void BandageTarget(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0)
+ {
+ var bandage = World.FindItem(reader.ReadUInt32());
+
+ if (bandage == null)
+ {
+ return;
+ }
+
+ var target = World.FindMobile(reader.ReadUInt32());
+
+ if (target == null)
+ {
+ return;
+ }
+
+ EventSink.InvokeBandageTargetRequest(from, bandage, target);
+
+ from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
+ }
+ else
+ {
+ from.SendActionMessage();
+ }
+ }
+
+ public static void TargetedSpell(NetState state, CircularBufferReader reader)
+ {
+ var spellId = (short)(reader.ReadInt16() - 1); // zero based;
+
+ EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity(reader.ReadUInt32()), spellId);
+ }
+
+ public static void TargetedSkillUse(NetState state, CircularBufferReader reader)
+ {
+ var skillId = reader.ReadInt16();
+
+ EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity(reader.ReadUInt32()), skillId);
+ }
+
+ public static void TargetByResourceMacro(NetState state, CircularBufferReader reader)
+ {
+ Serial serial = reader.ReadUInt32();
+
+ if (serial.IsItem)
+ {
+ EventSink.InvokeTargetByResourceMacro(state.Mobile, World.FindItem(serial), reader.ReadInt16());
+ }
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingHousePackets.cs b/Projects/Server/Network/Packets/IncomingHousePackets.cs
new file mode 100644
index 000000000..02e752ff6
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingHousePackets.cs
@@ -0,0 +1,30 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingHousePackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+namespace Server.Network
+{
+ public static class IncomingHousePackets
+ {
+ public static void Configure()
+ {
+ IncomingPackets.Register(0xFB, 2, false, ShowPublicHouseContent);
+ }
+
+ public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader)
+ {
+ var showPublicHouseContent = reader.ReadBoolean();
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingItemPackets.cs b/Projects/Server/Network/Packets/IncomingItemPackets.cs
new file mode 100644
index 000000000..ac7d0eb21
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingItemPackets.cs
@@ -0,0 +1,167 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingItemPackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using System.Collections.Generic;
+using System.IO;
+using Server.Items;
+
+namespace Server.Network
+{
+ public static class IncomingItemPackets
+ {
+ public static void Configure()
+ {
+ IncomingPackets.Register(0x07, 7, true, LiftReq);
+ IncomingPackets.Register(0x08, 14, true, DropReq);
+ IncomingPackets.Register(0x13, 10, true, EquipReq);
+ IncomingPackets.Register(0xEC, 0, false, EquipMacro);
+ IncomingPackets.Register(0xED, 0, false, UnequipMacro);
+ IncomingPackets.Register6017(0x08, 15, true, DropReq6017);
+ }
+
+ public static void LiftReq(NetState state, CircularBufferReader reader)
+ {
+ Serial serial = reader.ReadUInt32();
+ int amount = reader.ReadUInt16();
+ var item = World.FindItem(serial);
+
+ state.Mobile.Lift(item, amount, out _, out _);
+ }
+
+ public static void EquipReq(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+ var item = from.Holding;
+
+ var valid = item != null && item.HeldBy == from && item.Map == Map.Internal;
+
+ from.Holding = null;
+
+ if (!valid)
+ {
+ return;
+ }
+
+ reader.Seek(5, SeekOrigin.Current);
+ var to = World.FindMobile(reader.ReadUInt32()) ?? from;
+
+ if (!to.AllowEquipFrom(from) || !to.EquipItem(item))
+ {
+ item.Bounce(from);
+ }
+
+ item.ClearBounce();
+ }
+
+ public static void DropReq(NetState state, CircularBufferReader reader)
+ {
+ reader.ReadInt32(); // serial, ignored
+ int x = reader.ReadInt16();
+ int y = reader.ReadInt16();
+ int z = reader.ReadSByte();
+ Serial dest = reader.ReadUInt32();
+
+ var loc = new Point3D(x, y, z);
+
+ var from = state.Mobile;
+
+ if (dest.IsMobile)
+ {
+ from.Drop(World.FindMobile(dest), loc);
+ }
+ else if (dest.IsItem)
+ {
+ var item = World.FindItem(dest);
+
+ if (item is BaseMulti multi && multi.AllowsRelativeDrop)
+ {
+ loc.m_X += multi.X;
+ loc.m_Y += multi.Y;
+ from.Drop(loc);
+ }
+ else
+ {
+ from.Drop(item, loc);
+ }
+ }
+ else
+ {
+ from.Drop(loc);
+ }
+ }
+
+ public static void DropReq6017(NetState state, CircularBufferReader reader)
+ {
+ reader.ReadInt32(); // serial, ignored
+ int x = reader.ReadInt16();
+ int y = reader.ReadInt16();
+ int z = reader.ReadSByte();
+ reader.ReadByte(); // Grid Location?
+ Serial dest = reader.ReadUInt32();
+
+ var loc = new Point3D(x, y, z);
+
+ var from = state.Mobile;
+
+ if (dest.IsMobile)
+ {
+ from.Drop(World.FindMobile(dest), loc);
+ }
+ else if (dest.IsItem)
+ {
+ var item = World.FindItem(dest);
+
+ if (item is BaseMulti multi && multi.AllowsRelativeDrop)
+ {
+ loc.m_X += multi.X;
+ loc.m_Y += multi.Y;
+ from.Drop(loc);
+ }
+ else
+ {
+ from.Drop(item, loc);
+ }
+ }
+ else
+ {
+ from.Drop(loc);
+ }
+ }
+
+ public static void EquipMacro(NetState state, CircularBufferReader reader)
+ {
+ int count = reader.ReadByte();
+ var serialList = new List(count);
+ for (var i = 0; i < count; ++i)
+ {
+ serialList.Add(reader.ReadUInt32());
+ }
+
+ EventSink.InvokeEquipMacro(state.Mobile, serialList);
+ }
+
+ public static void UnequipMacro(NetState state, CircularBufferReader reader)
+ {
+ int count = reader.ReadByte();
+ var layers = new List(count);
+ for (var i = 0; i < count; ++i)
+ {
+ layers.Add((Layer)reader.ReadUInt16());
+ }
+
+ EventSink.InvokeUnequipMacro(state.Mobile, layers);
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingMessagePackets.cs b/Projects/Server/Network/Packets/IncomingMessagePackets.cs
new file mode 100644
index 000000000..2421a17a2
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingMessagePackets.cs
@@ -0,0 +1,160 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: Packets.Messages.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using System;
+
+namespace Server.Network
+{
+ [Flags]
+ public enum MessageType
+ {
+ Regular = 0x00,
+ System = 0x01,
+ Emote = 0x02,
+ Label = 0x06,
+ Focus = 0x07,
+ Whisper = 0x08,
+ Yell = 0x09,
+ Spell = 0x0A,
+
+ Guild = 0x0D,
+ Alliance = 0x0E,
+ Command = 0x0F,
+
+ Encoded = 0xC0
+ }
+
+ public static class IncomingMessagePackets
+ {
+ private static readonly KeywordList m_KeywordList = new KeywordList();
+
+ public static void Configure()
+ {
+ IncomingPackets.Register(0x03, 0, true, AsciiSpeech);
+ IncomingPackets.Register(0xAD, 0, true, UnicodeSpeech);
+ }
+
+ public static void AsciiSpeech(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var type = (MessageType)reader.ReadByte();
+ int hue = reader.ReadInt16();
+ reader.ReadInt16(); // font
+ var text = reader.ReadAsciiSafe().Trim();
+
+ if (text.Length <= 0 || text.Length > 128)
+ {
+ return;
+ }
+
+ if (!Enum.IsDefined(typeof(MessageType), type))
+ {
+ type = MessageType.Regular;
+ }
+
+ from.DoSpeech(text, Array.Empty(), type, Utility.ClipDyedHue(hue));
+ }
+
+ public static void UnicodeSpeech(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var type = (MessageType)reader.ReadByte();
+ int hue = reader.ReadInt16();
+ reader.ReadInt16(); // font
+ var lang = reader.ReadAscii(4);
+ string text;
+
+ var isEncoded = (type & MessageType.Encoded) != 0;
+ int[] keywords;
+
+ if (isEncoded)
+ {
+ int value = reader.ReadInt16();
+ var count = (value & 0xFFF0) >> 4;
+ var hold = value & 0xF;
+
+ if (count < 0 || count > 50)
+ {
+ return;
+ }
+
+ var keyList = m_KeywordList;
+
+ for (var i = 0; i < count; ++i)
+ {
+ int speechID;
+
+ if ((i & 1) == 0)
+ {
+ hold <<= 8;
+ hold |= reader.ReadByte();
+ speechID = hold;
+ hold = 0;
+ }
+ else
+ {
+ value = reader.ReadInt16();
+ speechID = (value & 0xFFF0) >> 4;
+ hold = value & 0xF;
+ }
+
+ if (!keyList.Contains(speechID))
+ {
+ keyList.Add(speechID);
+ }
+ }
+
+ text = reader.ReadUTF8Safe();
+
+ keywords = keyList.ToArray();
+ }
+ else
+ {
+ text = reader.ReadBigUniSafe();
+
+ keywords = Array.Empty();
+ }
+
+ text = text.Trim();
+
+ if (text.Length <= 0 || text.Length > 128)
+ {
+ return;
+ }
+
+ type &= ~MessageType.Encoded;
+
+ if (!Enum.IsDefined(typeof(MessageType), type))
+ {
+ type = MessageType.Regular;
+ }
+
+ from.Language = lang;
+ from.DoSpeech(text, keywords, type, Utility.ClipDyedHue(hue));
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingMobilePackets.cs b/Projects/Server/Network/Packets/IncomingMobilePackets.cs
new file mode 100644
index 000000000..e1ae881cd
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingMobilePackets.cs
@@ -0,0 +1,166 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingMobilePackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using Server.Items;
+
+namespace Server.Network
+{
+ public static class IncomingMobilePackets
+ {
+ public static void Configure()
+ {
+ IncomingPackets.Register(0x75, 35, true, RenameRequest);
+ IncomingPackets.Register(0x98, 0, true, MobileNameRequest);
+ IncomingPackets.Register(0xB8, 0, true, ProfileReq);
+ IncomingPackets.Register(0x6F, 0, true, SecureTrade);
+ }
+
+ public static void RenameRequest(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+ var targ = World.FindMobile(reader.ReadUInt32());
+
+ if (targ != null)
+ {
+ EventSink.InvokeRenameRequest(from, targ, reader.ReadAsciiSafe());
+ }
+ }
+
+ public static void MobileNameRequest(NetState state, CircularBufferReader reader)
+ {
+ var m = World.FindMobile(reader.ReadUInt32());
+
+ if (m != null && Utility.InUpdateRange(state.Mobile, m) && state.Mobile.CanSee(m))
+ {
+ state.Send(new MobileName(m));
+ }
+ }
+
+ public static void ProfileReq(NetState state, CircularBufferReader reader)
+ {
+ int type = reader.ReadByte();
+ Serial serial = reader.ReadUInt32();
+
+ var beholder = state.Mobile;
+ var beheld = World.FindMobile(serial);
+
+ if (beheld == null)
+ {
+ return;
+ }
+
+ switch (type)
+ {
+ case 0x00: // display request
+ {
+ EventSink.InvokeProfileRequest(beholder, beheld);
+
+ break;
+ }
+ case 0x01: // edit request
+ {
+ reader.ReadInt16(); // Skip
+ int length = reader.ReadUInt16();
+
+ if (length > 511)
+ {
+ return;
+ }
+
+ var text = reader.ReadBigUni(length);
+
+ EventSink.InvokeChangeProfileRequest(beholder, beheld, text);
+
+ break;
+ }
+ }
+ }
+
+ public static void SecureTrade(NetState state, CircularBufferReader reader)
+ {
+ switch (reader.ReadByte())
+ {
+ case 1: // Cancel
+ {
+ Serial serial = reader.ReadUInt32();
+
+ if (World.FindItem(serial) is SecureTradeContainer cont && cont.Trade != null &&
+ (cont.Trade.From.Mobile == state.Mobile || cont.Trade.To.Mobile == state.Mobile))
+ {
+ cont.Trade.Cancel();
+ }
+
+ break;
+ }
+ case 2: // Check
+ {
+ Serial serial = reader.ReadUInt32();
+
+ if (World.FindItem(serial) is SecureTradeContainer cont)
+ {
+ var trade = cont.Trade;
+
+ var value = reader.ReadInt32() != 0;
+
+ if (trade != null)
+ {
+ if (trade.From.Mobile == state.Mobile)
+ {
+ trade.From.Accepted = value;
+ trade.Update();
+ }
+ else if (trade.To.Mobile == state.Mobile)
+ {
+ trade.To.Accepted = value;
+ trade.Update();
+ }
+ }
+ }
+
+ break;
+ }
+ case 3: // Update Gold
+ {
+ Serial serial = reader.ReadUInt32();
+
+ if (World.FindItem(serial) is SecureTradeContainer cont)
+ {
+ var gold = reader.ReadInt32();
+ var plat = reader.ReadInt32();
+
+ var trade = cont.Trade;
+
+ if (trade != null)
+ {
+ if (trade.From.Mobile == state.Mobile)
+ {
+ trade.From.Gold = gold;
+ trade.From.Plat = plat;
+ trade.UpdateFromCurrency();
+ }
+ else if (trade.To.Mobile == state.Mobile)
+ {
+ trade.To.Gold = gold;
+ trade.To.Plat = plat;
+ trade.UpdateToCurrency();
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingPackets.cs b/Projects/Server/Network/Packets/IncomingPackets.cs
new file mode 100644
index 000000000..e25906dd8
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingPackets.cs
@@ -0,0 +1,178 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: Packets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using System;
+using System.Collections.Generic;
+
+namespace Server.Network
+{
+ public static class IncomingPackets
+ {
+ private static readonly PacketHandler[] m_6017Handlers = new PacketHandler[0x100];
+
+ private static readonly EncodedPacketHandler[] m_EncodedHandlersLow = new EncodedPacketHandler[0x100];
+
+ private static readonly Dictionary m_EncodedHandlersHigh =
+ new Dictionary();
+
+ public static PacketHandler[] Handlers { get; } = new PacketHandler[0x100];
+
+ public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive)
+ {
+ Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
+ m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive);
+ }
+
+ public static PacketHandler GetHandler(int packetID) => Handlers[packetID];
+
+ public static void Register6017(int packetID, int length, bool ingame, OnPacketReceive onReceive)
+ {
+ m_6017Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
+ }
+
+ public static PacketHandler Get6017Handler(int packetID) => m_6017Handlers[packetID];
+
+ public static void RegisterEncoded(int packetID, bool ingame, OnEncodedPacketReceive onReceive)
+ {
+ if (packetID >= 0 && packetID < 0x100)
+ {
+ m_EncodedHandlersLow[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
+ }
+ else
+ {
+ m_EncodedHandlersHigh[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
+ }
+ }
+
+ public static EncodedPacketHandler GetEncodedHandler(int packetID)
+ {
+ if (packetID >= 0 && packetID < 0x100)
+ {
+ return m_EncodedHandlersLow[packetID];
+ }
+
+ m_EncodedHandlersHigh.TryGetValue(packetID, out var handler);
+ return handler;
+ }
+
+ public static void RemoveEncodedHandler(int packetID)
+ {
+ if (packetID >= 0 && packetID < 0x100)
+ {
+ m_EncodedHandlersLow[packetID] = null;
+ }
+ else
+ {
+ m_EncodedHandlersHigh.Remove(packetID);
+ }
+ }
+
+ public static void RegisterThrottler(int packetID, ThrottlePacketCallback t)
+ {
+ var ph = GetHandler(packetID);
+
+ if (ph != null)
+ {
+ ph.ThrottleCallback = t;
+ }
+
+ ph = Get6017Handler(packetID);
+
+ if (ph != null)
+ {
+ ph.ThrottleCallback = t;
+ }
+ }
+
+ public static int ProcessPacket(this NetState ns, ArraySegment[] buffer)
+ {
+ var reader = new CircularBufferReader(buffer);
+
+ var packetId = reader.ReadByte();
+
+ 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
+ {
+ var seed = (packetId << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte();
+
+ if (seed == 0)
+ {
+ ns.WriteConsole("Invalid client detected, disconnecting");
+ return -1;
+ }
+
+ ns.m_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;
+ }
+
+ 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;
+ }
+
+ handler.OnReceive(ns, reader);
+
+ return packetLength;
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs
new file mode 100644
index 000000000..8fef5bcb7
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs
@@ -0,0 +1,658 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingPlayerPackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using Server.Diagnostics;
+using Server.Gumps;
+
+namespace Server.Network
+{
+ public static class IncomingPlayerPackets
+ {
+ public static void Configure()
+ {
+ IncomingPackets.Register(0x01, 5, false, Disconnect);
+ IncomingPackets.Register(0x02, 7, true, MovementReq);
+ IncomingPackets.Register(0x05, 5, true, AttackReq);
+ IncomingPackets.Register(0x12, 0, true, TextCommand);
+ IncomingPackets.Register(0x22, 3, true, Resynchronize);
+ IncomingPackets.Register(0x2C, 2, true, DeathStatusResponse);
+ IncomingPackets.Register(0x34, 10, true, MobileQuery);
+ IncomingPackets.Register(0x3A, 0, true, ChangeSkillLock);
+ IncomingPackets.Register(0x72, 5, true, SetWarMode);
+ IncomingPackets.Register(0x73, 2, false, PingReq);
+ IncomingPackets.Register(0x7D, 13, true, MenuResponse);
+ IncomingPackets.Register(0x95, 9, true, HuePickerResponse);
+ IncomingPackets.Register(0x9A, 0, true, AsciiPromptResponse);
+ IncomingPackets.Register(0x9B, 258, true, HelpRequest);
+ IncomingPackets.Register(0xA4, 149, false, SystemInfo);
+ IncomingPackets.Register(0xA7, 4, true, RequestScrollWindow);
+ IncomingPackets.Register(0xB1, 0, true, DisplayGumpResponse);
+ IncomingPackets.Register(0xC2, 0, true, UnicodePromptResponse);
+ IncomingPackets.Register(0xC8, 2, true, SetUpdateRange);
+ IncomingPackets.Register(0xD0, 0, true, ConfigurationFile);
+ IncomingPackets.Register(0xD1, 2, true, LogoutReq);
+ IncomingPackets.Register(0xD7, 0, true, EncodedCommand);
+ IncomingPackets.Register(0xF4, 0, false, CrashReport);
+
+ IncomingPackets.RegisterEncoded(0x19, true, SetAbility);
+ IncomingPackets.RegisterEncoded(0x28, true, GuildGumpRequest);
+ IncomingPackets.RegisterEncoded(0x32, true, QuestGumpRequest);
+ }
+
+ public static void DeathStatusResponse(NetState state, CircularBufferReader reader)
+ {
+ // Ignored
+ }
+
+ public static void RequestScrollWindow(NetState state, CircularBufferReader reader)
+ {
+ int lastTip = reader.ReadInt16();
+ int type = reader.ReadByte();
+ }
+
+ public static void AttackReq(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var m = World.FindMobile(reader.ReadUInt32());
+
+ if (m != null)
+ {
+ from.Attack(m);
+ }
+ }
+
+ public static void HuePickerResponse(NetState state, CircularBufferReader reader)
+ {
+ var serial = reader.ReadUInt32();
+ _ = reader.ReadInt16(); // Item ID
+ var hue = Utility.ClipDyedHue(reader.ReadInt16() & 0x3FFF);
+
+ foreach (var huePicker in state.HuePickers)
+ {
+ if (huePicker.Serial == serial)
+ {
+ state.RemoveHuePicker(huePicker);
+ huePicker.OnResponse(hue);
+ break;
+ }
+ }
+ }
+
+ public static void SystemInfo(NetState state, CircularBufferReader reader)
+ {
+ int v1 = reader.ReadByte();
+ int v2 = reader.ReadUInt16();
+ int v3 = reader.ReadByte();
+ var s1 = reader.ReadAscii(32);
+ var s2 = reader.ReadAscii(32);
+ var s3 = reader.ReadAscii(32);
+ var s4 = reader.ReadAscii(32);
+ int v4 = reader.ReadUInt16();
+ int v5 = reader.ReadUInt16();
+ var v6 = reader.ReadInt32();
+ var v7 = reader.ReadInt32();
+ var v8 = reader.ReadInt32();
+ }
+
+ public static void TextCommand(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ int type = reader.ReadByte();
+ var command = reader.ReadAscii();
+
+ switch (type)
+ {
+ case 0xC7: // Animate
+ {
+ EventSink.InvokeAnimateRequest(from, command);
+
+ break;
+ }
+ case 0x24: // Use skill
+ {
+ if (!int.TryParse(command.Split(' ')[0], out var skillIndex))
+ {
+ break;
+ }
+
+ Skills.UseSkill(from, skillIndex);
+
+ break;
+ }
+ case 0x43: // Open spellbook
+ {
+ if (!int.TryParse(command, out var booktype))
+ {
+ booktype = 1;
+ }
+
+ EventSink.InvokeOpenSpellbookRequest(from, booktype);
+
+ break;
+ }
+ case 0x27: // Cast spell from book
+ {
+ var split = command.Split(' ');
+
+ if (split.Length > 0)
+ {
+ var spellID = Utility.ToInt32(split[0]) - 1;
+ var serial = split.Length > 1 ? Utility.ToUInt32(split[1]) : (uint)Serial.MinusOne;
+
+ EventSink.InvokeCastSpellRequest(from, spellID, World.FindItem(serial));
+ }
+
+ break;
+ }
+ case 0x58: // Open door
+ {
+ EventSink.InvokeOpenDoorMacroUsed(from);
+
+ break;
+ }
+ case 0x56: // Cast spell from macro
+ {
+ var spellID = Utility.ToInt32(command) - 1;
+
+ EventSink.InvokeCastSpellRequest(from, spellID, null);
+
+ break;
+ }
+ case 0xF4: // Invoke virtues from macro
+ {
+ var virtueID = Utility.ToInt32(command) - 1;
+
+ EventSink.InvokeVirtueMacroRequest(from, virtueID);
+
+ break;
+ }
+ case 0x2F: // Old scroll double click
+ {
+ /*
+ * This command is still sent for items 0xEF3 - 0xEF9
+ *
+ * Command is one of three, depending on the item ID of the scroll:
+ * - [scroll serial]
+ * - [scroll serial] [target serial]
+ * - [scroll serial] [x] [y] [z]
+ */
+ break;
+ }
+ default:
+ {
+ state.WriteConsole("Unknown text-command type 0x{0:X2}: {1}", state, type, command);
+ break;
+ }
+ }
+ }
+
+ public static void AsciiPromptResponse(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var serial = reader.ReadUInt32();
+ var prompt = reader.ReadInt32();
+ var type = reader.ReadInt32();
+ var text = reader.ReadAsciiSafe();
+
+ if (text.Length > 128)
+ {
+ return;
+ }
+
+ var p = from.Prompt;
+
+ if (p != null && p.Serial == serial && p.Serial == prompt)
+ {
+ from.Prompt = null;
+
+ if (type == 0)
+ {
+ p.OnCancel(from);
+ }
+ else
+ {
+ p.OnResponse(from, text);
+ }
+ }
+ }
+
+ public static void UnicodePromptResponse(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var serial = reader.ReadUInt32();
+ var prompt = reader.ReadInt32();
+ var type = reader.ReadInt32();
+ var lang = reader.ReadAscii(4);
+ var text = reader.ReadLittleUniSafe();
+
+ if (text.Length > 128)
+ {
+ return;
+ }
+
+ var p = from.Prompt;
+
+ if (p != null && p.Serial == serial && p.Serial == prompt)
+ {
+ from.Prompt = null;
+
+ if (type == 0)
+ {
+ p.OnCancel(from);
+ }
+ else
+ {
+ p.OnResponse(from, text);
+ }
+ }
+ }
+
+ public static void MenuResponse(NetState state, CircularBufferReader reader)
+ {
+ var serial = reader.ReadUInt32();
+ int menuID = reader.ReadInt16(); // unused in our implementation
+ int index = reader.ReadInt16();
+ int itemID = reader.ReadInt16();
+ int hue = reader.ReadInt16();
+
+ index -= 1; // convert from 1-based to 0-based
+
+ foreach (var menu in state.Menus)
+ {
+ if (menu.Serial == serial)
+ {
+ state.RemoveMenu(menu);
+
+ if (index >= 0 && index < menu.EntryLength)
+ {
+ menu.OnResponse(state, index);
+ }
+ else
+ {
+ menu.OnCancel(state);
+ }
+
+ break;
+ }
+ }
+ }
+
+ public static void Disconnect(NetState state, CircularBufferReader reader)
+ {
+ var minusOne = reader.ReadInt32();
+ }
+
+ public static void ConfigurationFile(NetState state, CircularBufferReader reader)
+ {
+ }
+
+ public static void LogoutReq(NetState state, CircularBufferReader reader)
+ {
+ state.Send(new LogoutAck());
+ }
+
+ public static void ChangeSkillLock(NetState state, CircularBufferReader reader)
+ {
+ var s = state.Mobile.Skills[reader.ReadInt16()];
+
+ s?.SetLockNoRelay((SkillLock)reader.ReadByte());
+ }
+
+ public static void HelpRequest(NetState state, CircularBufferReader reader)
+ {
+ EventSink.InvokeHelpRequest(state.Mobile);
+ }
+
+ public static void DisplayGumpResponse(NetState state, CircularBufferReader reader)
+ {
+ var serial = reader.ReadUInt32();
+ var typeID = reader.ReadInt32();
+ var buttonID = reader.ReadInt32();
+
+ foreach (var gump in state.Gumps)
+ {
+ if (gump.Serial != serial || gump.TypeID != typeID)
+ {
+ continue;
+ }
+
+ var buttonExists = buttonID == 0; // 0 is always 'close'
+
+ if (!buttonExists)
+ {
+ foreach (var e in gump.Entries)
+ {
+ if (e is GumpButton button && button.ButtonID == buttonID)
+ {
+ buttonExists = true;
+ break;
+ }
+
+ if (e is GumpImageTileButton tileButton && tileButton.ButtonID == buttonID)
+ {
+ buttonExists = true;
+ break;
+ }
+ }
+ }
+
+ if (!buttonExists)
+ {
+ state.WriteConsole("Invalid gump response, disconnecting...");
+ state.Dispose();
+ return;
+ }
+
+ var switchCount = reader.ReadInt32();
+
+ if (switchCount < 0 || switchCount > gump.m_Switches)
+ {
+ state.WriteConsole("Invalid gump response, disconnecting...");
+ state.Dispose();
+ return;
+ }
+
+ var switches = new int[switchCount];
+
+ for (var j = 0; j < switches.Length; ++j)
+ {
+ switches[j] = reader.ReadInt32();
+ }
+
+ var textCount = reader.ReadInt32();
+
+ if (textCount < 0 || textCount > gump.m_TextEntries)
+ {
+ state.WriteConsole("Invalid gump response, disconnecting...");
+ state.Dispose();
+ return;
+ }
+
+ var textEntries = new TextRelay[textCount];
+
+ for (var j = 0; j < textEntries.Length; ++j)
+ {
+ int entryID = reader.ReadUInt16();
+ int textLength = reader.ReadUInt16();
+
+ if (textLength > 239)
+ {
+ state.WriteConsole("Invalid gump response, disconnecting...");
+ state.Dispose();
+ return;
+ }
+
+ var text = reader.ReadBigUniSafe(textLength);
+ textEntries[j] = new TextRelay(entryID, text);
+ }
+
+ state.RemoveGump(gump);
+
+ var prof = GumpProfile.Acquire(gump.GetType());
+
+ prof?.Start();
+
+ gump.OnResponse(state, new RelayInfo(buttonID, switches, textEntries));
+
+ prof?.Finish();
+
+ return;
+ }
+
+ if (typeID == 461)
+ {
+ // Virtue gump
+ var switchCount = reader.ReadInt32();
+
+ if (buttonID == 1 && switchCount > 0)
+ {
+ var beheld = World.FindMobile(reader.ReadUInt32());
+
+ if (beheld != null)
+ {
+ EventSink.InvokeVirtueGumpRequest(state.Mobile, beheld);
+ }
+ }
+ else
+ {
+ var beheld = World.FindMobile(serial);
+
+ if (beheld != null)
+ {
+ EventSink.InvokeVirtueItemRequest(state.Mobile, beheld, buttonID);
+ }
+ }
+ }
+ }
+
+ public static void SetWarMode(NetState state, CircularBufferReader reader)
+ {
+ state.Mobile?.DelayChangeWarmode(reader.ReadBoolean());
+ }
+
+ // TODO: Throttle/make this more safe
+ public static void Resynchronize(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ if (state.StygianAbyss)
+ {
+ state.Send(new MobileUpdate(from));
+ }
+ else
+ {
+ state.Send(new MobileUpdateOld(from));
+ }
+
+ state.Send(MobileIncoming.Create(state, from, from));
+
+ from.SendEverything();
+
+ state.Sequence = 0;
+
+ from.ClearFastwalkStack();
+ }
+
+ public static void PingReq(NetState state, CircularBufferReader reader)
+ {
+ state.Send(PingAck.Instantiate(reader.ReadByte()));
+ }
+
+ public static void SetUpdateRange(NetState state, CircularBufferReader reader)
+ {
+ state.Send(ChangeUpdateRange.Instantiate(18));
+ }
+
+ // TODO: Change to OSI fastwalk stack
+ public static void MovementReq(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+
+ if (from == null)
+ {
+ return;
+ }
+
+ var dir = (Direction)reader.ReadByte();
+ int seq = reader.ReadByte();
+ var key = reader.ReadInt32(); // Fastwalk stack key
+
+ if (state.Sequence == 0 && seq != 0 || !from.Move(dir))
+ {
+ state.Send(new MovementRej(seq, from));
+ state.Sequence = 0;
+
+ from.ClearFastwalkStack();
+ }
+ else
+ {
+ ++seq;
+
+ if (seq == 256)
+ {
+ seq = 1;
+ }
+
+ state.Sequence = seq;
+ }
+ }
+
+ public static void MobileQuery(NetState state, CircularBufferReader reader)
+ {
+ var from = state.Mobile;
+ if (from == null)
+ {
+ return;
+ }
+
+ reader.ReadInt32(); // 0xEDEDEDED
+ int type = reader.ReadByte();
+ var m = World.FindMobile(reader.ReadUInt32());
+
+ if (m == null)
+ {
+ return;
+ }
+
+ switch (type)
+ {
+ case 0x04: // Stats
+ {
+ m.OnStatsQuery(from);
+ break;
+ }
+ case 0x05:
+ {
+ m.OnSkillsQuery(from);
+ break;
+ }
+ default:
+ {
+ reader.Trace(state);
+ break;
+ }
+ }
+ }
+
+ public static void CrashReport(NetState state, CircularBufferReader reader)
+ {
+ var clientMaj = reader.ReadByte();
+ var clientMin = reader.ReadByte();
+ var clientRev = reader.ReadByte();
+ var clientPat = reader.ReadByte();
+
+ var x = reader.ReadUInt16();
+ var y = reader.ReadUInt16();
+ var z = reader.ReadSByte();
+ var map = reader.ReadByte();
+
+ var account = reader.ReadAscii(32);
+ var character = reader.ReadAscii(32);
+ var ip = reader.ReadAscii(15);
+
+ var unk1 = reader.ReadInt32();
+ var exception = reader.ReadInt32();
+
+ var process = reader.ReadAscii(100);
+ var report = reader.ReadAscii(100);
+
+ reader.ReadByte(); // 0x00
+
+ var offset = reader.ReadInt32();
+
+ int count = reader.ReadByte();
+
+ for (var i = 0; i < count; i++)
+ {
+ var address = reader.ReadInt32();
+ }
+ }
+
+ public static void SetAbility(this NetState state, IEntity e, EncodedReader reader)
+ {
+ EventSink.InvokeSetAbility(state.Mobile, reader.ReadInt32());
+ }
+
+ public static void GuildGumpRequest(this NetState state, IEntity e, EncodedReader reader)
+ {
+ EventSink.InvokeGuildGumpRequest(state.Mobile);
+ }
+
+ public static void QuestGumpRequest(this NetState state, IEntity e, EncodedReader reader)
+ {
+ EventSink.InvokeQuestGumpRequest(state.Mobile);
+ }
+
+ public static void EncodedCommand(NetState state, CircularBufferReader reader)
+ {
+ var e = World.FindEntity(reader.ReadUInt32());
+ int packetId = reader.ReadUInt16();
+
+ var ph = IncomingPackets.GetEncodedHandler(packetId);
+
+ if (ph == null)
+ {
+ reader.Trace(state);
+ return;
+ }
+
+ if (ph.Ingame && state.Mobile == null)
+ {
+ state.WriteConsole(
+ "Sent ingame packet (0xD7x{0:X2}) before having been attached to a mobile",
+ packetId
+ );
+ state.Dispose();
+ }
+ else if (ph.Ingame && state.Mobile.Deleted)
+ {
+ state.Dispose();
+ }
+ else
+ {
+ ph.OnReceive(state, e, new EncodedReader(reader));
+ }
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingTargetingPackets.cs b/Projects/Server/Network/Packets/IncomingTargetingPackets.cs
new file mode 100644
index 000000000..393c4bb63
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingTargetingPackets.cs
@@ -0,0 +1,144 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingTargetingPackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using Server.Diagnostics;
+using Server.Targeting;
+
+namespace Server.Network
+{
+ public static class IncomingTargetingPackets
+ {
+ public static void Configure()
+ {
+ IncomingPackets.Register(0x6C, 19, true, TargetResponse);
+ }
+
+ public static void TargetResponse(NetState state, CircularBufferReader reader)
+ {
+ int type = reader.ReadByte();
+ var targetID = reader.ReadInt32();
+ int flags = reader.ReadByte();
+ Serial serial = reader.ReadUInt32();
+ int x = reader.ReadInt16();
+ int y = reader.ReadInt16();
+ int z = reader.ReadInt16();
+ int graphic = reader.ReadUInt16();
+
+ if (targetID == unchecked((int)0xDEADBEEF))
+ {
+ return;
+ }
+
+ var from = state.Mobile;
+
+ var t = from.Target;
+
+ if (t == null)
+ {
+ return;
+ }
+
+ var prof = TargetProfile.Acquire(t.GetType());
+ prof?.Start();
+
+ try
+ {
+ if (x == -1 && y == -1 && !serial.IsValid)
+ {
+ // User pressed escape
+ t.Cancel(from, TargetCancelType.Canceled);
+ }
+ else if (t.TargetID != targetID)
+ {
+ // Sanity, prevent fake target
+ }
+ else
+ {
+ object toTarget;
+
+ if (type == 1)
+ {
+ if (graphic == 0)
+ {
+ toTarget = new LandTarget(new Point3D(x, y, z), from.Map);
+ }
+ else
+ {
+ var map = from.Map;
+
+ if (map == null || map == Map.Internal)
+ {
+ t.Cancel(from, TargetCancelType.Canceled);
+ return;
+ }
+ else
+ {
+ var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis);
+
+ var valid = false;
+
+ if (state.HighSeas)
+ {
+ var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
+ if (id.Surface)
+ {
+ z -= id.Height;
+ }
+ }
+
+ for (var i = 0; !valid && i < tiles.Length; ++i)
+ {
+ if (tiles[i].Z == z && tiles[i].ID == graphic)
+ {
+ valid = true;
+ }
+ }
+
+ if (!valid)
+ {
+ t.Cancel(from, TargetCancelType.Canceled);
+ return;
+ }
+ else
+ {
+ toTarget = new StaticTarget(new Point3D(x, y, z), graphic);
+ }
+ }
+ }
+ }
+ else if (serial.IsMobile)
+ {
+ toTarget = World.FindMobile(serial);
+ }
+ else if (serial.IsItem)
+ {
+ toTarget = World.FindItem(serial);
+ }
+ else
+ {
+ t.Cancel(from, TargetCancelType.Canceled);
+ return;
+ }
+
+ t.Invoke(from, toTarget);
+ }
+ }
+ finally
+ {
+ prof?.Finish();
+ }
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/IncomingVendorPackets.cs b/Projects/Server/Network/Packets/IncomingVendorPackets.cs
new file mode 100644
index 000000000..c4148647f
--- /dev/null
+++ b/Projects/Server/Network/Packets/IncomingVendorPackets.cs
@@ -0,0 +1,117 @@
+/*************************************************************************
+ * ModernUO *
+ * Copyright 2019-2020 - ModernUO Development Team *
+ * Email: hi@modernuo.com *
+ * File: IncomingVendorPackets.cs *
+ * *
+ * 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 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see . *
+ *************************************************************************/
+
+using System.Collections.Generic;
+
+namespace Server.Network
+{
+ public static class IncomingVendorPackets
+ {
+ public static void Configure()
+ {
+ IncomingPackets.Register(0x3B, 0, true, VendorBuyReply);
+ IncomingPackets.Register(0x9F, 0, true, VendorSellReply);
+ }
+
+ public static void VendorBuyReply(NetState state, CircularBufferReader reader)
+ {
+ var vendor = World.FindMobile(reader.ReadUInt32());
+ var flag = reader.ReadByte();
+
+ if (vendor == null)
+ {
+ return;
+ }
+
+ if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
+ {
+ state.Send(new EndVendorBuy(vendor));
+ return;
+ }
+
+ if (flag == 0x02)
+ {
+ var msgSize = reader.Remaining;
+
+ if (msgSize / 7 > 100)
+ {
+ return;
+ }
+
+ var buyList = new List(msgSize / 7);
+ while (msgSize > 0)
+ {
+ var layer = reader.ReadByte();
+ Serial serial = reader.ReadUInt32();
+ int amount = reader.ReadInt16();
+
+ buyList.Add(new BuyItemResponse(serial, amount));
+ msgSize -= 7;
+ }
+
+ if (buyList.Count > 0 && vendor is IVendor v && v.OnBuyItems(state.Mobile, buyList))
+ {
+ state.Send(new EndVendorBuy(vendor));
+ }
+ }
+ else
+ {
+ state.Send(new EndVendorBuy(vendor));
+ }
+ }
+
+ public static void VendorSellReply(NetState state, CircularBufferReader reader)
+ {
+ Serial serial = reader.ReadUInt32();
+ var vendor = World.FindMobile(serial);
+
+ if (vendor == null)
+ {
+ return;
+ }
+
+ if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
+ {
+ state.Send(new EndVendorSell(vendor));
+ return;
+ }
+
+ int count = reader.ReadUInt16();
+
+ if (count >= 100 || reader.Remaining != count * 6)
+ {
+ return;
+ }
+
+ var sellList = new List(count);
+
+ for (var i = 0; i < count; i++)
+ {
+ var item = World.FindItem(reader.ReadUInt32());
+ int amount = reader.ReadInt16();
+
+ if (item != null && amount > 0)
+ {
+ sellList.Add(new SellItemResponse(item, amount));
+ }
+ }
+
+ if (sellList.Count > 0 && vendor is IVendor v && v.OnSellItems(state.Mobile, sellList))
+ {
+ state.Send(new EndVendorSell(vendor));
+ }
+ }
+ }
+}
diff --git a/Projects/Server/Network/Packets/Old Packets/ItemPackets.cs b/Projects/Server/Network/Packets/ItemPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/ItemPackets.cs
rename to Projects/Server/Network/Packets/ItemPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/LightPackets.cs b/Projects/Server/Network/Packets/LightPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/LightPackets.cs
rename to Projects/Server/Network/Packets/LightPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/MapPackets.cs b/Projects/Server/Network/Packets/MapPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/MapPackets.cs
rename to Projects/Server/Network/Packets/MapPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/MenuPackets.cs b/Projects/Server/Network/Packets/MenuPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/MenuPackets.cs
rename to Projects/Server/Network/Packets/MenuPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/MessagePackets.cs b/Projects/Server/Network/Packets/MessagePackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/MessagePackets.cs
rename to Projects/Server/Network/Packets/MessagePackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs b/Projects/Server/Network/Packets/MobilePackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/MobilePackets.cs
rename to Projects/Server/Network/Packets/MobilePackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/MovementPackets.cs b/Projects/Server/Network/Packets/MovementPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/MovementPackets.cs
rename to Projects/Server/Network/Packets/MovementPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/ObjectHelpResponsePackets.cs b/Projects/Server/Network/Packets/ObjectHelpResponsePackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/ObjectHelpResponsePackets.cs
rename to Projects/Server/Network/Packets/ObjectHelpResponsePackets.cs
diff --git a/Projects/Server/Network/Packets/AccountPackets.cs b/Projects/Server/Network/Packets/OutgoingAccountPackets.cs
similarity index 93%
rename from Projects/Server/Network/Packets/AccountPackets.cs
rename to Projects/Server/Network/Packets/OutgoingAccountPackets.cs
index 2766a8f27..ff0d167e4 100644
--- a/Projects/Server/Network/Packets/AccountPackets.cs
+++ b/Projects/Server/Network/Packets/OutgoingAccountPackets.cs
@@ -2,7 +2,7 @@
* ModernUO *
* Copyright 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
- * File: AccountPackets.cs *
+ * File: OutgoingAccountPackets.cs *
* *
* 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 *
@@ -50,7 +50,7 @@ namespace Server.Network
BadRequest
}
- public static partial class Packets
+ public static class OutgoingAccountPackets
{
/**
* Packet: 0x81
@@ -59,7 +59,7 @@ namespace Server.Network
* Displays the list of characters during the login process.
* Note: Currently Unused
*/
- public static void SendChangeCharacter(NetState ns, IAccount a)
+ public static void SendChangeCharacter(this NetState ns, IAccount a)
{
if (ns == null || a == null || !ns.GetSendBuffer(out var buffer))
{
@@ -107,7 +107,7 @@ namespace Server.Network
*
* Sends a requests for the client version
*/
- public static void SendClientVersionRequest(NetState ns)
+ public static void SendClientVersionRequest(this NetState ns)
{
if (ns != null && ns.GetSendBuffer(out var buffer))
{
@@ -125,7 +125,7 @@ namespace Server.Network
*
* Sends the result of a deletion request
*/
- public static void SendCharacterDeleteResult(NetState ns, DeleteResultType res)
+ public static void SendCharacterDeleteResult(this NetState ns, DeleteResultType res)
{
if (ns != null && ns.GetSendBuffer(out var buffer))
{
@@ -142,7 +142,7 @@ namespace Server.Network
*
* Sends a PopupMessage with a predetermined message
*/
- public static void SendPopupMessage(NetState ns, PMMessage msg)
+ public static void SendPopupMessage(this NetState ns, PMMessage msg)
{
if (ns != null && ns.GetSendBuffer(out var buffer))
{
@@ -159,7 +159,7 @@ namespace Server.Network
*
* Sends support features based on the client version
*/
- public static void SendSupportedFeature(NetState ns)
+ public static void SendSupportedFeature(this NetState ns)
{
if (ns == null || !ns.GetSendBuffer(out var buffer))
{
@@ -204,7 +204,7 @@ namespace Server.Network
*
* Sends login confirmation
*/
- public static void SendLoginConfirmation(NetState ns, Mobile m)
+ public static void SendLoginConfirmation(this NetState ns, Mobile m)
{
if (ns == null || !ns.GetSendBuffer(out var buffer))
{
@@ -245,7 +245,7 @@ namespace Server.Network
*
* Sends login completion
*/
- public static void SendLoginComplete(NetState ns)
+ public static void SendLoginComplete(this NetState ns)
{
if (ns != null && ns.GetSendBuffer(out var buffer))
{
@@ -261,7 +261,7 @@ namespace Server.Network
*
* Sends updated character list
*/
- public static void SendCharacterListUpdate(NetState ns, IAccount a)
+ public static void SendCharacterListUpdate(this NetState ns, IAccount a)
{
if (ns == null || a == null || !ns.GetSendBuffer(out var buffer))
{
@@ -312,7 +312,7 @@ namespace Server.Network
*
* Sends list of characters and starting cities.
*/
- public static void SendCharacterList(NetState ns)
+ public static void SendCharacterList(this NetState ns)
{
var acct = ns?.Account;
@@ -413,7 +413,7 @@ namespace Server.Network
*
* Sends a reason for rejecting the login
*/
- public static void SendAccountLoginRejected(NetState ns, ALRReason reason)
+ public static void SendAccountLoginRejected(this NetState ns, ALRReason reason)
{
if (ns != null && ns.GetSendBuffer(out var buffer))
{
@@ -430,7 +430,7 @@ namespace Server.Network
*
* Sends login acknowledge with server listing
*/
- public static void SendAccountLoginAck(NetState ns)
+ public static void SendAccountLoginAck(this NetState ns)
{
if (ns == null || !ns.GetSendBuffer(out var buffer))
{
@@ -467,7 +467,7 @@ namespace Server.Network
*
* Sends acknowledge play server
*/
- public static void SendPlayServerAck(NetState ns, ServerInfo si, int authId)
+ public static void SendPlayServerAck(this NetState ns, ServerInfo si, int authId)
{
if (ns == null || !ns.GetSendBuffer(out var buffer))
{
diff --git a/Projects/Server/Network/Packets/Old Packets/PlayerPackets.cs b/Projects/Server/Network/Packets/PlayerPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/PlayerPackets.cs
rename to Projects/Server/Network/Packets/PlayerPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/SecureTradePackets.cs b/Projects/Server/Network/Packets/SecureTradePackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/SecureTradePackets.cs
rename to Projects/Server/Network/Packets/SecureTradePackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/TargetPackets.cs b/Projects/Server/Network/Packets/TargetPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/TargetPackets.cs
rename to Projects/Server/Network/Packets/TargetPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/UnicodePromptPackets.cs b/Projects/Server/Network/Packets/UnicodePromptPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/UnicodePromptPackets.cs
rename to Projects/Server/Network/Packets/UnicodePromptPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/VendorBuyPackets.cs b/Projects/Server/Network/Packets/VendorBuyPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/VendorBuyPackets.cs
rename to Projects/Server/Network/Packets/VendorBuyPackets.cs
diff --git a/Projects/Server/Network/Packets/Old Packets/VendorSellPackets.cs b/Projects/Server/Network/Packets/VendorSellPackets.cs
similarity index 100%
rename from Projects/Server/Network/Packets/Old Packets/VendorSellPackets.cs
rename to Projects/Server/Network/Packets/VendorSellPackets.cs
diff --git a/Projects/Server/Race.cs b/Projects/Server/Race.cs
index 5c60994b4..92877cf87 100644
--- a/Projects/Server/Race.cs
+++ b/Projects/Server/Race.cs
@@ -28,10 +28,10 @@ namespace Server
PluralName = pluralName;
}
- public static Race DefaultRace => Races[0];
-
public static Race[] Races { get; } = new Race[0x100];
+ public static Race DefaultRace => Races[0];
+
public static Race Human => Races[0];
public static Race Elf => Races[1];
public static Race Gargoyle => Races[2];
diff --git a/Projects/UOContent/Accounting/AccountAttackLimiter.cs b/Projects/UOContent/Accounting/AccountAttackLimiter.cs
index 3a9f6f4c4..ba02e2c85 100644
--- a/Projects/UOContent/Accounting/AccountAttackLimiter.cs
+++ b/Projects/UOContent/Accounting/AccountAttackLimiter.cs
@@ -24,9 +24,9 @@ namespace Server.Accounting
return;
}
- PacketHandlers.RegisterThrottler(0x80, Throttle_Callback);
- PacketHandlers.RegisterThrottler(0x91, Throttle_Callback);
- PacketHandlers.RegisterThrottler(0xCF, Throttle_Callback);
+ IncomingPackets.RegisterThrottler(0x80, Throttle_Callback);
+ IncomingPackets.RegisterThrottler(0x91, Throttle_Callback);
+ IncomingPackets.RegisterThrottler(0xCF, Throttle_Callback);
}
public static TimeSpan Throttle_Callback(NetState ns)
diff --git a/Projects/UOContent/Accounting/AccountHandler.cs b/Projects/UOContent/Accounting/AccountHandler.cs
index 68ae2769b..d443e840b 100644
--- a/Projects/UOContent/Accounting/AccountHandler.cs
+++ b/Projects/UOContent/Accounting/AccountHandler.cs
@@ -256,13 +256,13 @@ namespace Server.Misc
acct.Comments.Add(new AccountComment("System", $"Character #{index + 1} {m} deleted by {state}"));
m.Delete();
- Packets.SendCharacterListUpdate(state, acct);
+ state.SendCharacterListUpdate(acct);
return;
}
}
- Packets.SendCharacterDeleteResult(state, res);
- Packets.SendCharacterListUpdate(state, acct);
+ state.SendCharacterDeleteResult(res);
+ state.SendCharacterListUpdate(acct);
}
diff --git a/Projects/UOContent/Configuration/ExpansionConfiguration.cs b/Projects/UOContent/Configuration/ExpansionConfiguration.cs
index 6fbdc6fe6..67bc17cf5 100644
--- a/Projects/UOContent/Configuration/ExpansionConfiguration.cs
+++ b/Projects/UOContent/Configuration/ExpansionConfiguration.cs
@@ -30,7 +30,7 @@ namespace Server
if (ObjectPropertyList.Enabled)
{
- PacketHandlers.SingleClickProps =
+ IncomingEntityPackets.SingleClickProps =
true; // single click for everything is overridden to check object property list
}
diff --git a/Projects/UOContent/Engines/Chat/Chat.cs b/Projects/UOContent/Engines/Chat/Chat.cs
index 4ce7ece73..94ea72c6f 100644
--- a/Projects/UOContent/Engines/Chat/Chat.cs
+++ b/Projects/UOContent/Engines/Chat/Chat.cs
@@ -14,8 +14,8 @@ namespace Server.Engines.Chat
public static void Initialize()
{
- PacketHandlers.Register(0xB5, 0x40, true, OpenChatWindowRequest);
- PacketHandlers.Register(0xB3, 0, true, ChatAction);
+ IncomingPackets.Register(0xB5, 0x40, true, OpenChatWindowRequest);
+ IncomingPackets.Register(0xB3, 0, true, ChatAction);
}
public static void SendCommandTo(Mobile to, ChatCommand type, string param1 = null, string param2 = null)
diff --git a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs
index ccd4493dd..93936ccb4 100644
--- a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs
+++ b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs
@@ -376,21 +376,21 @@ namespace Server.Engines.ConPVP
int climb = count / 2;
if (climb > 3)
climb = 3;
-
+
for ( i = 0; i < climb; i++ )
{
p = ((Point3D)list[i]);
p.Z += (i+1) * 4;
list[i] = p;
}
-
+
for ( ; i < count - climb; i++ )
{
p = ((Point3D)list[i]);
p.Z += 16;
list[i] = p;
}
-
+
for ( i = climb; i > 0; i-- )
{
p = ((Point3D)list[i]);
diff --git a/Projects/UOContent/Engines/MLQuests/Gumps/RaceChangeGump.cs b/Projects/UOContent/Engines/MLQuests/Gumps/RaceChangeGump.cs
index db2efa2ef..8cadcf7e5 100644
--- a/Projects/UOContent/Engines/MLQuests/Gumps/RaceChangeGump.cs
+++ b/Projects/UOContent/Engines/MLQuests/Gumps/RaceChangeGump.cs
@@ -80,7 +80,7 @@ namespace Server.Engines.MLQuests.Gumps
{
m_Pending = new Dictionary();
- PacketHandlers.RegisterExtended(0x2A, true, RaceChangeReply);
+ IncomingExtendedCommandPackets.RegisterExtended(0x2A, true, RaceChangeReply);
}
public static bool IsPending(NetState state) => state != null && m_Pending.ContainsKey(state);
diff --git a/Projects/UOContent/Items/Books/BaseBook.cs b/Projects/UOContent/Items/Books/BaseBook.cs
index 1ff98b2be..50d6cacba 100644
--- a/Projects/UOContent/Items/Books/BaseBook.cs
+++ b/Projects/UOContent/Items/Books/BaseBook.cs
@@ -361,9 +361,9 @@ namespace Server.Items
public static void Initialize()
{
- PacketHandlers.Register(0xD4, 0, true, HeaderChange);
- PacketHandlers.Register(0x66, 0, true, ContentChange);
- PacketHandlers.Register(0x93, 99, true, OldHeaderChange);
+ IncomingPackets.Register(0xD4, 0, true, HeaderChange);
+ IncomingPackets.Register(0x66, 0, true, ContentChange);
+ IncomingPackets.Register(0x93, 99, true, OldHeaderChange);
}
public static void OldHeaderChange(NetState state, CircularBufferReader reader)
diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPacketHandlers.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPacketHandlers.cs
index e9d6675ae..6ceef011f 100644
--- a/Projects/UOContent/Items/Games/Mahjong/MahjongPacketHandlers.cs
+++ b/Projects/UOContent/Items/Games/Mahjong/MahjongPacketHandlers.cs
@@ -25,7 +25,7 @@ namespace Server.Engines.Mahjong
public static void Initialize()
{
- PacketHandlers.Register(0xDA, 0, true, OnPacket);
+ IncomingPackets.Register(0xDA, 0, true, OnPacket);
RegisterSubCommand(0x6, ExitGame);
RegisterSubCommand(0xA, GivePoints);
diff --git a/Projects/UOContent/Items/Maps/MapItem.cs b/Projects/UOContent/Items/Maps/MapItem.cs
index cfea9fac2..cf849e645 100644
--- a/Projects/UOContent/Items/Maps/MapItem.cs
+++ b/Projects/UOContent/Items/Maps/MapItem.cs
@@ -331,7 +331,7 @@ namespace Server.Items
public static void Initialize()
{
- PacketHandlers.Register(0x56, 11, true, OnMapCommand);
+ IncomingPackets.Register(0x56, 11, true, OnMapCommand);
}
private static void OnMapCommand(NetState state, CircularBufferReader reader)
diff --git a/Projects/UOContent/Items/Misc/BulletinBoards.cs b/Projects/UOContent/Items/Misc/BulletinBoards.cs
index e0f0c53cb..52cabaa43 100644
--- a/Projects/UOContent/Items/Misc/BulletinBoards.cs
+++ b/Projects/UOContent/Items/Misc/BulletinBoards.cs
@@ -229,7 +229,7 @@ namespace Server.Items
public static void Initialize()
{
- PacketHandlers.Register(0x71, 0, true, BBClientRequest);
+ IncomingPackets.Register(0x71, 0, true, BBClientRequest);
}
public static void BBClientRequest(NetState state, CircularBufferReader reader)
diff --git a/Projects/UOContent/Items/Shields/BaseShield.cs b/Projects/UOContent/Items/Shields/BaseShield.cs
index c51223e2e..165bb748f 100644
--- a/Projects/UOContent/Items/Shields/BaseShield.cs
+++ b/Projects/UOContent/Items/Shields/BaseShield.cs
@@ -142,9 +142,9 @@ namespace Server.Items
/*
FORMULA: Displayed AR = ((Parrying Skill * Base AR of Shield) � 200) + 1
-
+
FORMULA: % Chance of Blocking = parry skill - (shieldAR * 2)
-
+
FORMULA: Melee Damage Absorbed = (AR of Shield) / 2 | Archery Damage Absorbed = AR of Shield
*/
if (owner.CheckSkill(SkillName.Parry, chance))
diff --git a/Projects/UOContent/Misc/HardwareInfo.cs b/Projects/UOContent/Misc/HardwareInfo.cs
index f0968aec4..053f1ca3f 100644
--- a/Projects/UOContent/Misc/HardwareInfo.cs
+++ b/Projects/UOContent/Misc/HardwareInfo.cs
@@ -89,7 +89,7 @@ namespace Server
public static void Initialize()
{
- PacketHandlers.Register(0xD9, 0x10C, false, OnReceive);
+ IncomingPackets.Register(0xD9, 0x10C, false, OnReceive);
CommandSystem.Register("HWInfo", AccessLevel.GameMaster, HWInfo_OnCommand);
}
diff --git a/Projects/UOContent/Misc/ProtocolExtensions.cs b/Projects/UOContent/Misc/ProtocolExtensions.cs
index 9504d57bd..66e823e53 100644
--- a/Projects/UOContent/Misc/ProtocolExtensions.cs
+++ b/Projects/UOContent/Misc/ProtocolExtensions.cs
@@ -8,7 +8,7 @@ namespace Server.Misc
public static void Initialize()
{
- PacketHandlers.Register(0xF0, 0, false, DecodeBundledPacket);
+ IncomingPackets.Register(0xF0, 0, false, DecodeBundledPacket);
}
public static void Register(int packetID, bool ingame, OnPacketReceive onReceive)
diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs
index d2af7e503..ac3a549c2 100644
--- a/Projects/UOContent/Mobiles/PlayerMobile.cs
+++ b/Projects/UOContent/Mobiles/PlayerMobile.cs
@@ -1015,7 +1015,7 @@ namespace Server.Mobiles
{
if (FastwalkPrevention)
{
- PacketHandlers.RegisterThrottler(0x02, MovementThrottle_Callback);
+ IncomingPackets.RegisterThrottler(0x02, MovementThrottle_Callback);
}
EventSink.Login += OnLogin;
diff --git a/Projects/UOContent/Multis/HouseFoundation.cs b/Projects/UOContent/Multis/HouseFoundation.cs
index ef2e2866b..5be269f0a 100644
--- a/Projects/UOContent/Multis/HouseFoundation.cs
+++ b/Projects/UOContent/Multis/HouseFoundation.cs
@@ -975,23 +975,23 @@ namespace Server.Multis
public static void Initialize()
{
- PacketHandlers.RegisterExtended(0x1E, true, QueryDesignDetails);
+ IncomingExtendedCommandPackets.RegisterExtended(0x1E, true, QueryDesignDetails);
- PacketHandlers.RegisterEncoded(0x02, true, Designer_Backup);
- PacketHandlers.RegisterEncoded(0x03, true, Designer_Restore);
- PacketHandlers.RegisterEncoded(0x04, true, Designer_Commit);
- PacketHandlers.RegisterEncoded(0x05, true, Designer_Delete);
- PacketHandlers.RegisterEncoded(0x06, true, Designer_Build);
- PacketHandlers.RegisterEncoded(0x0C, true, Designer_Close);
- PacketHandlers.RegisterEncoded(0x0D, true, Designer_Stairs);
- PacketHandlers.RegisterEncoded(0x0E, true, Designer_Sync);
- PacketHandlers.RegisterEncoded(0x10, true, Designer_Clear);
- PacketHandlers.RegisterEncoded(0x12, true, Designer_Level);
+ IncomingPackets.RegisterEncoded(0x02, true, Designer_Backup);
+ IncomingPackets.RegisterEncoded(0x03, true, Designer_Restore);
+ IncomingPackets.RegisterEncoded(0x04, true, Designer_Commit);
+ IncomingPackets.RegisterEncoded(0x05, true, Designer_Delete);
+ IncomingPackets.RegisterEncoded(0x06, true, Designer_Build);
+ IncomingPackets.RegisterEncoded(0x0C, true, Designer_Close);
+ IncomingPackets.RegisterEncoded(0x0D, true, Designer_Stairs);
+ IncomingPackets.RegisterEncoded(0x0E, true, Designer_Sync);
+ IncomingPackets.RegisterEncoded(0x10, true, Designer_Clear);
+ IncomingPackets.RegisterEncoded(0x12, true, Designer_Level);
- PacketHandlers.RegisterEncoded(0x13, true, Designer_Roof); // Samurai Empire roof
- PacketHandlers.RegisterEncoded(0x14, true, Designer_RoofDelete); // Samurai Empire roof
+ IncomingPackets.RegisterEncoded(0x13, true, Designer_Roof); // Samurai Empire roof
+ IncomingPackets.RegisterEncoded(0x14, true, Designer_RoofDelete); // Samurai Empire roof
- PacketHandlers.RegisterEncoded(0x1A, true, Designer_Revert);
+ IncomingPackets.RegisterEncoded(0x1A, true, Designer_Revert);
EventSink.Speech += EventSink_Speech;
}