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; }