diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs new file mode 100644 index 000000000..c66dba37d --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AccountPacketTests.cs @@ -0,0 +1,552 @@ +using System; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Linq; +using System.Net; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http.Features; +using Server.Accounting; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class AccountPacketTests : IClassFixture + { + internal class TestAccount : IAccount, IComparable + { + public int TotalGold { get; private set; } + public int TotalPlat { get; private set; } + + public bool DepositGold(int amount) + { + TotalGold += amount; + return true; + } + + public bool DepositPlat(int amount) + { + TotalPlat += amount; + return true; + } + + public bool WithdrawGold(int amount) + { + if (TotalGold - amount < 0) return false; + + TotalGold -= amount; + return true; + } + + public bool WithdrawPlat(int amount) + { + if (TotalPlat - amount < 0) return false; + + TotalPlat -= amount; + return true; + } + + public long GetTotalGold() => TotalGold + TotalPlat * 100; + + public int CompareTo(TestAccount other) => other == null ? 1 : Username.CompareTo(other.Username); + + public int CompareTo(IAccount other) => other == null ? 1 : Username.CompareTo(other.Username); + + public string Username { get; set; } + public string Email { get; set; } + public AccessLevel AccessLevel { get; set; } + public int Length { get; } + public int Limit { get; } + public int Count { get; } + + private Mobile[] m_Mobiles; + private string m_Password; + + public Mobile this[int index] + { + get => m_Mobiles[index]; + set => m_Mobiles[index] = value; + } + + public void Delete() + { + } + + public void SetPassword(string password) + { + m_Password = password; + } + + public bool CheckPassword(string password) => m_Password == password; + + public TestAccount(Mobile[] mobiles) + { + m_Mobiles = mobiles; + foreach (var mobile in mobiles) + if (mobile != null) + mobile.Account = this; + + Length = mobiles.Length; + Count = mobiles.Count(t => t != null); + Limit = mobiles.Length; + } + } + + internal class TestConnectionContext : ConnectionContext + { + public override string ConnectionId { get; set; } + public override IFeatureCollection Features { get; } + public override IDictionary Items { get; set; } + public override IDuplexPipe Transport { get; set; } + } + + [Fact] + public void TestChangeCharacter() + { + var firstMobile = new Mobile(0x1); + firstMobile.DefaultMobileInit(); + firstMobile.Name = "Test Mobile"; + + var account = new TestAccount(new[] {firstMobile, null, null}); + + Span data = new ChangeCharacter(account).Compile(); + + Span expectedData = stackalloc byte[5 + account.Length * 60]; + int pos = 0; + + expectedData[pos++] = 0x81; // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + expectedData[pos++] = 1; // Count of non-null characters + expectedData[pos++] = 0; + + for (var i = 0; i < account.Length; ++i) + { + Mobile m = account[i]; + if (m == null) + { + expectedData.Clear(ref pos, 60); + } + else + { + m.Name.CopyASCIIFixedTo(ref pos, 30, expectedData); + expectedData.Clear(ref pos, 30); // Password (empty) + } + } + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestClientVersionReq() + { + Span data = new ClientVersionReq().Compile(); + + Span expectedData = stackalloc byte[] + { + 0xBD, // Packet ID + 0x00, 0x03 // Length + }; + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestDeleteResult() + { + Span data = new DeleteResult(DeleteResultType.BadRequest).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x85, // Packet ID + (byte)DeleteResultType.BadRequest + }; + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestPopupMessage() + { + Span data = new PopupMessage(PMMessage.IdleWarning).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x53, // Packet ID + (byte)PMMessage.IdleWarning + }; + + AssertThat.Equal(data, expectedData); + } + + [Theory] + [InlineData(ProtocolChanges.Version70610)] + [InlineData(ProtocolChanges.Version6000)] + public void TestSupportedFeatures(ProtocolChanges protocolChanges) + { + NetState ns = new NetState(new TestConnectionContext + { + RemoteEndPoint = IPEndPoint.Parse("127.0.0.1") + }); + + ns.ProtocolChanges = protocolChanges; + ns.Account = new TestAccount(new Mobile[0]); + + Span data = new SupportedFeatures(ns).Compile(); + + Span expectedData = stackalloc byte[ns.ExtendedSupportedFeatures ? 5 : 3]; + expectedData[0] = 0xB9; // Packet ID + + var flags = ExpansionInfo.GetFeatures(Expansion.EJ); + + if (ns.Account.Limit >= 6) + { + flags |= FeatureFlags.LiveAccount; + flags &= ~FeatureFlags.UOTD; + + if (ns.Account.Limit > 6) + flags |= FeatureFlags.SeventhCharacterSlot; + else + flags |= FeatureFlags.SixthCharacterSlot; + } + + if (ns.ExtendedSupportedFeatures) + ((uint)flags).CopyTo(expectedData.Slice(1, 4)); + else + ((ushort)flags).CopyTo(expectedData.Slice(1, 2)); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestLoginConfirm() + { + var m = new Mobile(0x1); + m.DefaultMobileInit(); + m.Body = 0x100; + m.X = 100; + m.Y = 10; + m.Z = -10; + m.Direction = Direction.Down; + m.LogoutMap = Map.Felucca; + + Span data = new LoginConfirm(m).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x1B, // Packet ID + 0x00, 0x00, 0x00, 0x00, // Serial + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, // Body + 0x00, 0x00, // X + 0x00, 0x00, // Y + 0x00, 0x00, // Z + 0x00, // Direction + 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, // Map Width + 0x00, 0x00, // Map Height + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + int pos = 1; + m.Serial.CopyTo(ref pos, expectedData); + pos += 4; + ((ushort)m.Body).CopyTo(ref pos, expectedData); + ((ushort)m.X).CopyTo(ref pos, expectedData); + ((ushort)m.Y).CopyTo(ref pos, expectedData); + ((ushort)m.Z).CopyTo(ref pos, expectedData); + expectedData[pos++] = (byte)m.Direction; + pos += 9; + var map = m.Map; + + if (map == null || map == Map.Internal) + map = m.LogoutMap; + + ((ushort)(map?.Width ?? Map.Felucca.Width)).CopyTo(ref pos, expectedData); + ((ushort)(map?.Height ?? Map.Felucca.Height)).CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestLoginComplete() + { + Span data = new LoginComplete().Compile(); + + Span expectedData = stackalloc byte[] + { + 0x55 // Packet ID + }; + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestCharacterListUpdate() + { + var firstMobile = new Mobile(0x1); + firstMobile.DefaultMobileInit(); + firstMobile.Name = "Test Mobile"; + + var account = new TestAccount(new[] {firstMobile, null, null, null, null}); + + Span data = new CharacterListUpdate(account).Compile(); + + Span expectedData = stackalloc byte[4 + account.Length * 60]; + + int pos = 0; + expectedData[pos++] = 0x86; // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + + int highSlot = -1; + for (int i = account.Length - 1; i >= 0; i--) + if (account[i] != null) + { + highSlot = i; + break; + } + + int count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5); + expectedData[pos++] = (byte)count; + + for (int i = 0; i < count; i++) + { + var m = account[i]; + if (m != null) + { + m.Name.CopyASCIIFixedTo(ref pos, 30, expectedData); + expectedData.Clear(ref pos, 30); + } + else + { + expectedData.Clear(ref pos, 60); + } + } + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestCharacterList() + { + var firstMobile = new Mobile(0x1); + firstMobile.DefaultMobileInit(); + firstMobile.Name = "Test Mobile"; + + var account = new TestAccount(new[] {firstMobile, null, null, null, null}); + var info = new[] + { + new CityInfo("Test City", "Test Building", 50, 100, 10, -10) + }; + + Span data = new CharacterList(account, info).Compile(); + + Span expectedData = stackalloc byte[11 + account.Length * 60 + info.Length * 89]; + + int pos = 0; + expectedData[pos++] = 0xA9; // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + + int highSlot = -1; + for (int i = account.Length - 1; i >= 0; i--) + if (account[i] != null) + { + highSlot = i; + break; + } + + int count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5); + expectedData[pos++] = (byte)count; + + for (int i = 0; i < count; i++) + { + var m = account[i]; + if (m != null) + { + m.Name.CopyASCIIFixedTo(ref pos, 30, expectedData); + expectedData.Clear(ref pos, 30); + } + else + { + expectedData.Clear(ref pos, 60); + } + } + + expectedData[pos++] = (byte)info.Length; + + for (int i = 0; i < info.Length; i++) + { + var ci = info[i]; + expectedData[pos++] = (byte)i; + ci.City.CopyASCIIFixedTo(ref pos, 32, expectedData); + ci.Building.CopyASCIIFixedTo(ref pos, 32, expectedData); + ci.X.CopyTo(ref pos, expectedData); + ci.Y.CopyTo(ref pos, expectedData); + ci.Z.CopyTo(ref pos, expectedData); + ci.Map.MapID.CopyTo(ref pos, expectedData); + ci.Description.CopyTo(ref pos, expectedData); + expectedData.Clear(ref pos, 4); + } + + var flags = ExpansionInfo.GetInfo(Expansion.EJ).CharacterListFlags; + if (count > 6) + flags |= CharacterListFlags.SeventhCharacterSlot | + CharacterListFlags.SixthCharacterSlot; // 7th Character Slot + else if (count == 6) + flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot + else if (account.Limit == 1) + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + + ((int)flags).CopyTo(ref pos, expectedData); + ((short)-1).CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestCharacterListOld() + { + var firstMobile = new Mobile(0x1); + firstMobile.DefaultMobileInit(); + firstMobile.Name = "Test Mobile"; + + var account = new TestAccount(new[] {firstMobile, null, null, null, null}); + var info = new[] + { + new CityInfo("Test City", "Test Building", 50, 100, 10, -10) + }; + + Span data = new CharacterListOld(account, info).Compile(); + + Span expectedData = stackalloc byte[9 + account.Length * 60 + info.Length * 63]; + + int pos = 0; + expectedData[pos++] = 0xA9; // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); // Length + + int highSlot = -1; + for (int i = account.Length - 1; i >= 0; i--) + if (account[i] != null) + { + highSlot = i; + break; + } + + int count = Math.Max(Math.Max(highSlot + 1, account.Limit), 5); + expectedData[pos++] = (byte)count; + + for (int i = 0; i < count; i++) + { + var m = account[i]; + if (m != null) + { + m.Name.CopyASCIIFixedTo(ref pos, 30, expectedData); + expectedData.Clear(ref pos, 30); + } + else + { + expectedData.Clear(ref pos, 60); + } + } + + expectedData[pos++] = (byte)info.Length; + + for (int i = 0; i < info.Length; i++) + { + var ci = info[i]; + expectedData[pos++] = (byte)i; + ci.City.CopyASCIIFixedTo(ref pos, 31, expectedData); + ci.Building.CopyASCIIFixedTo(ref pos, 31, expectedData); + } + + var flags = ExpansionInfo.GetInfo(Expansion.EJ).CharacterListFlags; + if (count > 6) + flags |= CharacterListFlags.SeventhCharacterSlot | + CharacterListFlags.SixthCharacterSlot; // 7th Character Slot + else if (count == 6) + flags |= CharacterListFlags.SixthCharacterSlot; // 6th Character Slot + else if (account.Limit == 1) + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + + ((int)flags).CopyTo(ref pos, expectedData); + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestAccountLoginRej() + { + var reason = ALRReason.BadComm; + Span data = new AccountLoginRej(reason).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x82, // Packet ID + (byte)reason + }; + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestAccountLoginAck() + { + var info = new[] + { + new ServerInfo("Test Server", 0, TimeZoneInfo.Local, IPEndPoint.Parse("127.0.0.1")) + }; + + Span data = new AccountLoginAck(info).Compile(); + + Span expectedData = stackalloc byte[6 + info.Length * 40]; + + int pos = 0; + expectedData[pos++] = 0xA8; // Packet ID + ((ushort)expectedData.Length).CopyTo(ref pos, expectedData); + expectedData[pos++] = 0x5D; // Unknown + ((ushort)info.Length).CopyTo(ref pos, expectedData); + + for (int i = 0; i < info.Length; i++) + { + var si = info[i]; + ((ushort)i).CopyTo(ref pos, expectedData); + si.Name.CopyASCIIFixedTo(ref pos, 32, expectedData); + expectedData[pos++] = (byte)si.FullPercent; + expectedData[pos++] = (byte)si.TimeZone; + Utility.GetAddressValue(si.Address.Address).CopyTo(ref pos, expectedData); + } + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestPlayServerAck() + { + var si = new ServerInfo("Test Server", 0, TimeZoneInfo.Local, IPEndPoint.Parse("127.0.0.1")); + + Span data = new PlayServerAck(si).Compile(); + + var addr = Utility.GetAddressValue(si.Address.Address); + + Span expectedData = stackalloc byte[] + { + 0x8C, // Packet ID + (byte)addr, // IP Address in LE + (byte)(addr >> 8), + (byte)(addr >> 16), + (byte)(addr >> 24), + 0x00, 0x00, // Port + 0x00, 0x00, 0x00, 0x00 // Auth ID + }; + + ((ushort)si.Address.Port).CopyTo(expectedData.Slice(5, 2)); + (-1).CopyTo(expectedData.Slice(7, 4)); // Auth ID + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/ArrowPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/ArrowPacketTests.cs index fb45facb5..36c16f6e8 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/ArrowPacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/ArrowPacketTests.cs @@ -13,7 +13,7 @@ namespace Server.Tests.Network.Packets Span expectedData = stackalloc byte[] { - 0xBA, // Packet + 0xBA, // Packet ID 0x00, // Command 0xFF, 0xFF, // X 0xFF, 0xFF // Y @@ -32,7 +32,7 @@ namespace Server.Tests.Network.Packets Span expectedData = stackalloc byte[] { - 0xBA, // Packet + 0xBA, // Packet ID 0x01, // Command 0x00, 0x00, // X 0x00, 0x00 // Y @@ -54,7 +54,7 @@ namespace Server.Tests.Network.Packets Span expectedData = stackalloc byte[] { - 0xBA, // Packet + 0xBA, // Packet ID 0x00, // Command 0x00, 0x00, // X 0x00, 0x00, // Y @@ -77,7 +77,7 @@ namespace Server.Tests.Network.Packets Span expectedData = stackalloc byte[] { - 0xBA, // Packet + 0xBA, // Packet ID 0x01, // Command 0x00, 0x00, // X 0x00, 0x00, // Y diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/AttributeNormalizerTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AttributeNormalizerTests.cs new file mode 100644 index 000000000..fd32d5670 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/AttributeNormalizerTests.cs @@ -0,0 +1,107 @@ +using System; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class AttributeNormalizerTests + { + [Fact] + public void TestAttributeNormalizerEnabled() + { + AttributeNormalizer.Enabled = true; + AttributeNormalizer.Maximum = 25; + + PacketWriter stream = new PacketWriter(4); + + const short cur = 50; + const short max = 100; + + AttributeNormalizer.Write(stream, cur, max); + + Span expectedData = stackalloc byte[] + { + 0x00, 0x00, // Maximum Normalized + 0x00, 0x00 // Current Normalized + }; + + ((short)AttributeNormalizer.Maximum).CopyTo(expectedData.Slice(0, 2)); + ((short)(cur * 25 / max)).CopyTo(expectedData.Slice(2, 2)); + + AssertThat.Equal(stream.ToArray(), expectedData); + } + + [Fact] + public void TestAttributeNormalizerReversedEnabled() + { + AttributeNormalizer.Enabled = true; + AttributeNormalizer.Maximum = 25; + + PacketWriter stream = new PacketWriter(4); + + const short cur = 50; + const short max = 100; + + AttributeNormalizer.WriteReverse(stream, cur, max); + + Span expectedData = stackalloc byte[] + { + 0x00, 0x00, // Current Normalized + 0x00, 0x00 // Maximum Normalized + }; + + ((short)AttributeNormalizer.Maximum).CopyTo(expectedData.Slice(2, 2)); + ((short)(cur * 25 / max)).CopyTo(expectedData.Slice(0, 2)); + + AssertThat.Equal(stream.ToArray(), expectedData); + } + + [Fact] + public void TestAttributeNormalizerDisabled() + { + AttributeNormalizer.Enabled = false; + + PacketWriter stream = new PacketWriter(4); + + const short cur = 50; + const short max = 100; + + AttributeNormalizer.Write(stream, cur, max); + + Span expectedData = stackalloc byte[] + { + 0x00, 0x00, // Maximum + 0x00, 0x00 // Current + }; + + max.CopyTo(expectedData.Slice(0, 2)); + cur.CopyTo(expectedData.Slice(2, 2)); + + AssertThat.Equal(stream.ToArray(), expectedData); + } + + [Fact] + public void TestAttributeNormalizerReversedDisabled() + { + AttributeNormalizer.Enabled = false; + + PacketWriter stream = new PacketWriter(4); + + const short cur = 50; + const short max = 100; + + AttributeNormalizer.WriteReverse(stream, cur, max); + + Span expectedData = stackalloc byte[] + { + 0x00, 0x00, // Current + 0x00, 0x00 // Maximum + }; + + max.CopyTo(expectedData.Slice(2, 2)); + cur.CopyTo(expectedData.Slice(0, 2)); + + AssertThat.Equal(stream.ToArray(), expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/CombatPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/CombatPacketTests.cs new file mode 100644 index 000000000..4c82eb911 --- /dev/null +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/CombatPacketTests.cs @@ -0,0 +1,66 @@ +using System; +using Server.Network; +using Xunit; + +namespace Server.Tests.Network.Packets +{ + public class CombatPacketTests + { + [Fact] + public void TestSwing() + { + Serial attacker = 0x1000; + Serial defender = 0x2000; + + Span data = new Swing(attacker, defender).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x2F, // Packet ID + 0x00, // Unknown + 0x00, 0x00, 0x00, 0x00, // Attacker + 0x00, 0x00, 0x00, 0x00, // Defender + }; + + attacker.CopyTo(expectedData.Slice(2, 4)); + defender.CopyTo(expectedData.Slice(6, 4)); + + AssertThat.Equal(data, expectedData); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void TestSetWarMode(bool warmode) + { + Span data = new SetWarMode(warmode).Compile(); + + Span expectedData = stackalloc byte[] + { + 0x72, // Packet ID + warmode ? (byte)0x01 : (byte)0x00, // Mode + 0x00, 0x32, 0x00 // Unknown + }; + + AssertThat.Equal(data, expectedData); + } + + [Fact] + public void TestChangeCombatant() + { + Serial combatant = 0x1000; + + Span data = new ChangeCombatant(combatant).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xAA, // Packet ID + 0x00, 0x00, 0x00, 0x00 // Combatant + }; + + combatant.CopyTo(expectedData.Slice(1, 4)); + + AssertThat.Equal(data, expectedData); + } + } +} diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/DamagePacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/DamagePacketTests.cs index 130aa6965..33e7ef98d 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/DamagePacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/DamagePacketTests.cs @@ -15,11 +15,11 @@ namespace Server.Tests.Network.Packets var m = new Mobile(0x1); m.DefaultMobileInit(); - Span data = new DamagePacketOld(m, inputAmount).Compile(); + Span data = new DamagePacketOld(m.Serial, inputAmount).Compile(); Span expectedData = stackalloc byte[] { - 0xBF, // Packet + 0xBF, // Packet ID 0x00, 0x0B, // Length 0x00, 0x22, // Sub-packet 0x01, // Command @@ -36,16 +36,16 @@ namespace Server.Tests.Network.Packets [InlineData(-5, 0)] [InlineData(1024, 1024)] [InlineData(100000, 0xFFFF)] - public void TestDamagePacket(int inputAmount, ushort expectedAmount) + public void TestDamage(int inputAmount, ushort expectedAmount) { var m = new Mobile(0x1); m.DefaultMobileInit(); - Span data = new DamagePacket(m, inputAmount).Compile(); + Span data = new DamagePacket(m.Serial, inputAmount).Compile(); Span expectedData = stackalloc byte[] { - 0x0B, // Packet + 0x0B, // Packet ID 0x00, 0x00, 0x00, 0x00, // Mobile Serial 0x00, 0x00 // Amount }; diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/DisplayHuePickerTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/DisplayHuePickerTests.cs index e62c17c1e..43d7a5a82 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/DisplayHuePickerTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/DisplayHuePickerTests.cs @@ -10,7 +10,7 @@ namespace Server.Tests.Network.Packets [Fact] public void TestDisplayHuePicker() { - ushort itemID = 0xFF01; + const ushort itemID = 0xFF01; var huePicker = new HuePicker(itemID); Span data = new DisplayHuePicker(huePicker).Compile(); diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/MapPacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/MapPacketTests.cs index 9b072f64b..9a5b1610d 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/MapPacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/MapPacketTests.cs @@ -13,7 +13,7 @@ namespace Server.Tests.Network.Packets Span expectedData = stackalloc byte[] { - 0xBF, // Packet + 0xBF, // Packet ID 0x00, 0x29, // Length 0x00, 0x18, // Sub-packet 0x00, 0x00, 0x00, 0x04, // 4 maps @@ -42,5 +42,21 @@ namespace Server.Tests.Network.Packets AssertThat.Equal(data, expectedData); } + + [Fact] + public void TestMapChange() + { + Span data = new MapChange(Map.Felucca).Compile(); + + Span expectedData = stackalloc byte[] + { + 0xBF, // Packet ID + 0x00, 0x06, // Length + 0x00, 0x08, // Sub-packet + 0x00, // Felucca + }; + + AssertThat.Equal(data, expectedData); + } } } diff --git a/Projects/Server.Tests/Network/Packets/Old/Outgoing/SecureTradePacketTests.cs b/Projects/Server.Tests/Network/Packets/Old/Outgoing/SecureTradePacketTests.cs index 6766de0a2..5d9187c9e 100644 --- a/Projects/Server.Tests/Network/Packets/Old/Outgoing/SecureTradePacketTests.cs +++ b/Projects/Server.Tests/Network/Packets/Old/Outgoing/SecureTradePacketTests.cs @@ -39,7 +39,7 @@ namespace Server.Tests.Network.Packets firstCont.Serial.CopyTo(ref pos, expectedData); secondCont.Serial.CopyTo(ref pos, expectedData); pos++; - name.CopyASCIITo(ref pos, 30, expectedData); + name.CopyASCIIFixedTo(ref pos, 30, expectedData); AssertThat.Equal(data, expectedData); } diff --git a/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs b/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs index 5ed95ad2c..d91b78401 100644 --- a/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs +++ b/Projects/Server.Tests/Network/Packets/PacketTestUtilities.cs @@ -80,5 +80,24 @@ namespace Server.Tests.Network.Packets { pos += Encoding.ASCII.GetBytes(str.AsSpan(0, Math.Min(str.Length, max)), bytes.Slice(pos)); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CopyASCIIFixedTo(this string str, ref int pos, int max, Span bytes) + { + int bytesWritten = Encoding.ASCII.GetBytes(str.AsSpan(0, Math.Min(str.Length, max)), bytes.Slice(pos)); + + // This is not needed in the current stackalloc implementation, but not guaranteed in the future. + if (bytesWritten < max) + bytes.Slice(pos + bytesWritten, max - bytesWritten).Clear(); + + pos += max; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Clear(this Span span, ref int pos, int amount) + { + span.Slice(pos, amount).Clear(); + pos += amount; + } } } diff --git a/Projects/Server.Tests/ServerFixture.cs b/Projects/Server.Tests/ServerFixture.cs index e08a659f3..2c084c854 100644 --- a/Projects/Server.Tests/ServerFixture.cs +++ b/Projects/Server.Tests/ServerFixture.cs @@ -9,6 +9,8 @@ namespace Server.Tests // Global setup static ServerFixture() { + Core.Expansion = Expansion.EJ; + // Load Configurations ServerConfiguration.Load(true); diff --git a/Projects/Server/Mobile.cs b/Projects/Server/Mobile.cs index d26b705f1..e1e5a4fb9 100644 --- a/Projects/Server/Mobile.cs +++ b/Projects/Server/Mobile.cs @@ -446,8 +446,7 @@ namespace Server private static readonly TimeSpan WarmodeSpamCatch = TimeSpan.FromSeconds(Core.SE ? 1.0 : 0.5); private static readonly TimeSpan WarmodeSpamDelay = TimeSpan.FromSeconds(Core.SE ? 4.0 : 2.0); - private static readonly Packet[][] m_MovingPacketCache = new Packet[][] - { + private static readonly Packet[][] m_MovingPacketCache = { new Packet[8], new Packet[8] }; @@ -897,10 +896,9 @@ namespace Server return; } - m_NetState?.Send(new ChangeCombatant(m_Combatant)); - if (m_Combatant == null) { + m_NetState?.Send(new ChangeCombatant(Serial.Zero)); m_ExpireCombatant?.Stop(); m_CombatTimer?.Stop(); @@ -909,18 +907,18 @@ namespace Server } else { + m_NetState?.Send(new ChangeCombatant(m_Combatant.Serial)); m_ExpireCombatant ??= new ExpireCombatantTimer(this); m_ExpireCombatant.Start(); m_CombatTimer ??= new CombatTimer(this); m_CombatTimer.Start(); - } - if (m_Combatant != null && CanBeHarmful(m_Combatant, false)) - { - DoHarmful(m_Combatant); - - m_Combatant?.PlaySound(m_Combatant.GetAngerSound()); + if (CanBeHarmful(m_Combatant, false)) + { + DoHarmful(m_Combatant); + m_Combatant.PlaySound(m_Combatant.GetAngerSound()); + } } OnCombatantChange(); @@ -2062,7 +2060,8 @@ namespace Server if (ns != null && m_Map != null) { ns.Sequence = 0; - ns.Send(new MapChange(this)); + if (Map != null) + ns.Send(new MapChange(Map)); if (!Core.SE && ns.ProtocolChanges < ProtocolChanges.Version6000) ns.Send(new MapPatches()); @@ -2187,7 +2186,8 @@ namespace Server if (ns != null && m_Map != null) { ns.Sequence = 0; - ns.Send(new MapChange(this)); + if (Map != null) + ns.Send(new MapChange(Map)); if (!Core.SE && ns.ProtocolChanges < ProtocolChanges.Version6000) ns.Send(new MapPatches()); @@ -4136,27 +4136,16 @@ namespace Server CheckAggrExpire(); PoisonTimer?.Stop(); - m_HitsTimer?.Stop(); - m_StamTimer?.Stop(); - m_ManaTimer?.Stop(); - m_CombatTimer?.Stop(); - m_ExpireCombatant?.Stop(); - m_LogoutTimer?.Stop(); - m_ExpireCriminal?.Stop(); - m_WarmodeTimer?.Stop(); - m_ParaTimer?.Stop(); - m_FrozenTimer?.Stop(); - m_AutoManifestTimer?.Stop(); } @@ -4305,11 +4294,12 @@ namespace Server Packet animPacket = null; var eable = m_Map.GetClientsInRange(m_Location); + var corpseSerial = c?.Serial ?? Serial.Zero; foreach (var state in eable) if (state != m_NetState) { - animPacket ??= Packet.Acquire(new DeathAnimation(this, c)); + animPacket ??= Packet.Acquire(new DeathAnimation(Serial, corpseSerial)); state.Send(animPacket); @@ -5307,8 +5297,8 @@ namespace Server if (ourState != null) { p = ourState.DamagePacket - ? Packet.Acquire(new DamagePacket(this, amount)) - : Packet.Acquire(new DamagePacketOld(this, amount)); + ? Packet.Acquire(new DamagePacket(Serial, amount)) + : Packet.Acquire(new DamagePacketOld(Serial, amount)); ourState.Send(p); } @@ -5320,12 +5310,12 @@ namespace Server if (newPacket && !(p is DamagePacket)) { Packet.Release(p); - p = Packet.Acquire(new DamagePacket(this, amount)); + p = Packet.Acquire(new DamagePacket(Serial, amount)); } else if (!newPacket && !(p is DamagePacketOld)) { Packet.Release(p); - p = Packet.Acquire(new DamagePacketOld(this, amount)); + p = Packet.Acquire(new DamagePacketOld(Serial, amount)); } theirState.Send(p); @@ -5355,13 +5345,13 @@ namespace Server { if (ns.DamagePacket) { - pNew ??= Packet.Acquire(new DamagePacket(this, amount)); + pNew ??= Packet.Acquire(new DamagePacket(Serial, amount)); ns.Send(pNew); } else { - pOld ??= Packet.Acquire(new DamagePacketOld(this, amount)); + pOld ??= Packet.Acquire(new DamagePacketOld(Serial, amount)); ns.Send(pOld); } @@ -5410,17 +5400,17 @@ namespace Server if (damaged.CanSeeVisibleDamage && ourState != null) { if (ourState.DamagePacket) - ourState.Send(new DamagePacket(this, amount)); + ourState.Send(new DamagePacket(Serial, amount)); else - ourState.Send(new DamagePacketOld(this, amount)); + ourState.Send(new DamagePacketOld(Serial, amount)); } if (theirState != null && theirState != ourState && damager.CanSeeVisibleDamage) { if (theirState.DamagePacket) - theirState.Send(new DamagePacket(this, amount)); + theirState.Send(new DamagePacket(Serial, amount)); else - theirState.Send(new DamagePacketOld(this, amount)); + theirState.Send(new DamagePacketOld(Serial, amount)); } } } @@ -6012,11 +6002,11 @@ namespace Server { state.Mobile.ProcessDelta(); - // if (state.StygianAbyss ) { + // if (state.StygianAbyss) { // if (pNew == null) - // pNew = Packet.Acquire( new NewMobileAnimation( this, action, frameCount, delay ) ); + // pNew = Packet.Acquire(new NewMobileAnimation(this.Serial, action, frameCount, delay)); - // state.Send( pNew ); + // state.Send(pNew); // } else { if (p == null) { @@ -6048,7 +6038,7 @@ namespace Server } } - p = Packet.Acquire(new MobileAnimation(this, action, frameCount, repeatCount, forward, repeat, + p = Packet.Acquire(new MobileAnimation(Serial, action, frameCount, repeatCount, forward, repeat, delay)); } diff --git a/Projects/Server/Network/PacketHandlers.cs b/Projects/Server/Network/PacketHandlers.cs index f27589285..4e0edd4c9 100644 --- a/Projects/Server/Network/PacketHandlers.cs +++ b/Projects/Server/Network/PacketHandlers.cs @@ -1938,7 +1938,7 @@ namespace Server.Network state.Send(new LoginConfirm(m)); if (m.Map != null) - state.Send(new MapChange(m)); + state.Send(new MapChange(m.Map)); if (!Core.SE && state.ProtocolChanges < ProtocolChanges.Version6000) state.Send(new MapPatches()); @@ -2022,7 +2022,8 @@ namespace Server.Network state.Send(LoginComplete.Instance); state.Send(new CurrentTime()); state.Send(SeasonChange.Instantiate(m.GetSeason(), true)); - state.Send(new MapChange(m)); + if (m.Map != null) + state.Send(new MapChange(m.Map)); EventSink.InvokeLogin(m); diff --git a/Projects/Server/Network/Packets.cs b/Projects/Server/Network/Packets.cs deleted file mode 100644 index 043a0da78..000000000 --- a/Projects/Server/Network/Packets.cs +++ /dev/null @@ -1,1286 +0,0 @@ -/*************************************************************************** - * Packets.cs - * ------------------- - * begin : May 1, 2002 - * copyright : (C) The RunUO Software Team - * email : info@runuo.com - * - * $Id$ - * - ***************************************************************************/ - -/*************************************************************************** - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - ***************************************************************************/ - -using System; -using System.Net; -using System.Threading; -using Server.Accounting; - -namespace Server.Network -{ - public enum LRReason : byte - { - CannotLift = 0, - OutOfRange = 1, - OutOfSight = 2, - TryToSteal = 3, - AreHolding = 4, - Inspecific = 5 - } - - public sealed class ScrollMessage : Packet - { - public ScrollMessage(int type, int tip, string text) : base(0xA6) - { - text ??= ""; - - EnsureCapacity(10 + text.Length); - - Stream.Write((byte)type); - Stream.Write(tip); - Stream.Write((ushort)text.Length); - Stream.WriteAsciiFixed(text, text.Length); - } - } - - public sealed class CurrentTime : Packet - { - public CurrentTime() : base(0x5B, 4) - { - var now = DateTime.UtcNow; - - Stream.Write((byte)now.Hour); - Stream.Write((byte)now.Minute); - Stream.Write((byte)now.Second); - } - } - - public sealed class MapChange : Packet - { - public MapChange(Mobile m) : base(0xBF) - { - EnsureCapacity(6); - - Stream.Write((short)0x08); - Stream.Write((byte)(m.Map?.MapID ?? 0)); - } - } - - public sealed class SupportedFeatures : Packet - { - public SupportedFeatures(NetState ns) : base(0xB9, ns.ExtendedSupportedFeatures ? 5 : 3) - { - var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; - - flags |= Value; - - if (ns.Account.Limit >= 6) - { - flags |= FeatureFlags.LiveAccount; - flags &= ~FeatureFlags.UOTD; - - if (ns.Account.Limit > 6) - flags |= FeatureFlags.SeventhCharacterSlot; - else - flags |= FeatureFlags.SixthCharacterSlot; - } - - if (ns.ExtendedSupportedFeatures) - Stream.Write((uint)flags); - else - Stream.Write((ushort)flags); - } - - public static FeatureFlags Value { get; set; } - - public static SupportedFeatures Instantiate(NetState ns) => new SupportedFeatures(ns); - } - - public static class AttributeNormalizer - { - public static int Maximum { get; set; } = 25; - - public static bool Enabled { get; set; } = true; - - public static void Write(PacketWriter stream, int cur, int max) - { - if (Enabled && max != 0) - { - stream.Write((short)Maximum); - stream.Write((short)(cur * Maximum / max)); - } - else - { - stream.Write((short)max); - stream.Write((short)cur); - } - } - - public static void WriteReverse(PacketWriter stream, int cur, int max) - { - if (Enabled && max != 0) - { - stream.Write((short)(cur * Maximum / max)); - stream.Write((short)Maximum); - } - else - { - stream.Write((short)cur); - stream.Write((short)max); - } - } - } - - public sealed class MobileHits : Packet - { - public MobileHits(Mobile m) : base(0xA1, 9) - { - Stream.Write(m.Serial); - Stream.Write((short)m.HitsMax); - Stream.Write((short)m.Hits); - } - } - - public sealed class MobileHitsN : Packet - { - public MobileHitsN(Mobile m) : base(0xA1, 9) - { - Stream.Write(m.Serial); - AttributeNormalizer.Write(Stream, m.Hits, m.HitsMax); - } - } - - public sealed class MobileMana : Packet - { - public MobileMana(Mobile m) : base(0xA2, 9) - { - Stream.Write(m.Serial); - Stream.Write((short)m.ManaMax); - Stream.Write((short)m.Mana); - } - } - - public sealed class MobileManaN : Packet - { - public MobileManaN(Mobile m) : base(0xA2, 9) - { - Stream.Write(m.Serial); - AttributeNormalizer.Write(Stream, m.Mana, m.ManaMax); - } - } - - public sealed class MobileStam : Packet - { - public MobileStam(Mobile m) : base(0xA3, 9) - { - Stream.Write(m.Serial); - Stream.Write((short)m.StamMax); - Stream.Write((short)m.Stam); - } - } - - public sealed class MobileStamN : Packet - { - public MobileStamN(Mobile m) : base(0xA3, 9) - { - Stream.Write(m.Serial); - AttributeNormalizer.Write(Stream, m.Stam, m.StamMax); - } - } - - public sealed class MobileAttributes : Packet - { - public MobileAttributes(Mobile m) : base(0x2D, 17) - { - Stream.Write(m.Serial); - - Stream.Write((short)m.HitsMax); - Stream.Write((short)m.Hits); - - Stream.Write((short)m.ManaMax); - Stream.Write((short)m.Mana); - - Stream.Write((short)m.StamMax); - Stream.Write((short)m.Stam); - } - } - - public sealed class MobileAttributesN : Packet - { - public MobileAttributesN(Mobile m) : base(0x2D, 17) - { - Stream.Write(m.Serial); - - AttributeNormalizer.Write(Stream, m.Hits, m.HitsMax); - AttributeNormalizer.Write(Stream, m.Mana, m.ManaMax); - AttributeNormalizer.Write(Stream, m.Stam, m.StamMax); - } - } - - public sealed class PathfindMessage : Packet - { - public PathfindMessage(IPoint3D p) : base(0x38, 7) - { - Stream.Write((short)p.X); - Stream.Write((short)p.Y); - Stream.Write((short)p.Z); - } - } - - // unsure of proper format, client crashes - public sealed class MobileName : Packet - { - public MobileName(Mobile m) : base(0x98) - { - EnsureCapacity(37); - - Stream.Write(m.Serial); - Stream.WriteAsciiFixed(m.Name ?? "", 30); - } - } - - public sealed class MobileAnimation : Packet - { - public MobileAnimation(Mobile m, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay) : base(0x6E, 14) - { - Stream.Write(m.Serial); - Stream.Write((short)action); - Stream.Write((short)frameCount); - Stream.Write((short)repeatCount); - Stream.Write(!forward); // protocol has really "reverse" but I find this more intuitive - Stream.Write(repeat); - Stream.Write((byte)delay); - } - } - - public sealed class NewMobileAnimation : Packet - { - public NewMobileAnimation(Mobile m, int action, int frameCount, int delay) : base(0xE2, 10) - { - Stream.Write(m.Serial); - Stream.Write((short)action); - Stream.Write((short)frameCount); - Stream.Write((byte)delay); - } - } - - public sealed class MobileStatusCompact : Packet - { - public MobileStatusCompact(bool canBeRenamed, Mobile m) : base(0x11) - { - EnsureCapacity(43); - - Stream.Write(m.Serial); - Stream.WriteAsciiFixed(m.Name ?? "", 30); - - AttributeNormalizer.WriteReverse(Stream, m.Hits, m.HitsMax); - - Stream.Write(canBeRenamed); - - Stream.Write((byte)0); // type - } - } - - public sealed class MobileStatusExtended : Packet - { - public MobileStatusExtended(Mobile m) : this(m, m.NetState) - { - } - - public MobileStatusExtended(Mobile m, NetState ns) : base(0x11) - { - var name = m.Name ?? ""; - - int type; - - if (Core.HS && ns?.ExtendedStatus == true) - { - type = 6; - EnsureCapacity(121); - } - else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true) - { - type = 5; - EnsureCapacity(91); - } - else - { - type = Core.AOS ? 4 : 3; - EnsureCapacity(88); - } - - Stream.Write(m.Serial); - Stream.WriteAsciiFixed(name, 30); - - Stream.Write((short)m.Hits); - Stream.Write((short)m.HitsMax); - - Stream.Write(m.CanBeRenamedBy(m)); - - Stream.Write((byte)type); - - Stream.Write(m.Female); - - Stream.Write((short)m.Str); - Stream.Write((short)m.Dex); - Stream.Write((short)m.Int); - - Stream.Write((short)m.Stam); - Stream.Write((short)m.StamMax); - - Stream.Write((short)m.Mana); - Stream.Write((short)m.ManaMax); - - Stream.Write(m.TotalGold); - Stream.Write((short)(Core.AOS ? m.PhysicalResistance : (int)(m.ArmorRating + 0.5))); - Stream.Write((short)(Mobile.BodyWeight + m.TotalWeight)); - - if (type >= 5) - { - Stream.Write((short)m.MaxWeight); - Stream.Write((byte)(m.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but... - } - - Stream.Write((short)m.StatCap); - - Stream.Write((byte)m.Followers); - Stream.Write((byte)m.FollowersMax); - - if (type >= 4) - { - Stream.Write((short)m.FireResistance); // Fire - Stream.Write((short)m.ColdResistance); // Cold - Stream.Write((short)m.PoisonResistance); // Poison - Stream.Write((short)m.EnergyResistance); // Energy - Stream.Write((short)m.Luck); // Luck - - var weapon = m.Weapon; - - if (weapon != null) - { - weapon.GetStatusDamage(m, out var min, out var max); - Stream.Write((short)min); // Damage min - Stream.Write((short)max); // Damage max - } - else - { - Stream.Write((short)0); // Damage min - Stream.Write((short)0); // Damage max - } - - Stream.Write(m.TithingPoints); - } - - if (type >= 6) - for (var i = 0; i < 15; ++i) - Stream.Write((short)m.GetAOSStatus(i)); - } - } - - public sealed class MobileStatus : Packet - { - public MobileStatus(Mobile beholder, Mobile beheld) : this(beholder, beheld, beheld.NetState) - { - } - - public MobileStatus(Mobile beholder, Mobile beheld, NetState ns) : base(0x11) - { - var name = beheld.Name ?? ""; - - int type; - - if (beholder != beheld) - { - type = 0; - EnsureCapacity(43); - } - else if (Core.HS && ns?.ExtendedStatus == true) - { - type = 6; - EnsureCapacity(121); - } - else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true) - { - type = 5; - EnsureCapacity(91); - } - else - { - type = Core.AOS ? 4 : 3; - EnsureCapacity(88); - } - - Stream.Write(beheld.Serial); - - Stream.WriteAsciiFixed(name, 30); - - if (beholder == beheld) - WriteAttr(beheld.Hits, beheld.HitsMax); - else - WriteAttrNorm(beheld.Hits, beheld.HitsMax); - - Stream.Write(beheld.CanBeRenamedBy(beholder)); - - Stream.Write((byte)type); - - if (type <= 0) - return; - - Stream.Write(beheld.Female); - - Stream.Write((short)beheld.Str); - Stream.Write((short)beheld.Dex); - Stream.Write((short)beheld.Int); - - WriteAttr(beheld.Stam, beheld.StamMax); - WriteAttr(beheld.Mana, beheld.ManaMax); - - Stream.Write(beheld.TotalGold); - Stream.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))); - Stream.Write((short)(Mobile.BodyWeight + beheld.TotalWeight)); - - if (type >= 5) - { - Stream.Write((short)beheld.MaxWeight); - Stream.Write((byte)(beheld.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but... - } - - Stream.Write((short)beheld.StatCap); - - Stream.Write((byte)beheld.Followers); - Stream.Write((byte)beheld.FollowersMax); - - if (type >= 4) - { - Stream.Write((short)beheld.FireResistance); // Fire - Stream.Write((short)beheld.ColdResistance); // Cold - Stream.Write((short)beheld.PoisonResistance); // Poison - Stream.Write((short)beheld.EnergyResistance); // Energy - Stream.Write((short)beheld.Luck); // Luck - - var weapon = beheld.Weapon; - - if (weapon != null) - { - weapon.GetStatusDamage(beheld, out var min, out var max); - Stream.Write((short)min); // Damage min - Stream.Write((short)max); // Damage max - } - else - { - Stream.Write((short)0); // Damage min - Stream.Write((short)0); // Damage max - } - - Stream.Write(beheld.TithingPoints); - } - - if (type >= 6) - for (var i = 0; i < 15; ++i) - Stream.Write((short)beheld.GetAOSStatus(i)); - } - - private void WriteAttr(int current, int maximum) - { - Stream.Write((short)current); - Stream.Write((short)maximum); - } - - private void WriteAttrNorm(int current, int maximum) - { - AttributeNormalizer.WriteReverse(Stream, current, maximum); - } - } - - public sealed class HealthbarPoison : Packet - { - public HealthbarPoison(Mobile m) : base(0x17) - { - EnsureCapacity(12); - - Stream.Write(m.Serial); - Stream.Write((short)1); - - Stream.Write((short)1); - - var p = m.Poison; - - if (p != null) - Stream.Write((byte)(p.Level + 1)); - else - Stream.Write((byte)0); - } - } - - public sealed class HealthbarYellow : Packet - { - public HealthbarYellow(Mobile m) : base(0x17) - { - EnsureCapacity(12); - - Stream.Write(m.Serial); - Stream.Write((short)1); - - Stream.Write((short)2); - - if (m.Blessed || m.YellowHealthbar) - Stream.Write((byte)1); - else - Stream.Write((byte)0); - } - } - - public sealed class MobileUpdate : Packet - { - public MobileUpdate(Mobile m) : base(0x20, 19) - { - var hue = m.Hue; - - if (m.SolidHueOverride >= 0) - hue = m.SolidHueOverride; - - Stream.Write(m.Serial); - Stream.Write((short)m.Body); - Stream.Write((byte)0); - Stream.Write((short)hue); - Stream.Write((byte)m.GetPacketFlags()); - Stream.Write((short)m.X); - Stream.Write((short)m.Y); - Stream.Write((short)0); - Stream.Write((byte)m.Direction); - Stream.Write((sbyte)m.Z); - } - } - - // Pre-7.0.0.0 Mobile Update - public sealed class MobileUpdateOld : Packet - { - public MobileUpdateOld(Mobile m) : base(0x20, 19) - { - var hue = m.Hue; - - if (m.SolidHueOverride >= 0) - hue = m.SolidHueOverride; - - Stream.Write(m.Serial); - Stream.Write((short)m.Body); - Stream.Write((byte)0); - Stream.Write((short)hue); - Stream.Write((byte)m.GetOldPacketFlags()); - Stream.Write((short)m.X); - Stream.Write((short)m.Y); - Stream.Write((short)0); - Stream.Write((byte)m.Direction); - Stream.Write((sbyte)m.Z); - } - } - - public sealed class MobileIncoming : Packet - { - private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); - private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); - - public MobileIncoming(Mobile beholder, Mobile beheld) : base(0x78) - { - var m_Version = ++m_VersionTL.Value; - var m_DupedLayers = m_DupedLayersTL.Value; - - var eq = beheld.Items; - var count = eq.Count; - - if (beheld.HairItemID > 0) - count++; - if (beheld.FacialHairItemID > 0) - count++; - - EnsureCapacity(23 + count * 9); - - var hue = beheld.Hue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - Stream.Write(beheld.Serial); - Stream.Write((short)beheld.Body); - Stream.Write((short)beheld.X); - Stream.Write((short)beheld.Y); - Stream.Write((sbyte)beheld.Z); - Stream.Write((byte)beheld.Direction); - Stream.Write((short)hue); - Stream.Write((byte)beheld.GetPacketFlags()); - Stream.Write((byte)Notoriety.Compute(beholder, beheld)); - - for (var i = 0; i < eq.Count; ++i) - { - var item = eq[i]; - - var layer = (byte)item.Layer; - - if (!item.Deleted && beholder.CanSee(item) && m_DupedLayers[layer] != m_Version) - { - m_DupedLayers[layer] = m_Version; - - hue = item.Hue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = item.ItemID & 0xFFFF; - - Stream.Write(item.Serial); - Stream.Write((ushort)itemID); - Stream.Write(layer); - - Stream.Write((short)hue); - } - } - - if (beheld.HairItemID > 0) - if (m_DupedLayers[(int)Layer.Hair] != m_Version) - { - m_DupedLayers[(int)Layer.Hair] = m_Version; - hue = beheld.HairHue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = beheld.HairItemID & 0xFFFF; - - Stream.Write(HairInfo.FakeSerial(beheld)); - Stream.Write((ushort)itemID); - Stream.Write((byte)Layer.Hair); - - Stream.Write((short)hue); - } - - if (beheld.FacialHairItemID > 0) - if (m_DupedLayers[(int)Layer.FacialHair] != m_Version) - { - m_DupedLayers[(int)Layer.FacialHair] = m_Version; - hue = beheld.FacialHairHue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = beheld.FacialHairItemID & 0xFFFF; - - Stream.Write(FacialHairInfo.FakeSerial(beheld)); - Stream.Write((ushort)itemID); - Stream.Write((byte)Layer.FacialHair); - - Stream.Write((short)hue); - } - - Stream.Write(0); // terminate - } - - public static Packet Create(NetState ns, Mobile beholder, Mobile beheld) - { - if (ns.NewMobileIncoming) - return new MobileIncoming(beholder, beheld); - if (ns.StygianAbyss) - return new MobileIncomingSA(beholder, beheld); - return new MobileIncomingOld(beholder, beheld); - } - } - - public sealed class MobileIncomingSA : Packet - { - private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); - private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); - - public MobileIncomingSA(Mobile beholder, Mobile beheld) : base(0x78) - { - var m_Version = ++m_VersionTL.Value; - var m_DupedLayers = m_DupedLayersTL.Value; - - var eq = beheld.Items; - var count = eq.Count; - - if (beheld.HairItemID > 0) - count++; - if (beheld.FacialHairItemID > 0) - count++; - - EnsureCapacity(23 + count * 9); - - var hue = beheld.Hue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - Stream.Write(beheld.Serial); - Stream.Write((short)beheld.Body); - Stream.Write((short)beheld.X); - Stream.Write((short)beheld.Y); - Stream.Write((sbyte)beheld.Z); - Stream.Write((byte)beheld.Direction); - Stream.Write((short)hue); - Stream.Write((byte)beheld.GetPacketFlags()); - Stream.Write((byte)Notoriety.Compute(beholder, beheld)); - - for (var i = 0; i < eq.Count; ++i) - { - var item = eq[i]; - - var layer = (byte)item.Layer; - - if (!item.Deleted && beholder.CanSee(item) && m_DupedLayers[layer] != m_Version) - { - m_DupedLayers[layer] = m_Version; - - hue = item.Hue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = item.ItemID & 0x7FFF; - var writeHue = hue != 0; - - if (writeHue) - itemID |= 0x8000; - - Stream.Write(item.Serial); - Stream.Write((ushort)itemID); - Stream.Write(layer); - - if (writeHue) - Stream.Write((short)hue); - } - } - - if (beheld.HairItemID > 0) - if (m_DupedLayers[(int)Layer.Hair] != m_Version) - { - m_DupedLayers[(int)Layer.Hair] = m_Version; - hue = beheld.HairHue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = beheld.HairItemID & 0x7FFF; - - var writeHue = hue != 0; - - if (writeHue) - itemID |= 0x8000; - - Stream.Write(HairInfo.FakeSerial(beheld)); - Stream.Write((ushort)itemID); - Stream.Write((byte)Layer.Hair); - - if (writeHue) - Stream.Write((short)hue); - } - - if (beheld.FacialHairItemID > 0) - if (m_DupedLayers[(int)Layer.FacialHair] != m_Version) - { - m_DupedLayers[(int)Layer.FacialHair] = m_Version; - hue = beheld.FacialHairHue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = beheld.FacialHairItemID & 0x7FFF; - - var writeHue = hue != 0; - - if (writeHue) - itemID |= 0x8000; - - Stream.Write(FacialHairInfo.FakeSerial(beheld)); - Stream.Write((ushort)itemID); - Stream.Write((byte)Layer.FacialHair); - - if (writeHue) - Stream.Write((short)hue); - } - - Stream.Write(0); // terminate - } - } - - // Pre-7.0.0.0 Mobile Incoming - public sealed class MobileIncomingOld : Packet - { - private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); - private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); - - public MobileIncomingOld(Mobile beholder, Mobile beheld) : base(0x78) - { - var m_Version = ++m_VersionTL.Value; - var m_DupedLayers = m_DupedLayersTL.Value; - - var eq = beheld.Items; - var count = eq.Count; - - if (beheld.HairItemID > 0) - count++; - if (beheld.FacialHairItemID > 0) - count++; - - EnsureCapacity(23 + count * 9); - - var hue = beheld.Hue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - Stream.Write(beheld.Serial); - Stream.Write((short)beheld.Body); - Stream.Write((short)beheld.X); - Stream.Write((short)beheld.Y); - Stream.Write((sbyte)beheld.Z); - Stream.Write((byte)beheld.Direction); - Stream.Write((short)hue); - Stream.Write((byte)beheld.GetOldPacketFlags()); - Stream.Write((byte)Notoriety.Compute(beholder, beheld)); - - for (var i = 0; i < eq.Count; ++i) - { - var item = eq[i]; - - var layer = (byte)item.Layer; - - if (!item.Deleted && beholder.CanSee(item) && m_DupedLayers[layer] != m_Version) - { - m_DupedLayers[layer] = m_Version; - - hue = item.Hue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = item.ItemID & 0x7FFF; - var writeHue = hue != 0; - - if (writeHue) - itemID |= 0x8000; - - Stream.Write(item.Serial); - Stream.Write((ushort)itemID); - Stream.Write(layer); - - if (writeHue) - Stream.Write((short)hue); - } - } - - if (beheld.HairItemID > 0) - if (m_DupedLayers[(int)Layer.Hair] != m_Version) - { - m_DupedLayers[(int)Layer.Hair] = m_Version; - hue = beheld.HairHue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = beheld.HairItemID & 0x7FFF; - - var writeHue = hue != 0; - - if (writeHue) - itemID |= 0x8000; - - Stream.Write(HairInfo.FakeSerial(beheld)); - Stream.Write((ushort)itemID); - Stream.Write((byte)Layer.Hair); - - if (writeHue) - Stream.Write((short)hue); - } - - if (beheld.FacialHairItemID > 0) - if (m_DupedLayers[(int)Layer.FacialHair] != m_Version) - { - m_DupedLayers[(int)Layer.FacialHair] = m_Version; - hue = beheld.FacialHairHue; - - if (beheld.SolidHueOverride >= 0) - hue = beheld.SolidHueOverride; - - var itemID = beheld.FacialHairItemID & 0x7FFF; - - var writeHue = hue != 0; - - if (writeHue) - itemID |= 0x8000; - - Stream.Write(FacialHairInfo.FakeSerial(beheld)); - Stream.Write((ushort)itemID); - Stream.Write((byte)Layer.FacialHair); - - if (writeHue) - Stream.Write((short)hue); - } - - Stream.Write(0); // terminate - } - } - - public sealed class PingAck : Packet - { - private static readonly PingAck[] m_Cache = new PingAck[0x100]; - - public PingAck(byte ping) : base(0x73, 2) - { - Stream.Write(ping); - } - - public static PingAck Instantiate(byte ping) - { - var p = m_Cache[ping]; - - if (p == null) - { - m_Cache[ping] = p = new PingAck(ping); - p.SetStatic(); - } - - return p; - } - } - - public sealed class MovementRej : Packet - { - public MovementRej(int seq, Mobile m) : base(0x21, 8) - { - Stream.Write((byte)seq); - Stream.Write((short)m.X); - Stream.Write((short)m.Y); - Stream.Write((byte)m.Direction); - Stream.Write((sbyte)m.Z); - } - } - - public sealed class MovementAck : Packet - { - private static readonly MovementAck[] m_Cache = new MovementAck[8 * 256]; - - private MovementAck(int seq, int noto) : base(0x22, 3) - { - Stream.Write((byte)seq); - Stream.Write((byte)noto); - } - - public static MovementAck Instantiate(int seq, Mobile m) - { - var noto = Notoriety.Compute(m, m); - - var p = m_Cache[noto * seq]; - - if (p == null) - { - m_Cache[noto * seq] = p = new MovementAck(seq, noto); - p.SetStatic(); - } - - return p; - } - } - - public sealed class LoginConfirm : Packet - { - public LoginConfirm(Mobile m) : base(0x1B, 37) - { - Stream.Write(m.Serial); - Stream.Write(0); - Stream.Write((short)m.Body); - Stream.Write((short)m.X); - Stream.Write((short)m.Y); - Stream.Write((short)m.Z); - Stream.Write((byte)m.Direction); - Stream.Write((byte)0); - Stream.Write(-1); - - var map = m.Map; - - if (map == null || map == Map.Internal) - map = m.LogoutMap; - - Stream.Write((short)0); - Stream.Write((short)0); - Stream.Write((short)(map?.Width ?? 6144)); - Stream.Write((short)(map?.Height ?? 4096)); - - Stream.Fill(); - } - } - - public sealed class LoginComplete : Packet - { - public static readonly Packet Instance = SetStatic(new LoginComplete()); - - public LoginComplete() : base(0x55, 1) - { - } - } - - public sealed class CharacterListUpdate : Packet - { - public CharacterListUpdate(IAccount a) : base(0x86) - { - EnsureCapacity(4 + a.Length * 60); - - var highSlot = -1; - - for (var i = 0; i < a.Length; ++i) - if (a[i] != null) - highSlot = i; - - var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); - - Stream.Write((byte)count); - - for (var i = 0; i < count; ++i) - { - var m = a[i]; - - if (m != null) - { - Stream.WriteAsciiFixed(m.Name, 30); - Stream.Fill(30); // password - } - else - { - Stream.Fill(60); - } - } - } - } - - public sealed class CharacterList : Packet - { - // private static MD5CryptoServiceProvider m_MD5Provider; - - public CharacterList(IAccount a, CityInfo[] info) : base(0xA9) - { - EnsureCapacity(11 + a.Length * 60 + info.Length * 89); - - var highSlot = -1; - - for (var i = 0; i < a.Length; ++i) - if (a[i] != null) - highSlot = i; - - var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); - - Stream.Write((byte)count); - - for (var i = 0; i < count; ++i) - if (a[i] != null) - { - Stream.WriteAsciiFixed(a[i].Name, 30); - Stream.Fill(30); // password - } - else - { - Stream.Fill(60); - } - - Stream.Write((byte)info.Length); - - for (var i = 0; i < info.Length; ++i) - { - var ci = info[i]; - - Stream.Write((byte)i); - Stream.WriteAsciiFixed(ci.City, 32); - Stream.WriteAsciiFixed(ci.Building, 32); - Stream.Write(ci.X); - Stream.Write(ci.Y); - Stream.Write(ci.Z); - Stream.Write(ci.Map.MapID); - Stream.Write(ci.Description); - Stream.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 (a.Limit == 1) - flags |= CharacterListFlags.SlotLimit & - CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character - - Stream.Write((int)(flags | AdditionalFlags)); // Additional Flags - - Stream.Write((short)-1); - } - - public static CharacterListFlags AdditionalFlags { get; set; } - } - - public sealed class CharacterListOld : Packet - { - // private static MD5CryptoServiceProvider m_MD5Provider; - - public CharacterListOld(IAccount a, CityInfo[] info) : base(0xA9) - { - EnsureCapacity(9 + a.Length * 60 + info.Length * 63); - - var highSlot = -1; - - for (var i = 0; i < a.Length; ++i) - if (a[i] != null) - highSlot = i; - - var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); - - Stream.Write((byte)count); - - for (var i = 0; i < count; ++i) - if (a[i] != null) - { - Stream.WriteAsciiFixed(a[i].Name, 30); - Stream.Fill(30); // password - } - else - { - Stream.Fill(60); - } - - Stream.Write((byte)info.Length); - - for (var i = 0; i < info.Length; ++i) - { - var ci = info[i]; - - Stream.Write((byte)i); - Stream.WriteAsciiFixed(ci.City, 31); - Stream.WriteAsciiFixed(ci.Building, 31); - } - - 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 (a.Limit == 1) - flags |= CharacterListFlags.SlotLimit & - CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character - - Stream.Write((int)(flags | CharacterList.AdditionalFlags)); // Additional Flags - } - } - - public sealed class ClearWeaponAbility : Packet - { - public static readonly Packet Instance = SetStatic(new ClearWeaponAbility()); - - public ClearWeaponAbility() : base(0xBF) - { - EnsureCapacity(5); - - Stream.Write((short)0x21); - } - } - - public enum ALRReason : byte - { - Invalid = 0x00, - InUse = 0x01, - Blocked = 0x02, - BadPass = 0x03, - Idle = 0xFE, - BadComm = 0xFF - } - - public sealed class AccountLoginRej : Packet - { - public AccountLoginRej(ALRReason reason) : base(0x82, 2) - { - Stream.Write((byte)reason); - } - } - - [Flags] - public enum AffixType : byte - { - Append = 0x00, - Prepend = 0x01, - System = 0x02 - } - - public sealed class ServerInfo - { - public ServerInfo(string name, int fullPercent, TimeZoneInfo tz, IPEndPoint address) - { - Name = name; - FullPercent = fullPercent; - TimeZone = tz.GetUtcOffset(DateTime.Now).Hours; - Address = address; - } - - public string Name { get; set; } - - public int FullPercent { get; set; } - - public int TimeZone { get; set; } - - public IPEndPoint Address { get; set; } - } - - public sealed class FollowMessage : Packet - { - public FollowMessage(Serial serial1, Serial serial2) : base(0x15, 9) - { - Stream.Write(serial1); - Stream.Write(serial2); - } - } - - public sealed class AccountLoginAck : Packet - { - public AccountLoginAck(ServerInfo[] info) : base(0xA8) - { - EnsureCapacity(6 + info.Length * 40); - - Stream.Write((byte)0x5D); // Unknown - - Stream.Write((ushort)info.Length); - - for (var i = 0; i < info.Length; ++i) - { - var si = info[i]; - - Stream.Write((ushort)i); - Stream.WriteAsciiFixed(si.Name, 32); - Stream.Write((byte)si.FullPercent); - Stream.Write((sbyte)si.TimeZone); - Stream.Write(Utility.GetAddressValue(si.Address.Address)); - } - } - } - - public sealed class PlayServerAck : Packet - { - internal static int m_AuthID = -1; - - public PlayServerAck(ServerInfo si) : base(0x8C, 11) - { - var addr = Utility.GetAddressValue(si.Address.Address); - - Stream.Write((byte)addr); - Stream.Write((byte)(addr >> 8)); - Stream.Write((byte)(addr >> 16)); - Stream.Write((byte)(addr >> 24)); - - Stream.Write((short)si.Address.Port); - Stream.Write(m_AuthID); - } - } -} diff --git a/Projects/Server/Network/Packets/Old Packets/AccountPackets.cs b/Projects/Server/Network/Packets/Old Packets/AccountPackets.cs index 5e9c68d73..9ec0f2d7b 100644 --- a/Projects/Server/Network/Packets/Old Packets/AccountPackets.cs +++ b/Projects/Server/Network/Packets/Old Packets/AccountPackets.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: AccountPackets.cs - Created: 2020/05/08 - Updated: 2020/05/08 * + * File: AccountPackets.cs - Created: 2020/05/08 - Updated: 2020/06/25 * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -18,10 +18,21 @@ * along with this program. If not, see . * *************************************************************************/ +using System; using Server.Accounting; namespace Server.Network { + public enum ALRReason : byte + { + Invalid = 0x00, + InUse = 0x01, + Blocked = 0x02, + BadPass = 0x03, + Idle = 0xFE, + BadComm = 0xFF + } + public enum PMMessage : byte { CharNoExist = 1, @@ -102,4 +113,269 @@ namespace Server.Network Stream.Write((byte)msg); } } + + public sealed class SupportedFeatures : Packet + { + public SupportedFeatures(NetState ns) : base(0xB9, ns.ExtendedSupportedFeatures ? 5 : 3) + { + var flags = ExpansionInfo.CoreExpansion.SupportedFeatures; + + flags |= Value; + + if (ns.Account.Limit >= 6) + { + flags |= FeatureFlags.LiveAccount; + flags &= ~FeatureFlags.UOTD; + + if (ns.Account.Limit > 6) + flags |= FeatureFlags.SeventhCharacterSlot; + else + flags |= FeatureFlags.SixthCharacterSlot; + } + + if (ns.ExtendedSupportedFeatures) + Stream.Write((uint)flags); + else + Stream.Write((ushort)flags); + } + + public static FeatureFlags Value { get; set; } + + public static SupportedFeatures Instantiate(NetState ns) => new SupportedFeatures(ns); + } + + public sealed class LoginConfirm : Packet + { + public LoginConfirm(Mobile m) : base(0x1B, 37) + { + Stream.Write(m.Serial); + Stream.Write(0); + Stream.Write((short)m.Body); + Stream.Write((short)m.X); + Stream.Write((short)m.Y); + Stream.Write((short)m.Z); + Stream.Write((byte)m.Direction); + Stream.Write((byte)0); + Stream.Write(-1); + + var map = m.Map; + + if (map == null || map == Map.Internal) + map = m.LogoutMap; + + Stream.Write((short)0); + Stream.Write((short)0); + Stream.Write((short)(map?.Width ?? Map.Felucca.Width)); + Stream.Write((short)(map?.Height ?? Map.Felucca.Height)); + + Stream.Fill(); + } + } + + public sealed class LoginComplete : Packet + { + public static readonly Packet Instance = SetStatic(new LoginComplete()); + + public LoginComplete() : base(0x55, 1) + { + } + } + + public sealed class CharacterListUpdate : Packet + { + public CharacterListUpdate(IAccount a) : base(0x86) + { + EnsureCapacity(4 + a.Length * 60); + + var highSlot = -1; + + for (var i = 0; i < a.Length; ++i) + if (a[i] != null) + highSlot = i; + + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + + Stream.Write((byte)count); + + for (var i = 0; i < count; ++i) + { + var m = a[i]; + + if (m != null) + { + Stream.WriteAsciiFixed(m.Name, 30); + Stream.Fill(30); // password + } + else + { + Stream.Fill(60); + } + } + } + } + + public sealed class CharacterList : Packet + { + public CharacterList(IAccount a, CityInfo[] info) : base(0xA9) + { + EnsureCapacity(11 + a.Length * 60 + info.Length * 89); + + var highSlot = -1; + + for (var i = 0; i < a.Length; ++i) + if (a[i] != null) + highSlot = i; + + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + + Stream.Write((byte)count); + + for (var i = 0; i < count; ++i) + if (a[i] != null) + { + Stream.WriteAsciiFixed(a[i].Name, 30); + Stream.Fill(30); // password + } + else + { + Stream.Fill(60); + } + + Stream.Write((byte)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var ci = info[i]; + + Stream.Write((byte)i); + Stream.WriteAsciiFixed(ci.City, 32); + Stream.WriteAsciiFixed(ci.Building, 32); + Stream.Write(ci.X); + Stream.Write(ci.Y); + Stream.Write(ci.Z); + Stream.Write(ci.Map.MapID); + Stream.Write(ci.Description); + Stream.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 (a.Limit == 1) + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + + Stream.Write((int)(flags | AdditionalFlags)); // Additional Flags + + Stream.Write((short)-1); + } + + public static CharacterListFlags AdditionalFlags { get; set; } + } + + public sealed class CharacterListOld : Packet + { + public CharacterListOld(IAccount a, CityInfo[] info) : base(0xA9) + { + EnsureCapacity(9 + a.Length * 60 + info.Length * 63); + + var highSlot = -1; + + for (var i = 0; i < a.Length; ++i) + if (a[i] != null) + highSlot = i; + + var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5); + + Stream.Write((byte)count); + + for (var i = 0; i < count; ++i) + if (a[i] != null) + { + Stream.WriteAsciiFixed(a[i].Name, 30); + Stream.Fill(30); // password + } + else + { + Stream.Fill(60); + } + + Stream.Write((byte)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var ci = info[i]; + + Stream.Write((byte)i); + Stream.WriteAsciiFixed(ci.City, 31); + Stream.WriteAsciiFixed(ci.Building, 31); + } + + 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 (a.Limit == 1) + flags |= CharacterListFlags.SlotLimit & + CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character + + Stream.Write((int)(flags | CharacterList.AdditionalFlags)); // Additional Flags + } + } + + public sealed class AccountLoginRej : Packet + { + public AccountLoginRej(ALRReason reason) : base(0x82, 2) + { + Stream.Write((byte)reason); + } + } + + public sealed class AccountLoginAck : Packet + { + public AccountLoginAck(ServerInfo[] info) : base(0xA8) + { + EnsureCapacity(6 + info.Length * 40); + + Stream.Write((byte)0x5D); // Unknown + + Stream.Write((ushort)info.Length); + + for (var i = 0; i < info.Length; ++i) + { + var si = info[i]; + + Stream.Write((ushort)i); + Stream.WriteAsciiFixed(si.Name, 32); + Stream.Write((byte)si.FullPercent); + Stream.Write((sbyte)si.TimeZone); + Stream.Write(Utility.GetAddressValue(si.Address.Address)); + } + } + } + + public sealed class PlayServerAck : Packet + { + internal static int m_AuthID = -1; + + public PlayServerAck(ServerInfo si) : base(0x8C, 11) + { + var addr = Utility.GetAddressValue(si.Address.Address); + + Stream.Write((byte)addr); + Stream.Write((byte)(addr >> 8)); + Stream.Write((byte)(addr >> 16)); + Stream.Write((byte)(addr >> 24)); + + Stream.Write((short)si.Address.Port); + Stream.Write(m_AuthID); + } + } } diff --git a/Projects/Server/Network/Packets/Old Packets/AttributeNormalizer.cs b/Projects/Server/Network/Packets/Old Packets/AttributeNormalizer.cs new file mode 100644 index 000000000..b9a34445a --- /dev/null +++ b/Projects/Server/Network/Packets/Old Packets/AttributeNormalizer.cs @@ -0,0 +1,58 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: AttributeNormalizer.cs * + * Created: 2020/06/24 - Updated: 2020/06/24 * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public static class AttributeNormalizer + { + public static int Maximum { get; set; } = 25; + + public static bool Enabled { get; set; } = true; + + public static void Write(PacketWriter stream, int cur, int max) + { + if (Enabled && max != 0) + { + stream.Write((short)Maximum); + stream.Write((short)(cur * Maximum / max)); + } + else + { + stream.Write((short)max); + stream.Write((short)cur); + } + } + + public static void WriteReverse(PacketWriter stream, int cur, int max) + { + if (Enabled && max != 0) + { + stream.Write((short)(cur * Maximum / max)); + stream.Write((short)Maximum); + } + else + { + stream.Write((short)cur); + stream.Write((short)max); + } + } + } +} diff --git a/Projects/Server/Network/Packets/Old Packets/CombatPackets.cs b/Projects/Server/Network/Packets/Old Packets/CombatPackets.cs new file mode 100644 index 000000000..a2fb9a220 --- /dev/null +++ b/Projects/Server/Network/Packets/Old Packets/CombatPackets.cs @@ -0,0 +1,56 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: CombatPackets.cs - Created: 2020/06/25 - Updated: 2020/06/25 * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public sealed class Swing : Packet + { + public Swing(Serial attacker, Serial defender) : base(0x2F, 10) + { + Stream.Write((byte)0); + Stream.Write(attacker); + Stream.Write(defender); + } + } + + public sealed class SetWarMode : Packet + { + public static readonly Packet InWarMode = SetStatic(new SetWarMode(true)); + public static readonly Packet InPeaceMode = SetStatic(new SetWarMode(false)); + + public SetWarMode(bool mode) : base(0x72, 5) + { + Stream.Write(mode); + Stream.Write((byte)0x00); + Stream.Write((byte)0x32); + Stream.Write((byte)0x00); + } + + public static Packet Instantiate(bool mode) => mode ? InWarMode : InPeaceMode; + } + + public sealed class ChangeCombatant : Packet + { + public ChangeCombatant(Serial combatant) : base(0xAA, 5) + { + Stream.Write(combatant); + } + } +} diff --git a/Projects/Server/Network/Packets/Old Packets/DamagePackets.cs b/Projects/Server/Network/Packets/Old Packets/DamagePackets.cs index de832bea8..8452eeb76 100644 --- a/Projects/Server/Network/Packets/Old Packets/DamagePackets.cs +++ b/Projects/Server/Network/Packets/Old Packets/DamagePackets.cs @@ -22,13 +22,13 @@ namespace Server.Network { public sealed class DamagePacketOld : Packet { - public DamagePacketOld(Mobile m, int amount) : base(0xBF) + public DamagePacketOld(Serial mobile, int amount) : base(0xBF) { EnsureCapacity(11); Stream.Write((short)0x22); Stream.Write((byte)1); - Stream.Write(m.Serial); + Stream.Write(mobile); if (amount > 255) amount = 255; @@ -41,9 +41,9 @@ namespace Server.Network public sealed class DamagePacket : Packet { - public DamagePacket(Mobile m, int amount) : base(0x0B, 7) + public DamagePacket(Serial mobile, int amount) : base(0x0B, 7) { - Stream.Write(m.Serial); + Stream.Write(mobile); if (amount > 0xFFFF) amount = 0xFFFF; diff --git a/Projects/Server/Network/Packets/Old Packets/EquipmentPackets.cs b/Projects/Server/Network/Packets/Old Packets/EquipmentPackets.cs index aa14061bc..ae6881c9f 100644 --- a/Projects/Server/Network/Packets/Old Packets/EquipmentPackets.cs +++ b/Projects/Server/Network/Packets/Old Packets/EquipmentPackets.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: EquipmentPackets.cs - Created: 2020/05/07 - Updated: 2020/05/07 * * + * File: EquipmentPackets.cs - Created: 2020/05/07 - Updated: 2020/05/07 * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * diff --git a/Projects/Server/Network/Packets/Old Packets/LightPackets.cs b/Projects/Server/Network/Packets/Old Packets/LightPackets.cs new file mode 100644 index 000000000..1d5099596 --- /dev/null +++ b/Projects/Server/Network/Packets/Old Packets/LightPackets.cs @@ -0,0 +1,55 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: LightPackets.cs - Created: 2020/06/25 - Updated: 2020/06/25 * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public sealed class GlobalLightLevel : Packet + { + private static readonly GlobalLightLevel[] m_Cache = new GlobalLightLevel[0x100]; + + public GlobalLightLevel(int level) : base(0x4F, 2) + { + Stream.Write((sbyte)level); + } + + public static GlobalLightLevel Instantiate(int level) + { + var lvl = (byte)level; + var p = m_Cache[lvl]; + + if (p == null) + { + m_Cache[lvl] = p = new GlobalLightLevel(level); + p.SetStatic(); + } + + return p; + } + } + + public sealed class PersonalLightLevel : Packet + { + public PersonalLightLevel(Serial mobile, int level = 0) : base(0x4E, 6) + { + Stream.Write(mobile); + Stream.Write((sbyte)level); + } + } +} diff --git a/Projects/Server/Network/Packets/Old Packets/MapPackets.cs b/Projects/Server/Network/Packets/Old Packets/MapPackets.cs index 3095c609f..5f583c756 100644 --- a/Projects/Server/Network/Packets/Old Packets/MapPackets.cs +++ b/Projects/Server/Network/Packets/Old Packets/MapPackets.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: MapPackets.cs - Created: 2020/05/03 - Updated: 2020/05/08 * + * File: MapPackets.cs - Created: 2020/05/03 - Updated: 2020/06/24 * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -27,7 +27,7 @@ namespace Server.Network { EnsureCapacity(9 + 4 * 8); - Stream.Write((short)0x0018); + Stream.Write((short)0x18); Stream.Write(4); @@ -51,4 +51,15 @@ namespace Server.Network { } } + + public sealed class MapChange : Packet + { + public MapChange(Map map) : base(0xBF) + { + EnsureCapacity(6); + + Stream.Write((short)0x08); + Stream.Write((byte)map.MapID); + } + } } diff --git a/Projects/Server/Network/Packets/Old Packets/MessagePackets.cs b/Projects/Server/Network/Packets/Old Packets/MessagePackets.cs index 3d48a3015..eee605245 100644 --- a/Projects/Server/Network/Packets/Old Packets/MessagePackets.cs +++ b/Projects/Server/Network/Packets/Old Packets/MessagePackets.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: MessagePackets.cs - Created: 2020/05/26 - Updated: 2020/05/26 * + * File: MessagePackets.cs - Created: 2020/05/26 - Updated: 2020/06/25 * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -18,8 +18,18 @@ * along with this program. If not, see . * *************************************************************************/ +using System; + namespace Server.Network { + [Flags] + public enum AffixType : byte + { + Append = 0x00, + Prepend = 0x01, + System = 0x02 + } + public sealed class MessageLocalized : Packet { private static readonly MessageLocalized[] m_Cache_IntLoc = new MessageLocalized[15000]; @@ -163,4 +173,13 @@ namespace Server.Network Stream.WriteBigUniNull(text); } } + + public sealed class FollowMessage : Packet + { + public FollowMessage(Serial serial1, Serial serial2) : base(0x15, 9) + { + Stream.Write(serial1); + Stream.Write(serial2); + } + } } diff --git a/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs b/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs index 1f1fc629d..b5589fac6 100644 --- a/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs +++ b/Projects/Server/Network/Packets/Old Packets/MobilePackets.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: MobilePackets.cs - Created: 2020/05/07 - Updated: 2020/05/26 * + * File: MobilePackets.cs - Created: 2020/05/07 - Updated: 2020/06/25 * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -18,14 +18,16 @@ * along with this program. If not, see . * *************************************************************************/ +using System.Threading; + namespace Server.Network { public sealed class DeathAnimation : Packet { - public DeathAnimation(Mobile killed, Item corpse) : base(0xAF, 13) + public DeathAnimation(Serial killed, Serial corpse) : base(0xAF, 13) { - Stream.Write(killed.Serial); - Stream.Write(corpse?.Serial ?? Serial.Zero); + Stream.Write(killed); + Stream.Write(corpse); Stream.Write(0); } } @@ -89,4 +91,782 @@ namespace Server.Network Stream.Write((byte)noto); } } + + public sealed class MobileHits : Packet + { + public MobileHits(Mobile m) : base(0xA1, 9) + { + Stream.Write(m.Serial); + Stream.Write((short)m.HitsMax); + Stream.Write((short)m.Hits); + } + } + + public sealed class MobileHitsN : Packet + { + public MobileHitsN(Mobile m) : base(0xA1, 9) + { + Stream.Write(m.Serial); + AttributeNormalizer.Write(Stream, m.Hits, m.HitsMax); + } + } + + public sealed class MobileMana : Packet + { + public MobileMana(Mobile m) : base(0xA2, 9) + { + Stream.Write(m.Serial); + Stream.Write((short)m.ManaMax); + Stream.Write((short)m.Mana); + } + } + + public sealed class MobileManaN : Packet + { + public MobileManaN(Mobile m) : base(0xA2, 9) + { + Stream.Write(m.Serial); + AttributeNormalizer.Write(Stream, m.Mana, m.ManaMax); + } + } + + public sealed class MobileStam : Packet + { + public MobileStam(Mobile m) : base(0xA3, 9) + { + Stream.Write(m.Serial); + Stream.Write((short)m.StamMax); + Stream.Write((short)m.Stam); + } + } + + public sealed class MobileStamN : Packet + { + public MobileStamN(Mobile m) : base(0xA3, 9) + { + Stream.Write(m.Serial); + AttributeNormalizer.Write(Stream, m.Stam, m.StamMax); + } + } + + public sealed class MobileAttributes : Packet + { + public MobileAttributes(Mobile m) : base(0x2D, 17) + { + Stream.Write(m.Serial); + + Stream.Write((short)m.HitsMax); + Stream.Write((short)m.Hits); + + Stream.Write((short)m.ManaMax); + Stream.Write((short)m.Mana); + + Stream.Write((short)m.StamMax); + Stream.Write((short)m.Stam); + } + } + + public sealed class MobileAttributesN : Packet + { + public MobileAttributesN(Mobile m) : base(0x2D, 17) + { + Stream.Write(m.Serial); + + AttributeNormalizer.Write(Stream, m.Hits, m.HitsMax); + AttributeNormalizer.Write(Stream, m.Mana, m.ManaMax); + AttributeNormalizer.Write(Stream, m.Stam, m.StamMax); + } + } + + // unsure of proper format, client crashes + public sealed class MobileName : Packet + { + public MobileName(Mobile m) : base(0x98) + { + EnsureCapacity(37); + + Stream.Write(m.Serial); + Stream.WriteAsciiFixed(m.Name ?? "", 30); + } + } + + public sealed class MobileAnimation : Packet + { + public MobileAnimation(Serial mobile, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay) : base(0x6E, 14) + { + Stream.Write(mobile); + Stream.Write((short)action); + Stream.Write((short)frameCount); + Stream.Write((short)repeatCount); + Stream.Write(!forward); // protocol has really "reverse" but I find this more intuitive + Stream.Write(repeat); + Stream.Write((byte)delay); + } + } + + public sealed class NewMobileAnimation : Packet + { + public NewMobileAnimation(Serial mobile, int action, int frameCount, int delay) : base(0xE2, 10) + { + Stream.Write(mobile); + Stream.Write((short)action); + Stream.Write((short)frameCount); + Stream.Write((byte)delay); + } + } + + public sealed class MobileStatusCompact : Packet + { + public MobileStatusCompact(bool canBeRenamed, Mobile m) : base(0x11) + { + EnsureCapacity(43); + + Stream.Write(m.Serial); + Stream.WriteAsciiFixed(m.Name ?? "", 30); + + AttributeNormalizer.WriteReverse(Stream, m.Hits, m.HitsMax); + + Stream.Write(canBeRenamed); + + Stream.Write((byte)0); // type + } + } + + public sealed class MobileStatusExtended : Packet + { + public MobileStatusExtended(Mobile m) : this(m, m.NetState) + { + } + + public MobileStatusExtended(Mobile m, NetState ns) : base(0x11) + { + var name = m.Name ?? ""; + + int type; + + if (Core.HS && ns?.ExtendedStatus == true) + { + type = 6; + EnsureCapacity(121); + } + else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true) + { + type = 5; + EnsureCapacity(91); + } + else + { + type = Core.AOS ? 4 : 3; + EnsureCapacity(88); + } + + Stream.Write(m.Serial); + Stream.WriteAsciiFixed(name, 30); + + Stream.Write((short)m.Hits); + Stream.Write((short)m.HitsMax); + + Stream.Write(m.CanBeRenamedBy(m)); + + Stream.Write((byte)type); + + Stream.Write(m.Female); + + Stream.Write((short)m.Str); + Stream.Write((short)m.Dex); + Stream.Write((short)m.Int); + + Stream.Write((short)m.Stam); + Stream.Write((short)m.StamMax); + + Stream.Write((short)m.Mana); + Stream.Write((short)m.ManaMax); + + Stream.Write(m.TotalGold); + Stream.Write((short)(Core.AOS ? m.PhysicalResistance : (int)(m.ArmorRating + 0.5))); + Stream.Write((short)(Mobile.BodyWeight + m.TotalWeight)); + + if (type >= 5) + { + Stream.Write((short)m.MaxWeight); + Stream.Write((byte)(m.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but... + } + + Stream.Write((short)m.StatCap); + + Stream.Write((byte)m.Followers); + Stream.Write((byte)m.FollowersMax); + + if (type >= 4) + { + Stream.Write((short)m.FireResistance); // Fire + Stream.Write((short)m.ColdResistance); // Cold + Stream.Write((short)m.PoisonResistance); // Poison + Stream.Write((short)m.EnergyResistance); // Energy + Stream.Write((short)m.Luck); // Luck + + var weapon = m.Weapon; + + if (weapon != null) + { + weapon.GetStatusDamage(m, out var min, out var max); + Stream.Write((short)min); // Damage min + Stream.Write((short)max); // Damage max + } + else + { + Stream.Write((short)0); // Damage min + Stream.Write((short)0); // Damage max + } + + Stream.Write(m.TithingPoints); + } + + if (type >= 6) + for (var i = 0; i < 15; ++i) + Stream.Write((short)m.GetAOSStatus(i)); + } + } + + public sealed class MobileStatus : Packet + { + public MobileStatus(Mobile beholder, Mobile beheld) : this(beholder, beheld, beheld.NetState) + { + } + + public MobileStatus(Mobile beholder, Mobile beheld, NetState ns) : base(0x11) + { + var name = beheld.Name ?? ""; + + int type; + + if (beholder != beheld) + { + type = 0; + EnsureCapacity(43); + } + else if (Core.HS && ns?.ExtendedStatus == true) + { + type = 6; + EnsureCapacity(121); + } + else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true) + { + type = 5; + EnsureCapacity(91); + } + else + { + type = Core.AOS ? 4 : 3; + EnsureCapacity(88); + } + + Stream.Write(beheld.Serial); + + Stream.WriteAsciiFixed(name, 30); + + if (beholder == beheld) + WriteAttr(beheld.Hits, beheld.HitsMax); + else + WriteAttrNorm(beheld.Hits, beheld.HitsMax); + + Stream.Write(beheld.CanBeRenamedBy(beholder)); + + Stream.Write((byte)type); + + if (type <= 0) + return; + + Stream.Write(beheld.Female); + + Stream.Write((short)beheld.Str); + Stream.Write((short)beheld.Dex); + Stream.Write((short)beheld.Int); + + WriteAttr(beheld.Stam, beheld.StamMax); + WriteAttr(beheld.Mana, beheld.ManaMax); + + Stream.Write(beheld.TotalGold); + Stream.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5))); + Stream.Write((short)(Mobile.BodyWeight + beheld.TotalWeight)); + + if (type >= 5) + { + Stream.Write((short)beheld.MaxWeight); + Stream.Write((byte)(beheld.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but... + } + + Stream.Write((short)beheld.StatCap); + + Stream.Write((byte)beheld.Followers); + Stream.Write((byte)beheld.FollowersMax); + + if (type >= 4) + { + Stream.Write((short)beheld.FireResistance); // Fire + Stream.Write((short)beheld.ColdResistance); // Cold + Stream.Write((short)beheld.PoisonResistance); // Poison + Stream.Write((short)beheld.EnergyResistance); // Energy + Stream.Write((short)beheld.Luck); // Luck + + var weapon = beheld.Weapon; + + if (weapon != null) + { + weapon.GetStatusDamage(beheld, out var min, out var max); + Stream.Write((short)min); // Damage min + Stream.Write((short)max); // Damage max + } + else + { + Stream.Write((short)0); // Damage min + Stream.Write((short)0); // Damage max + } + + Stream.Write(beheld.TithingPoints); + } + + if (type >= 6) + for (var i = 0; i < 15; ++i) + Stream.Write((short)beheld.GetAOSStatus(i)); + } + + private void WriteAttr(int current, int maximum) + { + Stream.Write((short)current); + Stream.Write((short)maximum); + } + + private void WriteAttrNorm(int current, int maximum) + { + AttributeNormalizer.WriteReverse(Stream, current, maximum); + } + } + + public sealed class HealthbarPoison : Packet + { + public HealthbarPoison(Mobile m) : base(0x17) + { + EnsureCapacity(12); + + Stream.Write(m.Serial); + Stream.Write((short)1); + + Stream.Write((short)1); + + var p = m.Poison; + + if (p != null) + Stream.Write((byte)(p.Level + 1)); + else + Stream.Write((byte)0); + } + } + + public sealed class HealthbarYellow : Packet + { + public HealthbarYellow(Mobile m) : base(0x17) + { + EnsureCapacity(12); + + Stream.Write(m.Serial); + Stream.Write((short)1); + + Stream.Write((short)2); + + if (m.Blessed || m.YellowHealthbar) + Stream.Write((byte)1); + else + Stream.Write((byte)0); + } + } + + public sealed class MobileUpdate : Packet + { + public MobileUpdate(Mobile m) : base(0x20, 19) + { + var hue = m.Hue; + + if (m.SolidHueOverride >= 0) + hue = m.SolidHueOverride; + + Stream.Write(m.Serial); + Stream.Write((short)m.Body); + Stream.Write((byte)0); + Stream.Write((short)hue); + Stream.Write((byte)m.GetPacketFlags()); + Stream.Write((short)m.X); + Stream.Write((short)m.Y); + Stream.Write((short)0); + Stream.Write((byte)m.Direction); + Stream.Write((sbyte)m.Z); + } + } + + // Pre-7.0.0.0 Mobile Update + public sealed class MobileUpdateOld : Packet + { + public MobileUpdateOld(Mobile m) : base(0x20, 19) + { + var hue = m.Hue; + + if (m.SolidHueOverride >= 0) + hue = m.SolidHueOverride; + + Stream.Write(m.Serial); + Stream.Write((short)m.Body); + Stream.Write((byte)0); + Stream.Write((short)hue); + Stream.Write((byte)m.GetOldPacketFlags()); + Stream.Write((short)m.X); + Stream.Write((short)m.Y); + Stream.Write((short)0); + Stream.Write((byte)m.Direction); + Stream.Write((sbyte)m.Z); + } + } + + public sealed class MobileIncoming : Packet + { + private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); + private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); + + public MobileIncoming(Mobile beholder, Mobile beheld) : base(0x78) + { + var m_Version = ++m_VersionTL.Value; + var m_DupedLayers = m_DupedLayersTL.Value; + + var eq = beheld.Items; + var count = eq.Count; + + if (beheld.HairItemID > 0) + count++; + if (beheld.FacialHairItemID > 0) + count++; + + EnsureCapacity(23 + count * 9); + + var hue = beheld.Hue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + Stream.Write(beheld.Serial); + Stream.Write((short)beheld.Body); + Stream.Write((short)beheld.X); + Stream.Write((short)beheld.Y); + Stream.Write((sbyte)beheld.Z); + Stream.Write((byte)beheld.Direction); + Stream.Write((short)hue); + Stream.Write((byte)beheld.GetPacketFlags()); + Stream.Write((byte)Notoriety.Compute(beholder, beheld)); + + for (var i = 0; i < eq.Count; ++i) + { + var item = eq[i]; + + var layer = (byte)item.Layer; + + if (!item.Deleted && beholder.CanSee(item) && m_DupedLayers[layer] != m_Version) + { + m_DupedLayers[layer] = m_Version; + + hue = item.Hue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = item.ItemID & 0xFFFF; + + Stream.Write(item.Serial); + Stream.Write((ushort)itemID); + Stream.Write(layer); + + Stream.Write((short)hue); + } + } + + if (beheld.HairItemID > 0) + if (m_DupedLayers[(int)Layer.Hair] != m_Version) + { + m_DupedLayers[(int)Layer.Hair] = m_Version; + hue = beheld.HairHue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = beheld.HairItemID & 0xFFFF; + + Stream.Write(HairInfo.FakeSerial(beheld)); + Stream.Write((ushort)itemID); + Stream.Write((byte)Layer.Hair); + + Stream.Write((short)hue); + } + + if (beheld.FacialHairItemID > 0) + if (m_DupedLayers[(int)Layer.FacialHair] != m_Version) + { + m_DupedLayers[(int)Layer.FacialHair] = m_Version; + hue = beheld.FacialHairHue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = beheld.FacialHairItemID & 0xFFFF; + + Stream.Write(FacialHairInfo.FakeSerial(beheld)); + Stream.Write((ushort)itemID); + Stream.Write((byte)Layer.FacialHair); + + Stream.Write((short)hue); + } + + Stream.Write(0); // terminate + } + + public static Packet Create(NetState ns, Mobile beholder, Mobile beheld) + { + if (ns.NewMobileIncoming) + return new MobileIncoming(beholder, beheld); + if (ns.StygianAbyss) + return new MobileIncomingSA(beholder, beheld); + return new MobileIncomingOld(beholder, beheld); + } + } + + public sealed class MobileIncomingSA : Packet + { + private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); + private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); + + public MobileIncomingSA(Mobile beholder, Mobile beheld) : base(0x78) + { + var m_Version = ++m_VersionTL.Value; + var m_DupedLayers = m_DupedLayersTL.Value; + + var eq = beheld.Items; + var count = eq.Count; + + if (beheld.HairItemID > 0) + count++; + if (beheld.FacialHairItemID > 0) + count++; + + EnsureCapacity(23 + count * 9); + + var hue = beheld.Hue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + Stream.Write(beheld.Serial); + Stream.Write((short)beheld.Body); + Stream.Write((short)beheld.X); + Stream.Write((short)beheld.Y); + Stream.Write((sbyte)beheld.Z); + Stream.Write((byte)beheld.Direction); + Stream.Write((short)hue); + Stream.Write((byte)beheld.GetPacketFlags()); + Stream.Write((byte)Notoriety.Compute(beholder, beheld)); + + for (var i = 0; i < eq.Count; ++i) + { + var item = eq[i]; + + var layer = (byte)item.Layer; + + if (!item.Deleted && beholder.CanSee(item) && m_DupedLayers[layer] != m_Version) + { + m_DupedLayers[layer] = m_Version; + + hue = item.Hue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = item.ItemID & 0x7FFF; + var writeHue = hue != 0; + + if (writeHue) + itemID |= 0x8000; + + Stream.Write(item.Serial); + Stream.Write((ushort)itemID); + Stream.Write(layer); + + if (writeHue) + Stream.Write((short)hue); + } + } + + if (beheld.HairItemID > 0) + if (m_DupedLayers[(int)Layer.Hair] != m_Version) + { + m_DupedLayers[(int)Layer.Hair] = m_Version; + hue = beheld.HairHue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = beheld.HairItemID & 0x7FFF; + + var writeHue = hue != 0; + + if (writeHue) + itemID |= 0x8000; + + Stream.Write(HairInfo.FakeSerial(beheld)); + Stream.Write((ushort)itemID); + Stream.Write((byte)Layer.Hair); + + if (writeHue) + Stream.Write((short)hue); + } + + if (beheld.FacialHairItemID > 0) + if (m_DupedLayers[(int)Layer.FacialHair] != m_Version) + { + m_DupedLayers[(int)Layer.FacialHair] = m_Version; + hue = beheld.FacialHairHue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = beheld.FacialHairItemID & 0x7FFF; + + var writeHue = hue != 0; + + if (writeHue) + itemID |= 0x8000; + + Stream.Write(FacialHairInfo.FakeSerial(beheld)); + Stream.Write((ushort)itemID); + Stream.Write((byte)Layer.FacialHair); + + if (writeHue) + Stream.Write((short)hue); + } + + Stream.Write(0); // terminate + } + } + + // Pre-7.0.0.0 Mobile Incoming + public sealed class MobileIncomingOld : Packet + { + private static readonly ThreadLocal m_DupedLayersTL = new ThreadLocal(() => { return new int[256]; }); + private static readonly ThreadLocal m_VersionTL = new ThreadLocal(); + + public MobileIncomingOld(Mobile beholder, Mobile beheld) : base(0x78) + { + var m_Version = ++m_VersionTL.Value; + var m_DupedLayers = m_DupedLayersTL.Value; + + var eq = beheld.Items; + var count = eq.Count; + + if (beheld.HairItemID > 0) + count++; + if (beheld.FacialHairItemID > 0) + count++; + + EnsureCapacity(23 + count * 9); + + var hue = beheld.Hue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + Stream.Write(beheld.Serial); + Stream.Write((short)beheld.Body); + Stream.Write((short)beheld.X); + Stream.Write((short)beheld.Y); + Stream.Write((sbyte)beheld.Z); + Stream.Write((byte)beheld.Direction); + Stream.Write((short)hue); + Stream.Write((byte)beheld.GetOldPacketFlags()); + Stream.Write((byte)Notoriety.Compute(beholder, beheld)); + + for (var i = 0; i < eq.Count; ++i) + { + var item = eq[i]; + + var layer = (byte)item.Layer; + + if (!item.Deleted && beholder.CanSee(item) && m_DupedLayers[layer] != m_Version) + { + m_DupedLayers[layer] = m_Version; + + hue = item.Hue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = item.ItemID & 0x7FFF; + var writeHue = hue != 0; + + if (writeHue) + itemID |= 0x8000; + + Stream.Write(item.Serial); + Stream.Write((ushort)itemID); + Stream.Write(layer); + + if (writeHue) + Stream.Write((short)hue); + } + } + + if (beheld.HairItemID > 0) + if (m_DupedLayers[(int)Layer.Hair] != m_Version) + { + m_DupedLayers[(int)Layer.Hair] = m_Version; + hue = beheld.HairHue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = beheld.HairItemID & 0x7FFF; + + var writeHue = hue != 0; + + if (writeHue) + itemID |= 0x8000; + + Stream.Write(HairInfo.FakeSerial(beheld)); + Stream.Write((ushort)itemID); + Stream.Write((byte)Layer.Hair); + + if (writeHue) + Stream.Write((short)hue); + } + + if (beheld.FacialHairItemID > 0) + if (m_DupedLayers[(int)Layer.FacialHair] != m_Version) + { + m_DupedLayers[(int)Layer.FacialHair] = m_Version; + hue = beheld.FacialHairHue; + + if (beheld.SolidHueOverride >= 0) + hue = beheld.SolidHueOverride; + + var itemID = beheld.FacialHairItemID & 0x7FFF; + + var writeHue = hue != 0; + + if (writeHue) + itemID |= 0x8000; + + Stream.Write(FacialHairInfo.FakeSerial(beheld)); + Stream.Write((ushort)itemID); + Stream.Write((byte)Layer.FacialHair); + + if (writeHue) + Stream.Write((short)hue); + } + + Stream.Write(0); // terminate + } + } } diff --git a/Projects/Server/Network/Packets/Old Packets/MovementPackets.cs b/Projects/Server/Network/Packets/Old Packets/MovementPackets.cs new file mode 100644 index 000000000..e4d7e0247 --- /dev/null +++ b/Projects/Server/Network/Packets/Old Packets/MovementPackets.cs @@ -0,0 +1,103 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: MovementPackets.cs - Created: 2020/06/25 - Updated: 2020/06/25 * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +namespace Server.Network +{ + public sealed class SpeedControl : Packet + { + public static readonly Packet WalkSpeed = SetStatic(new SpeedControl(2)); + public static readonly Packet MountSpeed = SetStatic(new SpeedControl(1)); + public static readonly Packet Disable = SetStatic(new SpeedControl(0)); + + public SpeedControl(int speedControl) : base(0xBF) + { + EnsureCapacity(3); + + Stream.Write((short)0x26); + Stream.Write((byte)speedControl); + } + } + + /// + /// Causes the client to walk in a given direction. It does not send a movement request. + /// + public sealed class MovePlayer : Packet + { + public MovePlayer(Direction d) : base(0x97, 2) + { + Stream.Write((byte)d); + + // @4C63B0 + } + } + + public sealed class MovementRej : Packet + { + public MovementRej(int seq, Mobile m) : base(0x21, 8) + { + Stream.Write((byte)seq); + Stream.Write((short)m.X); + Stream.Write((short)m.Y); + Stream.Write((byte)m.Direction); + Stream.Write((sbyte)m.Z); + } + } + + public sealed class MovementAck : Packet + { + private static readonly MovementAck[] m_Cache = new MovementAck[8 * 256]; + + private MovementAck(int seq, int noto) : base(0x22, 3) + { + Stream.Write((byte)seq); + Stream.Write((byte)noto); + } + + public static MovementAck Instantiate(int seq, Mobile m) + { + var noto = Notoriety.Compute(m, m); + + var p = m_Cache[noto * seq]; + + if (p == null) + { + m_Cache[noto * seq] = p = new MovementAck(seq, noto); + p.SetStatic(); + } + + return p; + } + } + + public sealed class NullFastwalkStack : Packet + { + public NullFastwalkStack() : base(0xBF) + { + EnsureCapacity(256); + Stream.Write((short)0x1); + Stream.Write(0x0); + Stream.Write(0x0); + Stream.Write(0x0); + Stream.Write(0x0); + Stream.Write(0x0); + Stream.Write(0x0); + } + } +} diff --git a/Projects/Server/Network/Packets/Old Packets/PlayerPackets.cs b/Projects/Server/Network/Packets/Old Packets/PlayerPackets.cs index a89596d6e..709f93aba 100644 --- a/Projects/Server/Network/Packets/Old Packets/PlayerPackets.cs +++ b/Projects/Server/Network/Packets/Old Packets/PlayerPackets.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: PlayerPackets.cs - Created: 2020/05/07 - Updated: 2020/05/26 * + * File: PlayerPackets.cs - Created: 2020/05/07 - Updated: 2020/06/25 * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -18,8 +18,20 @@ * along with this program. If not, see . * *************************************************************************/ +using System; + namespace Server.Network { + public enum LRReason : byte + { + CannotLift = 0, + OutOfRange = 1, + OutOfSight = 2, + TryToSteal = 3, + AreHolding = 4, + Inspecific = 5 + } + public sealed class StatLockInfo : Packet { public StatLockInfo(Mobile m) : base(0xBF) @@ -37,14 +49,6 @@ namespace Server.Network } } - public sealed class ChangeCombatant : Packet - { - public ChangeCombatant(Mobile combatant) : base(0xAA, 5) - { - Stream.Write(combatant?.Serial ?? Serial.Zero); - } - } - public sealed class ChangeUpdateRange : Packet { private static readonly ChangeUpdateRange[] m_Cache = new ChangeUpdateRange[0x100]; @@ -82,21 +86,6 @@ namespace Server.Network public static Packet Instantiate(bool dead) => dead ? Dead : Alive; } - public sealed class SpeedControl : Packet - { - public static readonly Packet WalkSpeed = SetStatic(new SpeedControl(2)); - public static readonly Packet MountSpeed = SetStatic(new SpeedControl(1)); - public static readonly Packet Disable = SetStatic(new SpeedControl(0)); - - public SpeedControl(int speedControl) : base(0xBF) - { - EnsureCapacity(3); - - Stream.Write((short)0x26); - Stream.Write((byte)speedControl); - } - } - public sealed class ToggleSpecialAbility : Packet { public ToggleSpecialAbility(int abilityID, bool active) : base(0xBF) @@ -110,48 +99,6 @@ namespace Server.Network } } - public sealed class GlobalLightLevel : Packet - { - private static readonly GlobalLightLevel[] m_Cache = new GlobalLightLevel[0x100]; - - public GlobalLightLevel(int level) : base(0x4F, 2) - { - Stream.Write((sbyte)level); - } - - public static GlobalLightLevel Instantiate(int level) - { - var lvl = (byte)level; - var p = m_Cache[lvl]; - - if (p == null) - { - m_Cache[lvl] = p = new GlobalLightLevel(level); - p.SetStatic(); - } - - return p; - } - } - - public sealed class PersonalLightLevel : Packet - { - public PersonalLightLevel(Mobile m, int level) : base(0x4E, 6) - { - Stream.Write(m.Serial); - Stream.Write((sbyte)level); - } - } - - public sealed class PersonalLightLevelZero : Packet - { - public PersonalLightLevelZero(Mobile m) : base(0x4E, 6) - { - Stream.Write(m.Serial); - Stream.Write((sbyte)0); - } - } - public sealed class DisplayProfile : Packet { public DisplayProfile(bool realSerial, Mobile m, string header, string body, string footer) : base(0xB8) @@ -195,61 +142,6 @@ namespace Server.Network } } - /// - /// Causes the client to walk in a given direction. It does not send a movement request. - /// - public sealed class MovePlayer : Packet - { - public MovePlayer(Direction d) : base(0x97, 2) - { - Stream.Write((byte)d); - - // @4C63B0 - } - } - - public sealed class SetWarMode : Packet - { - public static readonly Packet InWarMode = SetStatic(new SetWarMode(true)); - public static readonly Packet InPeaceMode = SetStatic(new SetWarMode(false)); - - public SetWarMode(bool mode) : base(0x72, 5) - { - Stream.Write(mode); - Stream.Write((byte)0x00); - Stream.Write((byte)0x32); - Stream.Write((byte)0x00); - // m_Stream.Fill(); - } - - public static Packet Instantiate(bool mode) => mode ? InWarMode : InPeaceMode; - } - - public sealed class Swing : Packet - { - public Swing(int flag, Mobile attacker, Mobile defender) : base(0x2F, 10) - { - Stream.Write((byte)flag); - Stream.Write(attacker.Serial); - Stream.Write(defender.Serial); - } - } - - public sealed class NullFastwalkStack : Packet - { - public NullFastwalkStack() : base(0xBF) - { - EnsureCapacity(256); - Stream.Write((short)0x1); - Stream.Write(0x0); - Stream.Write(0x0); - Stream.Write(0x0); - Stream.Write(0x0); - Stream.Write(0x0); - Stream.Write(0x0); - } - } - public sealed class RemoveEntity : Packet { public RemoveEntity(IEntity entity) : base(0x1D, 5) @@ -358,8 +250,7 @@ namespace Server.Network public sealed class SeasonChange : Packet { - private static readonly SeasonChange[][] m_Cache = new SeasonChange[][] - { + private static readonly SeasonChange[][] m_Cache = { new SeasonChange[2], new SeasonChange[2], new SeasonChange[2], @@ -461,4 +352,76 @@ namespace Server.Network return p; } } + + public sealed class ScrollMessage : Packet + { + public ScrollMessage(int type, int tip, string text) : base(0xA6) + { + text ??= ""; + + EnsureCapacity(10 + text.Length); + + Stream.Write((byte)type); + Stream.Write(tip); + Stream.Write((ushort)text.Length); + Stream.WriteAsciiFixed(text, text.Length); + } + } + + public sealed class CurrentTime : Packet + { + public CurrentTime() : base(0x5B, 4) + { + var now = DateTime.UtcNow; + + Stream.Write((byte)now.Hour); + Stream.Write((byte)now.Minute); + Stream.Write((byte)now.Second); + } + } + + public sealed class PathfindMessage : Packet + { + public PathfindMessage(IPoint3D p) : base(0x38, 7) + { + Stream.Write((short)p.X); + Stream.Write((short)p.Y); + Stream.Write((short)p.Z); + } + } + + public sealed class PingAck : Packet + { + private static readonly PingAck[] m_Cache = new PingAck[0x100]; + + public PingAck(byte ping) : base(0x73, 2) + { + Stream.Write(ping); + } + + public static PingAck Instantiate(byte ping) + { + var p = m_Cache[ping]; + + if (p == null) + { + m_Cache[ping] = p = new PingAck(ping); + p.SetStatic(); + } + + return p; + } + } + + public sealed class ClearWeaponAbility : Packet + { + public static readonly Packet Instance = SetStatic(new ClearWeaponAbility()); + + public ClearWeaponAbility() : base(0xBF) + { + EnsureCapacity(5); + + Stream.Write((short)0x21); + } + } } diff --git a/Projects/Server/Network/ServerInfo.cs b/Projects/Server/Network/ServerInfo.cs new file mode 100644 index 000000000..d31ad0792 --- /dev/null +++ b/Projects/Server/Network/ServerInfo.cs @@ -0,0 +1,44 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ServerInfo.cs - Created: 2020/06/25 - Updated: 2020/06/25 * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Net; + +namespace Server.Network +{ + public sealed class ServerInfo + { + public ServerInfo(string name, int fullPercent, TimeZoneInfo tz, IPEndPoint address) + { + Name = name; + FullPercent = fullPercent; + TimeZone = tz.GetUtcOffset(DateTime.Now).Hours; + Address = address; + } + + public string Name { get; set; } + + public int FullPercent { get; set; } + + public int TimeZone { get; set; } + + public IPEndPoint Address { get; set; } + } +} diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index 9fc558432..b83a69dbc 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -898,9 +898,9 @@ namespace Server.Accounting { int count = 0; - for (int i = 0; i < Length; ++i) + for (int i = 0; i < Length; i++) if (this[i] != null) - ++count; + count++; return count; } @@ -927,13 +927,13 @@ namespace Server.Accounting { Mobile m = m_Mobiles[index]; - if (m?.Deleted == true) - { - m.Account = null; - m_Mobiles[index] = m = null; - } + if (m?.Deleted != true) + return m; - return m; + // This is the only place that clears a mobile for garbage collection + // outside of an entire account deletion. + m.Account = null; + m_Mobiles[index] = null; } return null; diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs b/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs index b5c40ef45..ede02a2c5 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/MiningCart.cs @@ -161,6 +161,7 @@ namespace Server.Items }; int amount = Math.Min(10, Ore); + // ReSharper disable once PossibleNullReferenceException ingots.Amount = amount; if (!from.PlaceInBackpack(ingots)) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs b/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs index 4475ddfd8..1096a0034 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/TreeStump.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using Server.Engines.VeteranRewards; using Server.Gumps; using Server.Multis; @@ -110,6 +109,7 @@ namespace Server.Items }; int amount = Math.Min(10, m_Logs); + // ReSharper disable once PossibleNullReferenceException logs.Amount = amount; if (!from.PlaceInBackpack(logs)) diff --git a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs index 1a8fa2a36..1f3677710 100644 --- a/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs +++ b/Projects/UOContent/Items/Weapons/Abilities/WeaponAbility.cs @@ -310,8 +310,8 @@ namespace Server.Items } public static bool IsWeaponAbility(Mobile m, WeaponAbility a) => - a == null || !m.Player || (m.Weapon is BaseWeapon weapon && - (weapon.PrimaryAbility == a || weapon.SecondaryAbility == a)); + a == null || !m.Player || m.Weapon is BaseWeapon weapon && + (weapon.PrimaryAbility == a || weapon.SecondaryAbility == a); public static WeaponAbility GetCurrentAbility(Mobile m) { diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 98ef0c5e5..360ec8b04 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -785,7 +785,7 @@ namespace Server.Items attacker.DisruptiveAction(); if (attacker.NetState != null) - attacker.Send(new Swing(0, attacker, defender)); + attacker.Send(new Swing(attacker.Serial, defender.Serial)); if (attacker is BaseCreature bc) { diff --git a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs index bc1a20121..a54cc930c 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs @@ -79,7 +79,7 @@ namespace Server.Items if (canSwing && attacker.HarmfulCheck(defender)) { attacker.DisruptiveAction(); - attacker.Send(new Swing(0, attacker, defender)); + attacker.Send(new Swing(attacker.Serial, defender.Serial)); if (OnFired(attacker, defender)) { diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index 9001af26d..15fb8944e 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -685,7 +685,7 @@ namespace Server.Mobiles m_LastPersonalLight = personal; ns.Send(GlobalLightLevel.Instantiate(global)); - ns.Send(new PersonalLightLevel(this, personal)); + ns.Send(new PersonalLightLevel(this.Serial, personal)); } public override int GetMinResistance(ResistanceType type)