#region References using System; using System.Collections.Generic; using System.IO; using Server.ContextMenus; using Server.Diagnostics; using Server.Gumps; using Server.Items; using Server.Mobiles; using Server.Targeting; using CV = Server.ClientVersion; #endregion namespace Server.Network { 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 static readonly PacketHandler[] m_Handlers; private static readonly PacketHandler[] m_ExtendedHandlersLow; private static readonly Dictionary m_ExtendedHandlersHigh; private static readonly EncodedPacketHandler[] m_EncodedHandlersLow; private static readonly Dictionary m_EncodedHandlersHigh; static PacketHandlers() { m_Handlers = new PacketHandler[0x100]; m_ExtendedHandlersLow = new PacketHandler[0x100]; m_ExtendedHandlersHigh = new Dictionary(); m_EncodedHandlersLow = new EncodedPacketHandler[0x100]; m_EncodedHandlersHigh = new Dictionary(); Register(0x00, 104, false, CreateCharacter); Register(0x01, 5, false, Disconnect); Register(0x02, 7, true, MovementReq); Register(0x03, 0, true, AsciiSpeech); Register(0x04, 2, true, GodModeRequest); Register(0x05, 5, true, AttackReq); Register(0x06, 5, true, UseReq); Register(0x07, 7, true, LiftReq); Register(0x08, 15, true, DropReq); Register(0x09, 5, true, LookReq); Register(0x0A, 11, true, Edit); Register(0x12, 0, true, TextCommand); Register(0x13, 10, true, EquipReq); Register(0x14, 6, true, ChangeZ); 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(0x47, 11, true, NewTerrain); Register(0x48, 73, true, NewAnimData); Register(0x58, 106, true, NewRegion); Register(0x5D, 73, false, PlayCharacter); Register(0x61, 9, true, DeleteStatic); 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(0x79, 9, true, ResourceQuery); Register(0x7E, 2, true, GodviewQuery); 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(0x96, 0, true, GameCentralMoniter); Register(0x98, 0, true, MobileNameRequest); Register(0x9A, 0, true, AsciiPromptResponse); Register(0x9B, 258, true, HelpRequest); Register(0x9D, 51, true, GMSingle); 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, true, ClientVersion); Register(0xBE, 0, true, AssistVersion); Register(0xBF, 0, true, ExtendedCommand); Register(0xC2, 0, true, UnicodePromptResponse); Register(0xC8, 2, true, SetUpdateRange); Register(0xC9, 6, true, TripTime); Register(0xCA, 6, true, UTripTime); 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(0xEC, 0, false, EquipMacro); Register(0xED, 0, false, UnequipMacro); Register(0xEF, 21, false, LoginServerSeed); Register(0xF4, 0, false, CrashReport); Register(0xF8, 106, false, CreateCharacter70160); //Register(0xFA, 1, true, Unhandled); // Currently Handled in UltimaStore.cs Register(0xFB, 2, false, PublicHouseContent); Register(0x8D, 0, false, ECCreateCharacter); 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); #region Stygian Abyss RegisterExtended(0x32, true, ToggleFlying); #endregion RegisterExtended(0x2D, true, TargetedSpell); RegisterExtended(0x2E, true, TargetedSkillUse); RegisterExtended(0x30, true, TargetByResourceMacro); RegisterEncoded(0x19, true, SetAbility); RegisterEncoded(0x28, true, GuildGumpRequest); RegisterEncoded(0x32, true, QuestGumpRequest); } public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive) { m_Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive); } public static PacketHandler GetHandler(int packetID) { return m_Handlers[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]; } else { 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]; } else { 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; } } private static void Unhandled(NetState state, PacketReader pvSrc) { } private static void UnhandledBF(NetState state, PacketReader pvSrc) { } public static void Empty(NetState state, PacketReader pvSrc) { } public static void SetAbility(NetState state, IEntity e, EncodedReader reader) { EventSink.InvokeSetAbility(new SetAbilityEventArgs(state.Mobile, reader.ReadInt32())); } public static void GuildGumpRequest(NetState state, IEntity e, EncodedReader reader) { EventSink.InvokeGuildGumpRequest(new GuildGumpRequestArgs(state.Mobile)); } public static void QuestGumpRequest(NetState state, IEntity e, EncodedReader reader) { EventSink.InvokeQuestGumpRequest(new QuestGumpRequestArgs(state.Mobile)); } public static void EncodedCommand(NetState state, PacketReader pvSrc) { var e = World.FindEntity(pvSrc.ReadInt32()); int packetID = pvSrc.ReadUInt16(); var ph = GetEncodedHandler(packetID); if (ph != null) { if (ph.Ingame && state.Mobile == null) { Console.WriteLine( "Client: {0}: Sent ingame packet (0xD7x{1:X2}) before having been attached to a mobile", state, packetID); state.Dispose(); } else if (ph.Ingame && state.Mobile.Deleted) { state.Dispose(); } else { ph.OnReceive(state, e, new EncodedReader(pvSrc)); } } else { pvSrc.Trace(state); } } public static void RenameRequest(NetState state, PacketReader pvSrc) { var from = state.Mobile; var targ = World.FindMobile(pvSrc.ReadInt32()); if (targ != null) { EventSink.InvokeRenameRequest(new RenameRequestEventArgs(from, targ, pvSrc.ReadStringSafe())); } } public static void SecureTrade(NetState state, PacketReader pvSrc) { switch (pvSrc.ReadByte()) { case 1: // Cancel { Serial serial = pvSrc.ReadInt32(); var cont = World.FindItem(serial) as SecureTradeContainer; if (cont != null) { var trade = cont.Trade; if (trade != null) { if (trade.From.Mobile == state.Mobile || trade.To.Mobile == state.Mobile) { trade.Cancel(); } } } } break; case 2: // Check { Serial serial = pvSrc.ReadInt32(); var cont = World.FindItem(serial) as SecureTradeContainer; if (cont != null) { var trade = cont.Trade; var value = pvSrc.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 = pvSrc.ReadInt32(); var cont = World.FindItem(serial) as SecureTradeContainer; if (cont != null) { var gold = pvSrc.ReadInt32(); var plat = pvSrc.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, PacketReader pvSrc) { pvSrc.Seek(1, SeekOrigin.Begin); int msgSize = pvSrc.ReadUInt16(); var vendor = World.FindMobile(pvSrc.ReadInt32()); var flag = pvSrc.ReadByte(); if (vendor == null) { return; } else if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10)) { state.Send(new EndVendorBuy(vendor)); return; } if (flag == 0x02) { msgSize -= 1 + 2 + 4 + 1; if ((msgSize / 7) > 100) { return; } var buyList = new List(msgSize / 7); for (; msgSize > 0; msgSize -= 7) { var layer = pvSrc.ReadByte(); Serial serial = pvSrc.ReadInt32(); int amount = pvSrc.ReadInt16(); buyList.Add(new BuyItemResponse(serial, amount)); } if (buyList.Count > 0) { var v = vendor as IVendor; if (v != null && v.OnBuyItems(state.Mobile, buyList)) { state.Send(new EndVendorBuy(vendor)); } } } else { state.Send(new EndVendorBuy(vendor)); } } public static void VendorSellReply(NetState state, PacketReader pvSrc) { Serial serial = pvSrc.ReadInt32(); var vendor = World.FindMobile(serial); if (vendor == null) { return; } else if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10)) { state.Send(new EndVendorSell(vendor)); return; } int count = pvSrc.ReadUInt16(); if (count < 100 && pvSrc.Size == (1 + 2 + 4 + 2 + (count * 6))) { var sellList = new List(count); for (var i = 0; i < count; i++) { var item = World.FindItem(pvSrc.ReadInt32()); int Amount = pvSrc.ReadInt16(); if (item != null && Amount > 0) { sellList.Add(new SellItemResponse(item, Amount)); } } if (sellList.Count > 0) { var v = vendor as IVendor; if (v != null && v.OnSellItems(state.Mobile, sellList)) { state.Send(new EndVendorSell(vendor)); } } } } public static void DeleteCharacter(NetState state, PacketReader pvSrc) { pvSrc.Seek(30, SeekOrigin.Current); var index = pvSrc.ReadInt32(); EventSink.InvokeDeleteRequest(new DeleteRequestEventArgs(state, index)); } public static void ResourceQuery(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { } } public static void GameCentralMoniter(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { int type = pvSrc.ReadByte(); var num1 = pvSrc.ReadInt32(); Console.WriteLine("God Client: {0}: Game central moniter", state); Console.WriteLine(" - Type: {0}", type); Console.WriteLine(" - Number: {0}", num1); pvSrc.Trace(state); } } public static void GodviewQuery(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { Console.WriteLine("God Client: {0}: Godview query 0x{1:X}", state, pvSrc.ReadByte()); } } public static void GMSingle(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { pvSrc.Trace(state); } } public static void DeathStatusResponse(NetState state, PacketReader pvSrc) { // Ignored } public static void ObjectHelpRequest(NetState state, PacketReader pvSrc) { var from = state.Mobile; Serial serial = pvSrc.ReadInt32(); int unk = pvSrc.ReadByte(); var lang = pvSrc.ReadString(3); if (serial.IsItem) { var item = World.FindItem(serial); if (item != null && from.Map == item.Map && Utility.InUpdateRange(from, item) && from.CanSee(item)) { item.OnHelpRequest(from); } } else if (serial.IsMobile) { var m = World.FindMobile(serial); if (m != null && from.Map == m.Map && Utility.InUpdateRange(from, m) && from.CanSee(m)) { m.OnHelpRequest(m); } } } public static void MobileNameRequest(NetState state, PacketReader pvSrc) { var m = World.FindMobile(pvSrc.ReadInt32()); if (m != null && Utility.InUpdateRange(state.Mobile, m) && state.Mobile.CanSee(m)) { state.Send(new MobileName(m)); } } public static void RequestScrollWindow(NetState state, PacketReader pvSrc) { int lastTip = pvSrc.ReadInt16(); int type = pvSrc.ReadByte(); } public static void AttackReq(NetState state, PacketReader pvSrc) { var from = state.Mobile; var m = World.FindMobile(pvSrc.ReadInt32()); if (m != null) { from.Attack(m); } } public static void HuePickerResponse(NetState state, PacketReader pvSrc) { var serial = pvSrc.ReadInt32(); int value = pvSrc.ReadInt16(); var hue = pvSrc.ReadInt16() & 0x3FFF; hue = Utility.ClipDyedHue(hue); foreach (var huePicker in state.HuePickers) { if (huePicker.Serial == serial) { state.RemoveHuePicker(huePicker); hue = Math.Max(0, hue); if (state.Mobile == null || state.Mobile.AccessLevel < AccessLevel.GameMaster) huePicker.Clip(ref hue); huePicker.OnResponse(hue); break; } } } public static void TripTime(NetState state, PacketReader pvSrc) { int unk1 = pvSrc.ReadByte(); var unk2 = pvSrc.ReadInt32(); state.Send(new TripTimeResponse(unk1)); } public static void UTripTime(NetState state, PacketReader pvSrc) { int unk1 = pvSrc.ReadByte(); var unk2 = pvSrc.ReadInt32(); state.Send(new UTripTimeResponse(unk1)); } public static void ChangeZ(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { int x = pvSrc.ReadInt16(); int y = pvSrc.ReadInt16(); int z = pvSrc.ReadSByte(); Console.WriteLine("God Client: {0}: Change Z ({1}, {2}, {3})", state, x, y, z); } } public static void SystemInfo(NetState state, PacketReader pvSrc) { int v1 = pvSrc.ReadByte(); int v2 = pvSrc.ReadUInt16(); int v3 = pvSrc.ReadByte(); var s1 = pvSrc.ReadString(32); var s2 = pvSrc.ReadString(32); var s3 = pvSrc.ReadString(32); var s4 = pvSrc.ReadString(32); int v4 = pvSrc.ReadUInt16(); int v5 = pvSrc.ReadUInt16(); var v6 = pvSrc.ReadInt32(); var v7 = pvSrc.ReadInt32(); var v8 = pvSrc.ReadInt32(); } public static void Edit(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { int type = pvSrc.ReadByte(); // 10 = static, 7 = npc, 4 = dynamic int x = pvSrc.ReadInt16(); int y = pvSrc.ReadInt16(); int id = pvSrc.ReadInt16(); int z = pvSrc.ReadSByte(); int hue = pvSrc.ReadUInt16(); Console.WriteLine("God Client: {0}: Edit {6} ({1}, {2}, {3}) 0x{4:X} (0x{5:X})", state, x, y, z, id, hue, type); } } public static void DeleteStatic(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { int x = pvSrc.ReadInt16(); int y = pvSrc.ReadInt16(); int z = pvSrc.ReadInt16(); int id = pvSrc.ReadUInt16(); Console.WriteLine("God Client: {0}: Delete Static ({1}, {2}, {3}) 0x{4:X}", state, x, y, z, id); } } public static void NewAnimData(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { Console.WriteLine("God Client: {0}: New tile animation", state); pvSrc.Trace(state); } } public static void NewTerrain(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { int x = pvSrc.ReadInt16(); int y = pvSrc.ReadInt16(); int id = pvSrc.ReadUInt16(); int width = pvSrc.ReadInt16(); int height = pvSrc.ReadInt16(); Console.WriteLine("God Client: {0}: New Terrain ({1}, {2})+({3}, {4}) 0x{5:X4}", state, x, y, width, height, id); } } public static void NewRegion(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { var name = pvSrc.ReadString(40); var unk = pvSrc.ReadInt32(); int x = pvSrc.ReadInt16(); int y = pvSrc.ReadInt16(); int width = pvSrc.ReadInt16(); int height = pvSrc.ReadInt16(); int zStart = pvSrc.ReadInt16(); int zEnd = pvSrc.ReadInt16(); var desc = pvSrc.ReadString(40); int soundFX = pvSrc.ReadInt16(); int music = pvSrc.ReadInt16(); int nightFX = pvSrc.ReadInt16(); int dungeon = pvSrc.ReadByte(); int light = pvSrc.ReadInt16(); Console.WriteLine("God Client: {0}: New Region '{1}' ('{2}')", state, name, desc); } } public static void AccountID(NetState state, PacketReader pvSrc) { } public static bool VerifyGC(NetState state) { if (state.Mobile == null || state.Mobile.IsPlayer()) { if (state.Running) { Console.WriteLine("Warning: {0}: Player using godclient, disconnecting", state); } state.Dispose(); return false; } else { return true; } } public static void TextCommand(NetState state, PacketReader pvSrc) { int type = pvSrc.ReadByte(); var command = pvSrc.ReadString(); var m = state.Mobile; switch (type) { case 0x00: // Go { if (VerifyGC(state)) { try { var split = command.Split(' '); var x = Utility.ToInt32(split[0]); var y = Utility.ToInt32(split[1]); int z; if (split.Length >= 3) { z = Utility.ToInt32(split[2]); } else if (m.Map != null) { z = m.Map.GetAverageZ(x, y); } else { z = 0; } m.Location = new Point3D(x, y, z); } catch (Exception e) { ExceptionLogging.LogException(e); } } break; } case 0xC7: // Animate { EventSink.InvokeAnimateRequest(new AnimateRequestEventArgs(m, command)); break; } case 0x24: // Use skill { if (!Int32.TryParse(command.Split(' ')[0], out var skillIndex)) { break; } Skills.UseSkill(m, skillIndex); break; } case 0x43: // Open spellbook { if (!Int32.TryParse(command, out var booktype)) { booktype = 1; } EventSink.InvokeOpenSpellbookRequest(new OpenSpellbookRequestEventArgs(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.ToInt32(split[1]) : -1; EventSink.InvokeCastSpellRequest(new CastSpellRequestEventArgs(m, spellID, World.FindItem(serial))); } break; } case 0x58: // Open door { EventSink.InvokeOpenDoorMacroUsed(new OpenDoorMacroEventArgs(m)); break; } case 0x56: // Cast spell from macro { var spellID = Utility.ToInt32(command) - 1; EventSink.InvokeCastSpellRequest(new CastSpellRequestEventArgs(m, spellID, null)); break; } case 0xF4: // Invoke virtues from macro { var virtueID = Utility.ToInt32(command) - 1; EventSink.InvokeVirtueMacroRequest(new VirtueMacroRequestEventArgs(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: { Console.WriteLine("Client: {0}: Unknown text-command type 0x{1:X2}: {2}", state, type, command); break; } } } public static void GodModeRequest(NetState state, PacketReader pvSrc) { if (VerifyGC(state)) { state.Send(new GodModeReply(pvSrc.ReadBoolean())); } } public static void AsciiPromptResponse(NetState state, PacketReader pvSrc) { var serial = pvSrc.ReadInt32(); var prompt = pvSrc.ReadInt32(); var type = pvSrc.ReadInt32(); var text = pvSrc.ReadStringSafe(); if (text == null || text.Length > 128) { return; } var from = state.Mobile; var p = from.Prompt; if (p != null && p.Sender.Serial == serial && p.TypeId == prompt) { from.Prompt = null; if (type == 0) { p.OnCancel(from); } else { p.OnResponse(from, text); } } } public static void UnicodePromptResponse(NetState state, PacketReader pvSrc) { var serial = pvSrc.ReadInt32(); var prompt = pvSrc.ReadInt32(); var type = pvSrc.ReadInt32(); var lang = pvSrc.ReadString(4); var text = pvSrc.ReadUnicodeStringLESafe(); if (text.Length > 128) { return; } var from = state.Mobile; var p = from.Prompt; var promptSerial = (p != null && p.Sender != null) ? p.Sender.Serial.Value : from.Serial.Value; if (p != null && promptSerial == serial && p.TypeId == prompt) { from.Prompt = null; if (type == 0) { p.OnCancel(from); } else { p.OnResponse(from, text); } } } public static void MenuResponse(NetState state, PacketReader pvSrc) { var serial = pvSrc.ReadInt32(); int menuID = pvSrc.ReadInt16(); // unused in our implementation int index = pvSrc.ReadInt16(); int itemID = pvSrc.ReadInt16(); int hue = pvSrc.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, PacketReader pvSrc) { int type = pvSrc.ReadByte(); Serial serial = pvSrc.ReadInt32(); var beholder = state.Mobile; var beheld = World.FindMobile(serial); if (beheld == null) { return; } switch (type) { case 0x00: // display request { EventSink.InvokeProfileRequest(new ProfileRequestEventArgs(beholder, beheld)); break; } case 0x01: // edit request { pvSrc.ReadInt16(); // Skip int length = pvSrc.ReadUInt16(); if (length > 511) { return; } var text = pvSrc.ReadUnicodeString(length); EventSink.InvokeChangeProfileRequest(new ChangeProfileRequestEventArgs(beholder, beheld, text)); break; } } } public static void Disconnect(NetState state, PacketReader pvSrc) { var minusOne = pvSrc.ReadInt32(); } public static void LiftReq(NetState state, PacketReader pvSrc) { Serial serial = pvSrc.ReadInt32(); int amount = pvSrc.ReadUInt16(); var item = World.FindItem(serial); state.Mobile.Lift(item, amount, out var rejected, out var reject); } public static void EquipReq(NetState state, PacketReader pvSrc) { 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; } pvSrc.Seek(5, SeekOrigin.Current); var to = World.FindMobile(pvSrc.ReadInt32()); if (to == null) { to = from; } if (!to.AllowEquipFrom(from) || !to.EquipItem(item)) { item.Bounce(from); } item.ClearBounce(); } public static void DropReq(NetState state, PacketReader pvSrc) { Serial serial = pvSrc.ReadInt32(); int x = pvSrc.ReadInt16(); int y = pvSrc.ReadInt16(); int z = pvSrc.ReadSByte(); var gridloc = pvSrc.ReadByte(); // grid location Serial dest = pvSrc.ReadInt32(); var loc = new Point3D(x, y, z); var from = state.Mobile; if (serial.IsItem) { var dropped = World.FindItem(serial); if (dropped != null) { dropped.GridLocation = gridloc; } } if (dest.IsMobile) { from.Drop(World.FindMobile(dest), loc); } else if (dest.IsItem) { var item = World.FindItem(dest); if (item is BaseMulti && ((BaseMulti)item).AllowsRelativeDrop) { loc.m_X += item.X; loc.m_Y += item.Y; from.Drop(loc); } else { from.Drop(item, loc); } } else { from.Drop(loc); } } public static void ConfigurationFile(NetState state, PacketReader pvSrc) { } public static void LogoutReq(NetState state, PacketReader pvSrc) { state.Send(new LogoutAck()); } public static void ChangeSkillLock(NetState state, PacketReader pvSrc) { var s = state.Mobile.Skills[pvSrc.ReadInt16()]; if (s != null) { s.SetLockNoRelay((SkillLock)pvSrc.ReadByte()); } } public static void HelpRequest(NetState state, PacketReader pvSrc) { EventSink.InvokeHelpRequest(new HelpRequestEventArgs(state.Mobile)); } public static void TargetResponse(NetState state, PacketReader pvSrc) { int type = pvSrc.ReadByte(); var targetID = pvSrc.ReadInt32(); int flags = pvSrc.ReadByte(); Serial serial = pvSrc.ReadInt32(); int x = pvSrc.ReadInt16(), y = pvSrc.ReadInt16(), z = pvSrc.ReadInt16(); int graphic = pvSrc.ReadUInt16(); if (targetID == unchecked((int)0xDEADBEEF)) { return; } var from = state.Mobile; var t = from.Target; if (t != null) { var prof = TargetProfile.Acquire(t.GetType()); if (prof != null) { prof.Start(); } try { if (x == -1 && y == -1 && !serial.IsValid) { // User pressed escape t.Cancel(from, TargetCancelType.Canceled); } else if (Target.TargetIDValidation && t.TargetID != targetID) { // Prevent fake target, reported by AssistUO Team! return; } 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; 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 { if (prof != null) { prof.Finish(); } } } } public static void DisplayGumpResponse(NetState state, PacketReader pvSrc) { var serial = pvSrc.ReadInt32(); var typeID = pvSrc.ReadInt32(); var buttonID = pvSrc.ReadInt32(); foreach (var gump in state.Gumps) { if (gump.Serial == serial && gump.TypeID == typeID) { var buttonExists = buttonID == 0; // 0 is always 'close' if (!buttonExists) { foreach (var e in gump.Entries) { if (e is GumpButton && ((GumpButton)e).ButtonID == buttonID) { buttonExists = true; break; } if (e is GumpImageTileButton && ((GumpImageTileButton)e).ButtonID == buttonID) { buttonExists = true; break; } } } if (!buttonExists) { Utility.PushColor(ConsoleColor.Red); state.WriteConsole("Invalid gump response, disconnecting..."); Utility.PopColor(); state.Dispose(); return; } var switchCount = pvSrc.ReadInt32(); if (switchCount < 0 || switchCount > gump.m_Switches) { Utility.PushColor(ConsoleColor.Red); state.WriteConsole("Invalid gump response, disconnecting..."); Utility.PopColor(); state.Dispose(); return; } var switches = new int[switchCount]; for (var j = 0; j < switches.Length; ++j) { switches[j] = pvSrc.ReadInt32(); } var textCount = pvSrc.ReadInt32(); if (textCount < 0 || textCount > gump.m_TextEntries) { Utility.PushColor(ConsoleColor.Red); state.WriteConsole("Invalid gump response, disconnecting..."); Utility.PopColor(); state.Dispose(); return; } var textEntries = new TextRelay[textCount]; for (var j = 0; j < textEntries.Length; ++j) { int entryID = pvSrc.ReadUInt16(); int textLength = pvSrc.ReadUInt16(); if (textLength > 239) { Utility.PushColor(ConsoleColor.Red); state.WriteConsole("Invalid gump response, disconnecting..."); Utility.PopColor(); state.Dispose(); return; } var text = pvSrc.ReadUnicodeStringSafe(textLength); textEntries[j] = new TextRelay(entryID, text); } state.RemoveGump(gump); var prof = GumpProfile.Acquire(gump.GetType()); if (prof != null) { prof.Start(); } gump.OnResponse(state, new RelayInfo(buttonID, switches, textEntries)); if (prof != null) { prof.Finish(); } return; } } if (typeID == 461) { // Virtue gump var switchCount = pvSrc.ReadInt32(); if (buttonID == 1 && switchCount > 0) { var beheld = World.FindMobile(pvSrc.ReadInt32()); if (beheld != null) { EventSink.InvokeVirtueGumpRequest(new VirtueGumpRequestEventArgs(state.Mobile, beheld)); } } else { var beheld = World.FindMobile(serial); if (beheld != null) { EventSink.InvokeVirtueItemRequest(new VirtueItemRequestEventArgs(state.Mobile, beheld, buttonID)); } } } } public static void SetWarMode(NetState state, PacketReader pvSrc) { if (state.Mobile.IsStaff() || Core.TickCount - state.Mobile.NextActionTime >= 0) { state.Mobile.DelayChangeWarmode(pvSrc.ReadBoolean()); } else { state.Mobile.SendActionMessage(); } } public static void Resynchronize(NetState state, PacketReader pvSrc) { var m = state.Mobile; state.Send(new MobileUpdate(m)); state.Send(MobileIncoming.Create(state, m, m)); m.SendEverything(); state.Sequence = 0; m.ClearFastwalkStack(); } private static readonly int[] m_EmptyInts = new int[0]; public static void AsciiSpeech(NetState state, PacketReader pvSrc) { var from = state.Mobile; var type = (MessageType)pvSrc.ReadByte(); int hue = pvSrc.ReadInt16(); pvSrc.ReadInt16(); // font var text = pvSrc.ReadStringSafe().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)); } private static readonly KeywordList m_KeywordList = new KeywordList(); public static void UnicodeSpeech(NetState state, PacketReader pvSrc) { var from = state.Mobile; var type = (MessageType)pvSrc.ReadByte(); int hue = pvSrc.ReadInt16(); pvSrc.ReadInt16(); // font var lang = pvSrc.ReadString(4); string text; var isEncoded = (type & MessageType.Encoded) != 0; int[] keywords; if (isEncoded) { int value = pvSrc.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 |= pvSrc.ReadByte(); speechID = hold; hold = 0; } else { value = pvSrc.ReadInt16(); speechID = (value & 0xFFF0) >> 4; hold = value & 0xF; } if (!keyList.Contains(speechID)) { keyList.Add(speechID); } } text = pvSrc.ReadUTF8StringSafe(); keywords = keyList.ToArray(); } else { text = pvSrc.ReadUnicodeStringSafe(); 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, PacketReader pvSrc) { var from = state.Mobile; if (from.IsStaff() || Core.TickCount - from.NextActionTime >= 0) { var value = pvSrc.ReadInt32(); if ((value & ~0x7FFFFFFF) != 0) { from.OnPaperdollRequest(); } else { Serial s = value; if (s.IsMobile) { var m = World.FindMobile(s); if (m != null && !m.Deleted) { from.Use(m); } } else if (s.IsItem) { var item = World.FindItem(s); if (item != null && !item.Deleted) { from.Use(item); } } } from.NextActionTime = Core.TickCount + Mobile.ActionDelay; } else { from.SendActionMessage(); } } private static void HandleSingleClick(Mobile m, IEntity target) { if (m == null || target == null || target.Deleted || !m.CanSee(target)) { return; } if (target is Item ti) { if (Utility.InUpdateRange(m.Location, ti.GetWorldLocation())) { ti.OnAosSingleClick(m); } } else if (target is Mobile tm) { if (Utility.InUpdateRange(m, tm)) { tm.OnAosSingleClick(m); } } } public static void LookReq(NetState state, PacketReader pvSrc) { if (state.Mobile != null) { HandleSingleClick(state.Mobile, World.FindEntity(pvSrc.ReadInt32())); } } public static void PingReq(NetState state, PacketReader pvSrc) { state.Send(PingAck.Instantiate(pvSrc.ReadByte())); } public static void SetUpdateRange(NetState state, PacketReader pvSrc) { // min max default /* 640x480 5 18 15 * 800x600 5 18 18 * 1024x768 5 24 24 * 1152x864 5 24 24 * 1280x720 5 24 24 */ int range = pvSrc.ReadByte(); // Don't let range drop below the minimum standard. range = Math.Max(Core.GlobalUpdateRange, range); var old = state.UpdateRange; if (old == range) { return; } state.UpdateRange = range; state.Send(ChangeUpdateRange.Instantiate(state.UpdateRange)); if (state.Mobile != null) { state.Mobile.OnUpdateRangeChanged(old, state.UpdateRange); } } private const int BadFood = unchecked((int)0xBAADF00D); private const int BadUOTD = unchecked((int)0xFFCEFFCE); public static void MovementReq(NetState state, PacketReader pvSrc) { var dir = (Direction)pvSrc.ReadByte(); int seq = pvSrc.ReadByte(); var key = pvSrc.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 int[] m_ValidAnimations = { 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 int[] ValidAnimations { get => m_ValidAnimations; set => m_ValidAnimations = value; } public static void Animate(NetState state, PacketReader pvSrc) { var from = state.Mobile; var action = pvSrc.ReadInt32(); var ok = false; for (var i = 0; !ok && i < m_ValidAnimations.Length; ++i) { ok = action == m_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, PacketReader pvSrc) { var rightClick = pvSrc.ReadBoolean(); var from = state.Mobile; if (from != null && from.QuestArrow != null) { from.QuestArrow.OnClick(rightClick); } } public static void ExtendedCommand(NetState state, PacketReader pvSrc) { int packetID = pvSrc.ReadUInt16(); var ph = GetExtendedHandler(packetID); if (ph != null) { if (ph.Ingame && state.Mobile == null) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Client: {0}: Packet (0xBF.0x{1:X2}) Requires State Mobile", state, packetID); Utility.PopColor(); state.Dispose(); } else if (ph.Ingame && state.Mobile.Deleted) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Client: {0}: Packet (0xBF.0x{1:X2}) Ivalid State Mobile", state, packetID); Utility.PopColor(); state.Dispose(); } else { ph.OnReceive(state, pvSrc); } } else { pvSrc.Trace(state); } } public static void CastSpell(NetState state, PacketReader pvSrc) { var from = state.Mobile; if (from == null) { return; } Item spellbook = null; if (pvSrc.ReadInt16() == 1) { spellbook = World.FindItem(pvSrc.ReadInt32()); } var spellID = pvSrc.ReadInt16() - 1; EventSink.InvokeCastSpellRequest(new CastSpellRequestEventArgs(from, spellID, spellbook)); } public static void BandageTarget(NetState state, PacketReader pvSrc) { var from = state.Mobile; if (from == null) { return; } if (from.IsStaff() || Core.TickCount - from.NextActionTime >= 0) { var bandage = World.FindItem(pvSrc.ReadInt32()); if (bandage == null) { return; } var target = World.FindMobile(pvSrc.ReadInt32()); if (target == null) { return; } EventSink.InvokeBandageTargetRequest(new BandageTargetRequestEventArgs(from, bandage, target)); from.NextActionTime = Core.TickCount + Mobile.ActionDelay; } else { from.SendActionMessage(); } } #region Stygain Abyss public static void ToggleFlying(NetState state, PacketReader pvSrc) { state.Mobile.ToggleFlying(); } #endregion public static void BatchQueryProperties(NetState state, PacketReader pvSrc) { if (state == null || state.Mobile == null) { return; } var from = state.Mobile; var length = pvSrc.Size - 3; if (length < 0 || (length % 4) != 0) { return; } var count = length / 4; for (var i = 0; i < count; ++i) { Serial s = pvSrc.ReadInt32(); if (s.IsMobile) { var m = World.FindMobile(s); if (m != null && from.CanSee(m) && from.InUpdateRange(m)) { m.SendPropertiesTo(from); } } else if (s.IsItem) { var item = World.FindItem(s); if (item != null && !item.Deleted && from.CanSee(item) && from.InUpdateRange(item.GetWorldLocation())) { item.SendPropertiesTo(from); } } } } public static void QueryProperties(NetState state, PacketReader pvSrc) { if (state == null || state.Mobile == null) { return; } var from = state.Mobile; Serial s = pvSrc.ReadInt32(); if (s.IsMobile) { var m = World.FindMobile(s); if (m != null && from.CanSee(m) && from.InUpdateRange(m)) { m.SendPropertiesTo(from); } } else if (s.IsItem) { var item = World.FindItem(s); if (item != null && !item.Deleted && from.CanSee(item) && from.InUpdateRange(item.GetWorldLocation())) { item.SendPropertiesTo(from); } } } public static void PartyMessage(NetState state, PacketReader pvSrc) { if (state.Mobile == null) { return; } switch (pvSrc.ReadByte()) { case 0x01: PartyMessage_AddMember(state, pvSrc); break; case 0x02: PartyMessage_RemoveMember(state, pvSrc); break; case 0x03: PartyMessage_PrivateMessage(state, pvSrc); break; case 0x04: PartyMessage_PublicMessage(state, pvSrc); break; case 0x06: PartyMessage_SetCanLoot(state, pvSrc); break; case 0x08: PartyMessage_Accept(state, pvSrc); break; case 0x09: PartyMessage_Decline(state, pvSrc); break; default: pvSrc.Trace(state); break; } } public static void PartyMessage_AddMember(NetState state, PacketReader pvSrc) { if (PartyCommands.Handler != null) { PartyCommands.Handler.OnAdd(state.Mobile); } } public static void PartyMessage_RemoveMember(NetState state, PacketReader pvSrc) { if (PartyCommands.Handler != null) { PartyCommands.Handler.OnRemove(state.Mobile, World.FindMobile(pvSrc.ReadInt32())); } } public static void PartyMessage_PrivateMessage(NetState state, PacketReader pvSrc) { if (PartyCommands.Handler != null) { PartyCommands.Handler.OnPrivateMessage( state.Mobile, World.FindMobile(pvSrc.ReadInt32()), pvSrc.ReadUnicodeStringSafe()); } } public static void PartyMessage_PublicMessage(NetState state, PacketReader pvSrc) { if (PartyCommands.Handler != null) { PartyCommands.Handler.OnPublicMessage(state.Mobile, pvSrc.ReadUnicodeStringSafe()); } } public static void PartyMessage_SetCanLoot(NetState state, PacketReader pvSrc) { if (PartyCommands.Handler != null) { PartyCommands.Handler.OnSetCanLoot(state.Mobile, pvSrc.ReadBoolean()); } } public static void PartyMessage_Accept(NetState state, PacketReader pvSrc) { if (PartyCommands.Handler != null) { PartyCommands.Handler.OnAccept(state.Mobile, World.FindMobile(pvSrc.ReadInt32())); } } public static void PartyMessage_Decline(NetState state, PacketReader pvSrc) { if (PartyCommands.Handler != null) { PartyCommands.Handler.OnDecline(state.Mobile, World.FindMobile(pvSrc.ReadInt32())); } } public static void StunRequest(NetState state, PacketReader pvSrc) { EventSink.InvokeStunRequest(new StunRequestEventArgs(state.Mobile)); } public static void DisarmRequest(NetState state, PacketReader pvSrc) { EventSink.InvokeDisarmRequest(new DisarmRequestEventArgs(state.Mobile)); } public static void StatLockChange(NetState state, PacketReader pvSrc) { int stat = pvSrc.ReadByte(); int lockValue = pvSrc.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, PacketReader pvSrc) { var width = pvSrc.ReadInt32(); var unk = pvSrc.ReadInt32(); } public static void ContextMenuResponse(NetState state, PacketReader pvSrc) { var user = state.Mobile; if (user == null) { return; } using (var menu = user.ContextMenu) { user.ContextMenu = null; if (menu != null && user == menu.From) { var entity = World.FindEntity(pvSrc.ReadInt32()); if (entity != null && entity == menu.Target && user.CanSee(entity)) { Point3D p; if (entity is Mobile) { p = entity.Location; } else if (entity is Item) { p = ((Item)entity).GetWorldLocation(); } else { return; } int index = pvSrc.ReadUInt16(); if (state.IsEnhancedClient && index > 0x64) { index = menu.GetIndexEC(index); } if (index >= 0 && index < menu.Entries.Length) { using (var e = menu.Entries[index]) { var range = e.Range; if (range == -1) { if (user.NetState != null && user.NetState.UpdateRange > 0) { range = user.NetState.UpdateRange; } else { range = Core.GlobalUpdateRange; } } if (user.InRange(p, range)) { if (e.Enabled) { e.OnClick(); } else { e.OnClickDisabled(); } } } } } } } } public static void ContextMenuRequest(NetState state, PacketReader pvSrc) { var target = World.FindEntity(pvSrc.ReadInt32()); if (target != null) { ContextMenu.Display(state.Mobile, target); } } public static void CloseStatus(NetState state, PacketReader pvSrc) { Serial serial = pvSrc.ReadInt32(); } public static void Language(NetState state, PacketReader pvSrc) { var lang = pvSrc.ReadString(4); if (state.Mobile != null) { state.Mobile.Language = lang; } } public static void AssistVersion(NetState state, PacketReader pvSrc) { var unk = pvSrc.ReadInt32(); var av = pvSrc.ReadString(); } public static void ClientVersion(NetState state, PacketReader pvSrc) { var version = state.Version = new CV(pvSrc.ReadString()); EventSink.InvokeClientVersionReceived(new ClientVersionReceivedArgs(state, version)); } public static void ClientType(NetState state, PacketReader pvSrc) { pvSrc.ReadUInt16(); // 0x1 pvSrc.ReadUInt32(); // 0x2 for KR, 0x3 for EC EventSink.InvokeClientTypeReceived(new ClientTypeReceivedArgs(state)); } public static void MobileQuery(NetState state, PacketReader pvSrc) { var from = state.Mobile; pvSrc.ReadInt32(); // 0xEDEDEDED int type = pvSrc.ReadByte(); Serial serial = pvSrc.ReadInt32(); if (serial.IsMobile) { var m = World.FindMobile(serial); if (m != null) { switch (type) { case 0x00: // Unknown, sent by godclient { if (VerifyGC(state)) { Console.WriteLine("God Client: {0}: Query 0x{1:X2} on {2} '{3}'", state, type, serial, m.Name); } break; } case 0x04: // Stats { m.OnStatsQuery(from); break; } case 0x05: { m.OnSkillsQuery(from); break; } default: { pvSrc.Trace(state); break; } } } } else if (serial.IsItem) { var item = World.FindItem(serial) as IDamageable; if (item != null) { switch (type) { case 0x00: { if (VerifyGC(state)) { Console.WriteLine("God Client: {0}: Query 0x{1:X2} on {2} '{3}'", state, type, serial, item.Name); } break; } case 0x04: // Stats { item.OnStatsQuery(from); break; } case 0x05: { break; } default: { pvSrc.Trace(state); break; } } } } } public delegate void PlayCharCallback(NetState state, bool val); public static PlayCharCallback ThirdPartyAuthCallback = null, ThirdPartyHackedCallback = null; private static readonly byte[] m_ThirdPartyAuthKey = { 0x9, 0x11, 0x83, (byte)'+', 0x4, 0x17, 0x83, 0x5, 0x24, 0x85, 0x7, 0x17, 0x87, 0x6, 0x19, 0x88 }; private class LoginTimer : Timer { private NetState m_State; public LoginTimer(NetState state) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) { m_State = state; } protected override void OnTick() { if (m_State == null || !m_State.Running) { Stop(); m_State = null; } else if (m_State.Version != null) { Stop(); m_State.BlockAllPackets = false; DoLogin(m_State); m_State = null; } } } public static void PlayCharacter(NetState state, PacketReader pvSrc) { pvSrc.ReadInt32(); // 0xEDEDEDED var name = pvSrc.ReadString(30); pvSrc.Seek(2, SeekOrigin.Current); var flags = pvSrc.ReadInt32(); if (FeatureProtection.DisabledFeatures != 0 && ThirdPartyAuthCallback != null) { var authOK = false; var razorFeatures = (((ulong)pvSrc.ReadUInt32()) << 32) | pvSrc.ReadUInt32(); if (razorFeatures == (ulong)FeatureProtection.DisabledFeatures) { var match = true; for (var i = 0; match && i < m_ThirdPartyAuthKey.Length; i++) { match = pvSrc.ReadByte() == m_ThirdPartyAuthKey[i]; } if (match) { authOK = true; } } else { pvSrc.Seek(16, SeekOrigin.Current); } ThirdPartyAuthCallback(state, authOK); } else { pvSrc.Seek(24, SeekOrigin.Current); } if (ThirdPartyHackedCallback != null) { pvSrc.Seek(-2, SeekOrigin.Current); if (pvSrc.ReadUInt16() == 0xDEAD) { ThirdPartyHackedCallback(state, true); } } if (!state.Running) { return; } var charSlot = pvSrc.ReadInt32(); var clientIP = pvSrc.ReadInt32(); var a = state.Account; if (a == null || charSlot < 0 || charSlot >= a.Length) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Invalid Character Selection.", state); Utility.PopColor(); 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) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Account In Use", state); Utility.PopColor(); state.Send(new PopupMessage(PMMessage.CharInWorld)); return; } } if (m == null) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Invalid Character Selection.", state); Utility.PopColor(); state.Dispose(); } else { if (m.NetState != null) { m.NetState.Dispose(); } NetState.ProcessDisposedQueue(); state.Flags = (ClientFlags)flags; state.Mobile = m; m.NetState = state; if (state.Version == null) { state.Send(new ClientVersionReq()); state.BlockAllPackets = true; new LoginTimer(state).Start(); } else { DoLogin(state); } } } } public static void DoLogin(NetState state) { var m = state.Mobile; state.Send(new LoginConfirm(m)); if (m.Map != null) { state.Send(new MapChange(m)); } state.Send(new MapPatches()); state.Send(SupportedFeatures.Instantiate(state)); state.Sequence = 0; state.Send(MobileIncoming.Create(state, m, m)); state.Send(new MobileUpdate(m)); m.SendEverything(); m.CheckLightLevels(true); state.Send(LoginComplete.Instance); state.Send(MobileIncoming.Create(state, m, m)); state.Send(new MobileStatus(m, m)); state.Send(Network.SetWarMode.Instantiate(m.Warmode)); state.Send(SeasonChange.Instantiate(m.GetSeason(), true)); state.Send(new CurrentTime()); state.Send(new MapChange(m)); EventSink.InvokeLogin(new LoginEventArgs(m)); Console.WriteLine("Client: {0}: Entered World ({1})", state, m); m.SendEverything(); m.ClearFastwalkStack(); } public static void CreateCharacter(NetState state, PacketReader pvSrc) { var unk1 = pvSrc.ReadInt32(); var unk2 = pvSrc.ReadInt32(); int unk3 = pvSrc.ReadByte(); var name = pvSrc.ReadString(30); pvSrc.Seek(2, SeekOrigin.Current); var flags = pvSrc.ReadInt32(); pvSrc.Seek(8, SeekOrigin.Current); int prof = pvSrc.ReadByte(); pvSrc.Seek(15, SeekOrigin.Current); //bool female = pvSrc.ReadBoolean(); int genderRace = pvSrc.ReadByte(); int str = pvSrc.ReadByte(); int dex = pvSrc.ReadByte(); int intl = pvSrc.ReadByte(); int is1 = pvSrc.ReadByte(); int vs1 = pvSrc.ReadByte(); int is2 = pvSrc.ReadByte(); int vs2 = pvSrc.ReadByte(); int is3 = pvSrc.ReadByte(); int vs3 = pvSrc.ReadByte(); int hue = pvSrc.ReadUInt16(); int hairVal = pvSrc.ReadInt16(); int hairHue = pvSrc.ReadInt16(); int hairValf = pvSrc.ReadInt16(); int hairHuef = pvSrc.ReadInt16(); pvSrc.ReadByte(); int cityIndex = pvSrc.ReadByte(); var charSlot = pvSrc.ReadInt32(); var clientIP = pvSrc.ReadInt32(); int shirtHue = pvSrc.ReadInt16(); int pantsHue = pvSrc.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 = (byte)(genderRace < 4 ? 0 : ((genderRace / 2) - 1)); var race = Race.Races[raceID]; if (race == null) { race = Race.DefaultRace; } hue = race.ClipSkinHue(hue); hairHue = race.ClipHairHue(hairHue); hairHuef = race.ClipHairHue(hairHuef); shirtHue = Math.Max(0, Math.Min(1000, shirtHue)); pantsHue = Math.Max(0, Math.Min(1000, pantsHue)); 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) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Account in use", state); Utility.PopColor(); state.Send(new PopupMessage(PMMessage.CharInWorld)); return; } } state.Flags = (ClientFlags)flags; var args = new CharacterCreatedEventArgs( state, a, name, female, hue, str, dex, intl, info[cityIndex], new SkillNameValue[3] { new SkillNameValue((SkillName)is1, vs1), new SkillNameValue((SkillName)is2, vs2), new SkillNameValue((SkillName)is3, vs3), }, shirtHue, pantsHue, hairVal, hairHue, hairValf, hairHuef, prof, race); if (state.Version == null) { state.Send(new ClientVersionReq()); state.BlockAllPackets = true; } EventSink.InvokeCharacterCreated(args); var m = args.Mobile; if (m != null) { state.Mobile = m; m.NetState = state; if (state.Version == null) { new LoginTimer(state).Start(); } else { DoLogin(state); } } else { state.BlockAllPackets = false; state.Dispose(); } } } public static void CreateCharacter70160(NetState state, PacketReader pvSrc) { var unk1 = pvSrc.ReadInt32(); var unk2 = pvSrc.ReadInt32(); int unk3 = pvSrc.ReadByte(); var name = pvSrc.ReadString(30); pvSrc.Seek(2, SeekOrigin.Current); var flags = pvSrc.ReadInt32(); pvSrc.Seek(8, SeekOrigin.Current); int prof = pvSrc.ReadByte(); pvSrc.Seek(15, SeekOrigin.Current); int genderRace = pvSrc.ReadByte(); int str = pvSrc.ReadByte(); int dex = pvSrc.ReadByte(); int intl = pvSrc.ReadByte(); int is1 = pvSrc.ReadByte(); int vs1 = pvSrc.ReadByte(); int is2 = pvSrc.ReadByte(); int vs2 = pvSrc.ReadByte(); int is3 = pvSrc.ReadByte(); int vs3 = pvSrc.ReadByte(); int is4 = pvSrc.ReadByte(); int vs4 = pvSrc.ReadByte(); int hue = pvSrc.ReadUInt16(); int hairVal = pvSrc.ReadInt16(); int hairHue = pvSrc.ReadInt16(); int hairValf = pvSrc.ReadInt16(); int hairHuef = pvSrc.ReadInt16(); pvSrc.ReadByte(); int cityIndex = pvSrc.ReadByte(); var charSlot = pvSrc.ReadInt32(); var clientIP = pvSrc.ReadInt32(); int shirtHue = pvSrc.ReadInt16(); int pantsHue = pvSrc.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; var raceID = (byte)(genderRace < 4 ? 0 : ((genderRace / 2) - 1)); var race = Race.Races[raceID]; if (race == null) { race = Race.DefaultRace; } hue = race.ClipSkinHue(hue); hairHue = race.ClipHairHue(hairHue); hairHuef = race.ClipHairHue(hairHuef); shirtHue = Math.Max(0, Math.Min(1000, shirtHue)); pantsHue = Math.Max(0, Math.Min(1000, pantsHue)); 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) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Account in use", state); Utility.PopColor(); state.Send(new PopupMessage(PMMessage.CharInWorld)); return; } } state.Flags = (ClientFlags)flags; var args = new CharacterCreatedEventArgs( state, a, name, female, hue, str, dex, intl, info[cityIndex], new SkillNameValue[4] { 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); if (state.Version == null) { state.Send(new ClientVersionReq()); state.BlockAllPackets = true; } EventSink.InvokeCharacterCreated(args); var m = args.Mobile; if (m != null) { state.Mobile = m; m.NetState = state; if (state.Version == null) { new LoginTimer(state).Start(); } else { DoLogin(state); } } else { state.BlockAllPackets = false; state.Dispose(); } } } public static void PublicHouseContent(NetState state, PacketReader pvSrc) { int value = pvSrc.ReadByte(); state.Mobile.PublicHouseContent = Convert.ToBoolean(value); } private static bool m_ClientVerification = true; public static bool ClientVerification { get => m_ClientVerification; set => m_ClientVerification = value; } internal struct AuthIDPersistence { public DateTime Age; public ClientVersion Version; public AuthIDPersistence(ClientVersion v) { Age = DateTime.UtcNow; Version = v; } } private const int m_AuthIDWindowSize = 128; private static readonly Dictionary m_AuthIDWindow = new Dictionary(m_AuthIDWindowSize); private static uint GenerateAuthID(NetState state) { if (m_AuthIDWindow.Count == m_AuthIDWindowSize) { uint 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); } uint authID; do { authID = (uint)Utility.RandomMinMax(1, UInt32.MaxValue - 1); if (Utility.RandomBool()) { authID |= 1U << 31; } } while (m_AuthIDWindow.ContainsKey(authID)); m_AuthIDWindow[authID] = new AuthIDPersistence(state.Version); return authID; } public static bool GetAuth(NetState state, out TimeSpan age, out ClientVersion version) { age = TimeSpan.Zero; version = null; if (m_AuthIDWindow.TryGetValue(state.AuthID, out var ap)) { age = DateTime.UtcNow - ap.Age; version = ap.Version; } return false; } public static void GameLogin(NetState state, PacketReader pvSrc) { if (state.SentFirstPacket) { state.Dispose(); return; } state.SentFirstPacket = true; var authID = pvSrc.ReadUInt32(); if (m_AuthIDWindow.ContainsKey(authID)) { var ap = m_AuthIDWindow[authID]; m_AuthIDWindow.Remove(authID); state.Version = ap.Version; } else if (m_ClientVerification) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Invalid Client", state); Utility.PopColor(); state.Dispose(); return; } if (state.AuthID != 0 && authID != state.AuthID) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Invalid Client", state); Utility.PopColor(); state.Dispose(); return; } if (state.AuthID == 0 && authID != state.Seed) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Invalid Client", state); Utility.PopColor(); state.Dispose(); return; } var username = pvSrc.ReadString(30); var password = pvSrc.ReadString(30); var e = new GameLoginEventArgs(state, username, password); EventSink.InvokeGameLogin(e); if (e.Accepted) { state.CityInfo = e.CityInfo; state.CompressionEnabled = true; state.Send(SupportedFeatures.Instantiate(state)); state.Send(new CharacterList(state.Account, state.CityInfo, state.IsEnhancedClient)); } else { state.Dispose(); } } public static void PlayServer(NetState state, PacketReader pvSrc) { int index = pvSrc.ReadInt16(); var info = state.ServerInfo; var a = state.Account; if (info == null || a == null || index < 0 || index >= info.Length) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Client: {0}: Invalid Server ({1})", state, index); Utility.PopColor(); state.Dispose(); } else { state.AuthID = GenerateAuthID(state); state.SentFirstPacket = false; state.Send(new PlayServerAck(info[index], state.AuthID)); } } public static void LoginServerSeed(NetState state, PacketReader pvSrc) { state.Seed = pvSrc.ReadUInt32(); state.Seeded = true; if (state.Seed == 0) { Utility.PushColor(ConsoleColor.Red); Console.WriteLine("Login: {0}: Invalid Client", state); Utility.PopColor(); state.Dispose(); return; } var clientMaj = pvSrc.ReadInt32(); var clientMin = pvSrc.ReadInt32(); var clientRev = pvSrc.ReadInt32(); var clientPat = pvSrc.ReadInt32(); state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat); } public static void CrashReport(NetState state, PacketReader pvSrc) { var clientMaj = pvSrc.ReadByte(); var clientMin = pvSrc.ReadByte(); var clientRev = pvSrc.ReadByte(); var clientPat = pvSrc.ReadByte(); var x = pvSrc.ReadUInt16(); var y = pvSrc.ReadUInt16(); var z = pvSrc.ReadSByte(); var map = pvSrc.ReadByte(); var account = pvSrc.ReadString(32); var character = pvSrc.ReadString(32); var ip = pvSrc.ReadString(15); var unk1 = pvSrc.ReadInt32(); var exception = pvSrc.ReadInt32(); var process = pvSrc.ReadString(100); var report = pvSrc.ReadString(100); pvSrc.ReadByte(); // 0x00 var offset = pvSrc.ReadInt32(); int count = pvSrc.ReadByte(); for (var i = 0; i < count; i++) { var address = pvSrc.ReadInt32(); } } public static void AccountLogin(NetState state, PacketReader pvSrc) { if (state.SentFirstPacket) { state.Dispose(); return; } state.SentFirstPacket = true; var username = pvSrc.ReadString(30); var password = pvSrc.ReadString(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; state.Send(new AccountLoginRej(ALRReason.BadComm)); state.Dispose(); } else { var info = e.Servers.ToArray(); state.ServerInfo = info; state.Send(new AccountLoginAck(info)); } } public static void AccountLogin_ReplyRej(NetState state, ALRReason reason) { state.Send(new AccountLoginRej(reason)); state.Dispose(); } // EC Client Character Creation public static void ECCreateCharacter(NetState state, PacketReader pvSrc) { var length = pvSrc.Size; var unk1 = pvSrc.ReadInt32(); // Pattern var charSlot = pvSrc.ReadInt32(); var name = pvSrc.ReadString(30); var unknown1 = pvSrc.ReadString(30); // "Unknow" int profession = pvSrc.ReadByte(); int cityIndex = pvSrc.ReadByte(); int gender = pvSrc.ReadByte(); int genderRace = pvSrc.ReadByte(); int str = pvSrc.ReadByte(); int dex = pvSrc.ReadByte(); int intel = pvSrc.ReadByte(); int hue = pvSrc.ReadInt16(); var unk5 = pvSrc.ReadInt32(); // 0x00 0x00 0x00 0x00 var unk6 = pvSrc.ReadInt32(); // 0x00 0x00 0x00 0x00 // isX = skill amount | vsX = skill int is1 = pvSrc.ReadByte(); int vs1 = pvSrc.ReadByte(); int is2 = pvSrc.ReadByte(); int vs2 = pvSrc.ReadByte(); int is3 = pvSrc.ReadByte(); int vs3 = pvSrc.ReadByte(); int is4 = pvSrc.ReadByte(); int vs4 = pvSrc.ReadByte(); var unknown2 = pvSrc.ReadString(25); // Pack of 0x00 int unk7 = pvSrc.ReadByte(); // Another 0x00 int hairColor = pvSrc.ReadInt16(); int hairID = pvSrc.ReadInt16(); int unk8 = pvSrc.ReadByte(); var unk9 = pvSrc.ReadInt32(); int unk10 = pvSrc.ReadByte(); int shirtHue = pvSrc.ReadInt16(); int shirtID = pvSrc.ReadInt16(); int unk13 = pvSrc.ReadByte(); int faceColor = pvSrc.ReadInt16(); int faceID = pvSrc.ReadInt16(); int unk14 = pvSrc.ReadByte(); int beardColor = pvSrc.ReadInt16(); int beardID = pvSrc.ReadInt16(); var pantsHue = shirtHue; // Obsolete var female = gender != 0; var race = Race.Races[(byte)(genderRace - 1)]; if (race == null) 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) { Console.WriteLine("Login: {0}: Account in use", state); state.Send(new PopupMessage(PMMessage.CharInWorld)); return; } } var args = new CharacterCreatedEventArgs( state, a, name, female, hue, str, dex, intel, info[cityIndex], new SkillNameValue[4] { new SkillNameValue( (SkillName)is1, vs1 ), new SkillNameValue( (SkillName)is2, vs2 ), new SkillNameValue( (SkillName)is3, vs3 ), new SkillNameValue( (SkillName)is4, vs4 ), }, shirtHue, pantsHue, hairID, hairColor, beardID, beardColor, profession, race, faceID, faceColor ); if (state.Version == null) { state.Send(new ClientVersionReq()); state.BlockAllPackets = true; } EventSink.InvokeCharacterCreated(args); var m = args.Mobile; if (m != null) { state.Mobile = m; m.NetState = state; if (state.Version == null) { new LoginTimer(state).Start(); } else { DoLogin(state); } } else { state.BlockAllPackets = false; state.Dispose(); } } } public static void EquipMacro(NetState ns, PacketReader pvSrc) { var length = pvSrc.Size; int count = pvSrc.ReadByte(); var serialList = new List(count); for (var i = 0; i < count; ++i) { Serial s = pvSrc.ReadInt32(); serialList.Add(s); } EventSink.InvokeEquipMacro(new EquipMacroEventArgs(ns.Mobile, serialList)); } public static void UnequipMacro(NetState ns, PacketReader pvSrc) { var length = pvSrc.Size; int count = pvSrc.ReadByte(); var layers = new List(count); for (var i = 0; i < count; ++i) { int s = pvSrc.ReadInt16(); layers.Add(s); } EventSink.InvokeUnequipMacro(new UnequipMacroEventArgs(ns.Mobile, layers)); } public static void TargetedSpell(NetState ns, PacketReader pvSrc) { var spellId = (short)(pvSrc.ReadInt16() - 1); // zero based; Serial target = pvSrc.ReadInt32(); EventSink.InvokeTargetedSpell(new TargetedSpellEventArgs(ns.Mobile, World.FindEntity(target), spellId)); } public static void TargetedSkillUse(NetState ns, PacketReader pvSrc) { var skillId = pvSrc.ReadInt16(); Serial target = pvSrc.ReadInt32(); EventSink.InvokeTargetedSkill(new TargetedSkillEventArgs(ns.Mobile, World.FindEntity(target), skillId)); } public static void TargetByResourceMacro(NetState ns, PacketReader pvSrc) { Serial serial = pvSrc.ReadInt32(); int resourcetype = pvSrc.ReadInt16(); if (serial.IsItem) { EventSink.InvokeTargetByResourceMacro(new TargetByResourceMacroEventArgs(ns.Mobile, World.FindItem(serial), resourcetype)); } } } }