diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 8531c5a86..8dc75c17c 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -4139,7 +4139,7 @@ namespace Server var ns = from.NetState; - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/Server/Items/VirtualHair.cs b/Projects/Server/Items/VirtualHair.cs index ba09bedf1..91debb93b 100644 --- a/Projects/Server/Items/VirtualHair.cs +++ b/Projects/Server/Items/VirtualHair.cs @@ -12,7 +12,7 @@ namespace Server public static void SendHairEquipUpdatePacket(this NetState ns, Mobile m, uint hairSerial, int itemId, int hue, Layer layer) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -42,7 +42,7 @@ namespace Server public static void SendRemoveHairPacket(this NetState ns, uint hairSerial) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 3a7803cb9..c94b62b9c 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -8317,7 +8317,7 @@ namespace Server { var ns = m_NetState; - if (ns == null) + if (ns.CannotSendPackets()) { return false; } diff --git a/Projects/Server/Network/NetState/NetState.cs b/Projects/Server/Network/NetState/NetState.cs index b1916bf7b..7adfa951e 100755 --- a/Projects/Server/Network/NetState/NetState.cs +++ b/Projects/Server/Network/NetState/NetState.cs @@ -481,7 +481,7 @@ namespace Server.Network public void Send(ReadOnlySpan span) { - if (span == null || Connection == null || BlockAllPackets) + if (span == null || this.CannotSendPackets()) { return; } diff --git a/Projects/Server/Network/Packets/IncomingAccountPackets.cs b/Projects/Server/Network/Packets/IncomingAccountPackets.cs index b7f1eebb5..41bf8919d 100644 --- a/Projects/Server/Network/Packets/IncomingAccountPackets.cs +++ b/Projects/Server/Network/Packets/IncomingAccountPackets.cs @@ -18,526 +18,525 @@ using System.Collections.Generic; using System.IO; using CV = Server.ClientVersion; -namespace Server.Network +namespace Server.Network; + +public static class IncomingAccountPackets { - public static class IncomingAccountPackets + private const int m_AuthIDWindowSize = 128; + private static readonly Dictionary m_AuthIDWindow = + new(m_AuthIDWindowSize); + + internal struct AuthIDPersistence { - private const int m_AuthIDWindowSize = 128; - private static readonly Dictionary m_AuthIDWindow = - new(m_AuthIDWindowSize); + public DateTime Age; + public readonly ClientVersion Version; - internal struct AuthIDPersistence + public AuthIDPersistence(ClientVersion v) { - public DateTime Age; - public readonly ClientVersion Version; + Age = Core.Now; + Version = v; + } + } - public AuthIDPersistence(ClientVersion v) - { - Age = Core.Now; - 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, ref int packetLength) + { + reader.Seek(9, SeekOrigin.Current); + /* + 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(); + + var stats = new StatNameValue[] + { + new(StatType.Str, reader.ReadByte()), + new(StatType.Dex, reader.ReadByte()), + new(StatType.Int, reader.ReadByte()) + }; + + var skills = new SkillNameValue[state.NewCharacterCreation ? 4 : 3]; + skills[0] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); + skills[1] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); + skills[2] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); + if (state.NewCharacterCreation) + { + skills[3] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); } - public static void Configure() + 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(); + reader.Seek(8, SeekOrigin.Current); + /* + 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) { - 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); + state.Disconnect("Invalid city selected during character creation."); + return; } - public static void CreateCharacter(NetState state, CircularBufferReader reader, ref int packetLength) + // Check if anyone is using this account + for (var i = 0; i < a.Length; ++i) { - reader.Seek(9, SeekOrigin.Current); - /* - var unk1 = reader.ReadInt32(); - var unk2 = reader.ReadInt32(); - int unk3 = reader.ReadByte(); - */ - var name = reader.ReadAscii(30); + var check = a[i]; - 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(); - - var stats = new StatNameValue[] + if (check != null && check.Map != Map.Internal) { - new(StatType.Str, reader.ReadByte()), - new(StatType.Dex, reader.ReadByte()), - new(StatType.Int, reader.ReadByte()) - }; - - var skills = new SkillNameValue[state.NewCharacterCreation ? 4 : 3]; - skills[0] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); - skills[1] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); - skills[2] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte()); - if (state.NewCharacterCreation) - { - skills[3] = new SkillNameValue((SkillName)reader.ReadByte(), 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(); - reader.Seek(8, SeekOrigin.Current); - /* - 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.Disconnect("Invalid city selected during character creation."); + state.LogInfo("Account in use"); + state.SendPopupMessage(PMMessage.CharInWorld); return; } - - // 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.LogInfo("Account in use"); - state.SendPopupMessage(PMMessage.CharInWorld); - return; - } - } - - state.Flags = (ClientFlags)flags; - - var args = new CharacterCreatedEventArgs( - state, - a, - name, - female, - hue, - stats, - 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.Disconnect("Character creation blocked."); - } } - public static void DeleteCharacter(NetState state, CircularBufferReader reader, ref int packetLength) + state.Flags = (ClientFlags)flags; + + var args = new CharacterCreatedEventArgs( + state, + a, + name, + female, + hue, + stats, + 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) { - reader.Seek(30, SeekOrigin.Current); - var index = reader.ReadInt32(); - - EventSink.InvokeDeleteRequest(state, index); - } - - public static void AccountID(NetState state, CircularBufferReader reader, ref int packetLength) - { - } - - public static void AssistVersion(NetState state, CircularBufferReader reader, ref int packetLength) - { - var unk = reader.ReadInt32(); - var av = reader.ReadAscii(); - } - - public static void ClientVersion(NetState state, CircularBufferReader reader, ref int packetLength) - { - var version = state.Version = new CV(reader.ReadAscii()); - - EventSink.InvokeClientVersionReceived(state, version); - } - - public static void ClientType(NetState state, CircularBufferReader reader, ref int packetLength) - { - 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, ref int packetLength) - { - reader.Seek(4, SeekOrigin.Current); // 0xEDEDEDED - - reader.Seek(30, SeekOrigin.Current); // var name = reader.ReadAscii(30); - - reader.Seek(2, SeekOrigin.Current); - - var flags = reader.ReadInt32(); - - reader.Seek(24, SeekOrigin.Current); - - var charSlot = reader.ReadInt32(); - reader.Seek(4, SeekOrigin.Current); // var clientIP = reader.ReadInt32(); - - var a = state.Account; - - if (a == null || charSlot < 0 || charSlot >= a.Length) - { - state.Disconnect("Invalid character slot selected."); - return; - } - - 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.LogInfo("Account in use"); - state.SendPopupMessage(PMMessage.CharInWorld); - return; - } - } - - if (m == null) - { - state.Disconnect("Empty character slot selected."); - return; - } - - m.NetState?.Disconnect("Character selected for a player already logged in."); - - 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) + else { - state.SendLoginConfirmation(m); + state.BlockAllPackets = false; + state.Disconnect("Character creation blocked."); + } + } - state.SendMapChange(m.Map); + public static void DeleteCharacter(NetState state, CircularBufferReader reader, ref int packetLength) + { + reader.Seek(30, SeekOrigin.Current); + var index = reader.ReadInt32(); - state.SendMapPatches(); + EventSink.InvokeDeleteRequest(state, index); + } - state.SendSeasonChange((byte)m.GetSeason(), true); + public static void AccountID(NetState state, CircularBufferReader reader, ref int packetLength) + { + } + + public static void AssistVersion(NetState state, CircularBufferReader reader, ref int packetLength) + { + var unk = reader.ReadInt32(); + var av = reader.ReadAscii(); + } + + public static void ClientVersion(NetState state, CircularBufferReader reader, ref int packetLength) + { + var version = state.Version = new CV(reader.ReadAscii()); + + EventSink.InvokeClientVersionReceived(state, version); + } + + public static void ClientType(NetState state, CircularBufferReader reader, ref int packetLength) + { + 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, ref int packetLength) + { + reader.Seek(4, SeekOrigin.Current); // 0xEDEDEDED + + reader.Seek(30, SeekOrigin.Current); // var name = reader.ReadAscii(30); + + reader.Seek(2, SeekOrigin.Current); + + var flags = reader.ReadInt32(); + + reader.Seek(24, SeekOrigin.Current); + + var charSlot = reader.ReadInt32(); + reader.Seek(4, SeekOrigin.Current); // var clientIP = reader.ReadInt32(); + + var a = state.Account; + + if (a == null || charSlot < 0 || charSlot >= a.Length) + { + state.Disconnect("Invalid character slot selected."); + return; + } + + 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.LogInfo("Account in use"); + state.SendPopupMessage(PMMessage.CharInWorld); + return; + } + } + + if (m == null) + { + state.Disconnect("Empty character slot selected."); + return; + } + + m.NetState?.Disconnect("Character selected for a player already logged in."); + + 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); + + state.SendMapChange(m.Map); + + state.SendMapPatches(); + + state.SendSeasonChange((byte)m.GetSeason(), true); + + state.SendSupportedFeature(); + + state.Sequence = 0; + + state.SendMobileUpdate(m); + state.SendMobileUpdate(m); + + m.CheckLightLevels(true); + + state.SendMobileUpdate(m); + + state.SendMobileIncoming(m, m); + + state.SendMobileStatus(m); + state.SendSetWarMode(m.Warmode); + + m.SendEverything(); + + state.SendSupportedFeature(); + state.SendMobileUpdate(m); + + state.SendMobileStatus(m); + state.SendSetWarMode(m.Warmode); + state.SendMobileIncoming(m, m); + + state.SendLoginComplete(); + state.SendCurrentTime(); + state.SendSeasonChange((byte)m.GetSeason(), true); + state.SendMapChange(m.Map); + + state.SendPlayMusic(m.Region.Music); + + EventSink.InvokeLogin(m); + } + + private static int GenerateAuthID(this NetState state) + { + if (m_AuthIDWindow.Count == m_AuthIDWindowSize) + { + var oldestID = 0; + var oldest = DateTime.MaxValue; + + foreach (var (key, authId) in m_AuthIDWindow) + { + if (authId.Age < oldest) + { + oldestID = key; + oldest = authId.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, ref int packetLength) + { + // TODO: Connection throttling + + if (state.SentFirstPacket) + { + state.Disconnect("Duplicate game login packet received."); + return; + } + + state.SentFirstPacket = true; + + var authID = reader.ReadInt32(); + + if (!m_AuthIDWindow.TryGetValue(authID, out var ap)) + { + state.LogInfo("Invalid client detected, disconnecting..."); + state.Disconnect("Unable to find auth id."); + } + + if (state._authId != 0 && authID != state._authId || state._authId == 0 && authID != state._seed) + { + state.LogInfo("Invalid client detected, disconnecting..."); + state.Disconnect("Invalid auth id in game login packet."); + 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; + + // Comment out these lines to turn off huffman compression + state.CompressionEnabled = true; + state.PacketEncoder ??= NetworkCompression.Compress; state.SendSupportedFeature(); + state.SendCharacterList(); + } + else + { + state.Disconnect("Login rejected by GameLogin packet handler."); + } + } - state.Sequence = 0; + public static void PlayServer(NetState state, CircularBufferReader reader, ref int packetLength) + { + int index = reader.ReadInt16(); + var info = state.ServerInfo; + var a = state.Account; - state.SendMobileUpdate(m); - state.SendMobileUpdate(m); + if (info == null || a == null || index < 0 || index >= info.Length) + { + state.Disconnect("Invalid server selected."); + } + else + { + var si = info[index]; - m.CheckLightLevels(true); + state._authId = GenerateAuthID(state); - state.SendMobileUpdate(m); + state.SentFirstPacket = false; + state.SendPlayServerAck(si, state._authId); + } + } - state.SendMobileIncoming(m, m); + public static void LoginServerSeed(NetState state, CircularBufferReader reader, ref int packetLength) + { + state._seed = reader.ReadInt32(); + state.Seeded = true; - state.SendMobileStatus(m); - state.SendSetWarMode(m.Warmode); - - m.SendEverything(); - - state.SendSupportedFeature(); - state.SendMobileUpdate(m); - - state.SendMobileStatus(m); - state.SendSetWarMode(m.Warmode); - state.SendMobileIncoming(m, m); - - state.SendLoginComplete(); - state.SendCurrentTime(); - state.SendSeasonChange((byte)m.GetSeason(), true); - state.SendMapChange(m.Map); - - state.SendPlayMusic(m.Region.Music); - - EventSink.InvokeLogin(m); + if (state._seed == 0) + { + state.LogInfo("Invalid client detected, disconnecting"); + state.Disconnect("Duplicate seed sent."); + return; } - private static int GenerateAuthID(this NetState state) + 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, ref int packetLength) + { + // TODO: Throttle Connection + + if (state.SentFirstPacket) { - if (m_AuthIDWindow.Count == m_AuthIDWindowSize) - { - var oldestID = 0; - var oldest = DateTime.MaxValue; - - foreach (var (key, authId) in m_AuthIDWindow) - { - if (authId.Age < oldest) - { - oldestID = key; - oldest = authId.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; + state.Disconnect("Duplicate account login packet sent."); + return; } - public static void GameLogin(NetState state, CircularBufferReader reader, ref int packetLength) + 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) { - // TODO: Connection throttling + var serverListEventArgs = new ServerListEventArgs(state, state.Account); - if (state.SentFirstPacket) + EventSink.InvokeServerList(serverListEventArgs); + + if (serverListEventArgs.Rejected) { - state.Disconnect("Duplicate game login packet received."); - return; - } - - state.SentFirstPacket = true; - - var authID = reader.ReadInt32(); - - if (!m_AuthIDWindow.TryGetValue(authID, out var ap)) - { - state.LogInfo("Invalid client detected, disconnecting..."); - state.Disconnect("Unable to find auth id."); - } - - if (state._authId != 0 && authID != state._authId || state._authId == 0 && authID != state._seed) - { - state.LogInfo("Invalid client detected, disconnecting..."); - state.Disconnect("Invalid auth id in game login packet."); - 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; - - // Comment out these lines to turn off huffman compression - state.CompressionEnabled = true; - state.PacketEncoder ??= NetworkCompression.Compress; - - state.SendSupportedFeature(); - state.SendCharacterList(); + state.Account = null; + AccountLogin_ReplyRej(state, ALRReason.BadComm); } else { - state.Disconnect("Login rejected by GameLogin packet handler."); + state.ServerInfo = serverListEventArgs.Servers.ToArray(); + state.SendAccountLoginAck(); } } - - public static void PlayServer(NetState state, CircularBufferReader reader, ref int packetLength) + else { - int index = reader.ReadInt16(); - var info = state.ServerInfo; - var a = state.Account; + AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason); + } + } - if (info == null || a == null || index < 0 || index >= info.Length) - { - state.Disconnect("Invalid server selected."); - } - else - { - var si = info[index]; + private static void AccountLogin_ReplyRej(this NetState state, ALRReason reason) + { + state.SendAccountLoginRejected(reason); + state.Disconnect($"Account login rejected due to {reason}"); + } - state._authId = GenerateAuthID(state); + private class LoginTimer : Timer + { + private readonly Mobile _mobile; + private readonly NetState _state; - state.SentFirstPacket = false; - state.SendPlayServerAck(si, state._authId); - } + public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromMilliseconds(64), TimeSpan.FromMilliseconds(64)) + { + _state = state; + _mobile = m; } - public static void LoginServerSeed(NetState state, CircularBufferReader reader, ref int packetLength) + protected override void OnTick() { - state._seed = reader.ReadInt32(); - state.Seeded = true; - - if (state._seed == 0) + if (_state != null) { - state.LogInfo("Invalid client detected, disconnecting"); - state.Disconnect("Duplicate seed sent."); - 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, ref int packetLength) - { - // TODO: Throttle Connection - - if (state.SentFirstPacket) - { - state.Disconnect("Duplicate account login packet sent."); - 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) + if (_state.Account == null) { - state.Account = null; - AccountLogin_ReplyRej(state, ALRReason.BadComm); + _state.Disconnect("Account was deleted during the login process."); } - else + else if (_mobile == null) { - state.ServerInfo = serverListEventArgs.Servers.ToArray(); - state.SendAccountLoginAck(); + _state.Disconnect("Player was deleted during the login process."); + } + else if (_state.Version != null) + { + _state.BlockAllPackets = false; + DoLogin(_state, _mobile); + } + else // Waiting to receive the client version before we continue the login process + { + return; } } - else - { - AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason); - } - } - private static void AccountLogin_ReplyRej(this NetState state, ALRReason reason) - { - state.SendAccountLoginRejected(reason); - state.Disconnect($"Account login rejected due to {reason}"); - } - - private class LoginTimer : Timer - { - private readonly Mobile _mobile; - private readonly NetState _state; - - public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0)) - { - _state = state; - _mobile = m; - } - - protected override void OnTick() - { - if (_state != null) - { - if (_state.Account == null) - { - _state.Disconnect("Account was deleted during the login process."); - } - else if (_mobile == null) - { - _state.Disconnect("Player was deleted during the login process."); - } - else if (_state.Version != null) - { - _state.BlockAllPackets = false; - DoLogin(_state, _mobile); - } - else // Waiting to receive the client version before we continue the login process - { - return; - } - } - - Stop(); - } + Stop(); } } } diff --git a/Projects/Server/Network/Packets/IncomingEntityPackets.cs b/Projects/Server/Network/Packets/IncomingEntityPackets.cs index ac6c0dd3d..bd2815eee 100644 --- a/Projects/Server/Network/Packets/IncomingEntityPackets.cs +++ b/Projects/Server/Network/Packets/IncomingEntityPackets.cs @@ -13,97 +13,160 @@ * along with this program. If not, see . * *************************************************************************/ -namespace Server.Network +namespace Server.Network; + +public static class IncomingEntityPackets { - public static class IncomingEntityPackets + public static bool SingleClickProps { get; set; } + + public static void Configure() { - public static bool SingleClickProps { get; set; } + 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 Configure() + public static void ObjectHelpRequest(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + var serial = (Serial)reader.ReadUInt32(); + int unk = reader.ReadByte(); + var lang = reader.ReadAscii(3); + + if (serial.IsItem) { - IncomingPackets.Register(0x06, 5, true, UseReq); - IncomingPackets.Register(0x09, 5, true, LookReq); - IncomingPackets.Register(0xB6, 9, true, ObjectHelpRequest); - IncomingPackets.Register(0xD6, 0, true, BatchQueryProperties); - } + var item = World.FindItem(serial); - public static void ObjectHelpRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - var serial = (Serial)reader.ReadUInt32(); - int unk = reader.ReadByte(); - var lang = reader.ReadAscii(3); - - if (serial.IsItem) + if (item != null && from.Map == item.Map && Utility.InUpdateRange(item.GetWorldLocation(), from.Location) && + from.CanSee(item)) { - 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); - } + item.OnHelpRequest(from); } } - - public static void UseReq(NetState state, CircularBufferReader reader, ref int packetLength) + else if (serial.IsMobile) { - var from = state.Mobile; + var m = World.FindMobile(serial); - if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0) + if (m != null && from.Map == m.Map && Utility.InUpdateRange(m.Location, from.Location) && from.CanSee(m)) { - var value = reader.ReadUInt32(); + m.OnHelpRequest(m); + } + } + } - if ((value & ~0x7FFFFFFF) != 0) - { - from.OnPaperdollRequest(); - } - else - { - Serial s = (Serial)value; + public static void UseReq(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; - if (s.IsMobile) - { - var m = World.FindMobile(s); + if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0) + { + var value = reader.ReadUInt32(); - 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; + if ((value & ~0x7FFFFFFF) != 0) + { + from.OnPaperdollRequest(); } else { - from.SendActionMessage(); + Serial s = (Serial)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, ref int packetLength) + { + var from = state.Mobile; + + Serial s = (Serial)reader.ReadUInt32(); + + if (s.IsMobile) + { + var m = World.FindMobile(s); + + if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) + { + if (SingleClickProps) + { + m.OnAosSingleClick(from); + } + else + { + if (from.Region.OnSingleClick(from, m)) + { + m.OnSingleClick(from); + } + } } } - - public static void LookReq(NetState state, CircularBufferReader reader, ref int packetLength) + else if (s.IsItem) { - var from = state.Mobile; + 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, ref int packetLength) + { + if (!ObjectPropertyList.Enabled) + { + return; + } + + var from = state.Mobile; + + var length = reader.Remaining; + + if (length % 4 != 0) + { + return; + } + + while (reader.Remaining > 0) + { Serial s = (Serial)reader.ReadUInt32(); if (s.IsMobile) @@ -112,17 +175,7 @@ namespace Server.Network if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) { - if (SingleClickProps) - { - m.OnAosSingleClick(from); - } - else - { - if (from.Region.OnSingleClick(from, m)) - { - m.OnSingleClick(from); - } - } + m.SendPropertiesTo(from); } } else if (s.IsItem) @@ -132,61 +185,7 @@ namespace Server.Network 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, ref int packetLength) - { - if (!ObjectPropertyList.Enabled) - { - return; - } - - var from = state.Mobile; - - var length = reader.Remaining; - - if (length % 4 != 0) - { - return; - } - - while (reader.Remaining > 0) - { - Serial s = (Serial)reader.ReadUInt32(); - - if (s.IsMobile) - { - var m = World.FindMobile(s); - - if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) - { - 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); - } + item.SendPropertiesTo(from); } } } diff --git a/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs b/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs index c78da3903..e082c70e1 100644 --- a/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs +++ b/Projects/Server/Network/Packets/IncomingExtendedCommandPackets.cs @@ -16,502 +16,501 @@ using System.Collections.Generic; using Server.ContextMenus; -namespace Server.Network +namespace Server.Network; + +public static class IncomingExtendedCommandPackets { - public static class IncomingExtendedCommandPackets + private static readonly PacketHandler[] m_ExtendedHandlersLow = new PacketHandler[0x100]; + private static readonly Dictionary m_ExtendedHandlersHigh = new(); + + // TODO: Change to outside configuration + public static int[] ValidAnimations { get; set; } = { - private static readonly PacketHandler[] m_ExtendedHandlersLow = new PacketHandler[0x100]; - private static readonly Dictionary m_ExtendedHandlersHigh = new(); + 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 + }; - // TODO: Change to outside configuration - public static int[] ValidAnimations { get; set; } = + public static void Configure() + { + IncomingPackets.Register(0xBF, 0, true, ExtendedCommand); + + RegisterExtended(0x05, false, ScreenSize); + RegisterExtended(0x06, true, PartyMessage); + 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, ref int packetLength) + { + } + + public static void Empty(NetState state, CircularBufferReader reader, ref int packetLength) + { + } + + public static void RegisterExtended(int packetID, bool ingame, OnPacketReceive onReceive) + { + if (packetID >= 0 && packetID < 0x100) { - 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() + m_ExtendedHandlersLow[packetID] = new PacketHandler(packetID, 0, ingame, onReceive); + } + else { - IncomingPackets.Register(0xBF, 0, true, ExtendedCommand); + m_ExtendedHandlersHigh[packetID] = new PacketHandler(packetID, 0, ingame, onReceive); + } + } - RegisterExtended(0x05, false, ScreenSize); - RegisterExtended(0x06, true, PartyMessage); - 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); + public static PacketHandler GetExtendedHandler(int packetID) + { + if (packetID >= 0 && packetID < 0x100) + { + return m_ExtendedHandlersLow[packetID]; } - private static void UnhandledBF(NetState state, CircularBufferReader reader, ref int packetLength) + 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, ref int packetLength) + { + int packetId = reader.ReadUInt16(); + + var ph = GetExtendedHandler(packetId); + + if (ph == null) + { + reader.Trace(state); + return; } - public static void Empty(NetState state, CircularBufferReader reader, ref int packetLength) - { - } - - 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, ref int packetLength) - { - 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.LogInfo( - "Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile", - packetId - ); - } - - state.Disconnect($"Sent in-game packet(0xBFx{packetId:X2}) but mobile is deleted."); - } - else - { - ph.OnReceive(state, reader, ref packetLength); - } - } - - public static void ScreenSize(NetState state, CircularBufferReader reader, ref int packetLength) - { - var width = reader.ReadInt32(); - var unk = reader.ReadInt32(); - } - - // TODO: Move out of the core - public static void PartyMessage(NetState state, CircularBufferReader reader, ref int packetLength) + if (ph.Ingame && state.Mobile?.Deleted != false) { if (state.Mobile == null) { - return; + state.LogInfo( + "Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile", + packetId + ); } - switch (reader.ReadByte()) + state.Disconnect($"Sent in-game packet(0xBFx{packetId:X2}) but mobile is deleted."); + } + else + { + ph.OnReceive(state, reader, ref packetLength); + } + } + + public static void ScreenSize(NetState state, CircularBufferReader reader, ref int packetLength) + { + var width = reader.ReadInt32(); + var unk = reader.ReadInt32(); + } + + // TODO: Move out of the core + public static void PartyMessage(NetState state, CircularBufferReader reader, ref int packetLength) + { + if (state.Mobile == null) + { + return; + } + + switch (reader.ReadByte()) + { + case 0x01: + PartyMessage_AddMember(state, reader, ref packetLength); + break; + case 0x02: + PartyMessage_RemoveMember(state, reader, ref packetLength); + break; + case 0x03: + PartyMessage_PrivateMessage(state, reader, ref packetLength); + break; + case 0x04: + PartyMessage_PublicMessage(state, reader, ref packetLength); + break; + case 0x06: + PartyMessage_SetCanLoot(state, reader, ref packetLength); + break; + case 0x08: + PartyMessage_Accept(state, reader, ref packetLength); + break; + case 0x09: + PartyMessage_Decline(state, reader, ref packetLength); + break; + default: + reader.Trace(state); + break; + } + } + + public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader, ref int packetLength) + { + PartyCommands.Handler?.OnAdd(state.Mobile); + } + + public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader, ref int packetLength) + { + PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); + } + + public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader, ref int packetLength) + { + PartyCommands.Handler?.OnPrivateMessage( + state.Mobile, + World.FindMobile((Serial)reader.ReadUInt32()), + reader.ReadBigUniSafe() + ); + } + + public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader, ref int packetLength) + { + PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe()); + } + + public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader, ref int packetLength) + { + PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean()); + } + + public static void PartyMessage_Accept(NetState state, CircularBufferReader reader, ref int packetLength) + { + PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); + } + + public static void PartyMessage_Decline(NetState state, CircularBufferReader reader, ref int packetLength) + { + PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); + } + + public static void Animate(NetState state, CircularBufferReader reader, ref int packetLength) + { + 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, ref int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + Item spellbook = reader.ReadInt16() == 1 ? World.FindItem((Serial)reader.ReadUInt32()) : null; + + var spellID = reader.ReadInt16() - 1; + EventSink.InvokeCastSpellRequest(from, spellID, spellbook); + } + + public static void ToggleFlying(NetState state, CircularBufferReader reader, ref int packetLength) + { + state.Mobile?.ToggleFlying(); + } + + public static void StunRequest(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + EventSink.InvokeStunRequest(from); + } + + public static void DisarmRequest(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + EventSink.InvokeDisarmRequest(from); + } + + public static void StatLockChange(NetState state, CircularBufferReader reader, ref int packetLength) + { + 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, ref int packetLength) + { + var serial = (Serial)reader.ReadUInt32(); + } + + public static void Language(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + from.Language = reader.ReadAscii(4); + } + + public static void QueryProperties(NetState state, CircularBufferReader reader, ref int packetLength) + { + if (!ObjectPropertyList.Enabled) + { + return; + } + + var from = state.Mobile; + + Serial s = (Serial)reader.ReadUInt32(); + + if (s.IsMobile) + { + var m = World.FindMobile(s); + + if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) { - case 0x01: - PartyMessage_AddMember(state, reader, ref packetLength); - break; - case 0x02: - PartyMessage_RemoveMember(state, reader, ref packetLength); - break; - case 0x03: - PartyMessage_PrivateMessage(state, reader, ref packetLength); - break; - case 0x04: - PartyMessage_PublicMessage(state, reader, ref packetLength); - break; - case 0x06: - PartyMessage_SetCanLoot(state, reader, ref packetLength); - break; - case 0x08: - PartyMessage_Accept(state, reader, ref packetLength); - break; - case 0x09: - PartyMessage_Decline(state, reader, ref packetLength); - break; - default: - reader.Trace(state); - break; + m.SendPropertiesTo(from); } } - - public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader, ref int packetLength) + else if (s.IsItem) { - PartyCommands.Handler?.OnAdd(state.Mobile); - } + var item = World.FindItem(s); - public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); - } - - public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnPrivateMessage( - state.Mobile, - World.FindMobile((Serial)reader.ReadUInt32()), - reader.ReadBigUniSafe() - ); - } - - public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe()); - } - - public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean()); - } - - public static void PartyMessage_Accept(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); - } - - public static void PartyMessage_Decline(NetState state, CircularBufferReader reader, ref int packetLength) - { - PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32())); - } - - public static void Animate(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) + if (item?.Deleted == false && from.CanSee(item) && + Utility.InUpdateRange(from.Location, item.GetWorldLocation())) { - 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, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - Item spellbook = reader.ReadInt16() == 1 ? World.FindItem((Serial)reader.ReadUInt32()) : null; - - var spellID = reader.ReadInt16() - 1; - EventSink.InvokeCastSpellRequest(from, spellID, spellbook); - } - - public static void ToggleFlying(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.Mobile?.ToggleFlying(); - } - - public static void StunRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - EventSink.InvokeStunRequest(from); - } - - public static void DisarmRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - EventSink.InvokeDisarmRequest(from); - } - - public static void StatLockChange(NetState state, CircularBufferReader reader, ref int packetLength) - { - 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, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - } - - public static void Language(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - from.Language = reader.ReadAscii(4); - } - - public static void QueryProperties(NetState state, CircularBufferReader reader, ref int packetLength) - { - if (!ObjectPropertyList.Enabled) - { - return; - } - - var from = state.Mobile; - - Serial s = (Serial)reader.ReadUInt32(); - - if (s.IsMobile) - { - var m = World.FindMobile(s); - - if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location)) - { - 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, ref int packetLength) - { - 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((Serial)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, ref int packetLength) - { - var from = state.Mobile; - var target = World.FindEntity((Serial)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, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0) - { - var bandage = World.FindItem((Serial)reader.ReadUInt32()); - - if (bandage == null) - { - return; - } - - var target = World.FindMobile((Serial)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, ref int packetLength) - { - var spellId = (short)(reader.ReadInt16() - 1); // zero based; - - EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), spellId); - } - - public static void TargetedSkillUse(NetState state, CircularBufferReader reader, ref int packetLength) - { - var skillId = reader.ReadInt16(); - - EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), skillId); - } - - public static void TargetByResourceMacro(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - - if (serial.IsItem) - { - EventSink.InvokeTargetByResourceMacro(state.Mobile, World.FindItem(serial), reader.ReadInt16()); + item.SendPropertiesTo(from); } } } + + public static void ContextMenuResponse(NetState state, CircularBufferReader reader, ref int packetLength) + { + 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((Serial)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, ref int packetLength) + { + var from = state.Mobile; + var target = World.FindEntity((Serial)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, ref int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0) + { + var bandage = World.FindItem((Serial)reader.ReadUInt32()); + + if (bandage == null) + { + return; + } + + var target = World.FindMobile((Serial)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, ref int packetLength) + { + var spellId = (short)(reader.ReadInt16() - 1); // zero based; + + EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), spellId); + } + + public static void TargetedSkillUse(NetState state, CircularBufferReader reader, ref int packetLength) + { + var skillId = reader.ReadInt16(); + + EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), skillId); + } + + public static void TargetByResourceMacro(NetState state, CircularBufferReader reader, ref int packetLength) + { + var 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 index 64fd2d4f3..b5969f56c 100644 --- a/Projects/Server/Network/Packets/IncomingHousePackets.cs +++ b/Projects/Server/Network/Packets/IncomingHousePackets.cs @@ -13,18 +13,17 @@ * along with this program. If not, see . * *************************************************************************/ -namespace Server.Network -{ - public static class IncomingHousePackets - { - public static void Configure() - { - IncomingPackets.Register(0xFB, 2, false, ShowPublicHouseContent); - } +namespace Server.Network; - public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader, ref int packetLength) - { - var showPublicHouseContent = reader.ReadBoolean(); - } +public static class IncomingHousePackets +{ + public static void Configure() + { + IncomingPackets.Register(0xFB, 2, false, ShowPublicHouseContent); + } + + public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader, ref int packetLength) + { + var showPublicHouseContent = reader.ReadBoolean(); } } diff --git a/Projects/Server/Network/Packets/IncomingItemPackets.cs b/Projects/Server/Network/Packets/IncomingItemPackets.cs index 6ad33ef4c..0e3cf6601 100644 --- a/Projects/Server/Network/Packets/IncomingItemPackets.cs +++ b/Projects/Server/Network/Packets/IncomingItemPackets.cs @@ -17,159 +17,158 @@ using System.Collections.Generic; using System.IO; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class IncomingItemPackets { - public static class IncomingItemPackets + public static void Configure() { - public static void Configure() + IncomingPackets.Register(0x07, 7, true, LiftReq); + IncomingPackets.Register(0x08, 15, true, DropReq); + IncomingPackets.Register(0x13, 10, true, EquipReq); + IncomingPackets.Register(0xEC, 0, false, EquipMacro); + IncomingPackets.Register(0xED, 0, false, UnequipMacro); + } + + public static void LiftReq(NetState state, CircularBufferReader reader, ref int packetLength) + { + var 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, ref int packetLength) + { + var from = state.Mobile; + var item = from.Holding; + + var valid = item != null && item.HeldBy == from && item.Map == Map.Internal; + + from.Holding = null; + + if (!valid) { - IncomingPackets.Register(0x07, 7, true, LiftReq); - IncomingPackets.Register(0x08, 15, true, DropReq); - IncomingPackets.Register(0x13, 10, true, EquipReq); - IncomingPackets.Register(0xEC, 0, false, EquipMacro); - IncomingPackets.Register(0xED, 0, false, UnequipMacro); + return; } - public static void LiftReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - int amount = reader.ReadUInt16(); - var item = World.FindItem(serial); + reader.Seek(5, SeekOrigin.Current); + var to = World.FindMobile((Serial)reader.ReadUInt32()) ?? from; - state.Mobile.Lift(item, amount, out _, out _); + if (!to.AllowEquipFrom(from) || !to.EquipItem(item)) + { + item.Bounce(from); } - public static void EquipReq(NetState state, CircularBufferReader reader, ref int packetLength) + item.ClearBounce(); + } + + public static void DropReq(NetState state, CircularBufferReader reader, ref int packetLength) + { + reader.ReadInt32(); // serial, ignored + int x = reader.ReadInt16(); + int y = reader.ReadInt16(); + int z = reader.ReadSByte(); + if (state.ContainerGridLines) { - 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((Serial)reader.ReadUInt32()) ?? from; - - if (!to.AllowEquipFrom(from) || !to.EquipItem(item)) - { - item.Bounce(from); - } - - item.ClearBounce(); - } - - public static void DropReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - reader.ReadInt32(); // serial, ignored - int x = reader.ReadInt16(); - int y = reader.ReadInt16(); - int z = reader.ReadSByte(); - if (state.ContainerGridLines) - { - reader.ReadByte(); // Grid Location? - } - else - { - packetLength -= 1; - } - - Serial dest = (Serial)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, ref int packetLength) - { - reader.ReadInt32(); // serial, ignored - int x = reader.ReadInt16(); - int y = reader.ReadInt16(); - int z = reader.ReadSByte(); reader.ReadByte(); // Grid Location? - Serial dest = (Serial)reader.ReadUInt32(); + } + else + { + packetLength -= 1; + } - var loc = new Point3D(x, y, z); + Serial dest = (Serial)reader.ReadUInt32(); - var from = state.Mobile; + var loc = new Point3D(x, y, z); - if (dest.IsMobile) + 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) { - 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); - } + loc.m_X += multi.X; + loc.m_Y += multi.Y; + from.Drop(loc); } else { - from.Drop(loc); + from.Drop(item, loc); } } - - public static void EquipMacro(NetState state, CircularBufferReader reader, ref int packetLength) + else { - int count = reader.ReadByte(); - var serialList = new List(count); - for (var i = 0; i < count; ++i) - { - serialList.Add((Serial)reader.ReadUInt32()); - } - - EventSink.InvokeEquipMacro(state.Mobile, serialList); - } - - public static void UnequipMacro(NetState state, CircularBufferReader reader, ref int packetLength) - { - 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); + from.Drop(loc); } } + + public static void DropReq6017(NetState state, CircularBufferReader reader, ref int packetLength) + { + reader.ReadInt32(); // serial, ignored + int x = reader.ReadInt16(); + int y = reader.ReadInt16(); + int z = reader.ReadSByte(); + reader.ReadByte(); // Grid Location? + Serial dest = (Serial)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, ref int packetLength) + { + int count = reader.ReadByte(); + var serialList = new List(count); + for (var i = 0; i < count; ++i) + { + serialList.Add((Serial)reader.ReadUInt32()); + } + + EventSink.InvokeEquipMacro(state.Mobile, serialList); + } + + public static void UnequipMacro(NetState state, CircularBufferReader reader, ref int packetLength) + { + 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 index a40355498..0f25bf85e 100644 --- a/Projects/Server/Network/Packets/IncomingMessagePackets.cs +++ b/Projects/Server/Network/Packets/IncomingMessagePackets.cs @@ -15,146 +15,145 @@ using System; -namespace Server.Network +namespace Server.Network; + +[Flags] +public enum MessageType { - [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(); + + public static void Configure() { - Regular = 0x00, - System = 0x01, - Emote = 0x02, - Label = 0x06, - Focus = 0x07, - Whisper = 0x08, - Yell = 0x09, - Spell = 0x0A, - - Guild = 0x0D, - Alliance = 0x0E, - Command = 0x0F, - - Encoded = 0xC0 + IncomingPackets.Register(0x03, 0, true, AsciiSpeech); + IncomingPackets.Register(0xAD, 0, true, UnicodeSpeech); } - public static class IncomingMessagePackets + public static void AsciiSpeech(NetState state, CircularBufferReader reader, ref int packetLength) { - private static readonly KeywordList m_KeywordList = new(); + var from = state.Mobile; - public static void Configure() + if (from == null) { - IncomingPackets.Register(0x03, 0, true, AsciiSpeech); - IncomingPackets.Register(0xAD, 0, true, UnicodeSpeech); + return; } - public static void AsciiSpeech(NetState state, CircularBufferReader reader, ref int packetLength) + var type = (MessageType)reader.ReadByte(); + int hue = reader.ReadInt16(); + reader.ReadInt16(); // font + var text = reader.ReadAsciiSafe().Trim(); + + if (text.Length is <= 0 or > 128) { - 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 is <= 0 or > 128) - { - return; - } - - if (!Enum.IsDefined(typeof(MessageType), type)) - { - type = MessageType.Regular; - } - - from.DoSpeech(text, Array.Empty(), type, Utility.ClipDyedHue(hue)); + return; } - public static void UnicodeSpeech(NetState state, CircularBufferReader reader, ref int packetLength) + if (!Enum.IsDefined(typeof(MessageType), type)) { - var from = state.Mobile; + type = MessageType.Regular; + } - if (from == null) + from.DoSpeech(text, Array.Empty(), type, Utility.ClipDyedHue(hue)); + } + + public static void UnicodeSpeech(NetState state, CircularBufferReader reader, ref int packetLength) + { + 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 is < 0 or > 50) { return; } - var type = (MessageType)reader.ReadByte(); - int hue = reader.ReadInt16(); - reader.ReadInt16(); // font - var lang = reader.ReadAscii(4); - string text; + var keyList = m_KeywordList; - var isEncoded = (type & MessageType.Encoded) != 0; - int[] keywords; - - if (isEncoded) + for (var i = 0; i < count; ++i) { - int value = reader.ReadInt16(); - var count = (value & 0xFFF0) >> 4; - var hold = value & 0xF; + int speechID; - if (count is < 0 or > 50) + if ((i & 1) == 0) { - return; + hold <<= 8; + hold |= reader.ReadByte(); + speechID = hold; + hold = 0; + } + else + { + value = reader.ReadInt16(); + speechID = (value & 0xFFF0) >> 4; + hold = value & 0xF; } - var keyList = m_KeywordList; - - for (var i = 0; i < count; ++i) + if (!keyList.Contains(speechID)) { - 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); - } + keyList.Add(speechID); } - - text = reader.ReadUTF8Safe(); - - keywords = keyList.ToArray(); - } - else - { - text = reader.ReadBigUniSafe(); - - keywords = Array.Empty(); } - text = text.Trim(); + text = reader.ReadUTF8Safe(); - if (text.Length is <= 0 or > 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)); + keywords = keyList.ToArray(); } + else + { + text = reader.ReadBigUniSafe(); + + keywords = Array.Empty(); + } + + text = text.Trim(); + + if (text.Length is <= 0 or > 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 index eed4135ce..569fa430b 100644 --- a/Projects/Server/Network/Packets/IncomingMobilePackets.cs +++ b/Projects/Server/Network/Packets/IncomingMobilePackets.cs @@ -15,152 +15,151 @@ using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class IncomingMobilePackets { - public static class IncomingMobilePackets + public static void Configure() { - 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, ref int packetLength) + { + var from = state.Mobile; + var targ = World.FindMobile((Serial)reader.ReadUInt32()); + + if (targ != null) { - IncomingPackets.Register(0x75, 35, true, RenameRequest); - IncomingPackets.Register(0x98, 0, true, MobileNameRequest); - IncomingPackets.Register(0xB8, 0, true, ProfileReq); - IncomingPackets.Register(0x6F, 0, true, SecureTrade); + EventSink.InvokeRenameRequest(from, targ, reader.ReadAsciiSafe()); + } + } + + public static void MobileNameRequest(NetState state, CircularBufferReader reader, ref int packetLength) + { + var m = World.FindMobile((Serial)reader.ReadUInt32()); + + if (m != null && Utility.InUpdateRange(state.Mobile.Location, m.Location) && state.Mobile.CanSee(m)) + { + state.SendMobileName(m); + } + } + + public static void ProfileReq(NetState state, CircularBufferReader reader, ref int packetLength) + { + int type = reader.ReadByte(); + var serial = (Serial)reader.ReadUInt32(); + + var beholder = state.Mobile; + var beheld = World.FindMobile(serial); + + if (beheld == null) + { + return; } - public static void RenameRequest(NetState state, CircularBufferReader reader, ref int packetLength) + switch (type) { - var from = state.Mobile; - var targ = World.FindMobile((Serial)reader.ReadUInt32()); + case 0x00: // display request + { + EventSink.InvokeProfileRequest(beholder, beheld); - if (targ != null) - { - EventSink.InvokeRenameRequest(from, targ, reader.ReadAsciiSafe()); - } - } - - public static void MobileNameRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - var m = World.FindMobile((Serial)reader.ReadUInt32()); - - if (m != null && Utility.InUpdateRange(state.Mobile.Location, m.Location) && state.Mobile.CanSee(m)) - { - state.SendMobileName(m); - } - } - - public static void ProfileReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - int type = reader.ReadByte(); - var 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, ref int packetLength) - { - switch (reader.ReadByte()) - { - case 1: // Cancel - { - var 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 - { - var 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 - { - var 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; - } + } + 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, ref int packetLength) + { + switch (reader.ReadByte()) + { + case 1: // Cancel + { + var 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 + { + var 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 + { + var 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/IncomingMovementPackets.cs b/Projects/Server/Network/Packets/IncomingMovementPackets.cs index 9dc38d598..0a82df9a7 100644 --- a/Projects/Server/Network/Packets/IncomingMovementPackets.cs +++ b/Projects/Server/Network/Packets/IncomingMovementPackets.cs @@ -13,88 +13,49 @@ * along with this program. If not, see . * *************************************************************************/ -namespace Server.Network +namespace Server.Network; + +public static class IncomingMovementPackets { - public static class IncomingMovementPackets + public static void Configure() { - public static void Configure() + IncomingPackets.Register(0x02, 7, true, MovementReq); + // Not used by OSI, and interferes with ClassicUO/Razor protocol extensions + // IncomingPackets.Register(0xF0, 0, true, NewMovementReq); + // IncomingPackets.Register(0xF1, 9, true, TimeSyncReq); + } + + public static void NewMovementReq(NetState ns, CircularBufferReader reader) + { + var from = ns.Mobile; + + if (from == null) { - IncomingPackets.Register(0x02, 7, true, MovementReq); - // Not used by OSI, and interferes with ClassicUO/Razor protocol extensions - // IncomingPackets.Register(0xF0, 0, true, NewMovementReq); - // IncomingPackets.Register(0xF1, 9, true, TimeSyncReq); + return; } - public static void NewMovementReq(NetState ns, CircularBufferReader reader) + var steps = reader.ReadByte(); + for (int i = 0; i < steps; i++) { - var from = ns.Mobile; - - if (from == null) - { - return; - } - - var steps = reader.ReadByte(); - for (int i = 0; i < steps; i++) - { - var t1 = reader.ReadUInt64(); // start time? - var t2 = reader.ReadUInt64(); // end time? - int seq = reader.ReadByte(); - var dir = (Direction)reader.ReadByte(); - var mode = reader.ReadInt32(); // 1 = walk, 2 = run - if (mode == 2) - { - dir |= Direction.Running; - } - - // Location - reader.ReadInt32(); // x - reader.ReadInt32(); // y - reader.ReadInt32(); // z - - if (ns.Sequence == 0 && seq != 0 || !from.Move(dir)) - { - ns.SendMovementRej(seq, from); - ns.Sequence = 0; - } - else - { - ++seq; - - if (seq == 256) - { - seq = 1; - } - - ns.Sequence = seq; - } - } - } - - public static void TimeSyncReq(NetState ns, CircularBufferReader reader) - { - reader.ReadUInt64(); // Client Time? - - ns.SendTimeSyncResponse(); - } - - public static void MovementReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - var dir = (Direction)reader.ReadByte(); + var t1 = reader.ReadUInt64(); // start time? + var t2 = reader.ReadUInt64(); // end time? int seq = reader.ReadByte(); - var key = reader.ReadUInt32(); - - if (state.Sequence == 0 && seq != 0 || !from.Move(dir)) + var dir = (Direction)reader.ReadByte(); + var mode = reader.ReadInt32(); // 1 = walk, 2 = run + if (mode == 2) { - state.SendMovementRej(seq, from); - state.Sequence = 0; + dir |= Direction.Running; + } + + // Location + reader.ReadInt32(); // x + reader.ReadInt32(); // y + reader.ReadInt32(); // z + + if (ns.Sequence == 0 && seq != 0 || !from.Move(dir)) + { + ns.SendMovementRej(seq, from); + ns.Sequence = 0; } else { @@ -105,8 +66,46 @@ namespace Server.Network seq = 1; } - state.Sequence = seq; + ns.Sequence = seq; } } } + + public static void TimeSyncReq(NetState ns, CircularBufferReader reader) + { + reader.ReadUInt64(); // Client Time? + + ns.SendTimeSyncResponse(); + } + + public static void MovementReq(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + var dir = (Direction)reader.ReadByte(); + int seq = reader.ReadByte(); + var key = reader.ReadUInt32(); + + if (state.Sequence == 0 && seq != 0 || !from.Move(dir)) + { + state.SendMovementRej(seq, from); + state.Sequence = 0; + } + else + { + ++seq; + + if (seq == 256) + { + seq = 1; + } + + state.Sequence = seq; + } + } } diff --git a/Projects/Server/Network/Packets/IncomingPackets.cs b/Projects/Server/Network/Packets/IncomingPackets.cs index a99a86053..71fbfd578 100644 --- a/Projects/Server/Network/Packets/IncomingPackets.cs +++ b/Projects/Server/Network/Packets/IncomingPackets.cs @@ -16,91 +16,90 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public static class IncomingPackets { - 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(); + + public static PacketHandler[] Handlers { get; } = new PacketHandler[0x100]; + + public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive) { - private static readonly PacketHandler[] m_6017Handlers = new PacketHandler[0x100]; + Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive); + m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive); + } - private static readonly EncodedPacketHandler[] m_EncodedHandlersLow = new EncodedPacketHandler[0x100]; + public static PacketHandler GetHandler(int packetID) => Handlers[packetID]; - private static readonly Dictionary m_EncodedHandlersHigh = - new(); - - public static PacketHandler[] Handlers { get; } = new PacketHandler[0x100]; - - public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive) + public static void RegisterEncoded(int packetID, bool ingame, OnEncodedPacketReceive onReceive) + { + if (packetID >= 0 && packetID < 0x100) { - Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive); - m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive); + m_EncodedHandlersLow[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive); } - - public static PacketHandler GetHandler(int packetID) => Handlers[packetID]; - - public static void RegisterEncoded(int packetID, bool ingame, OnEncodedPacketReceive onReceive) + else { - 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; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsInfoPacket(byte packetId) - { - // These packets can arrive at any time during the login process. They're just informational. - return packetId switch - { - 0x01 => true, // Disconnect - 0x73 => true, // Ping - 0xA4 => true, // SystemInfo - 0xB1 => true, // Gump Response - 0xBB => true, // Account ID - 0xBD => true, // Client Version - 0xBE => true, // Assist Version - 0xD9 => true, // Hardware Info - 0xDD => true, // Gumps (Packed) - 0xE1 => true, // Client Type - 0xF4 => true, // CrashReport - _ => false - }; + 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; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInfoPacket(byte packetId) + { + // These packets can arrive at any time during the login process. They're just informational. + return packetId switch + { + 0x01 => true, // Disconnect + 0x73 => true, // Ping + 0xA4 => true, // SystemInfo + 0xB1 => true, // Gump Response + 0xBB => true, // Account ID + 0xBD => true, // Client Version + 0xBE => true, // Assist Version + 0xD9 => true, // Hardware Info + 0xDD => true, // Gumps (Packed) + 0xE1 => true, // Client Type + 0xF4 => true, // CrashReport + _ => false + }; + } } diff --git a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs index 00371638d..2b7e14515 100644 --- a/Projects/Server/Network/Packets/IncomingPlayerPackets.cs +++ b/Projects/Server/Network/Packets/IncomingPlayerPackets.cs @@ -19,609 +19,608 @@ using Server.Diagnostics; using Server.Exceptions; using Server.Gumps; -namespace Server.Network +namespace Server.Network; + +public static class IncomingPlayerPackets { - public static class IncomingPlayerPackets + public static void Configure() { - public static void Configure() - { - IncomingPackets.Register(0x01, 5, false, Disconnect); - 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.Register(0x01, 5, false, Disconnect); + 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(0x28, true, GuildGumpRequest); - IncomingPackets.RegisterEncoded(0x32, true, QuestGumpRequest); + IncomingPackets.RegisterEncoded(0x28, true, GuildGumpRequest); + IncomingPackets.RegisterEncoded(0x32, true, QuestGumpRequest); + } + + public static void DeathStatusResponse(NetState state, CircularBufferReader reader, ref int packetLength) + { + // Ignored + } + + public static void RequestScrollWindow(NetState state, CircularBufferReader reader, ref int packetLength) + { + int lastTip = reader.ReadInt16(); + int type = reader.ReadByte(); + } + + public static void AttackReq(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; } - public static void DeathStatusResponse(NetState state, CircularBufferReader reader, ref int packetLength) + var m = World.FindMobile((Serial)reader.ReadUInt32()); + + if (m != null) { - // Ignored + from.Attack(m); } + } - public static void RequestScrollWindow(NetState state, CircularBufferReader reader, ref int packetLength) + public static void HuePickerResponse(NetState state, CircularBufferReader reader, ref int packetLength) + { + var serial = reader.ReadUInt32(); + _ = reader.ReadInt16(); // Item ID + var hue = Utility.ClipDyedHue(reader.ReadInt16() & 0x3FFF); + + foreach (var huePicker in state.HuePickers) { - int lastTip = reader.ReadInt16(); - int type = reader.ReadByte(); - } - - public static void AttackReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) + if (huePicker.Serial == serial) { - return; - } - - var m = World.FindMobile((Serial)reader.ReadUInt32()); - - if (m != null) - { - from.Attack(m); - } - } - - public static void HuePickerResponse(NetState state, CircularBufferReader reader, ref int packetLength) - { - 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, ref int packetLength) - { - 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, ref int packetLength) - { - 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 - { - var tokenizer = command.Tokenize(' '); - if (!tokenizer.MoveNext() || !int.TryParse(tokenizer.Current, 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 tokenizer = command.Tokenize(' '); - var spellID = (tokenizer.MoveNext() ? Utility.ToInt32(tokenizer.Current) : 0) - 1; - var serial = tokenizer.MoveNext() ? (Serial)Utility.ToUInt32(tokenizer.Current) : 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.LogInfo("Unknown text-command type 0x{0:X2}: {1}", state, type, command); - break; - } - } - } - - public static void AsciiPromptResponse(NetState state, CircularBufferReader reader, ref int packetLength) - { - 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?.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, ref int packetLength) - { - 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?.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, ref int packetLength) - { - 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, ref int packetLength) - { - var minusOne = reader.ReadInt32(); - } - - public static void ConfigurationFile(NetState state, CircularBufferReader reader, ref int packetLength) - { - } - - public static void LogoutReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.SendLogoutAck(); - } - - public static void ChangeSkillLock(NetState state, CircularBufferReader reader, ref int packetLength) - { - var s = state.Mobile.Skills[reader.ReadInt16()]; - - s?.SetLockNoRelay((SkillLock)reader.ReadByte()); - } - - public static void HelpRequest(NetState state, CircularBufferReader reader, ref int packetLength) - { - EventSink.InvokeHelpRequest(state.Mobile); - } - - public static void DisplayGumpResponse(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (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.LogInfo("Invalid gump response, disconnecting..."); - var exception = new InvalidGumpResponseException($"Button {buttonID} doesn't exist"); - exception.SetStackTrace(new StackTrace()); - NetState.TraceException(exception); - state.Mobile?.SendMessage("Invalid gump response."); - - // state.Disconnect("Invalid gump response."); - return; - } - - var switchCount = reader.ReadInt32(); - - if (switchCount < 0 || switchCount > gump.m_Switches) - { - state.LogInfo("Invalid gump response, disconnecting..."); - var exception = new InvalidGumpResponseException($"Bad switch count {switchCount}"); - exception.SetStackTrace(new StackTrace()); - NetState.TraceException(exception); - state.Mobile?.SendMessage("Invalid gump response."); - - // state.Disconnect("Invalid gump response."); - return; - } - - var switches = new int[switchCount]; - - for (var i = 0; i < switches.Length; ++i) - { - switches[i] = reader.ReadInt32(); - } - - var textCount = reader.ReadInt32(); - - if (textCount < 0 || textCount > gump.m_TextEntries) - { - state.LogInfo("Invalid gump response, disconnecting..."); - var exception = new InvalidGumpResponseException($"Bad text entry count {textCount}"); - exception.SetStackTrace(new StackTrace()); - NetState.TraceException(exception); - state.Mobile?.SendMessage("Invalid gump response."); - - // state.Disconnect("Invalid gump response."); - return; - } - - var textEntries = new TextRelay[textCount]; - - for (var i = 0; i < textEntries.Length; ++i) - { - int entryID = reader.ReadUInt16(); - int textLength = reader.ReadUInt16(); - - if (textLength > 239) - { - state.LogInfo("Invalid gump response, disconnecting..."); - var exception = new InvalidGumpResponseException($"Text entry {i} is too long ({textLength})"); - exception.SetStackTrace(new StackTrace()); - NetState.TraceException(exception); - state.Mobile?.SendMessage("Invalid gump response."); - - // state.Disconnect("Invalid gump response."); - return; - } - - var text = reader.ReadBigUniSafe(textLength); - textEntries[i] = 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.Remaining >= 4 ? reader.ReadInt32() : 0; - - if (buttonID == 1 && switchCount > 0) - { - var beheld = World.FindMobile((Serial)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, ref int packetLength) - { - state.Mobile?.DelayChangeWarmode(reader.ReadBoolean()); - } - - // TODO: Throttle/make this more safe - public static void Resynchronize(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - - if (from == null) - { - return; - } - - state.SendMobileUpdate(from); - state.SendMobileIncoming(from, from); - - from.SendEverything(); - - state.Sequence = 0; - } - - public static void PingReq(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.SendPingAck(reader.ReadByte()); - } - - public static void SetUpdateRange(NetState state, CircularBufferReader reader, ref int packetLength) - { - state.SendChangeUpdateRange(18); - } - - public static void MobileQuery(NetState state, CircularBufferReader reader, ref int packetLength) - { - var from = state.Mobile; - if (from == null) - { - return; - } - - reader.ReadInt32(); // 0xEDEDEDED - int type = reader.ReadByte(); - var m = World.FindMobile((Serial)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, ref int packetLength) - { - 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 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, ref int packetLength) - { - var e = World.FindEntity((Serial)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.LogInfo( - "Sent in-game packet (0xD7x{0:X2}) before being attached to a mobile", - packetId - ); - state.Disconnect($"Sent in-game packet (0xD7x{packetId:X2}) before being attached to a mobile."); - } - else if (ph.Ingame && state.Mobile.Deleted) - { - state.Disconnect($"Sent in-game packet(0xD7x{packetId:X2}) but mobile is deleted."); - } - else - { - ph.OnReceive(state, e, new EncodedReader(reader)); + state.RemoveHuePicker(huePicker); + huePicker.OnResponse(hue); + break; } } } + + public static void SystemInfo(NetState state, CircularBufferReader reader, ref int packetLength) + { + 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, ref int packetLength) + { + 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 + { + var tokenizer = command.Tokenize(' '); + if (!tokenizer.MoveNext() || !int.TryParse(tokenizer.Current, 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 tokenizer = command.Tokenize(' '); + var spellID = (tokenizer.MoveNext() ? Utility.ToInt32(tokenizer.Current) : 0) - 1; + var serial = tokenizer.MoveNext() ? (Serial)Utility.ToUInt32(tokenizer.Current) : 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.LogInfo("Unknown text-command type 0x{0:X2}: {1}", state, type, command); + break; + } + } + } + + public static void AsciiPromptResponse(NetState state, CircularBufferReader reader, ref int packetLength) + { + 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?.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, ref int packetLength) + { + 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?.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, ref int packetLength) + { + 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, ref int packetLength) + { + var minusOne = reader.ReadInt32(); + } + + public static void ConfigurationFile(NetState state, CircularBufferReader reader, ref int packetLength) + { + } + + public static void LogoutReq(NetState state, CircularBufferReader reader, ref int packetLength) + { + state.SendLogoutAck(); + } + + public static void ChangeSkillLock(NetState state, CircularBufferReader reader, ref int packetLength) + { + var s = state.Mobile.Skills[reader.ReadInt16()]; + + s?.SetLockNoRelay((SkillLock)reader.ReadByte()); + } + + public static void HelpRequest(NetState state, CircularBufferReader reader, ref int packetLength) + { + EventSink.InvokeHelpRequest(state.Mobile); + } + + public static void DisplayGumpResponse(NetState state, CircularBufferReader reader, ref int packetLength) + { + var serial = (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.LogInfo("Invalid gump response, disconnecting..."); + var exception = new InvalidGumpResponseException($"Button {buttonID} doesn't exist"); + exception.SetStackTrace(new StackTrace()); + NetState.TraceException(exception); + state.Mobile?.SendMessage("Invalid gump response."); + + // state.Disconnect("Invalid gump response."); + return; + } + + var switchCount = reader.ReadInt32(); + + if (switchCount < 0 || switchCount > gump.m_Switches) + { + state.LogInfo("Invalid gump response, disconnecting..."); + var exception = new InvalidGumpResponseException($"Bad switch count {switchCount}"); + exception.SetStackTrace(new StackTrace()); + NetState.TraceException(exception); + state.Mobile?.SendMessage("Invalid gump response."); + + // state.Disconnect("Invalid gump response."); + return; + } + + var switches = new int[switchCount]; + + for (var i = 0; i < switches.Length; ++i) + { + switches[i] = reader.ReadInt32(); + } + + var textCount = reader.ReadInt32(); + + if (textCount < 0 || textCount > gump.m_TextEntries) + { + state.LogInfo("Invalid gump response, disconnecting..."); + var exception = new InvalidGumpResponseException($"Bad text entry count {textCount}"); + exception.SetStackTrace(new StackTrace()); + NetState.TraceException(exception); + state.Mobile?.SendMessage("Invalid gump response."); + + // state.Disconnect("Invalid gump response."); + return; + } + + var textEntries = new TextRelay[textCount]; + + for (var i = 0; i < textEntries.Length; ++i) + { + int entryID = reader.ReadUInt16(); + int textLength = reader.ReadUInt16(); + + if (textLength > 239) + { + state.LogInfo("Invalid gump response, disconnecting..."); + var exception = new InvalidGumpResponseException($"Text entry {i} is too long ({textLength})"); + exception.SetStackTrace(new StackTrace()); + NetState.TraceException(exception); + state.Mobile?.SendMessage("Invalid gump response."); + + // state.Disconnect("Invalid gump response."); + return; + } + + var text = reader.ReadBigUniSafe(textLength); + textEntries[i] = 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.Remaining >= 4 ? reader.ReadInt32() : 0; + + if (buttonID == 1 && switchCount > 0) + { + var beheld = World.FindMobile((Serial)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, ref int packetLength) + { + state.Mobile?.DelayChangeWarmode(reader.ReadBoolean()); + } + + // TODO: Throttle/make this more safe + public static void Resynchronize(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + + if (from == null) + { + return; + } + + state.SendMobileUpdate(from); + state.SendMobileIncoming(from, from); + + from.SendEverything(); + + state.Sequence = 0; + } + + public static void PingReq(NetState state, CircularBufferReader reader, ref int packetLength) + { + state.SendPingAck(reader.ReadByte()); + } + + public static void SetUpdateRange(NetState state, CircularBufferReader reader, ref int packetLength) + { + state.SendChangeUpdateRange(18); + } + + public static void MobileQuery(NetState state, CircularBufferReader reader, ref int packetLength) + { + var from = state.Mobile; + if (from == null) + { + return; + } + + reader.ReadInt32(); // 0xEDEDEDED + int type = reader.ReadByte(); + var m = World.FindMobile((Serial)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, ref int packetLength) + { + 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 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, ref int packetLength) + { + var e = World.FindEntity((Serial)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.LogInfo( + "Sent in-game packet (0xD7x{0:X2}) before being attached to a mobile", + packetId + ); + state.Disconnect($"Sent in-game packet (0xD7x{packetId:X2}) before being attached to a mobile."); + } + else if (ph.Ingame && state.Mobile.Deleted) + { + state.Disconnect($"Sent in-game packet(0xD7x{packetId:X2}) but mobile is deleted."); + } + else + { + ph.OnReceive(state, e, new EncodedReader(reader)); + } + } } diff --git a/Projects/Server/Network/Packets/IncomingTargetingPackets.cs b/Projects/Server/Network/Packets/IncomingTargetingPackets.cs index b87b221de..1d03d85e9 100644 --- a/Projects/Server/Network/Packets/IncomingTargetingPackets.cs +++ b/Projects/Server/Network/Packets/IncomingTargetingPackets.cs @@ -16,130 +16,129 @@ using Server.Diagnostics; using Server.Targeting; -namespace Server.Network +namespace Server.Network; + +public static class IncomingTargetingPackets { - public static class IncomingTargetingPackets + public static void Configure() { - public static void Configure() + IncomingPackets.Register(0x6C, 19, true, TargetResponse); + } + + public static void TargetResponse(NetState state, CircularBufferReader reader, ref int packetLength) + { + int type = reader.ReadByte(); + var targetID = reader.ReadInt32(); + int flags = reader.ReadByte(); + var serial = (Serial)reader.ReadUInt32(); + int x = reader.ReadInt16(); + int y = reader.ReadInt16(); + reader.ReadByte(); + int z = reader.ReadSByte(); + int graphic = reader.ReadUInt16(); + + if (targetID == unchecked((int)0xDEADBEEF)) { - IncomingPackets.Register(0x6C, 19, true, TargetResponse); + return; } - public static void TargetResponse(NetState state, CircularBufferReader reader, ref int packetLength) + var from = state.Mobile; + + var t = from.Target; + + if (t == null) { - int type = reader.ReadByte(); - var targetID = reader.ReadInt32(); - int flags = reader.ReadByte(); - var serial = (Serial)reader.ReadUInt32(); - int x = reader.ReadInt16(); - int y = reader.ReadInt16(); - reader.ReadByte(); - int z = reader.ReadSByte(); - int graphic = reader.ReadUInt16(); + return; + } - if (targetID == unchecked((int)0xDEADBEEF)) + var prof = TargetProfile.Acquire(t.GetType()); + prof?.Start(); + + try + { + if (x == -1 && y == -1 && !serial.IsValid) { - return; + // User pressed escape + t.Cancel(from, TargetCancelType.Canceled); } - - var from = state.Mobile; - - var t = from.Target; - - if (t == null) + else if (t.TargetID != targetID) { - return; + // Sanity, prevent fake target } - - var prof = TargetProfile.Acquire(t.GetType()); - prof?.Start(); - - try + else { - 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; + object toTarget; - if (type == 1) + if (type == 1) + { + if (graphic == 0) { - if (graphic == 0) + toTarget = new LandTarget(new Point3D(x, y, z), from.Map); + } + else + { + var map = from.Map; + + if (map == null || map == Map.Internal) { - toTarget = new LandTarget(new Point3D(x, y, z), from.Map); + t.Cancel(from, TargetCancelType.Canceled); + return; } else { - var map = from.Map; + var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis); - if (map == null || map == Map.Internal) + 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 { - 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); - } + 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); } + 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(); - } + } + finally + { + prof?.Finish(); } } } diff --git a/Projects/Server/Network/Packets/IncomingVendorPackets.cs b/Projects/Server/Network/Packets/IncomingVendorPackets.cs index 4a9f9cf8a..0a11dd2d4 100644 --- a/Projects/Server/Network/Packets/IncomingVendorPackets.cs +++ b/Projects/Server/Network/Packets/IncomingVendorPackets.cs @@ -15,96 +15,95 @@ using System.Collections.Generic; -namespace Server.Network +namespace Server.Network; + +public static class IncomingVendorPackets { - public static class IncomingVendorPackets + public static void Configure() { - public static void Configure() + IncomingPackets.Register(0x3B, 0, true, VendorBuyReply); + IncomingPackets.Register(0x9F, 0, true, VendorSellReply); + } + + public static void VendorBuyReply(NetState state, CircularBufferReader reader, ref int packetLength) + { + var vendor = World.FindMobile((Serial)reader.ReadUInt32()); + + if (vendor == null) { - IncomingPackets.Register(0x3B, 0, true, VendorBuyReply); - IncomingPackets.Register(0x9F, 0, true, VendorSellReply); + return; } - public static void VendorBuyReply(NetState state, CircularBufferReader reader, ref int packetLength) + var flag = reader.ReadByte(); + + if (!vendor.Deleted && Utility.InRange(vendor.Location, state.Mobile.Location, 10) && flag == 0x02) { - var vendor = World.FindMobile((Serial)reader.ReadUInt32()); + var msgSize = packetLength - 8; // Remaining bytes - if (vendor == null) + if (msgSize / 7 > 100) { return; } - var flag = reader.ReadByte(); - - if (!vendor.Deleted && Utility.InRange(vendor.Location, state.Mobile.Location, 10) && flag == 0x02) + var buyList = new List(msgSize / 7); + while (msgSize > 0) { - var msgSize = packetLength - 8; // Remaining bytes - - if (msgSize / 7 > 100) - { - return; - } - - var buyList = new List(msgSize / 7); - while (msgSize > 0) - { - var layer = reader.ReadByte(); - var serial = (Serial)reader.ReadUInt32(); - int amount = reader.ReadInt16(); - - buyList.Add(new BuyItemResponse(serial, amount)); - msgSize -= 7; - } - - if (buyList.Count <= 0 || (vendor as IVendor)?.OnBuyItems(state.Mobile, buyList) != true) - { - return; - } - } - - state.SendEndVendorBuy(vendor.Serial); - } - - public static void VendorSellReply(NetState state, CircularBufferReader reader, ref int packetLength) - { - var serial = (Serial)reader.ReadUInt32(); - var vendor = World.FindMobile(serial); - - if (vendor == null) - { - return; - } - - if (vendor.Deleted || !Utility.InRange(vendor.Location, state.Mobile.Location, 10)) - { - state.SendEndVendorSell(vendor.Serial); - 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((Serial)reader.ReadUInt32()); + var layer = reader.ReadByte(); + var serial = (Serial)reader.ReadUInt32(); int amount = reader.ReadInt16(); - if (item != null && amount > 0) - { - sellList.Add(new SellItemResponse(item, amount)); - } + buyList.Add(new BuyItemResponse(serial, amount)); + msgSize -= 7; } - if (sellList.Count > 0 && vendor is IVendor v && v.OnSellItems(state.Mobile, sellList)) + if (buyList.Count <= 0 || (vendor as IVendor)?.OnBuyItems(state.Mobile, buyList) != true) { - state.SendEndVendorSell(vendor.Serial); + return; } } + + state.SendEndVendorBuy(vendor.Serial); + } + + public static void VendorSellReply(NetState state, CircularBufferReader reader, ref int packetLength) + { + var serial = (Serial)reader.ReadUInt32(); + var vendor = World.FindMobile(serial); + + if (vendor == null) + { + return; + } + + if (vendor.Deleted || !Utility.InRange(vendor.Location, state.Mobile.Location, 10)) + { + state.SendEndVendorSell(vendor.Serial); + 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((Serial)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.SendEndVendorSell(vendor.Serial); + } } } diff --git a/Projects/Server/Network/Packets/OutgoingAccountPackets.cs b/Projects/Server/Network/Packets/OutgoingAccountPackets.cs index f49793649..d29a8cbe4 100644 --- a/Projects/Server/Network/Packets/OutgoingAccountPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingAccountPackets.cs @@ -19,436 +19,435 @@ using System.Buffers; using System.Runtime.CompilerServices; using Server.Accounting; -namespace Server.Network +namespace Server.Network; + +public enum ALRReason : byte { - public enum ALRReason : byte - { - Invalid = 0, - InUse = 1, - Blocked = 2, - BadPass = 3, - Idle = 254, - BadComm = 255 - } + Invalid = 0, + InUse = 1, + Blocked = 2, + BadPass = 3, + Idle = 254, + BadComm = 255 +} - public enum PMMessage : byte - { - None = 0, - CharNoExist = 1, - CharExists = 2, - CharInWorld = 5, - LoginSyncError = 6, - IdleWarning = 7 - } +public enum PMMessage : byte +{ + None = 0, + CharNoExist = 1, + CharExists = 2, + CharInWorld = 5, + LoginSyncError = 6, + IdleWarning = 7 +} - public enum DeleteResultType - { - PasswordInvalid, - CharNotExist, - CharBeingPlayed, - CharTooYoung, - CharQueued, - BadRequest - } +public enum DeleteResultType +{ + PasswordInvalid, + CharNotExist, + CharBeingPlayed, + CharTooYoung, + CharQueued, + BadRequest +} - public static class OutgoingAccountPackets - { - /** +public static class OutgoingAccountPackets +{ + /** * Packet: 0x81 * Length: Up to 425 bytes * * Displays the list of characters during the login process. * Note: Currently Unused */ - public static void SendChangeCharacter(this NetState ns, IAccount a) + public static void SendChangeCharacter(this NetState ns, IAccount a) + { + if (ns == null || a == null) { - if (ns == null || a == null) - { - return; - } - - var length = 5 + a.Length * 60; - var writer = new SpanWriter(stackalloc byte[length]); - - writer.Write((byte)0x81); // Packet ID - writer.Write((ushort)length); - writer.Write((ushort)0); // Count & Placeholder - - int count = 0; - - for (var i = 0; i < a.Length; ++i) - { - var m = a[i]; - - if (m == null) - { - writer.Clear(60); - } - else - { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - - count++; - writer.WriteAscii(name, 30); - writer.Clear(30); // Password (empty) - } - } - - var position = writer.Position; - writer.Seek(3, SeekOrigin.Begin); - writer.Write((byte)count); - writer.Seek(position, SeekOrigin.Begin); - - ns.Send(writer.Span); + return; } - /** + var length = 5 + a.Length * 60; + var writer = new SpanWriter(stackalloc byte[length]); + + writer.Write((byte)0x81); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0); // Count & Placeholder + + int count = 0; + + for (var i = 0; i < a.Length; ++i) + { + var m = a[i]; + + if (m == null) + { + writer.Clear(60); + } + else + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + + count++; + writer.WriteAscii(name, 30); + writer.Clear(30); // Password (empty) + } + } + + var position = writer.Position; + writer.Seek(3, SeekOrigin.Begin); + writer.Write((byte)count); + writer.Seek(position, SeekOrigin.Begin); + + ns.Send(writer.Span); + } + + /** * Packet: 0xBD * Length: 3 bytes * * Sends a requests for the client version */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendClientVersionRequest(this NetState ns) => ns?.Send(stackalloc byte[] { 0xBD, 0x00, 0x03 }); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendClientVersionRequest(this NetState ns) => ns?.Send(stackalloc byte[] { 0xBD, 0x00, 0x03 }); - /** + /** * Packet: 0x85 * Length: 2 bytes * * Sends the result of a deletion request */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendCharacterDeleteResult(this NetState ns, DeleteResultType res) => - ns?.Send(stackalloc byte[] { 0x85, (byte)res }); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendCharacterDeleteResult(this NetState ns, DeleteResultType res) => + ns?.Send(stackalloc byte[] { 0x85, (byte)res }); - /** + /** * Packet: 0x53 * Length: 2 bytes * * Sends a PopupMessage with a predetermined message */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendPopupMessage(this NetState ns, PMMessage msg) => - ns?.Send(stackalloc byte[] { 0x53, (byte)msg }); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendPopupMessage(this NetState ns, PMMessage msg) => + ns?.Send(stackalloc byte[] { 0x53, (byte)msg }); - /** + /** * Packet: 0xB9 * Length: 3 or 5 bytes * * Sends support features based on the client version */ - public static void SendSupportedFeature(this NetState ns) + public static void SendSupportedFeature(this NetState ns) + { + if (ns.CannotSendPackets()) { - if (ns == null) + return; + } + + var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; + + if (ns.Account.Limit >= 6) + { + flags |= FeatureFlags.LiveAccount; + flags &= ~FeatureFlags.UOTD; + + if (ns.Account.Limit > 6) { - return; - } - - var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; - - if (ns.Account.Limit >= 6) - { - flags |= FeatureFlags.LiveAccount; - flags &= ~FeatureFlags.UOTD; - - if (ns.Account.Limit > 6) - { - flags |= FeatureFlags.SeventhCharacterSlot; - } - else - { - flags |= FeatureFlags.SixthCharacterSlot; - } - } - - var length = ns.ExtendedSupportedFeatures ? 5 : 3; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xB9); // Packet ID - - if (ns.ExtendedSupportedFeatures) - { - writer.Write((uint)flags); + flags |= FeatureFlags.SeventhCharacterSlot; } else { - writer.Write((ushort)flags); + flags |= FeatureFlags.SixthCharacterSlot; } - - ns.Send(writer.Span); } - /** + var length = ns.ExtendedSupportedFeatures ? 5 : 3; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xB9); // Packet ID + + if (ns.ExtendedSupportedFeatures) + { + writer.Write((uint)flags); + } + else + { + writer.Write((ushort)flags); + } + + ns.Send(writer.Span); + } + + /** * Packet: 0x1B * Length: 37 bytes * * Sends login confirmation */ - public static void SendLoginConfirmation(this NetState ns, Mobile m) + public static void SendLoginConfirmation(this NetState ns, Mobile m) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[37]); - writer.Write((byte)0x1B); // PacketID - writer.Write(m.Serial); - writer.Write(0); - writer.Write((short)m.Body); - writer.Write((short)m.X); - writer.Write((short)m.Y); - writer.Write((short)m.Z); - writer.Write((byte)m.Direction); - writer.Write((byte)0); - writer.Write(-1); - - writer.Write(0); - - var map = m.Map; - - if (map == null || map == Map.Internal) - { - map = m.LogoutMap; - } - - writer.Write((short)(map?.Width ?? Map.Felucca.Width)); - writer.Write((short)(map?.Height ?? Map.Felucca.Height)); - writer.Clear(writer.Capacity - writer.Position); // Remaining is zero - - ns.Send(writer.Span); + return; } - /** + var writer = new SpanWriter(stackalloc byte[37]); + writer.Write((byte)0x1B); // PacketID + writer.Write(m.Serial); + writer.Write(0); + writer.Write((short)m.Body); + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((short)m.Z); + writer.Write((byte)m.Direction); + writer.Write((byte)0); + writer.Write(-1); + + writer.Write(0); + + var map = m.Map; + + if (map == null || map == Map.Internal) + { + map = m.LogoutMap; + } + + writer.Write((short)(map?.Width ?? Map.Felucca.Width)); + writer.Write((short)(map?.Height ?? Map.Felucca.Height)); + writer.Clear(writer.Capacity - writer.Position); // Remaining is zero + + ns.Send(writer.Span); + } + + /** * Packet: 0x55 * Length: 1 byte * * Sends login completion */ - public static void SendLoginComplete(this NetState ns) - { - ns?.Send(stackalloc byte[] { 0x55 }); - } + public static void SendLoginComplete(this NetState ns) + { + ns?.Send(stackalloc byte[] { 0x55 }); + } - /** + /** * Packet: 0x86 * Length: Up to 424 bytes * * Sends updated character list */ - public static void SendCharacterListUpdate(this NetState ns, IAccount a) + public static void SendCharacterListUpdate(this NetState ns, IAccount a) + { + if (ns == null || a == null) { - if (ns == null || a == null) - { - return; - } - - var highSlot = -1; - - for (var i = a.Length - 1; i >= 0; i--) - { - if (a[i] != null) - { - highSlot = i; - break; - } - } - - var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); - var length = 4 + count * 60; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x86); // Packet ID - writer.Write((ushort)length); - - writer.Write((byte)count); - - for (int i = 0; i < count; i++) - { - var m = a[i]; - - if (m == null) - { - writer.Clear(60); - } - else - { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - writer.WriteAscii(name, 30); - writer.Clear(30); // password - } - } - - ns.Send(writer.Span); + return; } - /** + var highSlot = -1; + + for (var i = a.Length - 1; i >= 0; i--) + { + if (a[i] != null) + { + highSlot = i; + break; + } + } + + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + var length = 4 + count * 60; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x86); // Packet ID + writer.Write((ushort)length); + + writer.Write((byte)count); + + for (int i = 0; i < count; i++) + { + var m = a[i]; + + if (m == null) + { + writer.Clear(60); + } + else + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + writer.WriteAscii(name, 30); + writer.Clear(30); // password + } + } + + ns.Send(writer.Span); + } + + /** * Packet: 0xA9 * Length: 1410 or more bytes * * Sends list of characters and starting cities. */ - public static void SendCharacterList(this NetState ns) + public static void SendCharacterList(this NetState ns) + { + var acct = ns?.Account; + + if (acct == null) { - var acct = ns?.Account; - - if (acct == null) - { - return; - } - - var client70130 = ns.NewCharacterList; - var textLength = client70130 ? 32 : 31; - - var cityInfo = ns.CityInfo; - - var highSlot = -1; - - for (var i = acct.Length - 1; i >= 0; i--) - { - if (acct[i] != null) - { - highSlot = i; - break; - } - } - - var count = Math.Max(Math.Max(highSlot + 1, acct.Limit), 5); - var length = (client70130 ? - 11 + (textLength * 2 + 25) * cityInfo.Length : - 9 + (textLength * 2 + 1) * cityInfo.Length) + count * 60; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xA9); // Packet ID - writer.Write((ushort)length); - writer.Write((byte)count); - - for (int i = 0; i < count; i++) - { - var m = acct[i]; - - if (m == null) - { - writer.Clear(60); - } - else - { - var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); - writer.WriteAscii(name, 30); - writer.Clear(30); // password - } - } - - writer.Write((byte)cityInfo.Length); - - for (int i = 0; i < cityInfo.Length; ++i) - { - var ci = cityInfo[i]; - - writer.Write((byte)i); - writer.WriteAscii(ci.City, textLength); - writer.WriteAscii(ci.Building, textLength); - if (client70130) - { - writer.Write(ci.X); - writer.Write(ci.Y); - writer.Write(ci.Z); - writer.Write(ci.Map?.MapID ?? 0); - writer.Write(ci.Description); - writer.Write(0); - } - } - - var flags = ExpansionInfo.CoreExpansion.CharacterListFlags; - - if (count > 6) - { - flags |= CharacterListFlags.SeventhCharacterSlot | - CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required? - } - else if (count == 6) - { - flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot - } - else if (acct.Limit == 1) - { - flags |= CharacterListFlags.SlotLimit & - CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character - } - - writer.Write((int)flags); - if (client70130) - { - writer.Write((short)-1); - } - - ns.Send(writer.Span); + return; } - /** + var client70130 = ns.NewCharacterList; + var textLength = client70130 ? 32 : 31; + + var cityInfo = ns.CityInfo; + + var highSlot = -1; + + for (var i = acct.Length - 1; i >= 0; i--) + { + if (acct[i] != null) + { + highSlot = i; + break; + } + } + + var count = Math.Max(Math.Max(highSlot + 1, acct.Limit), 5); + var length = (client70130 ? + 11 + (textLength * 2 + 25) * cityInfo.Length : + 9 + (textLength * 2 + 1) * cityInfo.Length) + count * 60; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xA9); // Packet ID + writer.Write((ushort)length); + writer.Write((byte)count); + + for (int i = 0; i < count; i++) + { + var m = acct[i]; + + if (m == null) + { + writer.Clear(60); + } + else + { + var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-"); + writer.WriteAscii(name, 30); + writer.Clear(30); // password + } + } + + writer.Write((byte)cityInfo.Length); + + for (int i = 0; i < cityInfo.Length; ++i) + { + var ci = cityInfo[i]; + + writer.Write((byte)i); + writer.WriteAscii(ci.City, textLength); + writer.WriteAscii(ci.Building, textLength); + if (client70130) + { + writer.Write(ci.X); + writer.Write(ci.Y); + writer.Write(ci.Z); + writer.Write(ci.Map?.MapID ?? 0); + writer.Write(ci.Description); + writer.Write(0); + } + } + + var flags = ExpansionInfo.CoreExpansion.CharacterListFlags; + + if (count > 6) + { + flags |= CharacterListFlags.SeventhCharacterSlot | + CharacterListFlags.SixthCharacterSlot; // 7th Character Slot - TODO: Is SixthCharacterSlot Required? + } + else if (count == 6) + { + flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot + } + else if (acct.Limit == 1) + { + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + } + + writer.Write((int)flags); + if (client70130) + { + writer.Write((short)-1); + } + + ns.Send(writer.Span); + } + + /** * Packet: 0x82 * Length: 2 bytes * * Sends a reason for rejecting the login */ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendAccountLoginRejected(this NetState ns, ALRReason reason) => - ns?.Send(stackalloc byte[] { 0x82, (byte)reason }); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendAccountLoginRejected(this NetState ns, ALRReason reason) => + ns?.Send(stackalloc byte[] { 0x82, (byte)reason }); - /** + /** * Packet: 0xA8 * Length: 6 + 40 bytes per server listing * * Sends login acknowledge with server listing */ - public static void SendAccountLoginAck(this NetState ns) + public static void SendAccountLoginAck(this NetState ns) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var info = ns.ServerInfo; - var length = 6 + 40 * info.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xA8); // Packet ID - writer.Write((ushort)length); - writer.Write((byte)0x5D); - writer.Write((ushort)info.Length); - - for (var i = 0; i < info.Length; ++i) - { - var si = info[i]; - - writer.Write((ushort)i); - writer.WriteAscii(si.Name, 32); - writer.Write((byte)si.FullPercent); - writer.Write((sbyte)si.TimeZone); - // UO only supports IPv4 - writer.Write(si.RawAddress); - } - - ns.Send(writer.Span); + return; } - /** + var info = ns.ServerInfo; + var length = 6 + 40 * info.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xA8); // Packet ID + writer.Write((ushort)length); + writer.Write((byte)0x5D); + writer.Write((ushort)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var si = info[i]; + + writer.Write((ushort)i); + writer.WriteAscii(si.Name, 32); + writer.Write((byte)si.FullPercent); + writer.Write((sbyte)si.TimeZone); + // UO only supports IPv4 + writer.Write(si.RawAddress); + } + + ns.Send(writer.Span); + } + + /** * Packet: 0x8C * Length: 11 bytes * * Sends acknowledge play server */ - public static void SendPlayServerAck(this NetState ns, ServerInfo si, int authId) + public static void SendPlayServerAck(this NetState ns, ServerInfo si, int authId) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[11]); - writer.Write((byte)0x8C); // Packet ID - - writer.WriteLE(si.RawAddress); - writer.Write((short)si.Address.Port); - writer.Write(authId); - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[11]); + writer.Write((byte)0x8C); // Packet ID + + writer.WriteLE(si.RawAddress); + writer.Write((short)si.Address.Port); + writer.Write(authId); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingCombatPackets.cs b/Projects/Server/Network/Packets/OutgoingCombatPackets.cs index e272a79cf..c5b23631b 100644 --- a/Projects/Server/Network/Packets/OutgoingCombatPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingCombatPackets.cs @@ -16,42 +16,41 @@ using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingCombatPackets { - public static class OutgoingCombatPackets + public static void SendSwing(this NetState ns, Serial attacker, Serial defender) { - public static void SendSwing(this NetState ns, Serial attacker, Serial defender) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[10]); - writer.Write((byte)0x2F); // Packet ID - writer.Write((byte)0); - writer.Write(attacker); - writer.Write(defender); - - ns.Send(writer.Span); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void SendSetWarMode(this NetState ns, bool warmode) => - ns?.Send(stackalloc byte[] { 0x72, *(byte*)&warmode, 0x00, 0x32, 0x00 }); + var writer = new SpanWriter(stackalloc byte[10]); + writer.Write((byte)0x2F); // Packet ID + writer.Write((byte)0); + writer.Write(attacker); + writer.Write(defender); - public static void SendChangeCombatant(this NetState ns, Serial combatant) + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void SendSetWarMode(this NetState ns, bool warmode) => + ns?.Send(stackalloc byte[] { 0x72, *(byte*)&warmode, 0x00, 0x32, 0x00 }); + + public static void SendChangeCombatant(this NetState ns, Serial combatant) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[5]); - writer.Write((byte)0xAA); // Packet ID - writer.Write(combatant); - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[5]); + writer.Write((byte)0xAA); // Packet ID + writer.Write(combatant); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingContainerPackets.cs b/Projects/Server/Network/Packets/OutgoingContainerPackets.cs index 69d6ffb01..d664e40f6 100644 --- a/Projects/Server/Network/Packets/OutgoingContainerPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingContainerPackets.cs @@ -17,194 +17,193 @@ using System; using System.Buffers; using System.IO; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingContainerPackets { - public static class OutgoingContainerPackets + public static void SendDisplaySpellbook(this NetState ns, Serial book) => ns.SendDisplayContainer(book, -1); + + public static void SendSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) { - public static void SendDisplaySpellbook(this NetState ns, Serial book) => ns.SendDisplayContainer(book, -1); - - public static void SendSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - if (ObjectPropertyList.Enabled && ns.NewSpellbook) - { - ns.SendNewSpellbookContent(book, graphic, offset, content); - } - else - { - ns.SendOldSpellbookContent(book, offset, content); - } + return; } - public static void SendNewSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) + if (ObjectPropertyList.Enabled && ns.NewSpellbook) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[23]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)23); // Length - writer.Write((short)0x1B); // Subpacket - writer.Write((short)0x01); // Command - - writer.Write(book); - writer.Write((short)graphic); - writer.Write((short)offset); - - for (var i = 0; i < 8; ++i) - { - writer.Write((byte)(content >> (i * 8))); - } - - ns.Send(writer.Span); + ns.SendNewSpellbookContent(book, graphic, offset, content); } - - public static void SendOldSpellbookContent(this NetState ns, Serial book, int offset, ulong content) + else { - if (ns == null) - { - return; - } - - var count = content.NumberOfSetBits(); - var length = 5 + count * (ns.ContainerGridLines ? 20 : 19); - - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x3C); // Packet ID - writer.Write((ushort)length); - writer.Write((ushort)count); - - ulong mask = 1; - for (var i = 0; i < 64; ++i, mask <<= 1) - { - if ((content & mask) != 0) - { - writer.Write(0x7FFFFFFF - i); - writer.Write((ushort)0); // child ItemID - writer.Write((byte)0); // ItemID offset - writer.Write((ushort)(i + offset)); // Amount - writer.Write(0); // X, Y - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid Location - } - writer.Write(book); - writer.Write((short)0); // Quest Hue - } - } - - ns.Send(writer.Span); - } - - public static void SendDisplayContainer(this NetState ns, Serial cont, int gumpId) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]); - writer.Write((byte)0x24); // Packet ID - writer.Write(cont); - writer.Write((ushort)gumpId); - if (ns.HighSeas) - { - writer.Write((short)0x7D); - } - - ns.Send(writer.Span); - } - - public static void SendContainerContentUpdate(this NetState ns, Item item) - { - if (ns == null) - { - return; - } - - Serial parentSerial; - - if (item.Parent is Item parentItem) - { - parentSerial = parentItem.Serial; - } - else - { - Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)"); - parentSerial = Serial.Zero; - } - - var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]); - writer.Write((byte)0x25); // Packet ID - writer.Write(item.Serial); - writer.Write((ushort)item.ItemID); - writer.Write((byte)0); // signed, itemID offset - writer.Write((ushort)Math.Min(item.Amount, ushort.MaxValue)); - writer.Write((short)item.X); - writer.Write((short)item.Y); - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid Location? - } - writer.Write(parentSerial); - writer.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)); - - ns.Send(writer.Span); - } - - public static void SendContainerContent(this NetState ns, Mobile beholder, Item beheld) - { - if (ns == null) - { - return; - } - - var items = beheld.Items; - var count = items.Count; - - var writer = new SpanWriter(stackalloc byte[5 + items.Count * (ns.ContainerGridLines ? 20 : 19)]); - writer.Write((byte)0x3C); // Packet ID - writer.Seek(4, SeekOrigin.Current); // Length & written count - - var written = 0; - - for (var i = 0; i < count; ++i) - { - var child = items[i]; - - if (!child.Deleted && beholder.CanSee(child)) - { - var loc = child.Location; - - writer.Write(child.Serial); - writer.Write((ushort)child.ItemID); - writer.Write((byte)0); // signed, itemID offset - writer.Write((ushort)Math.Min(child.Amount, ushort.MaxValue)); - writer.Write((short)loc.X); - writer.Write((short)loc.Y); - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid Location? - } - writer.Write(beheld.Serial); - writer.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)); - - ++written; - } - } - - writer.Seek(1, SeekOrigin.Begin); - writer.Write((ushort)writer.BytesWritten); - writer.Write((ushort)written); - writer.Seek(0, SeekOrigin.End); - - ns.Send(writer.Span); + ns.SendOldSpellbookContent(book, offset, content); } } + + public static void SendNewSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[23]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)23); // Length + writer.Write((short)0x1B); // Subpacket + writer.Write((short)0x01); // Command + + writer.Write(book); + writer.Write((short)graphic); + writer.Write((short)offset); + + for (var i = 0; i < 8; ++i) + { + writer.Write((byte)(content >> (i * 8))); + } + + ns.Send(writer.Span); + } + + public static void SendOldSpellbookContent(this NetState ns, Serial book, int offset, ulong content) + { + if (ns.CannotSendPackets()) + { + return; + } + + var count = content.NumberOfSetBits(); + var length = 5 + count * (ns.ContainerGridLines ? 20 : 19); + + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x3C); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)count); + + ulong mask = 1; + for (var i = 0; i < 64; ++i, mask <<= 1) + { + if ((content & mask) != 0) + { + writer.Write(0x7FFFFFFF - i); + writer.Write((ushort)0); // child ItemID + writer.Write((byte)0); // ItemID offset + writer.Write((ushort)(i + offset)); // Amount + writer.Write(0); // X, Y + if (ns.ContainerGridLines) + { + writer.Write((byte)0); // Grid Location + } + writer.Write(book); + writer.Write((short)0); // Quest Hue + } + } + + ns.Send(writer.Span); + } + + public static void SendDisplayContainer(this NetState ns, Serial cont, int gumpId) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]); + writer.Write((byte)0x24); // Packet ID + writer.Write(cont); + writer.Write((ushort)gumpId); + if (ns.HighSeas) + { + writer.Write((short)0x7D); + } + + ns.Send(writer.Span); + } + + public static void SendContainerContentUpdate(this NetState ns, Item item) + { + if (ns.CannotSendPackets()) + { + return; + } + + Serial parentSerial; + + if (item.Parent is Item parentItem) + { + parentSerial = parentItem.Serial; + } + else + { + Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)"); + parentSerial = Serial.Zero; + } + + var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]); + writer.Write((byte)0x25); // Packet ID + writer.Write(item.Serial); + writer.Write((ushort)item.ItemID); + writer.Write((byte)0); // signed, itemID offset + writer.Write((ushort)Math.Min(item.Amount, ushort.MaxValue)); + writer.Write((short)item.X); + writer.Write((short)item.Y); + if (ns.ContainerGridLines) + { + writer.Write((byte)0); // Grid Location? + } + writer.Write(parentSerial); + writer.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue)); + + ns.Send(writer.Span); + } + + public static void SendContainerContent(this NetState ns, Mobile beholder, Item beheld) + { + if (ns.CannotSendPackets()) + { + return; + } + + var items = beheld.Items; + var count = items.Count; + + var writer = new SpanWriter(stackalloc byte[5 + items.Count * (ns.ContainerGridLines ? 20 : 19)]); + writer.Write((byte)0x3C); // Packet ID + writer.Seek(4, SeekOrigin.Current); // Length & written count + + var written = 0; + + for (var i = 0; i < count; ++i) + { + var child = items[i]; + + if (!child.Deleted && beholder.CanSee(child)) + { + var loc = child.Location; + + writer.Write(child.Serial); + writer.Write((ushort)child.ItemID); + writer.Write((byte)0); // signed, itemID offset + writer.Write((ushort)Math.Min(child.Amount, ushort.MaxValue)); + writer.Write((short)loc.X); + writer.Write((short)loc.Y); + if (ns.ContainerGridLines) + { + writer.Write((byte)0); // Grid Location? + } + writer.Write(beheld.Serial); + writer.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue)); + + ++written; + } + } + + writer.Seek(1, SeekOrigin.Begin); + writer.Write((ushort)writer.BytesWritten); + writer.Write((ushort)written); + writer.Seek(0, SeekOrigin.End); + + ns.Send(writer.Span); + } } diff --git a/Projects/Server/Network/Packets/OutgoingDamagePackets.cs b/Projects/Server/Network/Packets/OutgoingDamagePackets.cs index 02e3b989b..41d99d0b9 100644 --- a/Projects/Server/Network/Packets/OutgoingDamagePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingDamagePackets.cs @@ -16,36 +16,35 @@ using System; using System.Buffers; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingDamagePackets { - public static class OutgoingDamagePackets + public static void SendDamage(this NetState ns, Serial serial, int amount) { - public static void SendDamage(this NetState ns, Serial serial, int amount) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[ns.DamagePacket ? 7 : 11]); - - if (ns.DamagePacket) - { - writer.Write((byte)0x0B); // Packet ID - writer.Write(serial); - writer.Write((ushort)Math.Clamp(amount, 0, 0xFFFF)); - } - else - { - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)11); // Length - writer.Write((ushort)0x22); - writer.Write((byte)1); - writer.Write(serial); - writer.Write((byte)Math.Clamp(amount, 0, 0xFF)); - } - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[ns.DamagePacket ? 7 : 11]); + + if (ns.DamagePacket) + { + writer.Write((byte)0x0B); // Packet ID + writer.Write(serial); + writer.Write((ushort)Math.Clamp(amount, 0, 0xFFFF)); + } + else + { + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)11); // Length + writer.Write((ushort)0x22); + writer.Write((byte)1); + writer.Write(serial); + writer.Write((byte)Math.Clamp(amount, 0, 0xFF)); + } + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingEffectPackets.cs b/Projects/Server/Network/Packets/OutgoingEffectPackets.cs index 1b09c042e..1b3d80c8c 100644 --- a/Projects/Server/Network/Packets/OutgoingEffectPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEffectPackets.cs @@ -16,325 +16,324 @@ using System; using System.Buffers; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingEffectPackets { - public static class OutgoingEffectPackets + public const int SoundPacketLength = 12; + public const int ParticleEffectLength = 49; + public const int HuedEffectLength = 36; + public const int BoltEffectLength = 36; + + public static void SendSoundEffect(this NetState ns, int soundID, IPoint3D target) { - public const int SoundPacketLength = 12; - public const int ParticleEffectLength = 49; - public const int HuedEffectLength = 36; - public const int BoltEffectLength = 36; - - public static void SendSoundEffect(this NetState ns, int soundID, IPoint3D target) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[SoundPacketLength].InitializePacket(); - CreateSoundEffect(buffer, soundID, target); - - ns.Send(buffer); + return; } - public static void CreateSoundEffect(Span buffer, int soundID, IPoint3D target) - { - if (buffer[0] != 0) - { - return; - } + Span buffer = stackalloc byte[SoundPacketLength].InitializePacket(); + CreateSoundEffect(buffer, soundID, target); - var writer = new SpanWriter(buffer); - writer.Write((byte)0x54); // Packet ID - writer.Write((byte)1); // flags - writer.Write((short)soundID); - writer.Write((short)0); // volume - writer.Write((short)target.X); - writer.Write((short)target.Y); - writer.Write((short)target.Z); + ns.Send(buffer); + } + + public static void CreateSoundEffect(Span buffer, int soundID, IPoint3D target) + { + if (buffer[0] != 0) + { + return; } - public static void CreateParticleEffect( - Span buffer, - EffectType type, Serial from, Serial to, int itemID, IPoint3D fromPoint, IPoint3D toPoint, - int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode, int effect, - int explodeEffect, int explodeSound, Serial serial, int layer, int unknown - ) - { - if (buffer[0] != 0) - { - return; - } + var writer = new SpanWriter(buffer); + writer.Write((byte)0x54); // Packet ID + writer.Write((byte)1); // flags + writer.Write((short)soundID); + writer.Write((short)0); // volume + writer.Write((short)target.X); + writer.Write((short)target.Y); + writer.Write((short)target.Z); + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0xC7); // Packet ID - writer.Write((byte)type); - writer.Write(from); - writer.Write(to); - writer.Write((short)itemID); - writer.Write((short)fromPoint.X); - writer.Write((short)fromPoint.Y); - writer.Write((sbyte)fromPoint.Z); - writer.Write((short)toPoint.X); - writer.Write((short)toPoint.Y); - writer.Write((sbyte)toPoint.Z); - writer.Write((byte)speed); - writer.Write((byte)duration); - writer.Write((byte)0); - writer.Write((byte)0); - writer.Write(fixedDirection); - writer.Write(explode); - writer.Write(hue); - writer.Write(renderMode); - writer.Write((short)effect); - writer.Write((short)explodeEffect); - writer.Write((short)explodeSound); - writer.Write(serial); - writer.Write((byte)layer); - writer.Write((short)unknown); + public static void CreateParticleEffect( + Span buffer, + EffectType type, Serial from, Serial to, int itemID, IPoint3D fromPoint, IPoint3D toPoint, + int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode, int effect, + int explodeEffect, int explodeSound, Serial serial, int layer, int unknown + ) + { + if (buffer[0] != 0) + { + return; } - public static void CreateTargetParticleEffect( - Span buffer, - IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int layer, int unknown - ) => CreateParticleEffect( - buffer, - EffectType.FixedFrom, - e.Serial, - Serial.Zero, - itemID, - e.Location, - e.Location, - speed, - duration, - true, - false, - hue, - renderMode, - effect, - 1, - 0, - e.Serial, - layer, - unknown - ); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xC7); // Packet ID + writer.Write((byte)type); + writer.Write(from); + writer.Write(to); + writer.Write((short)itemID); + writer.Write((short)fromPoint.X); + writer.Write((short)fromPoint.Y); + writer.Write((sbyte)fromPoint.Z); + writer.Write((short)toPoint.X); + writer.Write((short)toPoint.Y); + writer.Write((sbyte)toPoint.Z); + writer.Write((byte)speed); + writer.Write((byte)duration); + writer.Write((byte)0); + writer.Write((byte)0); + writer.Write(fixedDirection); + writer.Write(explode); + writer.Write(hue); + writer.Write(renderMode); + writer.Write((short)effect); + writer.Write((short)explodeEffect); + writer.Write((short)explodeSound); + writer.Write(serial); + writer.Write((byte)layer); + writer.Write((short)unknown); + } - public static void CreateLocationParticleEffect( - Span buffer, - IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int unknown - ) => CreateParticleEffect( - buffer, - EffectType.FixedXYZ, - e.Serial, - Serial.Zero, - itemID, - e.Location, - e.Location, - speed, - duration, - true, - false, - hue, - renderMode, - effect, - 1, - 0, - e.Serial, - 255, - unknown - ); + public static void CreateTargetParticleEffect( + Span buffer, + IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int layer, int unknown + ) => CreateParticleEffect( + buffer, + EffectType.FixedFrom, + e.Serial, + Serial.Zero, + itemID, + e.Location, + e.Location, + speed, + duration, + true, + false, + hue, + renderMode, + effect, + 1, + 0, + e.Serial, + layer, + unknown + ); - public static void CreateMovingParticleEffect( - Span buffer, - IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, - bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, EffectLayer layer, - int unknown - ) => CreateParticleEffect( - buffer, - EffectType.Moving, - from.Serial, - to.Serial, - itemID, - from.Location, - to.Location, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode, - effect, - explodeEffect, - explodeSound, - Serial.Zero, - (int)layer, - unknown - ); + public static void CreateLocationParticleEffect( + Span buffer, + IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int unknown + ) => CreateParticleEffect( + buffer, + EffectType.FixedXYZ, + e.Serial, + Serial.Zero, + itemID, + e.Location, + e.Location, + speed, + duration, + true, + false, + hue, + renderMode, + effect, + 1, + 0, + e.Serial, + 255, + unknown + ); - public static void CreateHuedEffect( - Span buffer, - EffectType type, Serial from, Serial to, int itemID, Point3D fromPoint, Point3D toPoint, - int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode - ) + public static void CreateMovingParticleEffect( + Span buffer, + IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, + bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, EffectLayer layer, + int unknown + ) => CreateParticleEffect( + buffer, + EffectType.Moving, + from.Serial, + to.Serial, + itemID, + from.Location, + to.Location, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode, + effect, + explodeEffect, + explodeSound, + Serial.Zero, + (int)layer, + unknown + ); + + public static void CreateHuedEffect( + Span buffer, + EffectType type, Serial from, Serial to, int itemID, Point3D fromPoint, Point3D toPoint, + int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode + ) + { + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xC0); // Packet ID - writer.Write((byte)type); - writer.Write(from); - writer.Write(to); - writer.Write((short)itemID); - writer.Write((short)fromPoint.X); - writer.Write((short)fromPoint.Y); - writer.Write((sbyte)fromPoint.Z); - writer.Write((short)toPoint.X); - writer.Write((short)toPoint.Y); - writer.Write((sbyte)toPoint.Z); - writer.Write((byte)speed); - writer.Write((byte)duration); - writer.Write((byte)0); - writer.Write((byte)0); - writer.Write(fixedDirection); - writer.Write(explode); - writer.Write(hue); - writer.Write(renderMode); + return; } - public static void CreateTargetHuedEffect( - Span buffer, - IEntity e, int itemID, int speed, int duration, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.FixedFrom, - e.Serial, - Serial.Zero, - itemID, - e.Location, - e.Location, - speed, - duration, - true, - false, - hue, - renderMode - ); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xC0); // Packet ID + writer.Write((byte)type); + writer.Write(from); + writer.Write(to); + writer.Write((short)itemID); + writer.Write((short)fromPoint.X); + writer.Write((short)fromPoint.Y); + writer.Write((sbyte)fromPoint.Z); + writer.Write((short)toPoint.X); + writer.Write((short)toPoint.Y); + writer.Write((sbyte)toPoint.Z); + writer.Write((byte)speed); + writer.Write((byte)duration); + writer.Write((byte)0); + writer.Write((byte)0); + writer.Write(fixedDirection); + writer.Write(explode); + writer.Write(hue); + writer.Write(renderMode); + } - public static void CreateLocationHuedEffect( - Span buffer, - Point3D p, int itemID, int speed, int duration, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.FixedXYZ, - Serial.Zero, - Serial.Zero, - itemID, - p, - p, - speed, - duration, - true, - false, - hue, - renderMode - ); + public static void CreateTargetHuedEffect( + Span buffer, + IEntity e, int itemID, int speed, int duration, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.FixedFrom, + e.Serial, + Serial.Zero, + itemID, + e.Location, + e.Location, + speed, + duration, + true, + false, + hue, + renderMode + ); - public static void CreateMovingHuedEffect( - Span buffer, - IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, - bool explodes, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.Moving, - from.Serial, - to.Serial, - itemID, - from.Location, - to.Location, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode - ); + public static void CreateLocationHuedEffect( + Span buffer, + Point3D p, int itemID, int speed, int duration, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.FixedXYZ, + Serial.Zero, + Serial.Zero, + itemID, + p, + p, + speed, + duration, + true, + false, + hue, + renderMode + ); - public static void CreateMovingHuedEffect( - Span buffer, - int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration, - bool fixedDirection, bool explodes, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.Moving, - Serial.Zero, - Serial.Zero, - itemID, - fromLocation, - toLocation, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode - ); + public static void CreateMovingHuedEffect( + Span buffer, + IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, + bool explodes, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.Moving, + from.Serial, + to.Serial, + itemID, + from.Location, + to.Location, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode + ); - public static void CreateMovingHuedEffect( - Span buffer, - Serial from, Serial to, int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration, - bool fixedDirection, bool explodes, int hue, int renderMode - ) => CreateHuedEffect( - buffer, - EffectType.Moving, - from, - to, - itemID, - fromLocation, - toLocation, - speed, - duration, - fixedDirection, - explodes, - hue, - renderMode - ); + public static void CreateMovingHuedEffect( + Span buffer, + int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration, + bool fixedDirection, bool explodes, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.Moving, + Serial.Zero, + Serial.Zero, + itemID, + fromLocation, + toLocation, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode + ); - public static void CreateBoltEffect(Span buffer, IEntity target, int hue) => CreateHuedEffect( - buffer, - EffectType.Lightning, - target.Serial, - Serial.Zero, - 0, - target.Location, - target.Location, - 0, - 0, - false, - false, - hue, - 0 - ); + public static void CreateMovingHuedEffect( + Span buffer, + Serial from, Serial to, int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration, + bool fixedDirection, bool explodes, int hue, int renderMode + ) => CreateHuedEffect( + buffer, + EffectType.Moving, + from, + to, + itemID, + fromLocation, + toLocation, + speed, + duration, + fixedDirection, + explodes, + hue, + renderMode + ); - public static void SendScreenEffect(this NetState ns, ScreenEffectType type) + public static void CreateBoltEffect(Span buffer, IEntity target, int hue) => CreateHuedEffect( + buffer, + EffectType.Lightning, + target.Serial, + Serial.Zero, + 0, + target.Location, + target.Location, + 0, + 0, + false, + false, + hue, + 0 + ); + + public static void SendScreenEffect(this NetState ns, ScreenEffectType type) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[28]); - - writer.Write((byte)0x70); // Packet ID - writer.Write((byte)0x4); - writer.Clear(8); - writer.Write((ushort)type); - writer.Clear(16); - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[28]); + + writer.Write((byte)0x70); // Packet ID + writer.Write((byte)0x4); + writer.Clear(8); + writer.Write((ushort)type); + writer.Clear(16); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingEntityPackets.cs b/Projects/Server/Network/Packets/OutgoingEntityPackets.cs index 5bb608e36..cf84157e2 100644 --- a/Projects/Server/Network/Packets/OutgoingEntityPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEntityPackets.cs @@ -17,135 +17,134 @@ using System; using System.Buffers; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingEntityPackets { - public static class OutgoingEntityPackets + public const int OPLPacketLength = 9; + public const int RemoveEntityLength = 5; + public const int MaxWorldEntityPacketLength = 26; + + public static void CreateOPLInfo(Span buffer, Item item) => + CreateOPLInfo(buffer, item.Serial, item.PropertyList.Hash); + + public static void CreateOPLInfo(Span buffer, Serial serial, int hash) { - public const int OPLPacketLength = 9; - public const int RemoveEntityLength = 5; - public const int MaxWorldEntityPacketLength = 26; - - public static void CreateOPLInfo(Span buffer, Item item) => - CreateOPLInfo(buffer, item.Serial, item.PropertyList.Hash); - - public static void CreateOPLInfo(Span buffer, Serial serial, int hash) + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xDC); // Packet ID - writer.Write(serial); - writer.Write(hash); + return; } - public static void SendOPLInfo(this NetState ns, IPropertyListObject obj) => - ns.SendOPLInfo(obj.Serial, obj.PropertyList.Hash); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xDC); // Packet ID + writer.Write(serial); + writer.Write(hash); + } - public static void SendOPLInfo(this NetState ns, Serial serial, int hash) + public static void SendOPLInfo(this NetState ns, IPropertyListObject obj) => + ns.SendOPLInfo(obj.Serial, obj.PropertyList.Hash); + + public static void SendOPLInfo(this NetState ns, Serial serial, int hash) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[OPLPacketLength].InitializePacket(); - CreateOPLInfo(buffer, serial, hash); - - ns.Send(buffer); + return; } - public static void CreateRemoveEntity(Span buffer, Serial serial) - { - if (buffer[0] != 0) - { - return; - } + Span buffer = stackalloc byte[OPLPacketLength].InitializePacket(); + CreateOPLInfo(buffer, serial, hash); - var writer = new SpanWriter(buffer); - writer.Write((byte)0x1D); // Packet ID - writer.Write(serial); + ns.Send(buffer); + } + + public static void CreateRemoveEntity(Span buffer, Serial serial) + { + if (buffer[0] != 0) + { + return; } - public static void SendRemoveEntity(this NetState ns, Serial serial) + var writer = new SpanWriter(buffer); + writer.Write((byte)0x1D); // Packet ID + writer.Write(serial); + } + + public static void SendRemoveEntity(this NetState ns, Serial serial) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[RemoveEntityLength].InitializePacket(); - CreateRemoveEntity(buffer, serial); - - ns.Send(buffer); + return; } - public static int CreateWorldEntity(Span buffer, IEntity entity, bool isHS) + Span buffer = stackalloc byte[RemoveEntityLength].InitializePacket(); + CreateRemoveEntity(buffer, serial); + + ns.Send(buffer); + } + + public static int CreateWorldEntity(Span buffer, IEntity entity, bool isHS) + { + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - return buffer.Length; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xF3); // Packet ID - writer.Write((short)0x1); // command - - int type = 0; - int gfx = 0; - int amount = 1; - int hue = 0; - byte light = 0; - int flags = 0; - - if (entity is BaseMulti multi) - { - type = 2; - gfx = multi.ItemID & (isHS ? 0xFFFF : 0x7FFF); - hue = multi.Hue; - amount = multi.Amount; - } - else if (entity is Item item) - { - // type = 3 if is damageable - gfx = item.ItemID & (isHS ? 0xFFFF : 0x7FFF); - hue = item.Hue; - amount = item.Amount; - light = (byte)item.Light; - flags = item.GetPacketFlags(); - } - else if (entity is Mobile mobile) - { - type = 1; - gfx = mobile.Body; - hue = mobile.Hue; - flags = mobile.GetPacketFlags(true); - } - - writer.Write((byte)type); - writer.Write(entity.Serial); - writer.Write((ushort)gfx); - writer.Write((byte)0); - - writer.Write((short)amount); // Min - writer.Write((short)amount); // Max - - writer.Write((short)(entity.X & 0x7FFF)); - writer.Write((short)(entity.Y & 0x3FFF)); - writer.Write((sbyte)entity.Z); - - writer.Write(light); - writer.Write((short)hue); - writer.Write((byte)flags); - - if (isHS) - { - writer.Write((short)0); - } - - return writer.Position; + return buffer.Length; } + + var writer = new SpanWriter(buffer); + writer.Write((byte)0xF3); // Packet ID + writer.Write((short)0x1); // command + + int type = 0; + int gfx = 0; + int amount = 1; + int hue = 0; + byte light = 0; + int flags = 0; + + if (entity is BaseMulti multi) + { + type = 2; + gfx = multi.ItemID & (isHS ? 0xFFFF : 0x7FFF); + hue = multi.Hue; + amount = multi.Amount; + } + else if (entity is Item item) + { + // type = 3 if is damageable + gfx = item.ItemID & (isHS ? 0xFFFF : 0x7FFF); + hue = item.Hue; + amount = item.Amount; + light = (byte)item.Light; + flags = item.GetPacketFlags(); + } + else if (entity is Mobile mobile) + { + type = 1; + gfx = mobile.Body; + hue = mobile.Hue; + flags = mobile.GetPacketFlags(true); + } + + writer.Write((byte)type); + writer.Write(entity.Serial); + writer.Write((ushort)gfx); + writer.Write((byte)0); + + writer.Write((short)amount); // Min + writer.Write((short)amount); // Max + + writer.Write((short)(entity.X & 0x7FFF)); + writer.Write((short)(entity.Y & 0x3FFF)); + writer.Write((sbyte)entity.Z); + + writer.Write(light); + writer.Write((short)hue); + writer.Write((byte)flags); + + if (isHS) + { + writer.Write((short)0); + } + + return writer.Position; } } diff --git a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs index 7f2a0a3f1..543203f6e 100644 --- a/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEquipmentPackets.cs @@ -17,108 +17,107 @@ using System; using System.Buffers; using System.Collections.Generic; -namespace Server.Network +namespace Server.Network; + +public class EquipInfoAttribute { - public class EquipInfoAttribute + public EquipInfoAttribute(int number, int charges = -1) { - public EquipInfoAttribute(int number, int charges = -1) - { - Number = number; - Charges = charges; - } - - public int Number { get; } - - public int Charges { get; } + Number = number; + Charges = charges; } - public static class OutgoingEquipmentPackets + public int Number { get; } + + public int Charges { get; } +} + +public static class OutgoingEquipmentPackets +{ + public static void SendDisplayEquipmentInfo( + this NetState ns, + Serial serial, int number, string crafterName, bool unidentified, List attrs + ) { - public static void SendDisplayEquipmentInfo( - this NetState ns, - Serial serial, int number, string crafterName, bool unidentified, List attrs - ) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - crafterName = crafterName.DefaultIfNullOrEmpty(""); - - var length = 17 + - (crafterName.Length > 0 ? 6 + crafterName.Length : 0) + - (unidentified ? 4 : 0) + - attrs.Count * 6; - - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)length); - writer.Write((ushort)0x10); // Subpacket - writer.Write(serial); - writer.Write(number); - - if (crafterName.Length > 0) - { - writer.Write(-3); // crafted by - - writer.Write((ushort)crafterName.Length); - writer.WriteAscii(crafterName); - } - - if (unidentified) - { - writer.Write(-4); - } - - for (var i = 0; i < attrs.Count; ++i) - { - var attr = attrs[i]; - writer.Write(attr.Number); - writer.Write((short)attr.Charges); - } - - writer.Write(-1); - - ns.Send(writer.Span); + return; } - public static void SendEquipUpdate(this NetState ns, Item item) + crafterName = crafterName.DefaultIfNullOrEmpty(""); + + var length = 17 + + (crafterName.Length > 0 ? 6 + crafterName.Length : 0) + + (unidentified ? 4 : 0) + + attrs.Count * 6; + + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0x10); // Subpacket + writer.Write(serial); + writer.Write(number); + + if (crafterName.Length > 0) { - if (ns == null) - { - return; - } + writer.Write(-3); // crafted by - Serial parentSerial; - - var parent = item.Parent as Mobile; - var hue = item.Hue; - - if (parent != null) - { - parentSerial = parent.Serial; - - if (parent.SolidHueOverride >= 0) - { - hue = parent.SolidHueOverride; - } - } - else - { - Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)"); - parentSerial = Serial.Zero; - } - - var writer = new SpanWriter(stackalloc byte[15]); - writer.Write((byte)0x2E); // Packet ID - writer.Write(item.Serial); - writer.Write((short)item.ItemID); - writer.Write((ushort)item.Layer); - writer.Write(parentSerial); - writer.Write((short)hue); - - ns.Send(writer.Span); + writer.Write((ushort)crafterName.Length); + writer.WriteAscii(crafterName); } + + if (unidentified) + { + writer.Write(-4); + } + + for (var i = 0; i < attrs.Count; ++i) + { + var attr = attrs[i]; + writer.Write(attr.Number); + writer.Write((short)attr.Charges); + } + + writer.Write(-1); + + ns.Send(writer.Span); + } + + public static void SendEquipUpdate(this NetState ns, Item item) + { + if (ns.CannotSendPackets()) + { + return; + } + + Serial parentSerial; + + var parent = item.Parent as Mobile; + var hue = item.Hue; + + if (parent != null) + { + parentSerial = parent.Serial; + + if (parent.SolidHueOverride >= 0) + { + hue = parent.SolidHueOverride; + } + } + else + { + Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)"); + parentSerial = Serial.Zero; + } + + var writer = new SpanWriter(stackalloc byte[15]); + writer.Write((byte)0x2E); // Packet ID + writer.Write(item.Serial); + writer.Write((short)item.ItemID); + writer.Write((ushort)item.Layer); + writer.Write(parentSerial); + writer.Write((short)hue); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingGumpPackets.cs b/Projects/Server/Network/Packets/OutgoingGumpPackets.cs index 8fab20ff2..9460f9c8f 100644 --- a/Projects/Server/Network/Packets/OutgoingGumpPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingGumpPackets.cs @@ -22,200 +22,199 @@ using Server.Collections; using Server.Gumps; using Server.Logging; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingGumpPackets { - public static class OutgoingGumpPackets + private static readonly ILogger logger = LogFactory.GetLogger(typeof(OutgoingGumpPackets)); + + public static void SendCloseGump(this NetState ns, int typeId, int buttonId) { - private static readonly ILogger logger = LogFactory.GetLogger(typeof(OutgoingGumpPackets)); - - public static void SendCloseGump(this NetState ns, int typeId, int buttonId) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[13]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)13); - - writer.Write((short)0x04); - writer.Write(typeId); - writer.Write(buttonId); - - ns.Send(writer.Span); + return; } - private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray(0x20000); - private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray(0x20000); - private static readonly byte[] _packBuffer = GC.AllocateUninitializedArray(0x20000); - private static readonly OrderedHashSet _stringsList = new(32); + var writer = new SpanWriter(stackalloc byte[13]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)13); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WritePacked(ReadOnlySpan span, ref SpanWriter writer) + writer.Write((short)0x04); + writer.Write(typeId); + writer.Write(buttonId); + + ns.Send(writer.Span); + } + + private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray(0x20000); + private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray(0x20000); + private static readonly byte[] _packBuffer = GC.AllocateUninitializedArray(0x20000); + private static readonly OrderedHashSet _stringsList = new(32); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WritePacked(ReadOnlySpan span, ref SpanWriter writer) + { + var length = span.Length; + + if (length == 0) { - var length = span.Length; - - if (length == 0) - { - writer.Write(0); - return; - } - - var wantLength = Zlib.MaxPackSize(length); - var packBuffer = _packBuffer; - byte[] rentedBuffer = null; - - if (wantLength > packBuffer.Length) - { - packBuffer = rentedBuffer = ArrayPool.Shared.Rent(wantLength); - } - - var packLength = wantLength; - - var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default); - - if (error != ZlibError.Okay) - { - logger.Warning($"Gump compression failed {error}"); - - writer.Write(4); - writer.Write(0); - return; - } - - writer.Write(4 + packLength); - writer.Write(length); - writer.Write(packBuffer.AsSpan(0, packLength)); - - if (rentedBuffer != null) - { - ArrayPool.Shared.Return(rentedBuffer); - } + writer.Write(0); + return; } - public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries) + var wantLength = Zlib.MaxPackSize(length); + var packBuffer = _packBuffer; + byte[] rentedBuffer = null; + + if (wantLength > packBuffer.Length) { - switches = 0; - entries = 0; - - if (ns == null) - { - return; - } - - var packed = ns.Unpack; - - var layoutWriter = new SpanWriter(_layoutBuffer); - - if (!gump.Draggable) - { - layoutWriter.Write(Gump.NoMove); - } - - if (!gump.Closable) - { - layoutWriter.Write(Gump.NoClose); - } - - if (!gump.Disposable) - { - layoutWriter.Write(Gump.NoDispose); - } - - if (!gump.Resizable) - { - layoutWriter.Write(Gump.NoResize); - } - - foreach (var entry in gump.Entries) - { - entry.AppendTo(ref layoutWriter, _stringsList, ref entries, ref switches); - } - - var stringsWriter = new SpanWriter(_stringsBuffer); - - foreach (var str in _stringsList) - { - var s = str ?? ""; - stringsWriter.Write((ushort)s.Length); - stringsWriter.WriteBigUni(s); - } - - int maxLength; - if (packed) - { - var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten); - var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten); - maxLength = 40 + worstLayoutLength + worstStringsLength; - } - else - { - maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten; - } - - var writer = new SpanWriter(maxLength); - writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID - writer.Seek(2, SeekOrigin.Current); - - writer.Write(gump.Serial); - writer.Write(gump.TypeID); - writer.Write(gump.X); - writer.Write(gump.Y); - - if (packed) - { - layoutWriter.Write((byte)0); // Layout text terminator - WritePacked(layoutWriter.Span, ref writer); - - writer.Write(_stringsList.Count); - WritePacked(stringsWriter.Span, ref writer); - } - else - { - writer.Write((ushort)layoutWriter.BytesWritten); - writer.Write(layoutWriter.Span); - - writer.Write((ushort)_stringsList.Count); - writer.Write(stringsWriter.Span); - } - - writer.WritePacketLength(); - - ns.Send(writer.Span); - - layoutWriter.Dispose(); // Just in case - stringsWriter.Dispose(); // Just in case - - if (_stringsList.Count > 0) - { - _stringsList.Clear(); - } + packBuffer = rentedBuffer = ArrayPool.Shared.Rent(wantLength); } - public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption) + var packLength = wantLength; + + var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default); + + if (error != ZlibError.Okay) { - if (ns == null) - { - return; - } + logger.Warning($"Gump compression failed {error}"); - unknown ??= ""; - caption ??= ""; + writer.Write(4); + writer.Write(0); + return; + } - var length = 15 + unknown.Length + caption.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x8B); // Packet ID - writer.Write((ushort)length); + writer.Write(4 + packLength); + writer.Write(length); + writer.Write(packBuffer.AsSpan(0, packLength)); - writer.Write(serial); - writer.Write((short)gumpId); - writer.Write((short)(unknown.Length + 1)); - writer.WriteAsciiNull(unknown); - writer.Write((short)(caption.Length + 1)); - writer.WriteAsciiNull(caption); - - ns.Send(writer.Span); + if (rentedBuffer != null) + { + ArrayPool.Shared.Return(rentedBuffer); } } + + public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries) + { + switches = 0; + entries = 0; + + if (ns.CannotSendPackets()) + { + return; + } + + var packed = ns.Unpack; + + var layoutWriter = new SpanWriter(_layoutBuffer); + + if (!gump.Draggable) + { + layoutWriter.Write(Gump.NoMove); + } + + if (!gump.Closable) + { + layoutWriter.Write(Gump.NoClose); + } + + if (!gump.Disposable) + { + layoutWriter.Write(Gump.NoDispose); + } + + if (!gump.Resizable) + { + layoutWriter.Write(Gump.NoResize); + } + + foreach (var entry in gump.Entries) + { + entry.AppendTo(ref layoutWriter, _stringsList, ref entries, ref switches); + } + + var stringsWriter = new SpanWriter(_stringsBuffer); + + foreach (var str in _stringsList) + { + var s = str ?? ""; + stringsWriter.Write((ushort)s.Length); + stringsWriter.WriteBigUni(s); + } + + int maxLength; + if (packed) + { + var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten); + var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten); + maxLength = 40 + worstLayoutLength + worstStringsLength; + } + else + { + maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten; + } + + var writer = new SpanWriter(maxLength); + writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID + writer.Seek(2, SeekOrigin.Current); + + writer.Write(gump.Serial); + writer.Write(gump.TypeID); + writer.Write(gump.X); + writer.Write(gump.Y); + + if (packed) + { + layoutWriter.Write((byte)0); // Layout text terminator + WritePacked(layoutWriter.Span, ref writer); + + writer.Write(_stringsList.Count); + WritePacked(stringsWriter.Span, ref writer); + } + else + { + writer.Write((ushort)layoutWriter.BytesWritten); + writer.Write(layoutWriter.Span); + + writer.Write((ushort)_stringsList.Count); + writer.Write(stringsWriter.Span); + } + + writer.WritePacketLength(); + + ns.Send(writer.Span); + + layoutWriter.Dispose(); // Just in case + stringsWriter.Dispose(); // Just in case + + if (_stringsList.Count > 0) + { + _stringsList.Clear(); + } + } + + public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption) + { + if (ns.CannotSendPackets()) + { + return; + } + + unknown ??= ""; + caption ??= ""; + + var length = 15 + unknown.Length + caption.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x8B); // Packet ID + writer.Write((ushort)length); + + writer.Write(serial); + writer.Write((short)gumpId); + writer.Write((short)(unknown.Length + 1)); + writer.WriteAsciiNull(unknown); + writer.Write((short)(caption.Length + 1)); + writer.WriteAsciiNull(caption); + + ns.Send(writer.Span); + } } diff --git a/Projects/Server/Network/Packets/OutgoingItemPackets.cs b/Projects/Server/Network/Packets/OutgoingItemPackets.cs index 47997e7de..7f72363ce 100644 --- a/Projects/Server/Network/Packets/OutgoingItemPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingItemPackets.cs @@ -17,84 +17,83 @@ using System; using System.Buffers; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingItemPackets { - public static class OutgoingItemPackets + public static int CreateWorldItem(Span buffer, Item item) { - public static int CreateWorldItem(Span buffer, Item item) + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - // This assumes the packet was sliced properly - return buffer.Length; - } - - var itemID = item is BaseMulti ? item.ItemID | 0x4000 : item.ItemID & 0x3FFF; - var amount = item.Amount; - var hasAmount = amount != 0; - var serial = hasAmount ? item.Serial.Value | 0x80000000 : item.Serial.Value & 0x7FFFFFFF; - var loc = item.Location; - var hue = item.Hue; - var flags = item.GetPacketFlags(); - var direction = (int)item.Direction; - var hasDirection = direction != 0; - var hasHue = hue != 0; - var hasFlags = flags != 0; - var x = hasDirection ? loc.X | 0x8000 : loc.X & 0x7FFF; - var y = (loc.Y & 0x3FFF) | (hasHue ? 0x8000 : 0) | (hasFlags ? 0x4000 : 0); - var length = 14 + (hasAmount ? 2 : 0) + - (hasDirection ? 1 : 0) + - (hasHue ? 2 : 0) + - (hasFlags ? 1 : 0); - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x1A); // Packet ID - writer.Write((ushort)length); - writer.Write(serial); - writer.Write((ushort)itemID); - - if (hasAmount) - { - writer.Write((ushort)amount); - } - - writer.Write((ushort)x); - writer.Write((ushort)y); - - if (hasDirection) - { - writer.Write((byte)direction); - } - - writer.Write((sbyte)loc.Z); - - if (hasHue) - { - writer.Write((ushort)hue); - } - - if (hasFlags) - { - writer.Write((byte)flags); - } - - return writer.BytesWritten; + // This assumes the packet was sliced properly + return buffer.Length; } - public static void SendWorldItem(this NetState ns, Item item) + var itemID = item is BaseMulti ? item.ItemID | 0x4000 : item.ItemID & 0x3FFF; + var amount = item.Amount; + var hasAmount = amount != 0; + var serial = hasAmount ? item.Serial.Value | 0x80000000 : item.Serial.Value & 0x7FFFFFFF; + var loc = item.Location; + var hue = item.Hue; + var flags = item.GetPacketFlags(); + var direction = (int)item.Direction; + var hasDirection = direction != 0; + var hasHue = hue != 0; + var hasFlags = flags != 0; + var x = hasDirection ? loc.X | 0x8000 : loc.X & 0x7FFF; + var y = (loc.Y & 0x3FFF) | (hasHue ? 0x8000 : 0) | (hasFlags ? 0x4000 : 0); + var length = 14 + (hasAmount ? 2 : 0) + + (hasDirection ? 1 : 0) + + (hasHue ? 2 : 0) + + (hasFlags ? 1 : 0); + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x1A); // Packet ID + writer.Write((ushort)length); + writer.Write(serial); + writer.Write((ushort)itemID); + + if (hasAmount) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); - - var length = ns.StygianAbyss ? - OutgoingEntityPackets.CreateWorldEntity(buffer, item, ns.HighSeas) : - CreateWorldItem(buffer, item); - - ns.Send(buffer[..length]); + writer.Write((ushort)amount); } + + writer.Write((ushort)x); + writer.Write((ushort)y); + + if (hasDirection) + { + writer.Write((byte)direction); + } + + writer.Write((sbyte)loc.Z); + + if (hasHue) + { + writer.Write((ushort)hue); + } + + if (hasFlags) + { + writer.Write((byte)flags); + } + + return writer.BytesWritten; + } + + public static void SendWorldItem(this NetState ns, Item item) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket(); + + var length = ns.StygianAbyss ? + OutgoingEntityPackets.CreateWorldEntity(buffer, item, ns.HighSeas) : + CreateWorldItem(buffer, item); + + ns.Send(buffer[..length]); } } diff --git a/Projects/Server/Network/Packets/OutgoingLightPackets.cs b/Projects/Server/Network/Packets/OutgoingLightPackets.cs index 7f738a004..f47042f0f 100644 --- a/Projects/Server/Network/Packets/OutgoingLightPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingLightPackets.cs @@ -16,27 +16,26 @@ using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingLightPackets { - public static class OutgoingLightPackets + public static void SendPersonalLightLevel(this NetState ns, Serial serial, int level) { - public static void SendPersonalLightLevel(this NetState ns, Serial serial, int level) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[6]); - writer.Write((byte)0x4E); // Packet ID - writer.Write(serial); - writer.Write((byte)level); - - ns.Send(writer.Span); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendGlobalLightLevel(this NetState ns, int level = 0) => - ns?.Send(stackalloc byte[] { 0x4F, (byte)level }); + var writer = new SpanWriter(stackalloc byte[6]); + writer.Write((byte)0x4E); // Packet ID + writer.Write(serial); + writer.Write((byte)level); + + ns.Send(writer.Span); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendGlobalLightLevel(this NetState ns, int level = 0) => + ns?.Send(stackalloc byte[] { 0x4F, (byte)level }); } diff --git a/Projects/Server/Network/Packets/OutgoingMapPackets.cs b/Projects/Server/Network/Packets/OutgoingMapPackets.cs index be692358d..a0e71abb7 100644 --- a/Projects/Server/Network/Packets/OutgoingMapPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMapPackets.cs @@ -16,50 +16,49 @@ using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingMapPackets { - public static class OutgoingMapPackets + private static byte[] _mapPatchesPacket = new byte[41]; + + public static void SendMapPatches(this NetState ns) { - private static byte[] _mapPatchesPacket = new byte[41]; - - public static void SendMapPatches(this NetState ns) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - if (_mapPatchesPacket[0] == 0) - { - var writer = new SpanWriter(_mapPatchesPacket); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)41); // Length - writer.Write((ushort)0x18); // Subpacket - writer.Write(4); - - for (int i = 0; i < 4; i++) - { - var map = Map.Maps[i]; - - writer.Write(map?.Tiles.Patch.StaticBlocks ?? 0); - writer.Write(map?.Tiles.Patch.LandBlocks ?? 0); - } - } - - ns.Send(_mapPatchesPacket); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendInvalidMap(this NetState ns) => ns?.Send(stackalloc byte[] { 0xC6 }); - - public static void SendMapChange(this NetState ns, Map map) + if (_mapPatchesPacket[0] == 0) { - if (map == null) - { - return; - } + var writer = new SpanWriter(_mapPatchesPacket); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)41); // Length + writer.Write((ushort)0x18); // Subpacket + writer.Write(4); - ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x06, 0x00, 0x08, (byte)map.MapID }); + for (int i = 0; i < 4; i++) + { + var map = Map.Maps[i]; + + writer.Write(map?.Tiles.Patch.StaticBlocks ?? 0); + writer.Write(map?.Tiles.Patch.LandBlocks ?? 0); + } } + + ns.Send(_mapPatchesPacket); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendInvalidMap(this NetState ns) => ns?.Send(stackalloc byte[] { 0xC6 }); + + public static void SendMapChange(this NetState ns, Map map) + { + if (map == null) + { + return; + } + + ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x06, 0x00, 0x08, (byte)map.MapID }); } } diff --git a/Projects/Server/Network/Packets/OutgoingMenuPackets.cs b/Projects/Server/Network/Packets/OutgoingMenuPackets.cs index 75c059d07..71db481cc 100644 --- a/Projects/Server/Network/Packets/OutgoingMenuPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMenuPackets.cs @@ -5,210 +5,209 @@ using Server.ContextMenus; using Server.Menus.ItemLists; using Server.Menus.Questions; -namespace Server.Network +namespace Server.Network; + +[Flags] +public enum CMEFlags { - [Flags] - public enum CMEFlags + None = 0x00, + Disabled = 0x01, + Arrow = 0x02, + Highlighted = 0x04, + Colored = 0x20 +} + +public static class OutgoingMenuPackets +{ + public static void SendDisplayItemListMenu(this NetState ns, ItemListMenu menu) { - None = 0x00, - Disabled = 0x01, - Arrow = 0x02, - Highlighted = 0x04, - Colored = 0x20 + if (ns == null || menu == null) + { + return; + } + + var question = menu.Question?.Trim(); + var questionLength = question?.Length ?? 0; + + var entries = menu.Entries; + int entriesLength = (byte)entries.Length; + + var maxLength = 11 + questionLength; + for (int i = 0; i < entriesLength; i++) + { + maxLength += 5 + entries[i].Name?.Length ?? 0; // could be trimmed + } + + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0x7C); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write(menu.Serial); + writer.Write((ushort)0); + + writer.Write((byte)questionLength); + + if (question != null) + { + writer.WriteAscii(question); + } + + writer.Write((byte)entriesLength); + + for (var i = 0; i < entriesLength; ++i) + { + var e = entries[i]; + + writer.Write((ushort)e.ItemID); + writer.Write((short)e.Hue); + + var name = e.Name?.Trim(); + + if (name == null) + { + writer.Write((byte)0); + } + else + { + var nameLength = name.Length; + writer.Write((byte)nameLength); + writer.WriteAscii(name); + } + } + + writer.WritePacketLength(); + ns.Send(writer.Span); } - public static class OutgoingMenuPackets + public static void SendDisplayQuestionMenu(this NetState ns, QuestionMenu menu) { - public static void SendDisplayItemListMenu(this NetState ns, ItemListMenu menu) + if (ns == null || menu == null) { - if (ns == null || menu == null) - { - return; - } - - var question = menu.Question?.Trim(); - var questionLength = question?.Length ?? 0; - - var entries = menu.Entries; - int entriesLength = (byte)entries.Length; - - var maxLength = 11 + questionLength; - for (int i = 0; i < entriesLength; i++) - { - maxLength += 5 + entries[i].Name?.Length ?? 0; // could be trimmed - } - - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0x7C); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write(menu.Serial); - writer.Write((ushort)0); - - writer.Write((byte)questionLength); - - if (question != null) - { - writer.WriteAscii(question); - } - - writer.Write((byte)entriesLength); - - for (var i = 0; i < entriesLength; ++i) - { - var e = entries[i]; - - writer.Write((ushort)e.ItemID); - writer.Write((short)e.Hue); - - var name = e.Name?.Trim(); - - if (name == null) - { - writer.Write((byte)0); - } - else - { - var nameLength = name.Length; - writer.Write((byte)nameLength); - writer.WriteAscii(name); - } - } - - writer.WritePacketLength(); - ns.Send(writer.Span); + return; } - public static void SendDisplayQuestionMenu(this NetState ns, QuestionMenu menu) + var question = menu.Question?.Trim(); + var questionLength = question?.Length ?? 0; + + var answers = menu.Answers; + int answersLength = (byte)answers.Length; + + var maxLength = 11 + questionLength; + for (int i = 0; i < answersLength; i++) { - if (ns == null || menu == null) - { - return; - } - - var question = menu.Question?.Trim(); - var questionLength = question?.Length ?? 0; - - var answers = menu.Answers; - int answersLength = (byte)answers.Length; - - var maxLength = 11 + questionLength; - for (int i = 0; i < answersLength; i++) - { - maxLength += 5 + answers[i]?.Length ?? 0; // could be trimmed - } - - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0x7C); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write(menu.Serial); - writer.Write((ushort)0); - writer.Write((byte)questionLength); - - if (question != null) - { - writer.WriteAscii(question); - } - - writer.Write((byte)answersLength); - - for (var i = 0; i < answersLength; ++i) - { - writer.Write(0); - - var answer = answers[i]?.Trim(); - - if (answer == null) - { - writer.Write((byte)0); - } - else - { - var nameLength = answer.Length; - writer.Write((byte)nameLength); - writer.WriteAscii(answer); - } - } - - writer.WritePacketLength(); - ns.Send(writer.Span); + maxLength += 5 + answers[i]?.Length ?? 0; // could be trimmed } - public static void SendDisplayContextMenu(this NetState ns, ContextMenu menu) + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0x7C); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write(menu.Serial); + writer.Write((ushort)0); + writer.Write((byte)questionLength); + + if (question != null) { - if (ns == null || menu == null) - { - return; - } - - var newCommand = ns.NewHaven && menu.RequiresNewPacket; - - var entries = menu.Entries; - var entriesLength = (byte)entries.Length; - var maxLength = 12 + entriesLength * 8; - - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0xBF); // Packet ID - writer.Seek(2, SeekOrigin.Current); // Length - writer.Write((short)0x14); // Subpacket - writer.Write((short)(newCommand ? 0x02 : 0x01)); // Command - - var target = menu.Target; - writer.Write(target.Serial); - writer.Write(entriesLength); - - var p = target switch - { - Mobile _ => target.Location, - Item item => item.GetWorldLocation(), - _ => Point3D.Zero - }; - - for (var i = 0; i < entriesLength; ++i) - { - var e = entries[i]; - - var range = e.Range; - - if (range == -1) - { - range = Core.GlobalUpdateRange; - } - - var flags = e.Flags; - if (!(e.Enabled && menu.From.InRange(p, range))) - { - flags |= CMEFlags.Disabled; - } - - if (newCommand) - { - writer.Write(e.Number); - writer.Write((short)i); - writer.Write((short)flags); - } - else - { - writer.Write((short)i); - writer.Write((ushort)(e.Number - 3000000)); - - var color = e.Color & 0xFFFF; - - if (color != 0xFFFF) - { - flags |= CMEFlags.Colored; - } - - writer.Write((short)flags); - - if ((flags & CMEFlags.Colored) != 0) - { - writer.Write((short)color); - } - } - } - - writer.WritePacketLength(); - ns.Send(writer.Span); + writer.WriteAscii(question); } + + writer.Write((byte)answersLength); + + for (var i = 0; i < answersLength; ++i) + { + writer.Write(0); + + var answer = answers[i]?.Trim(); + + if (answer == null) + { + writer.Write((byte)0); + } + else + { + var nameLength = answer.Length; + writer.Write((byte)nameLength); + writer.WriteAscii(answer); + } + } + + writer.WritePacketLength(); + ns.Send(writer.Span); + } + + public static void SendDisplayContextMenu(this NetState ns, ContextMenu menu) + { + if (ns == null || menu == null) + { + return; + } + + var newCommand = ns.NewHaven && menu.RequiresNewPacket; + + var entries = menu.Entries; + var entriesLength = (byte)entries.Length; + var maxLength = 12 + entriesLength * 8; + + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0xBF); // Packet ID + writer.Seek(2, SeekOrigin.Current); // Length + writer.Write((short)0x14); // Subpacket + writer.Write((short)(newCommand ? 0x02 : 0x01)); // Command + + var target = menu.Target; + writer.Write(target.Serial); + writer.Write(entriesLength); + + var p = target switch + { + Mobile _ => target.Location, + Item item => item.GetWorldLocation(), + _ => Point3D.Zero + }; + + for (var i = 0; i < entriesLength; ++i) + { + var e = entries[i]; + + var range = e.Range; + + if (range == -1) + { + range = Core.GlobalUpdateRange; + } + + var flags = e.Flags; + if (!(e.Enabled && menu.From.InRange(p, range))) + { + flags |= CMEFlags.Disabled; + } + + if (newCommand) + { + writer.Write(e.Number); + writer.Write((short)i); + writer.Write((short)flags); + } + else + { + writer.Write((short)i); + writer.Write((ushort)(e.Number - 3000000)); + + var color = e.Color & 0xFFFF; + + if (color != 0xFFFF) + { + flags |= CMEFlags.Colored; + } + + writer.Write((short)flags); + + if ((flags & CMEFlags.Colored) != 0) + { + writer.Write((short)color); + } + } + } + + writer.WritePacketLength(); + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingMessagePackets.cs b/Projects/Server/Network/Packets/OutgoingMessagePackets.cs index a7a8d8e46..77763f92f 100644 --- a/Projects/Server/Network/Packets/OutgoingMessagePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMessagePackets.cs @@ -19,264 +19,263 @@ using System.IO; using System.Runtime.CompilerServices; using Server.Prompts; -namespace Server.Network +namespace Server.Network; + +[Flags] +public enum AffixType : byte { - [Flags] - public enum AffixType : byte + Append = 0x00, + Prepend = 0x01, + System = 0x02 +} + +public static class OutgoingMessagePackets +{ + public static void SendMessageLocalized( + this NetState ns, + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" + ) { - Append = 0x00, - Prepend = 0x01, - System = 0x02 + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[GetMaxMessageLocalizedLength(args)]; + var length = CreateMessageLocalized( + buffer, serial, graphic, type, hue, font, number, name, args + ); + + ns.Send(buffer[..length]); } - public static class OutgoingMessagePackets + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxMessageLocalizedLength(string args) => 50 + (args?.Length ?? 0) * 2; + + public static int CreateMessageLocalized( + Span buffer, + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" + ) { - public static void SendMessageLocalized( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" - ) + if (buffer[0] != 0) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[GetMaxMessageLocalizedLength(args)]; - var length = CreateMessageLocalized( - buffer, serial, graphic, type, hue, font, number, name, args - ); - - ns.Send(buffer[..length]); + return buffer.Length; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLocalizedLength(string args) => 50 + (args?.Length ?? 0) * 2; + name ??= ""; + args ??= ""; - public static int CreateMessageLocalized( - Span buffer, - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = "" - ) + if (hue == 0) { - if (buffer[0] != 0) - { - return buffer.Length; - } + hue = 0x3B2; + } - name ??= ""; - args ??= ""; + var writer = new SpanWriter(buffer); + writer.Write((byte)0xC1); + writer.Seek(2, SeekOrigin.Current); + writer.Write(serial); + writer.Write((short)graphic); + writer.Write((byte)type); + writer.Write((short)hue); + writer.Write((short)font); + writer.Write(number); + writer.WriteAscii(name, 30); + writer.WriteLittleUniNull(args); - if (hue == 0) - { - hue = 0x3B2; - } + writer.WritePacketLength(); + return writer.Position; + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0xC1); - writer.Seek(2, SeekOrigin.Current); - writer.Write(serial); - writer.Write((short)graphic); - writer.Write((byte)type); - writer.Write((short)hue); - writer.Write((short)font); - writer.Write(number); + public static void SendMessageLocalizedAffix( + this NetState ns, + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, + AffixType affixType, string affix = "", string args = "" + ) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix, args)].InitializePacket(); + var length = CreateMessageLocalizedAffix( + buffer, serial, graphic, type, hue, font, number, name, affixType, affix, args + ); + + ns.Send(buffer[..length]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxMessageLocalizedAffixLength(string affix, string args) => + 52 + (affix?.Length ?? 0) + (args?.Length ?? 0) * 2; + + public static int CreateMessageLocalizedAffix( + Span buffer, + Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, + AffixType affixType, string affix = "", string args = "" + ) + { + if (buffer[0] != 0) + { + return buffer.Length; + } + + name ??= ""; + affix ??= ""; + args ??= ""; + + if (hue == 0) + { + hue = 0x3B2; + } + + var writer = new SpanWriter(buffer); + writer.Write((byte)0xCC); + writer.Seek(2, SeekOrigin.Current); + writer.Write(serial); + writer.Write((short)graphic); + writer.Write((byte)type); + writer.Write((short)hue); + writer.Write((short)font); + writer.Write(number); + writer.Write((byte)affixType); + writer.WriteAscii(name, 30); + writer.WriteAsciiNull(affix); + writer.WriteBigUniNull(args); + + writer.WritePacketLength(); + return writer.Position; + } + + public static void SendMessage( + this NetState ns, + Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, string name, string text + ) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[GetMaxMessageLength(text)].InitializePacket(); + var length = CreateMessage( + buffer, + serial, + graphic, + type, + hue, + font, + ascii, + lang, + name, + text + ); + + ns.Send(buffer[..length]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxMessageLength(string text) => 50 + (text?.Length ?? 0) * 2; + + public static int CreateMessage( + Span buffer, + Serial serial, + int graphic, + MessageType type, + int hue, + int font, + bool ascii, + string lang, + string name, + string text + ) + { + if (buffer[0] != 0) + { + return buffer.Length; + } + + name ??= ""; + text ??= ""; + lang ??= "ENU"; + + if (hue == 0) + { + hue = 0x3B2; + } + + var writer = new SpanWriter(buffer); + writer.Write((byte)(ascii ? 0x1C : 0xAE)); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write(serial); + writer.Write((short)graphic); + writer.Write((byte)type); + writer.Write((short)hue); + writer.Write((short)font); + if (ascii) + { writer.WriteAscii(name, 30); - writer.WriteLittleUniNull(args); - - writer.WritePacketLength(); - return writer.Position; + writer.WriteAsciiNull(text); } - - public static void SendMessageLocalizedAffix( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, - AffixType affixType, string affix = "", string args = "" - ) + else { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix, args)].InitializePacket(); - var length = CreateMessageLocalizedAffix( - buffer, serial, graphic, type, hue, font, number, name, affixType, affix, args - ); - - ns.Send(buffer[..length]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLocalizedAffixLength(string affix, string args) => - 52 + (affix?.Length ?? 0) + (args?.Length ?? 0) * 2; - - public static int CreateMessageLocalizedAffix( - Span buffer, - Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, - AffixType affixType, string affix = "", string args = "" - ) - { - if (buffer[0] != 0) - { - return buffer.Length; - } - - name ??= ""; - affix ??= ""; - args ??= ""; - - if (hue == 0) - { - hue = 0x3B2; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xCC); - writer.Seek(2, SeekOrigin.Current); - writer.Write(serial); - writer.Write((short)graphic); - writer.Write((byte)type); - writer.Write((short)hue); - writer.Write((short)font); - writer.Write(number); - writer.Write((byte)affixType); + writer.WriteAscii(lang, 4); writer.WriteAscii(name, 30); - writer.WriteAsciiNull(affix); - writer.WriteBigUniNull(args); - - writer.WritePacketLength(); - return writer.Position; - } - - public static void SendMessage( - this NetState ns, - Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, string name, string text - ) - { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[GetMaxMessageLength(text)].InitializePacket(); - var length = CreateMessage( - buffer, - serial, - graphic, - type, - hue, - font, - ascii, - lang, - name, - text - ); - - ns.Send(buffer[..length]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetMaxMessageLength(string text) => 50 + (text?.Length ?? 0) * 2; - - public static int CreateMessage( - Span buffer, - Serial serial, - int graphic, - MessageType type, - int hue, - int font, - bool ascii, - string lang, - string name, - string text - ) - { - if (buffer[0] != 0) - { - return buffer.Length; - } - - name ??= ""; - text ??= ""; - lang ??= "ENU"; - - if (hue == 0) - { - hue = 0x3B2; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)(ascii ? 0x1C : 0xAE)); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write(serial); - writer.Write((short)graphic); - writer.Write((byte)type); - writer.Write((short)hue); - writer.Write((short)font); - if (ascii) - { - writer.WriteAscii(name, 30); - writer.WriteAsciiNull(text); - } - else - { - writer.WriteAscii(lang, 4); - writer.WriteAscii(name, 30); - writer.WriteBigUniNull(text); - } - - writer.WritePacketLength(); - return writer.Position; - } - - public static void SendFollowMessage(this NetState ns, Serial s1, Serial s2) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[9]); - writer.Write((byte)0x15); // Packet ID - writer.Write(s1); - writer.Write(s2); - - ns.Send(writer.Span); - } - - public static void SendPrompt(this NetState ns, Prompt prompt) - { - if (ns == null || prompt == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[21]); - writer.Write((byte)0xC2); // Packet ID - writer.Write((ushort)21); - writer.Write(prompt.Serial); - writer.Write(prompt.Serial); - writer.Clear(10); - - ns.Send(writer.Span); - } - - public static void SendHelpResponse(this NetState ns, Serial s, string text) - { - text = text?.Trim() ?? ""; - - if (ns == null || text.Length == 0) - { - return; - } - - var length = 9 + text.Length * 2; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xB7); - writer.Write((ushort)length); - writer.Write(s); writer.WriteBigUniNull(text); - - ns.Send(writer.Span); } + + writer.WritePacketLength(); + return writer.Position; + } + + public static void SendFollowMessage(this NetState ns, Serial s1, Serial s2) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[9]); + writer.Write((byte)0x15); // Packet ID + writer.Write(s1); + writer.Write(s2); + + ns.Send(writer.Span); + } + + public static void SendPrompt(this NetState ns, Prompt prompt) + { + if (ns == null || prompt == null) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[21]); + writer.Write((byte)0xC2); // Packet ID + writer.Write((ushort)21); + writer.Write(prompt.Serial); + writer.Write(prompt.Serial); + writer.Clear(10); + + ns.Send(writer.Span); + } + + public static void SendHelpResponse(this NetState ns, Serial s, string text) + { + text = text?.Trim() ?? ""; + + if (ns == null || text.Length == 0) + { + return; + } + + var length = 9 + text.Length * 2; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xB7); + writer.Write((ushort)length); + writer.Write(s); + writer.WriteBigUniNull(text); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs index 36a7a61ef..751737fec 100644 --- a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs @@ -19,691 +19,690 @@ using System.IO; using System.Runtime.CompilerServices; using Microsoft.Toolkit.HighPerformance; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingMobilePackets { - public static class OutgoingMobilePackets + public const int BondedStatusPacketLength = 11; + public const int DeathAnimationPacketLength = 13; + public const int MobileMovingPacketLength = 17; + public const int MobileMovingPacketCacheHeight = 16; // 8 notoriety, 2 client versions + public const int MobileMovingPacketCacheByteLength = MobileMovingPacketLength * MobileMovingPacketCacheHeight; + public const int AttributeMaximum = 100; + public const int MobileAttributePacketLength = 9; + public const int MobileAttributesPacketLength = 17; + public const int MobileAnimationPacketLength = 14; + public const int NewMobileAnimationPacketLength = 10; + public const int MobileHealthbarPacketLength = 12; + public const int MobileStatusCompactLength = 43; + public const int MobileStatusMaxLength = 121; + + public static bool ExtendedStatus { get; set; } + + public static void Initialize() { - public const int BondedStatusPacketLength = 11; - public const int DeathAnimationPacketLength = 13; - public const int MobileMovingPacketLength = 17; - public const int MobileMovingPacketCacheHeight = 16; // 8 notoriety, 2 client versions - public const int MobileMovingPacketCacheByteLength = MobileMovingPacketLength * MobileMovingPacketCacheHeight; - public const int AttributeMaximum = 100; - public const int MobileAttributePacketLength = 9; - public const int MobileAttributesPacketLength = 17; - public const int MobileAnimationPacketLength = 14; - public const int NewMobileAnimationPacketLength = 10; - public const int MobileHealthbarPacketLength = 12; - public const int MobileStatusCompactLength = 43; - public const int MobileStatusMaxLength = 121; + ExtendedStatus = ServerConfiguration.GetOrUpdateSetting("extendedStatus", false); + } - public static bool ExtendedStatus { get; set; } - - public static void Initialize() + public static void CreateBondedStatus(Span buffer, Serial serial, bool bonded) + { + if (buffer[0] != 0) { - ExtendedStatus = ServerConfiguration.GetOrUpdateSetting("extendedStatus", false); + return; } - public static void CreateBondedStatus(Span buffer, Serial serial, bool bonded) - { - if (buffer[0] != 0) - { - return; - } + var writer = new SpanWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)11); // Length + writer.Write((ushort)0x19); // Subpacket ID + writer.Write((byte)0); // Command + writer.Write(serial); + writer.Write(bonded); + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)11); // Length - writer.Write((ushort)0x19); // Subpacket ID - writer.Write((byte)0); // Command - writer.Write(serial); - writer.Write(bonded); + public static void SendBondedStatus(this NetState ns, Serial serial, bool bonded) + { + if (ns.CannotSendPackets()) + { + return; } - public static void SendBondedStatus(this NetState ns, Serial serial, bool bonded) + var writer = new SpanWriter(stackalloc byte[11]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)11); // Length + writer.Write((ushort)0x19); // Subpacket ID + writer.Write((byte)0); // Command + writer.Write(serial); + writer.Write(bonded); + + ns.Send(writer.Span); + } + + public static void CreateDeathAnimation(Span buffer, Serial killed, Serial corpse) + { + if (buffer[0] != 0) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[11]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)11); // Length - writer.Write((ushort)0x19); // Subpacket ID - writer.Write((byte)0); // Command - writer.Write(serial); - writer.Write(bonded); - - ns.Send(writer.Span); + return; } - public static void CreateDeathAnimation(Span buffer, Serial killed, Serial corpse) - { - if (buffer[0] != 0) - { - return; - } + var writer = new SpanWriter(buffer); + writer.Write((byte)0xAF); // Packet ID + writer.Write(killed); + writer.Write(corpse); + writer.Write(0); // ?? + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0xAF); // Packet ID - writer.Write(killed); - writer.Write(corpse); - writer.Write(0); // ?? + public static void SendDeathAnimation(this NetState ns, Serial killed, Serial corpse) + { + if (ns.CannotSendPackets()) + { + return; } - public static void SendDeathAnimation(this NetState ns, Serial killed, Serial corpse) - { - if (ns == null) - { - return; - } + Span span = stackalloc byte[DeathAnimationPacketLength]; + CreateDeathAnimation(span, killed, corpse); + ns.Send(span); + } - Span span = stackalloc byte[DeathAnimationPacketLength]; - CreateDeathAnimation(span, killed, corpse); - ns.Send(span); + public static void CreateMobileMoving(Span buffer, Mobile m, int noto, bool stygianAbyss) + { + if (buffer[0] != 0) + { + return; } - public static void CreateMobileMoving(Span buffer, Mobile m, int noto, bool stygianAbyss) + var loc = m.Location; + var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue; + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x77); // Packet ID + writer.Write(m.Serial); + writer.Write((short)m.Body); + writer.Write((short)loc.m_X); + writer.Write((short)loc.m_Y); + writer.Write((sbyte)loc.m_Z); + writer.Write((byte)m.Direction); + writer.Write((short)hue); + writer.Write((byte)m.GetPacketFlags(stygianAbyss)); + writer.Write((byte)noto); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMobileMoving(this NetState ns, Mobile source, Mobile target) => + ns.SendMobileMoving(target, Notoriety.Compute(source, target)); + + public static void SendMobileMoving(this NetState ns, Mobile target, int noto) + { + if (ns.CannotSendPackets()) { - if (buffer[0] != 0) - { - return; - } - - var loc = m.Location; - var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue; - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x77); // Packet ID - writer.Write(m.Serial); - writer.Write((short)m.Body); - writer.Write((short)loc.m_X); - writer.Write((short)loc.m_Y); - writer.Write((sbyte)loc.m_Z); - writer.Write((byte)m.Direction); - writer.Write((short)hue); - writer.Write((byte)m.GetPacketFlags(stygianAbyss)); - writer.Write((byte)noto); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMobileMoving(this NetState ns, Mobile source, Mobile target) => - ns.SendMobileMoving(target, Notoriety.Compute(source, target)); + Span buffer = stackalloc byte[MobileMovingPacketLength].InitializePacket(); + CreateMobileMoving(buffer, target, noto, ns.StygianAbyss); + ns.Send(buffer); + } - public static void SendMobileMoving(this NetState ns, Mobile target, int noto) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMobileMovingUsingCache(this NetState ns, Span2D cache, Mobile source, Mobile target) => + ns.SendMobileMovingUsingCache(cache, target, Notoriety.Compute(source, target)); + + // Requires a buffer of 16 packets, 17bytes per packet (272 bytes). + // Requires cache to have the first byte of each packet zeroed. + public static void SendMobileMovingUsingCache(this NetState ns, Span2D cache, Mobile target, int noto) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[MobileMovingPacketLength].InitializePacket(); - CreateMobileMoving(buffer, target, noto, ns.StygianAbyss); - ns.Send(buffer); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMobileMovingUsingCache(this NetState ns, Span2D cache, Mobile source, Mobile target) => - ns.SendMobileMovingUsingCache(cache, target, Notoriety.Compute(source, target)); + var stygianAbyss = ns.StygianAbyss; + var row = noto * 2 + (stygianAbyss ? 1 : 0); + var buffer = cache.GetRowSpan(row); + CreateMobileMoving(buffer, target, noto, stygianAbyss); - // Requires a buffer of 16 packets, 17bytes per packet (272 bytes). - // Requires cache to have the first byte of each packet zeroed. - public static void SendMobileMovingUsingCache(this NetState ns, Span2D cache, Mobile target, int noto) + ns.Send(buffer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteAttribute( + this ref SpanWriter writer, int max, int cur, bool normalize = false, bool reverse = false + ) + { + if (normalize && max != 0) { - if (ns == null) + if (reverse) { - return; - } - - var stygianAbyss = ns.StygianAbyss; - var row = noto * 2 + (stygianAbyss ? 1 : 0); - var buffer = cache.GetRowSpan(row); - CreateMobileMoving(buffer, target, noto, stygianAbyss); - - ns.Send(buffer); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WriteAttribute( - this ref SpanWriter writer, int max, int cur, bool normalize = false, bool reverse = false - ) - { - if (normalize && max != 0) - { - if (reverse) - { - writer.Write((short)(cur * AttributeMaximum / max)); - writer.Write((short)AttributeMaximum); - } - else - { - writer.Write((short)AttributeMaximum); - writer.Write((short)(cur * AttributeMaximum / max)); - } + writer.Write((short)(cur * AttributeMaximum / max)); + writer.Write((short)AttributeMaximum); } else { - if (reverse) - { - writer.Write((short)cur); - writer.Write((short)max); - } - else - { - writer.Write((short)max); - writer.Write((short)cur); - } + writer.Write((short)AttributeMaximum); + writer.Write((short)(cur * AttributeMaximum / max)); } } - - public static void SendMobileHits(this NetState ns, Mobile m, bool normalize = false) + else { - if (ns == null) + if (reverse) { - return; - } - - Span span = stackalloc byte[MobileAttributePacketLength]; - CreateMobileHits(span, m, normalize); - ns.Send(span); - } - - public static void CreateMobileHits(Span buffer, Mobile m, bool normalize = false) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xA1); // Packet ID - writer.Write(m.Serial); - writer.WriteAttribute(m.HitsMax, m.Hits, normalize); - } - - public static void SendMobileMana(this NetState ns, Mobile m, bool normalize = false) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileAttributePacketLength]; - CreateMobileMana(span, m, normalize); - ns.Send(span); - } - - public static void CreateMobileMana(Span buffer, Mobile m, bool normalize = false) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xA2); // Packet ID - writer.Write(m.Serial); - writer.WriteAttribute(m.ManaMax, m.Mana, normalize); - } - - public static void SendMobileStam(this NetState ns, Mobile m, bool normalize = false) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileAttributePacketLength]; - CreateMobileStam(span, m, normalize); - ns.Send(span); - } - - public static void CreateMobileStam(Span buffer, Mobile m, bool normalize = false) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0xA3); // Packet ID - writer.Write(m.Serial); - writer.WriteAttribute(m.StamMax, m.Stam, normalize); - } - - public static void SendMobileAttributes(this NetState ns, Mobile m, bool normalize = false) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileAttributesPacketLength]; - CreateMobileAttributes(span, m, normalize); - ns.Send(span); - } - - public static void CreateMobileAttributes(Span buffer, Mobile m, bool normalize = false) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x2D); // Packet ID - writer.Write(m.Serial); - - writer.WriteAttribute(m.HitsMax, m.Hits, normalize); - writer.WriteAttribute(m.ManaMax, m.Mana, normalize); - writer.WriteAttribute(m.StamMax, m.Stam, normalize); - } - - public static void SendMobileName(this NetState ns, Mobile m) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[37]); - writer.Write((byte)0x98); // Packet ID - writer.Write((ushort)37); - writer.Write(m.Serial); - writer.WriteAscii(m.Name ?? "", 29); - writer.Write((byte)0); // Null terminator - - ns.Send(writer.Span); - } - - public static void CreateMobileAnimation( - Span buffer, - Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay - ) - { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x6E); // Packet ID - writer.Write(mobile); - writer.Write((short)action); - writer.Write((short)frameCount); - writer.Write((short)repeatCount); - writer.Write(!forward); // protocol has really "reverse" but I find this more intuitive - writer.Write(repeat); - writer.Write((byte)delay); - } - - public static void SendMobileAnimation( - this NetState ns, - Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay - ) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileAnimationPacketLength]; - CreateMobileAnimation(span, mobile, action, frameCount, repeatCount, forward, repeat, delay); - ns.Send(span); - } - - public static void CreateNewMobileAnimation( - Span buffer, - Serial mobile, int action, int frameCount, int delay - ) - { - var writer = new SpanWriter(buffer); - writer.Write((byte)0xE2); // Packet ID - writer.Write(mobile); - writer.Write((short)action); - writer.Write((short)frameCount); - writer.Write((byte)delay); - } - - public static void SendNewMobileAnimation(this NetState ns, Serial mobile, int action, int frameCount, int delay) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[NewMobileAnimationPacketLength]; - CreateNewMobileAnimation(span, mobile, action, frameCount, delay); - ns.Send(span); - } - - public static void SendMobileHealthbar(this NetState ns, Mobile m, Healthbar healthbar) - { - if (ns == null) - { - return; - } - - Span span = stackalloc byte[MobileHealthbarPacketLength]; - CreateMobileHealthbar(span, m, healthbar); - ns.Send(span); - } - - public static void CreateMobileHealthbar(Span buffer, Mobile m, Healthbar healthbar) - { - if (buffer[0] != 0) - { - return; - } - - switch (healthbar) - { - case Healthbar.Poison: - { - CreateMobileHealthbar(buffer, m.Serial, Healthbar.Poison, m.Poison?.Level + 1 ?? 0); - break; - } - case Healthbar.Yellow: - { - CreateMobileHealthbar(buffer, m.Serial, Healthbar.Yellow, m.Blessed || m.YellowHealthbar ? 1 : 0); - break; - } - default: - { - Console.WriteLine("Packets: Invalid Healthbar {0} in {1}", healthbar, nameof(CreateMobileHealthbar)); - CreateMobileHealthbar(buffer, m.Serial, Healthbar.Normal, 0); - break; - } - } - } - - public static void CreateMobileHealthbar(Span buffer, Serial serial, Healthbar healthbar, int level) - { - var writer = new SpanWriter(buffer); - writer.Write((byte)0x17); // Packet ID - writer.Write((ushort)12); - writer.Write(serial); - writer.Write((short)1); // Show bar - writer.Write((short)healthbar); - writer.Write((byte)level); // 0 is off for that bar type - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateMobileStatusCompact(Span buffer, Mobile m, bool canBeRenamed) => - CreateMobileStatus(buffer, null, m, 0, canBeRenamed); - - public static void SendMobileStatusCompact(this NetState ns, Mobile m, bool canBeRenamed) - { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[MobileStatusCompactLength]; - CreateMobileStatusCompact(buffer, m, canBeRenamed); - - ns.Send(buffer); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMobileStatus(this NetState ns, Mobile m) => ns.SendMobileStatus(m, m); - - public static void SendMobileStatus(this NetState ns, Mobile beholder, Mobile beheld) - { - if (ns == null || beheld == null) - { - return; - } - - Span buffer = stackalloc byte[MobileStatusMaxLength]; - int version; - - if (beholder != beheld) - { - version = 0; - } - else if (Core.HS && ns.ExtendedStatus) - { - version = 6; - } - else if (Core.ML && ns.SupportsExpansion(Expansion.ML)) - { - /* - * For the ML era, the version value must be 5 if the original UO distribution - * is used and the client is not lower than version 5 - */ - version = ExtendedStatus ? 6 : 5; + writer.Write((short)cur); + writer.Write((short)max); } else { - version = Core.AOS ? 4 : 3; + writer.Write((short)max); + writer.Write((short)cur); } + } + } - var length = CreateMobileStatus(buffer, beholder, beheld, version, beheld.CanBeRenamedBy(beholder)); - ns.Send(buffer[..length]); + public static void SendMobileHits(this NetState ns, Mobile m, bool normalize = false) + { + if (ns.CannotSendPackets()) + { + return; } - public static int CreateMobileStatus( - Span buffer, Mobile beholder, Mobile beheld, int version, bool canBeRenamed - ) + Span span = stackalloc byte[MobileAttributePacketLength]; + CreateMobileHits(span, m, normalize); + ns.Send(span); + } + + public static void CreateMobileHits(Span buffer, Mobile m, bool normalize = false) + { + if (buffer[0] != 0) { - if (buffer[0] != 0) - { - return buffer.Length; - } + return; + } - var name = beheld.Name ?? ""; + var writer = new SpanWriter(buffer); + writer.Write((byte)0xA1); // Packet ID + writer.Write(m.Serial); + writer.WriteAttribute(m.HitsMax, m.Hits, normalize); + } - var writer = new SpanWriter(buffer); - writer.Write((byte)0x11); // Packet ID - writer.Seek(2, SeekOrigin.Current); - writer.Write(beheld.Serial); - writer.WriteAscii(name, 30); - writer.WriteAttribute(beheld.HitsMax, beheld.Hits, version == 0, true); - writer.Write(canBeRenamed); - writer.Write((byte)version); + public static void SendMobileMana(this NetState ns, Mobile m, bool normalize = false) + { + if (ns.CannotSendPackets()) + { + return; + } - if (version <= 0) - { - writer.WritePacketLength(); - return writer.Position; - } + Span span = stackalloc byte[MobileAttributePacketLength]; + CreateMobileMana(span, m, normalize); + ns.Send(span); + } - writer.Write(beheld.Female); + public static void CreateMobileMana(Span buffer, Mobile m, bool normalize = false) + { + if (buffer[0] != 0) + { + return; + } - writer.Write((short)beheld.Str); - writer.Write((short)beheld.Dex); - writer.Write((short)beheld.Int); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xA2); // Packet ID + writer.Write(m.Serial); + writer.WriteAttribute(m.ManaMax, m.Mana, normalize); + } - writer.Write((short)beheld.Stam); - writer.Write((short)beheld.StamMax); - writer.Write((short)beheld.Mana); - writer.Write((short)beheld.ManaMax); + public static void SendMobileStam(this NetState ns, Mobile m, bool normalize = false) + { + if (ns.CannotSendPackets()) + { + return; + } - writer.Write(beheld.TotalGold); - writer.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))); - writer.Write((short)(Mobile.BodyWeight + beheld.TotalWeight)); + Span span = stackalloc byte[MobileAttributePacketLength]; + CreateMobileStam(span, m, normalize); + ns.Send(span); + } - if (version >= 5) - { - writer.Write((short)beheld.MaxWeight); - writer.Write((byte)(beheld.Race?.RaceID + 1 ?? 0)); // Would be 0x00 if it's a non-ML enabled account but... - } + public static void CreateMobileStam(Span buffer, Mobile m, bool normalize = false) + { + if (buffer[0] != 0) + { + return; + } - writer.Write((short)beheld.StatCap); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xA3); // Packet ID + writer.Write(m.Serial); + writer.WriteAttribute(m.StamMax, m.Stam, normalize); + } - writer.Write((byte)beheld.Followers); - writer.Write((byte)beheld.FollowersMax); + public static void SendMobileAttributes(this NetState ns, Mobile m, bool normalize = false) + { + if (ns.CannotSendPackets()) + { + return; + } - if (version >= 4) - { - writer.Write((short)beheld.FireResistance); // Fire - writer.Write((short)beheld.ColdResistance); // Cold - writer.Write((short)beheld.PoisonResistance); // Poison - writer.Write((short)beheld.EnergyResistance); // Energy - writer.Write((short)beheld.Luck); // Luck + Span span = stackalloc byte[MobileAttributesPacketLength]; + CreateMobileAttributes(span, m, normalize); + ns.Send(span); + } - var weapon = beheld.Weapon; + public static void CreateMobileAttributes(Span buffer, Mobile m, bool normalize = false) + { + if (buffer[0] != 0) + { + return; + } - int min = 0, max = 0; - weapon?.GetStatusDamage(beheld, out min, out max); - writer.Write((short)min); // Damage min - writer.Write((short)max); // Damage max + var writer = new SpanWriter(buffer); + writer.Write((byte)0x2D); // Packet ID + writer.Write(m.Serial); - writer.Write(beheld.TithingPoints); - } + writer.WriteAttribute(m.HitsMax, m.Hits, normalize); + writer.WriteAttribute(m.ManaMax, m.Mana, normalize); + writer.WriteAttribute(m.StamMax, m.Stam, normalize); + } - if (version >= 6) - { - for (var i = 0; i < 15; ++i) + public static void SendMobileName(this NetState ns, Mobile m) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[37]); + writer.Write((byte)0x98); // Packet ID + writer.Write((ushort)37); + writer.Write(m.Serial); + writer.WriteAscii(m.Name ?? "", 29); + writer.Write((byte)0); // Null terminator + + ns.Send(writer.Span); + } + + public static void CreateMobileAnimation( + Span buffer, + Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay + ) + { + if (buffer[0] != 0) + { + return; + } + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x6E); // Packet ID + writer.Write(mobile); + writer.Write((short)action); + writer.Write((short)frameCount); + writer.Write((short)repeatCount); + writer.Write(!forward); // protocol has really "reverse" but I find this more intuitive + writer.Write(repeat); + writer.Write((byte)delay); + } + + public static void SendMobileAnimation( + this NetState ns, + Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay + ) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span span = stackalloc byte[MobileAnimationPacketLength]; + CreateMobileAnimation(span, mobile, action, frameCount, repeatCount, forward, repeat, delay); + ns.Send(span); + } + + public static void CreateNewMobileAnimation( + Span buffer, + Serial mobile, int action, int frameCount, int delay + ) + { + var writer = new SpanWriter(buffer); + writer.Write((byte)0xE2); // Packet ID + writer.Write(mobile); + writer.Write((short)action); + writer.Write((short)frameCount); + writer.Write((byte)delay); + } + + public static void SendNewMobileAnimation(this NetState ns, Serial mobile, int action, int frameCount, int delay) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span span = stackalloc byte[NewMobileAnimationPacketLength]; + CreateNewMobileAnimation(span, mobile, action, frameCount, delay); + ns.Send(span); + } + + public static void SendMobileHealthbar(this NetState ns, Mobile m, Healthbar healthbar) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span span = stackalloc byte[MobileHealthbarPacketLength]; + CreateMobileHealthbar(span, m, healthbar); + ns.Send(span); + } + + public static void CreateMobileHealthbar(Span buffer, Mobile m, Healthbar healthbar) + { + if (buffer[0] != 0) + { + return; + } + + switch (healthbar) + { + case Healthbar.Poison: { - writer.Write((short)beheld.GetAOSStatus(i)); + CreateMobileHealthbar(buffer, m.Serial, Healthbar.Poison, m.Poison?.Level + 1 ?? 0); + break; } - } + case Healthbar.Yellow: + { + CreateMobileHealthbar(buffer, m.Serial, Healthbar.Yellow, m.Blessed || m.YellowHealthbar ? 1 : 0); + break; + } + default: + { + Console.WriteLine("Packets: Invalid Healthbar {0} in {1}", healthbar, nameof(CreateMobileHealthbar)); + CreateMobileHealthbar(buffer, m.Serial, Healthbar.Normal, 0); + break; + } + } + } + public static void CreateMobileHealthbar(Span buffer, Serial serial, Healthbar healthbar, int level) + { + var writer = new SpanWriter(buffer); + writer.Write((byte)0x17); // Packet ID + writer.Write((ushort)12); + writer.Write(serial); + writer.Write((short)1); // Show bar + writer.Write((short)healthbar); + writer.Write((byte)level); // 0 is off for that bar type + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CreateMobileStatusCompact(Span buffer, Mobile m, bool canBeRenamed) => + CreateMobileStatus(buffer, null, m, 0, canBeRenamed); + + public static void SendMobileStatusCompact(this NetState ns, Mobile m, bool canBeRenamed) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span buffer = stackalloc byte[MobileStatusCompactLength]; + CreateMobileStatusCompact(buffer, m, canBeRenamed); + + ns.Send(buffer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMobileStatus(this NetState ns, Mobile m) => ns.SendMobileStatus(m, m); + + public static void SendMobileStatus(this NetState ns, Mobile beholder, Mobile beheld) + { + if (ns == null || beheld == null) + { + return; + } + + Span buffer = stackalloc byte[MobileStatusMaxLength]; + int version; + + if (beholder != beheld) + { + version = 0; + } + else if (Core.HS && ns.ExtendedStatus) + { + version = 6; + } + else if (Core.ML && ns.SupportsExpansion(Expansion.ML)) + { + /* + * For the ML era, the version value must be 5 if the original UO distribution + * is used and the client is not lower than version 5 + */ + version = ExtendedStatus ? 6 : 5; + } + else + { + version = Core.AOS ? 4 : 3; + } + + var length = CreateMobileStatus(buffer, beholder, beheld, version, beheld.CanBeRenamedBy(beholder)); + ns.Send(buffer[..length]); + } + + public static int CreateMobileStatus( + Span buffer, Mobile beholder, Mobile beheld, int version, bool canBeRenamed + ) + { + if (buffer[0] != 0) + { + return buffer.Length; + } + + var name = beheld.Name ?? ""; + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x11); // Packet ID + writer.Seek(2, SeekOrigin.Current); + writer.Write(beheld.Serial); + writer.WriteAscii(name, 30); + writer.WriteAttribute(beheld.HitsMax, beheld.Hits, version == 0, true); + writer.Write(canBeRenamed); + writer.Write((byte)version); + + if (version <= 0) + { writer.WritePacketLength(); return writer.Position; } - public static void SendMobileUpdate(this NetState ns, Mobile m) + writer.Write(beheld.Female); + + writer.Write((short)beheld.Str); + writer.Write((short)beheld.Dex); + writer.Write((short)beheld.Int); + + writer.Write((short)beheld.Stam); + writer.Write((short)beheld.StamMax); + writer.Write((short)beheld.Mana); + writer.Write((short)beheld.ManaMax); + + writer.Write(beheld.TotalGold); + writer.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))); + writer.Write((short)(Mobile.BodyWeight + beheld.TotalWeight)); + + if (version >= 5) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[19]); - writer.Write((byte)0x20); // Packet ID - writer.Write(m.Serial); - writer.Write((short)m.Body); - writer.Write((byte)0); - writer.Write((short)(m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue)); - writer.Write((byte)m.GetPacketFlags(ns.StygianAbyss)); - writer.Write((short)m.X); - writer.Write((short)m.Y); - writer.Write((short)0); - writer.Write((byte)m.Direction); - writer.Write((sbyte)m.Z); - - ns.Send(writer.Span); + writer.Write((short)beheld.MaxWeight); + writer.Write((byte)(beheld.Race?.RaceID + 1 ?? 0)); // Would be 0x00 if it's a non-ML enabled account but... } - public static void SendMobileIncoming(this NetState ns, Mobile beholder, Mobile beheld) - { - if (ns == null) - { - return; - } + writer.Write((short)beheld.StatCap); - Span layers = stackalloc bool[256]; + writer.Write((byte)beheld.Followers); + writer.Write((byte)beheld.FollowersMax); + + if (version >= 4) + { + writer.Write((short)beheld.FireResistance); // Fire + writer.Write((short)beheld.ColdResistance); // Cold + writer.Write((short)beheld.PoisonResistance); // Poison + writer.Write((short)beheld.EnergyResistance); // Energy + writer.Write((short)beheld.Luck); // Luck + + var weapon = beheld.Weapon; + + int min = 0, max = 0; + weapon?.GetStatusDamage(beheld, out min, out max); + writer.Write((short)min); // Damage min + writer.Write((short)max); // Damage max + + writer.Write(beheld.TithingPoints); + } + + if (version >= 6) + { + for (var i = 0; i < 15; ++i) + { + writer.Write((short)beheld.GetAOSStatus(i)); + } + } + + writer.WritePacketLength(); + return writer.Position; + } + + public static void SendMobileUpdate(this NetState ns, Mobile m) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[19]); + writer.Write((byte)0x20); // Packet ID + writer.Write(m.Serial); + writer.Write((short)m.Body); + writer.Write((byte)0); + writer.Write((short)(m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue)); + writer.Write((byte)m.GetPacketFlags(ns.StygianAbyss)); + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((short)0); + writer.Write((byte)m.Direction); + writer.Write((sbyte)m.Z); + + ns.Send(writer.Span); + } + + public static void SendMobileIncoming(this NetState ns, Mobile beholder, Mobile beheld) + { + if (ns.CannotSendPackets()) + { + return; + } + + Span layers = stackalloc bool[256]; #if NO_LOCAL_INIT layers.Clear(); #endif - var eq = beheld.Items; - var maxLength = 23 + (eq.Count + 2) * 9; - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0x78); // Packet ID - writer.Seek(2, SeekOrigin.Current); + var eq = beheld.Items; + var maxLength = 23 + (eq.Count + 2) * 9; + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0x78); // Packet ID + writer.Seek(2, SeekOrigin.Current); - var sa = ns.StygianAbyss; - var newPacket = ns.NewMobileIncoming; - var itemIdMask = newPacket ? 0xFFFF : 0x7FFF; + var sa = ns.StygianAbyss; + var newPacket = ns.NewMobileIncoming; + var itemIdMask = newPacket ? 0xFFFF : 0x7FFF; - var hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.Hue; + var hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.Hue; - writer.Write(beheld.Serial); - writer.Write((short)beheld.Body); - writer.Write((short)beheld.X); - writer.Write((short)beheld.Y); - writer.Write((sbyte)beheld.Z); - writer.Write((byte)beheld.Direction); - writer.Write((short)hue); - writer.Write((byte)beheld.GetPacketFlags(sa)); - writer.Write((byte)Notoriety.Compute(beholder, beheld)); + writer.Write(beheld.Serial); + writer.Write((short)beheld.Body); + writer.Write((short)beheld.X); + writer.Write((short)beheld.Y); + writer.Write((sbyte)beheld.Z); + writer.Write((byte)beheld.Direction); + writer.Write((short)hue); + writer.Write((byte)beheld.GetPacketFlags(sa)); + writer.Write((byte)Notoriety.Compute(beholder, beheld)); - for (var i = 0; i < eq.Count; ++i) + for (var i = 0; i < eq.Count; ++i) + { + var item = eq[i]; + var layer = (byte)item.Layer; + + if (item.Deleted || !beholder.CanSee(item) || layers[layer]) { - var item = eq[i]; - var layer = (byte)item.Layer; - - if (item.Deleted || !beholder.CanSee(item) || layers[layer]) - { - continue; - } - - layers[layer] = true; - hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : item.Hue; - - var itemID = item.ItemID & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - writer.Write(item.Serial); - writer.Write((ushort)itemID); - writer.Write(layer); - - if (writeHue) - { - writer.Write((short)hue); - } + continue; } - if (beheld.HairItemID > 0 && !layers[(int)Layer.Hair]) + layers[layer] = true; + hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : item.Hue; + + var itemID = item.ItemID & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) { - layers[(int)Layer.Hair] = true; - hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.HairHue; - - var itemID = beheld.HairItemID & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - writer.Write(HairInfo.FakeSerial(beheld.Serial)); - writer.Write((ushort)itemID); - writer.Write((byte)Layer.Hair); - - if (writeHue) - { - writer.Write((short)hue); - } + itemID |= 0x8000; } - if (beheld.FacialHairItemID > 0 && !layers[(int)Layer.FacialHair]) + writer.Write(item.Serial); + writer.Write((ushort)itemID); + writer.Write(layer); + + if (writeHue) { - layers[(int)Layer.FacialHair] = true; - hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.FacialHairHue; - - var itemID = beheld.FacialHairItemID & itemIdMask; - var writeHue = newPacket || hue != 0; - - if (!newPacket && writeHue) - { - itemID |= 0x8000; - } - - writer.Write(FacialHairInfo.FakeSerial(beheld.Serial)); - writer.Write((ushort)itemID); - writer.Write((byte)Layer.FacialHair); - - if (writeHue) - { - writer.Write((short)hue); - } + writer.Write((short)hue); } - - writer.Write(0); // terminate - - writer.WritePacketLength(); - ns.Send(writer.Span); } + + if (beheld.HairItemID > 0 && !layers[(int)Layer.Hair]) + { + layers[(int)Layer.Hair] = true; + hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.HairHue; + + var itemID = beheld.HairItemID & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) + { + itemID |= 0x8000; + } + + writer.Write(HairInfo.FakeSerial(beheld.Serial)); + writer.Write((ushort)itemID); + writer.Write((byte)Layer.Hair); + + if (writeHue) + { + writer.Write((short)hue); + } + } + + if (beheld.FacialHairItemID > 0 && !layers[(int)Layer.FacialHair]) + { + layers[(int)Layer.FacialHair] = true; + hue = beheld.SolidHueOverride >= 0 ? beheld.SolidHueOverride : beheld.FacialHairHue; + + var itemID = beheld.FacialHairItemID & itemIdMask; + var writeHue = newPacket || hue != 0; + + if (!newPacket && writeHue) + { + itemID |= 0x8000; + } + + writer.Write(FacialHairInfo.FakeSerial(beheld.Serial)); + writer.Write((ushort)itemID); + writer.Write((byte)Layer.FacialHair); + + if (writeHue) + { + writer.Write((short)hue); + } + } + + writer.Write(0); // terminate + + writer.WritePacketLength(); + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingMovementPackets.cs b/Projects/Server/Network/Packets/OutgoingMovementPackets.cs index 78ee3c892..b366a7dec 100644 --- a/Projects/Server/Network/Packets/OutgoingMovementPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMovementPackets.cs @@ -16,102 +16,101 @@ using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public enum SpeedControlSetting { - public enum SpeedControlSetting + Disable, + Mount, + Walk +} + +public static class OutgoingMovementPackets +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendSpeedControl(this NetState ns, SpeedControlSetting speedControl) => + ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x6, 0x00, 0x26, (byte)speedControl }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMovePlayer(this NetState ns, Direction d) => ns?.Send(stackalloc byte[] { 0x97, (byte)d }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMovementAck(this NetState ns, int seq, Mobile m) => + ns.SendMovementAck(seq, Notoriety.Compute(m, m)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendMovementAck(this NetState ns, int seq, int noto) => + ns?.Send(stackalloc byte[] { 0x22, (byte)seq, (byte)noto }); + + public static void SendMovementRej(this NetState ns, int seq, Mobile m) { - Disable, - Mount, - Walk + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[8]); + writer.Write((byte)0x21); // Packet ID + writer.Write((byte)seq); + writer.Write((short)m.X); + writer.Write((short)m.Y); + writer.Write((byte)m.Direction); + writer.Write((sbyte)m.Z); + + ns.Send(writer.Span); } - public static class OutgoingMovementPackets + public static void SendInitialFastwalkStack(this NetState ns, uint[] keys) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendSpeedControl(this NetState ns, SpeedControlSetting speedControl) => - ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x6, 0x00, 0x26, (byte)speedControl }); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMovePlayer(this NetState ns, Direction d) => ns?.Send(stackalloc byte[] { 0x97, (byte)d }); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMovementAck(this NetState ns, int seq, Mobile m) => - ns.SendMovementAck(seq, Notoriety.Compute(m, m)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendMovementAck(this NetState ns, int seq, int noto) => - ns?.Send(stackalloc byte[] { 0x22, (byte)seq, (byte)noto }); - - public static void SendMovementRej(this NetState ns, int seq, Mobile m) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[8]); - writer.Write((byte)0x21); // Packet ID - writer.Write((byte)seq); - writer.Write((short)m.X); - writer.Write((short)m.Y); - writer.Write((byte)m.Direction); - writer.Write((sbyte)m.Z); - - ns.Send(writer.Span); + return; } - public static void SendInitialFastwalkStack(this NetState ns, uint[] keys) + var writer = new SpanWriter(stackalloc byte[29]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)29); + writer.Write((ushort)0x1); // Subpacket + writer.Write(keys[0]); + writer.Write(keys[1]); + writer.Write(keys[2]); + writer.Write(keys[3]); + writer.Write(keys[4]); + writer.Write(keys[5]); + + ns.Send(writer.Span); + } + + public static void SendFastwalkStackKey(this NetState ns, uint key = 0) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[29]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)29); - writer.Write((ushort)0x1); // Subpacket - writer.Write(keys[0]); - writer.Write(keys[1]); - writer.Write(keys[2]); - writer.Write(keys[3]); - writer.Write(keys[4]); - writer.Write(keys[5]); - - ns.Send(writer.Span); + return; } - public static void SendFastwalkStackKey(this NetState ns, uint key = 0) + var writer = new SpanWriter(stackalloc byte[9]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)9); + writer.Write((ushort)0x2); // Subpacket + writer.Write(key); + + ns.Send(writer.Span); + } + + public static void SendTimeSyncResponse(this NetState ns) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[9]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)9); - writer.Write((ushort)0x2); // Subpacket - writer.Write(key); - - ns.Send(writer.Span); + return; } - public static void SendTimeSyncResponse(this NetState ns) - { - if (ns == null) - { - return; - } + var writer = new SpanWriter(stackalloc byte[25]); + writer.Write((byte)0xF2); // Packet ID - var writer = new SpanWriter(stackalloc byte[25]); - writer.Write((byte)0xF2); // Packet ID + writer.Write(Core.TickCount); // ?? + writer.Write(Core.TickCount); // ?? + writer.Write(Core.TickCount); // ?? - writer.Write(Core.TickCount); // ?? - writer.Write(Core.TickCount); // ?? - writer.Write(Core.TickCount); // ?? - - ns.Send(writer.Span); - } + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingPackets.cs b/Projects/Server/Network/Packets/OutgoingPackets.cs new file mode 100644 index 000000000..a20356fe9 --- /dev/null +++ b/Projects/Server/Network/Packets/OutgoingPackets.cs @@ -0,0 +1,9 @@ +using System.Runtime.CompilerServices; + +namespace Server.Network; + +public static class OutgoingPackets +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CannotSendPackets(this NetState ns) => ns?.Connection == null || ns.BlockAllPackets; +} diff --git a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs index 051ac8f7e..af33c444e 100644 --- a/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingPlayerPackets.cs @@ -17,338 +17,337 @@ using System; using System.Buffers; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public enum LRReason : byte { - public enum LRReason : byte + CannotLift, + OutOfRange, + OutOfSight, + TryToSteal, + AreHolding, + Inspecific +} + +public static class OutgoingPlayerPackets +{ + public const int DragEffectPacketLength = 26; + + public static void SendStatLockInfo(this NetState ns, Mobile m) { - CannotLift, - OutOfRange, - OutOfSight, - TryToSteal, - AreHolding, - Inspecific + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[12]); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)12); + writer.Write((short)0x19); + writer.Write((byte)2); + writer.Write(m.Serial); + writer.Write((byte)0); + + var lockBits = ((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock; + + writer.Write((byte)lockBits); + + ns.Send(writer.Span); } - public static class OutgoingPlayerPackets + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendChangeUpdateRange(this NetState ns, byte range) => + ns?.Send(stackalloc byte[] { 0xC8, range }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendDeathStatus(this NetState ns, bool dead) => + ns?.Send(stackalloc byte[] { 0x2C, dead ? (byte)0 : (byte)2 }); + + public static void SendDisplayProfile(this NetState ns, Serial m, string header, string body, string footer) { - public const int DragEffectPacketLength = 26; - - public static void SendStatLockInfo(this NetState ns, Mobile m) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[12]); - writer.Write((byte)0xBF); // Packet ID - writer.Write((ushort)12); - writer.Write((short)0x19); - writer.Write((byte)2); - writer.Write(m.Serial); - writer.Write((byte)0); - - var lockBits = ((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock; - - writer.Write((byte)lockBits); - - ns.Send(writer.Span); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendChangeUpdateRange(this NetState ns, byte range) => - ns?.Send(stackalloc byte[] { 0xC8, range }); + header ??= ""; + body ??= ""; + footer ??= ""; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendDeathStatus(this NetState ns, bool dead) => - ns?.Send(stackalloc byte[] { 0x2C, dead ? (byte)0 : (byte)2 }); + var length = 12 + header.Length + footer.Length * 2 + body.Length * 2; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xB8); // Packet ID + writer.Write((ushort)length); + writer.Write(m); + writer.WriteAsciiNull(header); + writer.WriteBigUniNull(footer); + writer.WriteBigUniNull(body); - public static void SendDisplayProfile(this NetState ns, Serial m, string header, string body, string footer) + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendLiftReject(this NetState ns, LRReason reason) => + ns?.Send(stackalloc byte[] { 0x27, (byte)reason }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendLogoutAck(this NetState ns) => ns?.Send(stackalloc byte[] { 0xD1, 0x01 }); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendWeather(this NetState ns, byte type, byte density, byte temp) => + ns?.Send(stackalloc byte[] { 0x65, type, density, temp }); + + public static void SendServerChange(this NetState ns, Point3D p, Map map) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - header ??= ""; - body ??= ""; - footer ??= ""; - - var length = 12 + header.Length + footer.Length * 2 + body.Length * 2; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xB8); // Packet ID - writer.Write((ushort)length); - writer.Write(m); - writer.WriteAsciiNull(header); - writer.WriteBigUniNull(footer); - writer.WriteBigUniNull(body); - - ns.Send(writer.Span); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendLiftReject(this NetState ns, LRReason reason) => - ns?.Send(stackalloc byte[] { 0x27, (byte)reason }); + var writer = new SpanWriter(stackalloc byte[16]); + writer.Write((byte)0x76); // Packet ID + writer.Write((short)p.X); + writer.Write((short)p.Y); + writer.Write((short)p.Z); + writer.Write((byte)0); + writer.Write((short)0); + writer.Write((short)0); + writer.Write((short)map.Width); + writer.Write((short)map.Height); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendLogoutAck(this NetState ns) => ns?.Send(stackalloc byte[] { 0xD1, 0x01 }); + ns.Send(writer.Span); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendWeather(this NetState ns, byte type, byte density, byte temp) => - ns?.Send(stackalloc byte[] { 0x65, type, density, temp }); - - public static void SendServerChange(this NetState ns, Point3D p, Map map) + public static void SendSkillsUpdate(this NetState ns, Skills skills) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[16]); - writer.Write((byte)0x76); // Packet ID - writer.Write((short)p.X); - writer.Write((short)p.Y); - writer.Write((short)p.Z); - writer.Write((byte)0); - writer.Write((short)0); - writer.Write((short)0); - writer.Write((short)map.Width); - writer.Write((short)map.Height); - - ns.Send(writer.Span); + return; } - public static void SendSkillsUpdate(this NetState ns, Skills skills) + var length = 6 + 9 * skills.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x3A); // Packet ID + writer.Write((ushort)length); + writer.Write((byte)0x02); // type: absolute, capped + + for (var i = 0; i < skills.Length; ++i) { - if (ns == null) - { - return; - } + var s = skills[i]; - var length = 6 + 9 * skills.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x3A); // Packet ID - writer.Write((ushort)length); - writer.Write((byte)0x02); // type: absolute, capped - - for (var i = 0; i < skills.Length; ++i) - { - var s = skills[i]; - - var v = s.NonRacialValue; - var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); - - writer.Write((ushort)(s.Info.SkillID + 1)); - writer.Write((ushort)uv); - writer.Write((ushort)s.BaseFixedPoint); - writer.Write((byte)s.Lock); - writer.Write((ushort)s.CapFixedPoint); - } - - writer.Write((short)0); // terminate - - ns.Send(writer.Span); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendSequence(this NetState ns, byte sequence) => ns?.Send(stackalloc byte[] { 0x7B, sequence }); - - public static void SendSkillChange(this NetState ns, Skill skill) - { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[13]); - writer.Write((byte)0x3A); // Packet ID - writer.Write((ushort)13); - - var v = skill.NonRacialValue; + var v = s.NonRacialValue; var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); - writer.Write((byte)0xDF); // type: delta, capped - writer.Write((ushort)skill.Info.SkillID); + writer.Write((ushort)(s.Info.SkillID + 1)); writer.Write((ushort)uv); - writer.Write((ushort)skill.BaseFixedPoint); - writer.Write((byte)skill.Lock); - writer.Write((ushort)skill.CapFixedPoint); - - ns.Send(writer.Span); + writer.Write((ushort)s.BaseFixedPoint); + writer.Write((byte)s.Lock); + writer.Write((ushort)s.CapFixedPoint); } - public static void SendLaunchBrowser(this NetState ns, string uri) + writer.Write((short)0); // terminate + + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendSequence(this NetState ns, byte sequence) => ns?.Send(stackalloc byte[] { 0x7B, sequence }); + + public static void SendSkillChange(this NetState ns, Skill skill) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - uri ??= ""; - - var length = 4 + uri.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xA5); // Packet ID - writer.Write((ushort)length); - writer.WriteAsciiNull(uri); - - ns.Send(writer.Span); + return; } - public static void CreateDragEffect( - Span buffer, - Serial srcSerial, Point3D srcLocation, - Serial trgSerial, Point3D trgLocation, - int itemID, int hue, int amount - ) + var writer = new SpanWriter(stackalloc byte[13]); + writer.Write((byte)0x3A); // Packet ID + writer.Write((ushort)13); + + var v = skill.NonRacialValue; + var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF); + + writer.Write((byte)0xDF); // type: delta, capped + writer.Write((ushort)skill.Info.SkillID); + writer.Write((ushort)uv); + writer.Write((ushort)skill.BaseFixedPoint); + writer.Write((byte)skill.Lock); + writer.Write((ushort)skill.CapFixedPoint); + + ns.Send(writer.Span); + } + + public static void SendLaunchBrowser(this NetState ns, string uri) + { + if (ns.CannotSendPackets()) { - if (buffer[0] != 0) - { - return; - } - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x23); // Packet ID - writer.Write((short)itemID); - writer.Write((byte)0); - writer.Write((short)hue); - writer.Write((short)amount); - writer.Write(srcSerial); - writer.Write((short)srcLocation.X); - writer.Write((short)srcLocation.Y); - writer.Write((sbyte)srcLocation.Z); - writer.Write(trgSerial); - writer.Write((short)trgLocation.X); - writer.Write((short)trgLocation.Y); - writer.Write((sbyte)trgLocation.Z); + return; } - public static void SendDragEffect( - this NetState ns, - Serial srcSerial, Point3D srcLocation, - Serial trgSerial, Point3D trgLocation, - int itemID, int hue, int amount - ) + uri ??= ""; + + var length = 4 + uri.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xA5); // Packet ID + writer.Write((ushort)length); + writer.WriteAsciiNull(uri); + + ns.Send(writer.Span); + } + + public static void CreateDragEffect( + Span buffer, + Serial srcSerial, Point3D srcLocation, + Serial trgSerial, Point3D trgLocation, + int itemID, int hue, int amount + ) + { + if (buffer[0] != 0) { - if (ns == null) - { - return; - } - - Span buffer = stackalloc byte[DragEffectPacketLength].InitializePacket(); - CreateDragEffect(buffer, srcSerial, srcLocation, trgSerial, trgLocation, itemID, hue, amount); - ns.Send(buffer); + return; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void SendSeasonChange(this NetState ns, byte season, bool playSound) => - ns?.Send(stackalloc byte[]{ 0xBC, season, *(byte*)&playSound }); + var writer = new SpanWriter(buffer); + writer.Write((byte)0x23); // Packet ID + writer.Write((short)itemID); + writer.Write((byte)0); + writer.Write((short)hue); + writer.Write((short)amount); + writer.Write(srcSerial); + writer.Write((short)srcLocation.X); + writer.Write((short)srcLocation.Y); + writer.Write((sbyte)srcLocation.Z); + writer.Write(trgSerial); + writer.Write((short)trgLocation.X); + writer.Write((short)trgLocation.Y); + writer.Write((sbyte)trgLocation.Z); + } - public static void SendDisplayPaperdoll(this NetState ns, Serial m, string title, bool warmode, bool canLift) + public static void SendDragEffect( + this NetState ns, + Serial srcSerial, Point3D srcLocation, + Serial trgSerial, Point3D trgLocation, + int itemID, int hue, int amount + ) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - byte flags = 0x00; - - if (warmode) - { - flags |= 0x01; - } - - if (canLift) - { - flags |= 0x02; - } - - var writer = new SpanWriter(stackalloc byte[66]); - writer.Write((byte)0x88); // Packet ID - writer.Write(m); - writer.WriteAscii(title, 60); - writer.Write(flags); - - ns.Send(writer.Span); + return; } - public static void SendPlayMusic(this NetState ns, MusicName music) + Span buffer = stackalloc byte[DragEffectPacketLength].InitializePacket(); + CreateDragEffect(buffer, srcSerial, srcLocation, trgSerial, trgLocation, itemID, hue, amount); + ns.Send(buffer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void SendSeasonChange(this NetState ns, byte season, bool playSound) => + ns?.Send(stackalloc byte[]{ 0xBC, season, *(byte*)&playSound }); + + public static void SendDisplayPaperdoll(this NetState ns, Serial m, string title, bool warmode, bool canLift) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[3]); - writer.Write((byte)0x6D); // Packet ID - writer.Write((short)music); - - ns.Send(writer.Span); + return; } - public static void SendStopMusic(this NetState ns) => ns?.Send(stackalloc byte[] { 0x6D, 0x1F, 0xFF }); + byte flags = 0x00; - public static void SendScrollMessage(this NetState ns, int type, int tip, string text) + if (warmode) { - if (ns == null) - { - return; - } - - text ??= ""; - - var length = 10 + text.Length; - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0xA6); // Packet ID - writer.Write((ushort)length); - writer.Write((byte)type); - writer.Write(tip); - writer.Write((ushort)text.Length); - writer.WriteAscii(text); - - ns.Send(writer.Span); + flags |= 0x01; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendCurrentTime(this NetState ns) => ns.SendCurrentTime(Core.Now); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendCurrentTime(this NetState ns, DateTime date) => - ns?.Send(stackalloc byte[] { 0x5B, (byte)date.Hour, (byte)date.Minute, (byte)date.Second }); - - public static void SendPathfindMessage(this NetState ns, Point3D p) + if (canLift) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[7]); - writer.Write((byte)0x38); // Packet ID - writer.Write((short)p.X); - writer.Write((short)p.Y); - writer.Write((short)p.Z); - - ns.Send(writer.Span); + flags |= 0x02; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SendPingAck(this NetState ns, byte ping) => ns?.Send(stackalloc byte[] { 0x73, ping }); + var writer = new SpanWriter(stackalloc byte[66]); + writer.Write((byte)0x88); // Packet ID + writer.Write(m); + writer.WriteAscii(title, 60); + writer.Write(flags); - public static void SendDisplayHuePicker(this NetState ns, Serial huePickerSerial, int huePickerItemID) + ns.Send(writer.Span); + } + + public static void SendPlayMusic(this NetState ns, MusicName music) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[9]); - writer.Write((byte)0x95); // Packet ID - writer.Write(huePickerSerial); - writer.Write((short)0); - writer.Write((short)huePickerItemID); - - ns.Send(writer.Span); + return; } + + var writer = new SpanWriter(stackalloc byte[3]); + writer.Write((byte)0x6D); // Packet ID + writer.Write((short)music); + + ns.Send(writer.Span); + } + + public static void SendStopMusic(this NetState ns) => ns?.Send(stackalloc byte[] { 0x6D, 0x1F, 0xFF }); + + public static void SendScrollMessage(this NetState ns, int type, int tip, string text) + { + if (ns.CannotSendPackets()) + { + return; + } + + text ??= ""; + + var length = 10 + text.Length; + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0xA6); // Packet ID + writer.Write((ushort)length); + writer.Write((byte)type); + writer.Write(tip); + writer.Write((ushort)text.Length); + writer.WriteAscii(text); + + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendCurrentTime(this NetState ns) => ns.SendCurrentTime(Core.Now); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendCurrentTime(this NetState ns, DateTime date) => + ns?.Send(stackalloc byte[] { 0x5B, (byte)date.Hour, (byte)date.Minute, (byte)date.Second }); + + public static void SendPathfindMessage(this NetState ns, Point3D p) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[7]); + writer.Write((byte)0x38); // Packet ID + writer.Write((short)p.X); + writer.Write((short)p.Y); + writer.Write((short)p.Z); + + ns.Send(writer.Span); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SendPingAck(this NetState ns, byte ping) => ns?.Send(stackalloc byte[] { 0x73, ping }); + + public static void SendDisplayHuePicker(this NetState ns, Serial huePickerSerial, int huePickerItemID) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[9]); + writer.Write((byte)0x95); // Packet ID + writer.Write(huePickerSerial); + writer.Write((short)0); + writer.Write((short)huePickerItemID); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs b/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs index eef3336ee..4459d3038 100644 --- a/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingSecureTradePackets.cs @@ -16,106 +16,105 @@ using System.Buffers; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public enum TradeFlag : byte { - public enum TradeFlag : byte + Display, + Close, + Update, + UpdateGold, + UpdateLedger +} + +public static class OutgoingSecureTradePackets +{ + public static void SendDisplaySecureTrade( + this NetState ns, Mobile them, Container first, Container second, string name + ) { - Display, - Close, - Update, - UpdateGold, - UpdateLedger + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[47]); + writer.Write((byte)0x6F); // Packet ID + writer.Write((ushort)47); // Length + writer.Write((byte)TradeFlag.Display); + writer.Write(them.Serial); + writer.Write(first.Serial); + writer.Write(second.Serial); + writer.Write(true); + + writer.WriteAscii(name ?? "", 30); + + ns.Send(writer.Span); } - public static class OutgoingSecureTradePackets + public static void SendCloseSecureTrade(this NetState ns, Container cont) { - public static void SendDisplaySecureTrade( - this NetState ns, Mobile them, Container first, Container second, string name - ) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[47]); - writer.Write((byte)0x6F); // Packet ID - writer.Write((ushort)47); // Length - writer.Write((byte)TradeFlag.Display); - writer.Write(them.Serial); - writer.Write(first.Serial); - writer.Write(second.Serial); - writer.Write(true); - - writer.WriteAscii(name ?? "", 30); - - ns.Send(writer.Span); + return; } - public static void SendCloseSecureTrade(this NetState ns, Container cont) + var writer = new SpanWriter(stackalloc byte[17]); + writer.Write((byte)0x6F); // Packet ID + writer.Write((ushort)17); // Length + writer.Write((byte)TradeFlag.Close); + writer.Write(cont.Serial); + writer.Write(0); + writer.Write(0); + writer.Write(false); + + ns.Send(writer.Span); + } + + public static void SendUpdateSecureTrade(this NetState ns, Container cont, bool first, bool second) => + ns.SendUpdateSecureTrade(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0); + + public static void SendUpdateSecureTrade(this NetState ns, Container cont, TradeFlag flag, int first, int second) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[17]); - writer.Write((byte)0x6F); // Packet ID - writer.Write((ushort)17); // Length - writer.Write((byte)TradeFlag.Close); - writer.Write(cont.Serial); - writer.Write(0); - writer.Write(0); - writer.Write(false); - - ns.Send(writer.Span); + return; } - public static void SendUpdateSecureTrade(this NetState ns, Container cont, bool first, bool second) => - ns.SendUpdateSecureTrade(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0); + var writer = new SpanWriter(stackalloc byte[17]); + writer.Write((byte)0x6F); // Packet ID + writer.Write((ushort)17); // Length + writer.Write((byte)flag); + writer.Write(cont.Serial); + writer.Write(first); + writer.Write(second); + writer.Write(false); - public static void SendUpdateSecureTrade(this NetState ns, Container cont, TradeFlag flag, int first, int second) + ns.Send(writer.Span); + } + + public static void SendSecureTradeEquip(this NetState ns, Item item, Mobile m) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[17]); - writer.Write((byte)0x6F); // Packet ID - writer.Write((ushort)17); // Length - writer.Write((byte)flag); - writer.Write(cont.Serial); - writer.Write(first); - writer.Write(second); - writer.Write(false); - - ns.Send(writer.Span); + return; } - public static void SendSecureTradeEquip(this NetState ns, Item item, Mobile m) + var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]); + writer.Write((byte)0x25); // Packet ID + writer.Write(item.Serial); + writer.Write((short)item.ItemID); + writer.Write((byte)0); + writer.Write((short)item.Amount); + writer.Write((short)item.X); + writer.Write((short)item.Y); + if (ns.ContainerGridLines) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]); - writer.Write((byte)0x25); // Packet ID - writer.Write(item.Serial); - writer.Write((short)item.ItemID); writer.Write((byte)0); - writer.Write((short)item.Amount); - writer.Write((short)item.X); - writer.Write((short)item.Y); - if (ns.ContainerGridLines) - { - writer.Write((byte)0); - } - writer.Write(m.Serial); - writer.Write((short)item.Hue); - - ns.Send(writer.Span); } + writer.Write(m.Serial); + writer.Write((short)item.Hue); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingTargetPackets.cs b/Projects/Server/Network/Packets/OutgoingTargetPackets.cs index 04b3a7e08..3de19ae85 100644 --- a/Projects/Server/Network/Packets/OutgoingTargetPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingTargetPackets.cs @@ -16,56 +16,55 @@ using System.Buffers; using Server.Targeting; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingTargetPackets { - public static class OutgoingTargetPackets + public static void SendMultiTargetReq(this NetState ns, MultiTarget t) { - public static void SendMultiTargetReq(this NetState ns, MultiTarget t) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 30 : 26]); - writer.Write((byte)0x99); // Packet ID - writer.Write(t.AllowGround); - writer.Write(t.TargetID); - writer.Write((byte)t.Flags); - writer.Clear(11); - writer.Write((short)t.MultiID); - writer.Write((short)t.Offset.X); - writer.Write((short)t.Offset.Y); - writer.Write((short)t.Offset.Z); - if (ns.HighSeas) - { - writer.Write(0); - } - - ns.Send(writer.Span); + return; } - public static void SendCancelTarget(this NetState ns) => - ns?.Send(stackalloc byte[] - { - 0x6C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 - }); - - public static void SendTargetReq(this NetState ns, Target t) + var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 30 : 26]); + writer.Write((byte)0x99); // Packet ID + writer.Write(t.AllowGround); + writer.Write(t.TargetID); + writer.Write((byte)t.Flags); + writer.Clear(11); + writer.Write((short)t.MultiID); + writer.Write((short)t.Offset.X); + writer.Write((short)t.Offset.Y); + writer.Write((short)t.Offset.Z); + if (ns.HighSeas) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[19]); - writer.Write((byte)0x6C); // Packet ID - writer.Write(t.AllowGround); - writer.Write(t.TargetID); - writer.Write((byte)t.Flags); - writer.Clear(12); - - ns.Send(writer.Span); + writer.Write(0); } + + ns.Send(writer.Span); + } + + public static void SendCancelTarget(this NetState ns) => + ns?.Send(stackalloc byte[] + { + 0x6C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + }); + + public static void SendTargetReq(this NetState ns, Target t) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[19]); + writer.Write((byte)0x6C); // Packet ID + writer.Write(t.AllowGround); + writer.Write(t.TargetID); + writer.Write((byte)t.Flags); + writer.Clear(12); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs b/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs index f70dfa49d..429792d6d 100644 --- a/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingVendorBuyPackets.cs @@ -17,111 +17,110 @@ using System.Buffers; using System.Collections.Generic; using Server.Items; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingVendorBuyPackets { - public static class OutgoingVendorBuyPackets + public static void SendVendorBuyContent(this NetState ns, List list) { - public static void SendVendorBuyContent(this NetState ns, List list) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var length = 5 + list.Count * (ns.ContainerGridLines ? 20 : 19); - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x3C); // Packet ID - writer.Write((ushort)length); - writer.Write((short)list.Count); - - for (var i = list.Count - 1; i >= 0; --i) - { - var bis = list[i]; - - writer.Write(bis.MySerial); - writer.Write((ushort)bis.ItemID); - writer.Write((byte)0); // itemID offset - writer.Write((ushort)bis.Amount); - writer.Write((short)(i + 1)); // x - writer.Write((short)1); // y - if (ns.ContainerGridLines) - { - writer.Write((byte)0); // Grid Location? - } - writer.Write(bis.ContainerSerial); - writer.Write((ushort)bis.Hue); - } - - ns.Send(writer.Span); + return; } - public static void SendDisplayBuyList(this NetState ns, Serial vendor) + var length = 5 + list.Count * (ns.ContainerGridLines ? 20 : 19); + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x3C); // Packet ID + writer.Write((ushort)length); + writer.Write((short)list.Count); + + for (var i = list.Count - 1; i >= 0; --i) { - if (ns == null) - { - return; - } + var bis = list[i]; - var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]); - writer.Write((byte)0x24); // Packet ID - writer.Write(vendor); - writer.Write((short)0x30); // Vendor Buy Window - if (ns.HighSeas) + writer.Write(bis.MySerial); + writer.Write((ushort)bis.ItemID); + writer.Write((byte)0); // itemID offset + writer.Write((ushort)bis.Amount); + writer.Write((short)(i + 1)); // x + writer.Write((short)1); // y + if (ns.ContainerGridLines) { - writer.Write((short)0x0); + writer.Write((byte)0); // Grid Location? } - - ns.Send(writer.Span); + writer.Write(bis.ContainerSerial); + writer.Write((ushort)bis.Hue); } - public static void SendVendorBuyList(this NetState ns, Mobile vendor, List list) + ns.Send(writer.Span); + } + + public static void SendDisplayBuyList(this NetState ns, Serial vendor) + { + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var length = 8; - for (int i = 0; i < list.Count; i++) - { - length += 6 + list[i].Description?.Length ?? 0; - } - - var writer = new SpanWriter(stackalloc byte[length]); - writer.Write((byte)0x74); // Packet ID - writer.Write((ushort)length); - writer.Write((vendor.FindItemOnLayer(Layer.ShopBuy) as Container)?.Serial ?? Serial.MinusOne); - writer.Write((byte)list.Count); - - for (var i = 0; i < list.Count; ++i) - { - var bis = list[i]; - - writer.Write(bis.Price); - - var desc = bis.Description ?? ""; - - writer.Write((byte)(desc.Length + 1)); - writer.WriteAsciiNull(desc); - } - - ns.Send(writer.Span); + return; } - public static void SendEndVendorBuy(this NetState ns, Serial vendor) + var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]); + writer.Write((byte)0x24); // Packet ID + writer.Write(vendor); + writer.Write((short)0x30); // Vendor Buy Window + if (ns.HighSeas) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[8]); - writer.Write((byte)0x3B); // Packet ID - writer.Write((ushort)8); - writer.Write(vendor); - writer.Write((byte)0); // Buy count - - ns.Send(writer.Span); + writer.Write((short)0x0); } + + ns.Send(writer.Span); + } + + public static void SendVendorBuyList(this NetState ns, Mobile vendor, List list) + { + if (ns.CannotSendPackets()) + { + return; + } + + var length = 8; + for (int i = 0; i < list.Count; i++) + { + length += 6 + list[i].Description?.Length ?? 0; + } + + var writer = new SpanWriter(stackalloc byte[length]); + writer.Write((byte)0x74); // Packet ID + writer.Write((ushort)length); + writer.Write((vendor.FindItemOnLayer(Layer.ShopBuy) as Container)?.Serial ?? Serial.MinusOne); + writer.Write((byte)list.Count); + + for (var i = 0; i < list.Count; ++i) + { + var bis = list[i]; + + writer.Write(bis.Price); + + var desc = bis.Description ?? ""; + + writer.Write((byte)(desc.Length + 1)); + writer.WriteAsciiNull(desc); + } + + ns.Send(writer.Span); + } + + public static void SendEndVendorBuy(this NetState ns, Serial vendor) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[8]); + writer.Write((byte)0x3B); // Packet ID + writer.Write((ushort)8); + writer.Write(vendor); + writer.Write((byte)0); // Buy count + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs b/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs index 5393a4802..90489b668 100644 --- a/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingVendorSellPackets.cs @@ -18,66 +18,65 @@ using System.Buffers; using System.Collections.Generic; using System.IO; -namespace Server.Network +namespace Server.Network; + +public static class OutgoingVendorSellPackets { - public static class OutgoingVendorSellPackets + public static void SendVendorSellList(this NetState ns, Serial vendor, List list) { - public static void SendVendorSellList(this NetState ns, Serial vendor, List list) + if (ns.CannotSendPackets()) { - if (ns == null) - { - return; - } - - var maxLength = 9; - for (int i = 0; i < list.Count; i++) - { - var sis = list[i]; - var item = sis.Item; - maxLength += 14 + Math.Max(item.Name?.Length ?? 0, sis.Name?.Length ?? 0); - } - - var writer = new SpanWriter(stackalloc byte[maxLength]); - writer.Write((byte)0x9E); // Packet ID - writer.Seek(2, SeekOrigin.Current); - - writer.Write(vendor); - writer.Write((ushort)list.Count); - - for (var i = 0; i < list.Count; i++) - { - var sis = list[i]; - var item = sis.Item; - writer.Write(item.Serial); - writer.Write((ushort)item.ItemID); - writer.Write((ushort)item.Hue); - writer.Write((ushort)item.Amount); - writer.Write((ushort)sis.Price); - - var name = (item.Name?.Trim()).DefaultIfNullOrEmpty(sis.Name ?? ""); - - writer.Write((ushort)name.Length); - writer.WriteAscii(name); - } - - writer.WritePacketLength(); - ns.Send(writer.Span); + return; } - public static void SendEndVendorSell(this NetState ns, Serial vendor) + var maxLength = 9; + for (int i = 0; i < list.Count; i++) { - if (ns == null) - { - return; - } - - var writer = new SpanWriter(stackalloc byte[8]); - writer.Write((byte)0x3B); // Packet ID - writer.Write((ushort)8); - writer.Write(vendor); - writer.Write((byte)0); - - ns.Send(writer.Span); + var sis = list[i]; + var item = sis.Item; + maxLength += 14 + Math.Max(item.Name?.Length ?? 0, sis.Name?.Length ?? 0); } + + var writer = new SpanWriter(stackalloc byte[maxLength]); + writer.Write((byte)0x9E); // Packet ID + writer.Seek(2, SeekOrigin.Current); + + writer.Write(vendor); + writer.Write((ushort)list.Count); + + for (var i = 0; i < list.Count; i++) + { + var sis = list[i]; + var item = sis.Item; + writer.Write(item.Serial); + writer.Write((ushort)item.ItemID); + writer.Write((ushort)item.Hue); + writer.Write((ushort)item.Amount); + writer.Write((ushort)sis.Price); + + var name = (item.Name?.Trim()).DefaultIfNullOrEmpty(sis.Name ?? ""); + + writer.Write((ushort)name.Length); + writer.WriteAscii(name); + } + + writer.WritePacketLength(); + ns.Send(writer.Span); + } + + public static void SendEndVendorSell(this NetState ns, Serial vendor) + { + if (ns.CannotSendPackets()) + { + return; + } + + var writer = new SpanWriter(stackalloc byte[8]); + writer.Write((byte)0x3B); // Packet ID + writer.Write((ushort)8); + writer.Write(vendor); + writer.Write((byte)0); + + ns.Send(writer.Span); } } diff --git a/Projects/Server/Network/Packets/PacketContainerBuilder.cs b/Projects/Server/Network/Packets/PacketContainerBuilder.cs index deee3e501..55eba01c1 100644 --- a/Projects/Server/Network/Packets/PacketContainerBuilder.cs +++ b/Projects/Server/Network/Packets/PacketContainerBuilder.cs @@ -18,98 +18,97 @@ using System.Buffers; using System.Buffers.Binary; using System.Runtime.CompilerServices; -namespace Server.Network +namespace Server.Network; + +public ref struct PacketContainerBuilder { - public ref struct PacketContainerBuilder + public const int MinPacketLength = 5; + + private bool _finished; + private int _count; + + private byte[] _arrayToReturnToPool; + private Span _bytes; + + public PacketContainerBuilder(Span initialBuffer) { - public const int MinPacketLength = 5; + _arrayToReturnToPool = null; + _finished = false; + _count = 0; - private bool _finished; - private int _count; + _bytes = initialBuffer; + _bytes[0] = 0xF7; // Packet ID + Length = MinPacketLength; // Length + Count + } - private byte[] _arrayToReturnToPool; - private Span _bytes; + public int Length { get; set; } - public PacketContainerBuilder(Span initialBuffer) + public int Capacity => _bytes.Length; + + [MethodImpl(MethodImplOptions.NoInlining)] + public ReadOnlySpan Finalize() + { + if (!_finished) { - _arrayToReturnToPool = null; - _finished = false; - _count = 0; - - _bytes = initialBuffer; - _bytes[0] = 0xF7; // Packet ID - Length = MinPacketLength; // Length + Count + BinaryPrimitives.WriteUInt16BigEndian(_bytes[1..3], (ushort)Length); + BinaryPrimitives.WriteUInt16BigEndian(_bytes[3..5], (ushort)_count); } - public int Length { get; set; } + return _bytes[..Length]; + } - public int Capacity => _bytes.Length; - - [MethodImpl(MethodImplOptions.NoInlining)] - public ReadOnlySpan Finalize() + [MethodImpl(MethodImplOptions.NoInlining)] + public Span GetSpan(int bytesNeeded) + { + if (_finished) { - if (!_finished) - { - BinaryPrimitives.WriteUInt16BigEndian(_bytes[1..3], (ushort)Length); - BinaryPrimitives.WriteUInt16BigEndian(_bytes[3..5], (ushort)_count); - } - - return _bytes[..Length]; + throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize"); } - [MethodImpl(MethodImplOptions.NoInlining)] - public Span GetSpan(int bytesNeeded) + if (Length > _bytes.Length - bytesNeeded) { - if (_finished) - { - throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize"); - } - - if (Length > _bytes.Length - bytesNeeded) - { - Grow(bytesNeeded); - } - - return _bytes[Length..]; + Grow(bytesNeeded); } - [MethodImpl(MethodImplOptions.NoInlining)] - public void Advance(int bytesWritten) - { - if (_finished) - { - throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize"); - } + return _bytes[Length..]; + } - _count++; - Length += bytesWritten; + [MethodImpl(MethodImplOptions.NoInlining)] + public void Advance(int bytesWritten) + { + if (_finished) + { + throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize"); } - [MethodImpl(MethodImplOptions.NoInlining)] - private void Grow(int additionalCapacityBeyondPos) + _count++; + Length += bytesWritten; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalCapacityBeyondPos) + { + var newLength = Math.Max(Length + additionalCapacityBeyondPos, _bytes.Length * 2); + byte[] poolArray = ArrayPool.Shared.Rent(newLength); + + _bytes[..Length].CopyTo(poolArray); + + byte[] toReturn = _arrayToReturnToPool; + _bytes = _arrayToReturnToPool = poolArray; + if (toReturn != null) { - var newLength = Math.Max(Length + additionalCapacityBeyondPos, _bytes.Length * 2); - byte[] poolArray = ArrayPool.Shared.Rent(newLength); - - _bytes[..Length].CopyTo(poolArray); - - byte[] toReturn = _arrayToReturnToPool; - _bytes = _arrayToReturnToPool = poolArray; - if (toReturn != null) - { - ArrayPool.Shared.Return(toReturn); - } + ArrayPool.Shared.Return(toReturn); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + byte[] toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) { - byte[] toReturn = _arrayToReturnToPool; - this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again - if (toReturn != null) - { - ArrayPool.Shared.Return(toReturn); - } + ArrayPool.Shared.Return(toReturn); } } } diff --git a/Projects/UOContent/Engines/Chat/ChatPackets.cs b/Projects/UOContent/Engines/Chat/ChatPackets.cs index 30ad48adc..01b47b471 100644 --- a/Projects/UOContent/Engines/Chat/ChatPackets.cs +++ b/Projects/UOContent/Engines/Chat/ChatPackets.cs @@ -103,7 +103,7 @@ namespace Server.Engines.Chat public static void SendChatMessage(this NetState ns, string lang, int number, string param1, string param2) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Engines/Harvest/Fishing.cs b/Projects/UOContent/Engines/Harvest/Fishing.cs index 035f21e4d..7e8cb9009 100644 --- a/Projects/UOContent/Engines/Harvest/Fishing.cs +++ b/Projects/UOContent/Engines/Harvest/Fishing.cs @@ -464,12 +464,7 @@ namespace Server.Engines.Harvest var ns = from.NetState; - if (ns == null) - { - return; - } - - if (number == 1043297 || ns.HighSeas) + if (number == 1043297 || ns?.HighSeas == true) { from.SendLocalizedMessage(number, name); } diff --git a/Projects/UOContent/Engines/Help/HelpTopic.cs b/Projects/UOContent/Engines/Help/HelpTopic.cs index 0fc242891..1b9179169 100644 --- a/Projects/UOContent/Engines/Help/HelpTopic.cs +++ b/Projects/UOContent/Engines/Help/HelpTopic.cs @@ -35,7 +35,7 @@ namespace Server.Engines.Help public static void SendDisplayHelpTopic(this NetState ns, int topicID, bool display = true) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Engines/Party/PartyPackets.cs b/Projects/UOContent/Engines/Party/PartyPackets.cs index 627f1811f..97c2bb952 100644 --- a/Projects/UOContent/Engines/Party/PartyPackets.cs +++ b/Projects/UOContent/Engines/Party/PartyPackets.cs @@ -48,7 +48,7 @@ namespace Server.Engines.PartySystem public static void SendPartyMemberList(this NetState ns, Party p) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -64,7 +64,7 @@ namespace Server.Engines.PartySystem public static void SendPartyRemoveMember(this NetState ns, Serial m, Party p = null) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -104,7 +104,7 @@ namespace Server.Engines.PartySystem public static void SendPartyTextMessage(this NetState ns, Serial m, string text, bool toAll) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -134,7 +134,7 @@ namespace Server.Engines.PartySystem public static void SendPartyInvitation(this NetState ns, Serial leader) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs index b7a6a59e3..a66d08291 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatuePackets.cs @@ -47,7 +47,7 @@ namespace Server.Engines.VeteranRewards public static void SendStatueAnimation(this NetState ns, Serial serial, int status, int anim, int frame) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Books/BookPackets.cs b/Projects/UOContent/Items/Books/BookPackets.cs index 6eee9c451..af59afa86 100644 --- a/Projects/UOContent/Items/Books/BookPackets.cs +++ b/Projects/UOContent/Items/Books/BookPackets.cs @@ -135,7 +135,7 @@ namespace Server.Items public static void SendBookContent(this NetState ns, BaseBook book) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -183,7 +183,7 @@ namespace Server.Items public static void SendBookCover(this NetState ns, Mobile from, BaseBook book) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs index 5a5ada1c9..0eac35aab 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinBoardPackets.cs @@ -168,7 +168,7 @@ namespace Server.Network public static void SendBBDisplayBoard(this NetState ns, BaseBulletinBoard board) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -203,7 +203,7 @@ namespace Server.Network public static void SendBBMessage(this NetState ns, BaseBulletinBoard board, BulletinMessage msg, bool content = false) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs index 73da28c08..601f676d4 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongPackets.cs @@ -287,7 +287,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongJoinGame(this NetState ns, Serial game) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -303,7 +303,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongPlayersInfo(this NetState ns, MahjongGame game, Mobile to) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -361,7 +361,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongTileInfo(this NetState ns, MahjongTile tile, Mobile to) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -407,7 +407,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongTilesInfo(this NetState ns, MahjongGame game, Mobile to) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -460,7 +460,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongGeneralInfo(this NetState ns, MahjongGame game) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -503,7 +503,7 @@ namespace Server.Engines.Mahjong public static void SendMahjongRelieve(this NetState ns, Serial game) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Maps/MapItemPackets.cs b/Projects/UOContent/Items/Maps/MapItemPackets.cs index 08229830c..2de7dd443 100644 --- a/Projects/UOContent/Items/Maps/MapItemPackets.cs +++ b/Projects/UOContent/Items/Maps/MapItemPackets.cs @@ -65,7 +65,7 @@ namespace Server.Network public static void SendMapDetails(this NetState ns, MapItem map) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -93,7 +93,7 @@ namespace Server.Network public static void SendMapCommand(this NetState ns, MapItem map, int command, int x = 0, int y = 0, bool editable = false) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs b/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs index 7de62807b..b78300f70 100644 --- a/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs +++ b/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs @@ -23,7 +23,7 @@ namespace Server.Network { public static void SendCorpseEquip(this NetState ns, Mobile beholder, Corpse beheld) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -67,7 +67,7 @@ namespace Server.Network public static void SendCorpseContent(this NetState ns, Mobile beholder, Corpse beheld) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs index 18d0dc042..500fc7b5c 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs @@ -671,7 +671,7 @@ namespace Server.Items // The client must know about the spellbook or it will crash! var ns = to.NetState; - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs index 8165628bf..744995544 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbilityPackets.cs @@ -32,7 +32,7 @@ namespace Server.Items public static void SendToggleSpecialAbility(this NetState ns, int abilityId, bool active) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Misc/BuffIcons.cs b/Projects/UOContent/Misc/BuffIcons.cs index 0c9e48340..7e7621568 100644 --- a/Projects/UOContent/Misc/BuffIcons.cs +++ b/Projects/UOContent/Misc/BuffIcons.cs @@ -162,7 +162,7 @@ namespace Server long ticks ) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -207,7 +207,7 @@ namespace Server public static void SendRemoveBuffPacket(NetState ns, Serial mob, BuffIcon iconID) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Misc/ProfessionInfo.cs b/Projects/UOContent/Misc/ProfessionInfo.cs index 6a2902d20..0c945556b 100644 --- a/Projects/UOContent/Misc/ProfessionInfo.cs +++ b/Projects/UOContent/Misc/ProfessionInfo.cs @@ -15,19 +15,21 @@ namespace Server return true; } - var lowerName = name.ToLowerInvariant().Replace(" ", ""); + var lowerName = name?.ToLowerInvariant().Replace(" ", ""); - foreach (var so in SkillInfo.Table) + if (!string.IsNullOrEmpty(lowerName)) { - if (lowerName == so.ProfessionSkillName.ToLowerInvariant()) + foreach (var so in SkillInfo.Table) { - skillName = (SkillName)so.SkillID; - return true; + if (lowerName == so.ProfessionSkillName.ToLowerInvariant()) + { + skillName = (SkillName)so.SkillID; + return true; + } } } return false; - } static ProfessionInfo() diff --git a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs index 8cb8eb5c2..712ba2698 100644 --- a/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs +++ b/Projects/UOContent/Mobiles/Vendors/BaseVendor.cs @@ -969,7 +969,7 @@ namespace Server.Mobiles var ns = from.NetState; - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Multis/Houses/HousePackets.cs b/Projects/UOContent/Multis/Houses/HousePackets.cs index 98e8a675e..a84c9bc45 100644 --- a/Projects/UOContent/Multis/Houses/HousePackets.cs +++ b/Projects/UOContent/Multis/Houses/HousePackets.cs @@ -29,7 +29,7 @@ namespace Server.Multis public static void SendBeginHouseCustomization(this NetState ns, Serial house) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -50,7 +50,7 @@ namespace Server.Multis public static void SendEndHouseCustomization(this NetState ns, Serial house) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -71,7 +71,7 @@ namespace Server.Multis public static void SendDesignStateGeneral(this NetState ns, Serial house, int revision) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Network/ConnectUO.cs b/Projects/UOContent/Network/ConnectUO.cs index 44749b378..fb4a51e29 100644 --- a/Projects/UOContent/Network/ConnectUO.cs +++ b/Projects/UOContent/Network/ConnectUO.cs @@ -102,7 +102,7 @@ namespace Server.Network public static void SendServerPollInfo(this NetState ns) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Network/MapUO.cs b/Projects/UOContent/Network/MapUO.cs index c5d4649a2..b0ddf0d75 100644 --- a/Projects/UOContent/Network/MapUO.cs +++ b/Projects/UOContent/Network/MapUO.cs @@ -56,7 +56,7 @@ namespace Server.Network public static void SendGuildMemberLocations(this NetState ns, Mobile from, Guild guild, bool sendLocations) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -116,7 +116,7 @@ namespace Server.Network public static void SendPartyMemberLocations(this NetState ns, Mobile from, Party party) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Network/UOGateway.cs b/Projects/UOContent/Network/UOGateway.cs index 97ecf3a22..2980760e4 100644 --- a/Projects/UOContent/Network/UOGateway.cs +++ b/Projects/UOContent/Network/UOGateway.cs @@ -61,7 +61,7 @@ namespace Server.Network this NetState ns, uint age, int clients, int items, int mobiles, long mem ) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } @@ -82,7 +82,7 @@ namespace Server.Network this NetState ns, string name, int age, int clients, int items, int mobiles, int mem ) { - if (ns == null) + if (ns.CannotSendPackets()) { return; } diff --git a/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs b/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs index 0006e8413..6bf666de4 100644 --- a/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs +++ b/Projects/UOContent/Skills/Tracking/OutgoingArrowPackets.cs @@ -28,7 +28,7 @@ namespace Server.Network public static void SendArrow(this NetState ns, byte command, int x, int y, Serial s) { - if (ns == null) + if (ns.CannotSendPackets()) { return; }