diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index c8ab6bcd9..e1d0944d9 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -3,10 +3,9 @@ false - + - diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 088566bdc..54b328a51 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -1151,9 +1151,9 @@ namespace Server { if (state.HighSeas) { - if (hsWorldItem[0] == 0) + var length = OutgoingItemPackets.CreateWorldItemNew(hsWorldItem, this, true); + if (length != hsWorldItem.Length) { - var length = OutgoingItemPackets.CreateWorldItemNew(hsWorldItem, this, true); hsWorldItem = hsWorldItem.Slice(0, length); } @@ -1161,9 +1161,9 @@ namespace Server } else if (state.StygianAbyss) { - if (saWorldItem[0] == 0) + var length = OutgoingItemPackets.CreateWorldItemNew(saWorldItem, this, false); + if (length != saWorldItem.Length) { - var length = OutgoingItemPackets.CreateWorldItemNew(saWorldItem, this, false); saWorldItem = saWorldItem.Slice(0, length); } @@ -1171,9 +1171,9 @@ namespace Server } else { - if (oldWorldItem[0] == 0) + var length = OutgoingItemPackets.CreateWorldItem(oldWorldItem, this); + if (length != oldWorldItem.Length) { - var length = OutgoingItemPackets.CreateWorldItem(oldWorldItem, this); oldWorldItem = oldWorldItem.Slice(0, length); } @@ -3091,11 +3091,7 @@ namespace Server return; } - if (opl[0] == 0) - { - OutgoingEntityPackets.CreateOPLInfo(opl, this); - } - + OutgoingEntityPackets.CreateOPLInfo(opl, this); ns.Send(opl); } diff --git a/Projects/Server/Items/VirtualHair.cs b/Projects/Server/Items/VirtualHair.cs index 56970d27d..2cda7dbb5 100644 --- a/Projects/Server/Items/VirtualHair.cs +++ b/Projects/Server/Items/VirtualHair.cs @@ -24,6 +24,11 @@ namespace Server public static void CreateHairEquipUpdatePacket(Span buffer, Mobile m, Serial hairSerial, Layer layer) { + if (buffer[0] != 0) + { + return; + } + var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.HairHue; var writer = new SpanWriter(buffer); @@ -51,6 +56,11 @@ namespace Server public static void CreateRemoveHairPacket(Span buffer, Serial hairSerial) { + if (buffer[0] != 0) + { + return; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0x1D); // Packet ID writer.Write(hairSerial); diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 2b78dc929..66c12e643 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -3191,11 +3191,7 @@ namespace Server if (IsDeadBondedPet) { - if (deadBuffer[0] == 0) - { - OutgoingMobilePackets.CreateBondedStatus(deadBuffer, Serial, true); - } - + OutgoingMobilePackets.CreateBondedStatus(deadBuffer, Serial, true); state.Send(deadBuffer); } } @@ -3209,21 +3205,13 @@ namespace Server { if (sendHealthbarPoison) { - if (hbpBuffer[0] == 0) - { - OutgoingMobilePackets.CreateMobileHealthbar(hbpBuffer, this, Healthbar.Poison); - } - + OutgoingMobilePackets.CreateMobileHealthbar(hbpBuffer, this, Healthbar.Poison); state.Send(hbpBuffer); } if (sendHealthbarYellow) { - if (hbyBuffer[0] == 0) - { - OutgoingMobilePackets.CreateMobileHealthbar(hbyBuffer, this, Healthbar.Yellow); - } - + OutgoingMobilePackets.CreateMobileHealthbar(hbyBuffer, this, Healthbar.Yellow); state.Send(hbyBuffer); } } @@ -3232,51 +3220,36 @@ namespace Server { if (CanBeRenamedBy(beholder)) { - if (statBufferTrue[0] == 0) - { - OutgoingMobilePackets.CreateMobileStatusCompact(statBufferTrue, this, true); - } - + OutgoingMobilePackets.CreateMobileStatusCompact(statBufferTrue, this, true); state.Send(statBufferTrue); } else { - if (statBufferFalse[0] == 0) - { - OutgoingMobilePackets.CreateMobileStatusCompact(statBufferFalse, this, false); - } - + OutgoingMobilePackets.CreateMobileStatusCompact(statBufferFalse, this, false); state.Send(statBufferFalse); } } else if (sendHits) { - if (hitsPacket[0] == 0) - { - OutgoingMobilePackets.CreateMobileHits(hitsPacket, this, true); - } - + OutgoingMobilePackets.CreateMobileHits(hitsPacket, this, true); state.Send(hitsPacket); } if (sendHair) { - if (hairPacket[0] == 0) + var hairSerial = HairInfo.FakeSerial(Serial); + if (removeHair) { - var hairSerial = HairInfo.FakeSerial(Serial); - if (removeHair) - { - OutgoingVirtualHairPackets.CreateRemoveHairPacket(hairPacket, hairSerial); - } - else - { - OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket( - hairPacket, - this, - hairSerial, - Layer.Hair - ); - } + OutgoingVirtualHairPackets.CreateRemoveHairPacket(hairPacket, hairSerial); + } + else + { + OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket( + hairPacket, + this, + hairSerial, + Layer.Hair + ); } state.Send(hairPacket); @@ -3284,24 +3257,20 @@ namespace Server if (sendFacialHair) { - if (facialhairPacket[0] == 0) + var hairSerial = HairInfo.FakeSerial(Serial); + if (removeFacialHair) { - var hairSerial = HairInfo.FakeSerial(Serial); - if (removeFacialHair) - { - OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialhairPacket, hairSerial); - } - else - { - OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket( - facialhairPacket, - this, - hairSerial, - Layer.FacialHair - ); - } + OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialhairPacket, hairSerial); + } + else + { + OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket( + facialhairPacket, + this, + hairSerial, + Layer.FacialHair + ); } - state.Send(facialhairPacket); } @@ -5024,11 +4993,7 @@ namespace Server { if (state != m_NetState) { - if (deathAnimation[0] == 0) - { - OutgoingMobilePackets.CreateDeathAnimation(deathAnimation, Serial, corpseSerial); - } - + OutgoingMobilePackets.CreateDeathAnimation(deathAnimation, Serial, corpseSerial); state.Send(deathAnimation); if (!state.Mobile.CanSee(this)) diff --git a/Projects/Server/Network/Packets/OutgoingEntityPackets.cs b/Projects/Server/Network/Packets/OutgoingEntityPackets.cs index 6771a9ca3..93d4132e8 100644 --- a/Projects/Server/Network/Packets/OutgoingEntityPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingEntityPackets.cs @@ -28,6 +28,11 @@ namespace Server.Network public static void CreateOPLInfo(Span buffer, Serial serial, int hash) { + if (buffer[0] != 0) + { + return; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0xDC); // Packet ID writer.Write(serial); diff --git a/Projects/Server/Network/Packets/OutgoingItemPackets.cs b/Projects/Server/Network/Packets/OutgoingItemPackets.cs index 476528f87..eeeae9835 100644 --- a/Projects/Server/Network/Packets/OutgoingItemPackets.cs +++ b/Projects/Server/Network/Packets/OutgoingItemPackets.cs @@ -25,6 +25,11 @@ namespace Server.Network public static int CreateWorldItem(Span buffer, Item item) { + if (buffer[0] != 0) + { + return buffer.Length; + } + var itemID = item is BaseMulti ? item.ItemID | 0x4000 : item.ItemID & 0x3FFF; var hasAmount = item.Amount != 0; var amount = item.Amount; @@ -95,6 +100,11 @@ namespace Server.Network public static int CreateWorldItemNew(Span buffer, Item item, bool isHS) { + if (buffer[0] != 0) + { + return buffer.Length; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0xF3); // Packet ID writer.Write((short)0x1); // command diff --git a/Projects/Server/Network/Packets/OutgoingMessagePackets.cs b/Projects/Server/Network/Packets/OutgoingMessagePackets.cs index 9bff08615..015d75d8f 100644 --- a/Projects/Server/Network/Packets/OutgoingMessagePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMessagePackets.cs @@ -49,6 +49,7 @@ namespace Server.Network ns.Send(buffer.Slice(0, length)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetMaxMessageLocalizedLength(string args) => 50 + (args?.Length ?? 0) * 2; public static int CreateMessageLocalized( @@ -104,6 +105,7 @@ namespace Server.Network ns.Send(buffer.Slice(0, length)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetMaxMessageLocalizedAffixLength(string affix, string args) => 52 + (affix?.Length ?? 0) + (args?.Length ?? 0) * 2; @@ -145,7 +147,6 @@ namespace Server.Network return writer.Position; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SendMessage( this NetState ns, Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, string name, string text diff --git a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs index c1ded8afc..fccbf42b0 100644 --- a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs @@ -39,6 +39,11 @@ namespace Server.Network public static void CreateBondedStatus(Span buffer, Serial serial, bool bonded) { + if (buffer[0] != 0) + { + return; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0xBF); // Packet ID writer.Write((ushort)11); // Length @@ -68,6 +73,11 @@ namespace Server.Network public static void CreateDeathAnimation(Span buffer, Serial killed, Serial corpse) { + if (buffer[0] != 0) + { + return; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0xAF); // Packet ID writer.Write(killed); @@ -194,6 +204,11 @@ namespace Server.Network public static void CreateMobileHits(Span buffer, Mobile m, bool normalize = false) { + if (buffer[0] != 0) + { + return; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0xA1); // Packet ID writer.Write(m.Serial); @@ -214,6 +229,11 @@ namespace Server.Network public static void CreateMobileMana(Span buffer, Mobile m, bool normalize = false) { + if (buffer[0] != 0) + { + return; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0xA2); // Packet ID writer.Write(m.Serial); @@ -234,6 +254,11 @@ namespace Server.Network public static void CreateMobileStam(Span buffer, Mobile m, bool normalize = false) { + if (buffer[0] != 0) + { + return; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0xA3); // Packet ID writer.Write(m.Serial); @@ -254,6 +279,11 @@ namespace Server.Network public static void CreateMobileAttributes(Span buffer, Mobile m, bool normalize = false) { + if (buffer[0] != 0) + { + return; + } + var writer = new SpanWriter(buffer); writer.Write((byte)0x2D); // Packet ID writer.Write(m.Serial); @@ -355,6 +385,11 @@ namespace Server.Network public static void CreateMobileHealthbar(Span buffer, Mobile m, Healthbar healthbar) { + if (buffer[0] != 0) + { + return; + } + switch (healthbar) { case Healthbar.Poison: @@ -442,20 +477,20 @@ namespace Server.Network Span buffer, Mobile beholder, Mobile beheld, int version, bool canBeRenamed ) { + if (buffer[0] != 0) + { + return buffer.Length; + } + var name = beheld.Name ?? ""; var writer = new SpanWriter(buffer); writer.Write((byte)0x11); // Packet ID writer.Seek(2, SeekOrigin.Current); - writer.Write(beheld.Serial); - writer.WriteAscii(name, 30); - writer.WriteAttribute(beheld.HitsMax, beheld.Hits, version == 0, true); - writer.Write(canBeRenamed); - writer.Write((byte)version); if (version <= 0) diff --git a/Projects/UOContent/Engines/Party/Packets.cs b/Projects/UOContent.Tests/Tests/Engines/Party/Packets.cs similarity index 76% rename from Projects/UOContent/Engines/Party/Packets.cs rename to Projects/UOContent.Tests/Tests/Engines/Party/Packets.cs index 829d9d599..e4601e651 100644 --- a/Projects/UOContent/Engines/Party/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Party/Packets.cs @@ -4,14 +4,14 @@ namespace Server.Engines.PartySystem { public sealed class PartyEmptyList : Packet { - public PartyEmptyList(Mobile m) : base(0xBF) + public PartyEmptyList(Serial m) : base(0xBF) { EnsureCapacity(7); Stream.Write((short)0x0006); Stream.Write((byte)0x02); Stream.Write((byte)0); - Stream.Write(m.Serial); + Stream.Write(m); } } @@ -34,7 +34,7 @@ namespace Server.Engines.PartySystem public sealed class PartyRemoveMember : Packet { - public PartyRemoveMember(Mobile removed, Party p) : base(0xBF) + public PartyRemoveMember(Serial removed, Party p) : base(0xBF) { EnsureCapacity(11 + p.Count * 4); @@ -42,7 +42,7 @@ namespace Server.Engines.PartySystem Stream.Write((byte)0x02); Stream.Write((byte)p.Count); - Stream.Write(removed.Serial); + Stream.Write(removed); for (var i = 0; i < p.Count; ++i) { @@ -53,31 +53,28 @@ namespace Server.Engines.PartySystem public sealed class PartyTextMessage : Packet { - public PartyTextMessage(bool toAll, Mobile from, string text) : base(0xBF) + public PartyTextMessage(bool toAll, Serial from, string text) : base(0xBF) { - if (text == null) - { - text = ""; - } + text ??= ""; EnsureCapacity(12 + text.Length * 2); Stream.Write((short)0x0006); Stream.Write((byte)(toAll ? 0x04 : 0x03)); - Stream.Write(from.Serial); + Stream.Write(from); Stream.WriteBigUniNull(text); } } public sealed class PartyInvitation : Packet { - public PartyInvitation(Mobile leader) : base(0xBF) + public PartyInvitation(Serial leader) : base(0xBF) { EnsureCapacity(10); Stream.Write((short)0x0006); Stream.Write((byte)0x07); - Stream.Write(leader.Serial); + Stream.Write(leader); } } } diff --git a/Projects/UOContent.Tests/Tests/Engines/Party/PartyPacketTests.cs b/Projects/UOContent.Tests/Tests/Engines/Party/PartyPacketTests.cs new file mode 100644 index 000000000..4aa7ed342 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Engines/Party/PartyPacketTests.cs @@ -0,0 +1,99 @@ +using System; +using Server; +using Server.Engines.PartySystem; +using Server.Tests; +using Server.Tests.Network; +using Xunit; + +namespace UOContent.Tests +{ + public class PartyPacketTests : IClassFixture + { + [Fact] + public void TestPartyEmptyList() + { + Serial m = 0x1024u; + + var expected = new PartyEmptyList(m).Compile(); + + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyRemoveMember(m); + + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); + } + + [Fact] + public void TestPartyRemoveMember() + { + var leader = new Mobile(0x1024u); + leader.DefaultMobileInit(); + + var member = new Mobile(0x2048u); + member.DefaultMobileInit(); + + var p = new Party(leader); + p.Add(member); + + var expected = new PartyRemoveMember(member.Serial, p).Compile(); + + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyRemoveMember(member.Serial, p); + + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); + } + + [Fact] + public void TestPartyMemberList() + { + var leader = new Mobile(0x1024u); + leader.DefaultMobileInit(); + + var member = new Mobile(0x2048u); + member.DefaultMobileInit(); + + var p = new Party(leader); + p.Add(member); + + var expected = new PartyMemberList(p).Compile(); + + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyMemberList(p); + + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void TestPartyTextMessage(bool toAll) + { + Serial serial = 0x1024u; + var text = "[Party] Stuff Happens"; + + var expected = new PartyTextMessage(toAll, serial, text).Compile(); + + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyTextMessage(serial, text, toAll); + + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); + } + + [Fact] + public void TestPartyInvitation() + { + Serial m = 0x1024u; + + var expected = new PartyInvitation(m).Compile(); + + using var ns = PacketTestUtilities.CreateTestNetState(); + ns.SendPartyInvitation(m); + + var result = ns.SendPipe.Reader.TryRead(); + AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); + } + } +} diff --git a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/PacketTests.cs b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpsePacketTests.cs similarity index 95% rename from Projects/UOContent.Tests/Tests/Items/Misc/Corpses/PacketTests.cs rename to Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpsePacketTests.cs index c1cfb5fde..aa4a2c412 100644 --- a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/PacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpsePacketTests.cs @@ -8,7 +8,7 @@ using Xunit; namespace UOContent.Tests { - public class PacketTests : IClassFixture + public class CorpsePacketTests : IClassFixture { [Fact] public void TestCorpseEquipPacket() diff --git a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/Packets.cs b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/Packets.cs index f0d46653f..35def09c6 100644 --- a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/Packets.cs @@ -1,5 +1,4 @@ using System.IO; -using Server; using Server.Items; namespace Server.Network diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index d2717dbd0..b722e4ddc 100644 --- a/Projects/UOContent.Tests/UOContent.Tests.csproj +++ b/Projects/UOContent.Tests/UOContent.Tests.csproj @@ -3,10 +3,9 @@ false - + - diff --git a/Projects/UOContent/Engines/Party/Party.cs b/Projects/UOContent/Engines/Party/Party.cs index 94acbc079..a0043be80 100644 --- a/Projects/UOContent/Engines/Party/Party.cs +++ b/Projects/UOContent/Engines/Party/Party.cs @@ -9,7 +9,7 @@ namespace Server.Engines.PartySystem public class Party : IParty { public const int Capacity = 10; - private readonly List m_Listeners; // staff listening + private readonly HashSet m_Listeners; // staff listening public Party(Mobile leader) { @@ -17,7 +17,7 @@ namespace Server.Engines.PartySystem Members = new List(); Candidates = new List(); - m_Listeners = new List(); + m_Listeners = new HashSet(); Members.Add(new PartyMemberInfo(leader)); } @@ -185,33 +185,34 @@ namespace Server.Engines.PartySystem public void Add(Mobile m) { var mi = this[m]; - - if (mi == null) + if (mi != null) { - var ns = m.NetState; - Members.Add(new PartyMemberInfo(m)); - m.Party = this; + return; + } - var memberList = Packet.Acquire(new PartyMemberList(this)); - Span attrsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributesPacketLength]; - OutgoingMobilePackets.CreateMobileAttributes(attrsPacket, m, true); + var ns = m.NetState; + Members.Add(new PartyMemberInfo(m)); + m.Party = this; - for (var i = 0; i < Members.Count; ++i) + Span memberList = + stackalloc byte[PartyPackets.GetPartyMemberListPacketLength(this)].InitializePacket(); + Span attrsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributesPacketLength].InitializePacket(); + + for (var i = 0; i < Members.Count; ++i) + { + var f = Members[i].Mobile; + + PartyPackets.CreatePartyMemberList(memberList, this); + f.NetState?.Send(memberList); + + if (f != m) { - var f = Members[i].Mobile; - - f.Send(memberList); - - if (f != m) - { - f.NetState.SendMobileStatusCompact(m, m.CanBeRenamedBy(f)); - f.NetState?.Send(attrsPacket); - ns.SendMobileStatusCompact(f, f.CanBeRenamedBy(m)); - ns.SendMobileAttributes(f, true); - } + f.NetState.SendMobileStatusCompact(m, m.CanBeRenamedBy(f)); + OutgoingMobilePackets.CreateMobileAttributes(attrsPacket, m, true); + f.NetState?.Send(attrsPacket); + ns.SendMobileStatusCompact(f, f.CanBeRenamedBy(m)); + ns.SendMobileAttributes(f, true); } - - Packet.Release(memberList); } } @@ -245,7 +246,9 @@ namespace Server.Engines.PartySystem ); // : joined the party. - SendToAll(buffer.Slice(0, length), true); + buffer = buffer.Slice(0, length); + SendToAll(buffer); + SendToAllListeners(buffer); from.SendLocalizedMessage(1005445); // You have been added to the party. @@ -261,18 +264,21 @@ namespace Server.Engines.PartySystem from.SendLocalizedMessage(1008092); // You notify them that you do not wish to join the party. Candidates.Remove(from); - from.Send(new PartyEmptyList(from)); + from.NetState.SendPartyRemoveMember(from.Serial); - if (Candidates.Count == 0 && Members.Count <= 1) + if (Candidates.Count != 0 || Members.Count > 1) { - for (var i = 0; i < Members.Count; ++i) - { - this[i].Mobile.Send(new PartyEmptyList(this[i].Mobile)); - this[i].Mobile.Party = null; - } - - Members.Clear(); + return; } + + for (var i = 0; i < Members.Count; ++i) + { + var m = this[i].Mobile; + m.NetState.SendPartyRemoveMember(m.Serial); + m.Party = null; + } + + Members.Clear(); } public void Remove(Mobile m) @@ -283,18 +289,22 @@ namespace Server.Engines.PartySystem return; } - for (var i = 0; i < Members.Count; ++i) + Span removeMember = + stackalloc byte[PartyPackets.GetPartyRemoveMemberPacketLength(this)].InitializePacket(); + + for (var i = 0; i < Members.Count; i++) { if (Members[i].Mobile == m) { Members.RemoveAt(i); + m.NetState.SendPartyRemoveMember(m.Serial); m.Party = null; - m.Send(new PartyEmptyList(m)); m.SendLocalizedMessage(1005451); // You have been removed from the party. - SendToAll(new PartyRemoveMember(m, this)); + PartyPackets.CreatePartyRemoveMember(removeMember, m.Serial, this); + SendToAll(removeMember); SendToAll(1005452); // A player has been removed from your party. break; @@ -316,8 +326,9 @@ namespace Server.Engines.PartySystem for (var i = 0; i < Members.Count; ++i) { - this[i].Mobile.Send(new PartyEmptyList(this[i].Mobile)); - this[i].Mobile.Party = null; + var m = this[i].Mobile; + m.NetState.SendPartyRemoveMember(m.Serial); + m.Party = null; } Members.Clear(); @@ -362,7 +373,7 @@ namespace Server.Engines.PartySystem from.SendLocalizedMessage(1008090); // You have invited them to join the party. - target.Send(new PartyInvitation(from)); + target.NetState.SendPartyInvitation(from.Serial); target.Party = from; DeclineTimer.Start(target, from); @@ -377,41 +388,28 @@ namespace Server.Engines.PartySystem Serial.MinusOne, -1, MessageType.Regular, hue, 3, number, "System", args ); - SendToAll(buffer.Slice(0, length), true); + buffer = buffer.Slice(0, length); + + SendToAll(buffer); + SendToAllListeners(buffer); } public void SendPublicMessage(Mobile from, string text) { - SendToAll(new PartyTextMessage(true, from, text)); + Span textMessagePacket = + stackalloc byte[PartyPackets.GetPartyTextMessagePacketLength(text)].InitializePacket(); + PartyPackets.CreatePartyTextMessage(textMessagePacket, from.Serial, text, true); + SendToAll(textMessagePacket); - for (var i = 0; i < m_Listeners.Count; ++i) - { - var mob = m_Listeners[i]; - - if (mob.Party != this) - { - m_Listeners[i].SendMessage("[{0}]: {1}", from.Name, text); - } - } - - SendToStaffMessage(from, "[Party]: {0}", text); + SendToAllListeners($"[{from.Name}]: {text}"); + SendToStaffMessage(from, $"[Party]: {text}"); } public void SendPrivateMessage(Mobile from, Mobile to, string text) { - to.Send(new PartyTextMessage(false, from, text)); - - for (var i = 0; i < m_Listeners.Count; ++i) - { - var mob = m_Listeners[i]; - - if (mob.Party != this) - { - m_Listeners[i].SendMessage("[{0}]->[{1}]: {2}", from.Name, to.Name, text); - } - } - - SendToStaffMessage(from, "[Party]->[{0}]: {1}", to.Name, text); + to.NetState.SendPartyTextMessage(from.Serial, text, false); + SendToAllListeners($"[{from.Name}]->[{to.Name}]: {text}"); + SendToStaffMessage(from, $"[Party]->[{to.Name}]: {text}"); } private void SendToStaffMessage(Mobile from, string text) @@ -448,42 +446,39 @@ namespace Server.Engines.PartySystem } } - private void SendToStaffMessage(Mobile from, string format, params object[] args) + public void SendToAllListeners(string text) { - SendToStaffMessage(from, string.Format(format, args)); - } - - public void SendToAll(Packet p) - { - p.Acquire(); - - for (var i = 0; i < Members.Count; ++i) + if (m_Listeners.Count == 0) { - Members[i].Mobile.Send(p); + return; } - p.Release(); + Span buffer = + stackalloc byte[OutgoingMessagePackets.GetMaxMessageLength(text)].InitializePacket(); + OutgoingMessagePackets.CreateMessage( + buffer, Serial.MinusOne, -1, MessageType.Regular, 0x3B2, + 3, false, "ENU", "System", text + ); + SendToAllListeners(buffer); } - public void SendToAll(Span span, bool isSpeech) + public void SendToAllListeners(Span span) + { + foreach (var mob in m_Listeners) + { + if (mob.Party != this) + { + mob.NetState?.Send(span); + } + } + } + + public void SendToAll(Span span) { for (var i = 0; i < Members.Count; ++i) { Members[i].Mobile.NetState?.Send(span); } - - if (isSpeech) - { - for (var i = 0; i < m_Listeners.Count; ++i) - { - var mob = m_Listeners[i]; - - if (mob.Party != this) - { - mob.NetState?.Send(span); - } - } - } } private class RejoinTimer : Timer @@ -502,12 +497,10 @@ namespace Server.Engines.PartySystem } m_Mobile.SendLocalizedMessage(1005437); // You have rejoined the party. - m_Mobile.Send(new PartyMemberList(p)); + m_Mobile.NetState.SendPartyMemberList(p); Span buffer = stackalloc byte[OutgoingMessagePackets.GetMaxMessageLocalizedAffixLength(m_Mobile.Name, "")].InitializePacket(); - - Span attrsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributesPacketLength]; - OutgoingMobilePackets.CreateMobileAttributes(attrsPacket, m_Mobile, true); + Span attrsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributesPacketLength].InitializePacket(); var ns = m_Mobile.NetState; @@ -540,6 +533,7 @@ namespace Server.Engines.PartySystem m.NetState?.Send(buffer); m.NetState.SendMobileStatusCompact(m_Mobile, m_Mobile.CanBeRenamedBy(m)); + OutgoingMobilePackets.CreateMobileAttributes(attrsPacket, m_Mobile, true); m.NetState?.Send(attrsPacket); ns.SendMobileStatusCompact(m, m.CanBeRenamedBy(m_Mobile)); ns.SendMobileAttributes(m, true); diff --git a/Projects/UOContent/Engines/Party/PartyPackets.cs b/Projects/UOContent/Engines/Party/PartyPackets.cs new file mode 100644 index 000000000..627f1811f --- /dev/null +++ b/Projects/UOContent/Engines/Party/PartyPackets.cs @@ -0,0 +1,153 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: PartyPackets.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using Server.Network; + +namespace Server.Engines.PartySystem +{ + public static class PartyPackets + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetPartyMemberListPacketLength(Party p) => 7 + p.Count * 4; + + public static void CreatePartyMemberList(Span buffer, Party p) + { + if (buffer[0] != 0) + { + return; + } + + var length = GetPartyMemberListPacketLength(p); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0x06); // Sub-packet + writer.Write((byte)0x01); // Command + writer.Write((byte)p.Count); + + for (var i = 0; i < p.Count; ++i) + { + writer.Write(p[i].Mobile.Serial); + } + } + + public static void SendPartyMemberList(this NetState ns, Party p) + { + if (ns == null) + { + return; + } + + Span buffer = stackalloc byte[GetPartyMemberListPacketLength(p)]; + CreatePartyMemberList(buffer, p); + + ns.Send(buffer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetPartyRemoveMemberPacketLength(Party p) => 11 + (p?.Count * 4 ?? 0); + + public static void SendPartyRemoveMember(this NetState ns, Serial m, Party p = null) + { + if (ns == null) + { + return; + } + + Span buffer = stackalloc byte[GetPartyRemoveMemberPacketLength(p)]; + CreatePartyRemoveMember(buffer, m, p); + + ns.Send(buffer); + } + + public static void CreatePartyRemoveMember(Span buffer, Serial removed, Party p = null) + { + if (buffer[0] != 0) + { + return; + } + + var length = GetPartyRemoveMemberPacketLength(p); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0x06); // Sub-packet + writer.Write((byte)0x02); // Command + + var count = p?.Count ?? 0; + writer.Write((byte)count); + writer.Write(removed); + + for (var i = 0; i < count; ++i) + { + writer.Write(p![i].Mobile.Serial); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetPartyTextMessagePacketLength(string text) => 12 + (text?.Length * 2 ?? 0); + + public static void SendPartyTextMessage(this NetState ns, Serial m, string text, bool toAll) + { + if (ns == null) + { + return; + } + + Span buffer = stackalloc byte[GetPartyTextMessagePacketLength(text)]; + CreatePartyTextMessage(buffer, m, text, toAll); + + ns.Send(buffer); + } + + public static void CreatePartyTextMessage(Span buffer, Serial m, string text, bool toAll) + { + if (buffer[0] != 0) + { + return; + } + + var length = GetPartyTextMessagePacketLength(text); + var writer = new SpanWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)length); + writer.Write((ushort)0x06); // Sub-packet + writer.Write((byte)(toAll ? 0x04 : 0x03)); + writer.Write(m); + writer.WriteBigUniNull(text); + } + + public static void SendPartyInvitation(this NetState ns, Serial leader) + { + if (ns == null) + { + return; + } + + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + writer.Write((byte)0xBF); // Packet ID + writer.Write((ushort)10); + writer.Write((ushort)0x06); // Sub-packet + writer.Write((byte)0x07); // command + writer.Write(leader); + + ns.Send(buffer); + } + } +} diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index 5b3d08d91..c3d844ff4 100644 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -28,7 +28,7 @@ false - +