Updates menu packets (#320)

This commit is contained in:
Kamron Batman 2020-11-17 21:47:00 -08:00 committed by GitHub
parent ceb8d4ad49
commit 602c14fed7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 260 additions and 228 deletions

View file

@ -12,20 +12,25 @@ namespace Server.Tests.Network
{
internal class ContextMenuItem : Item
{
public ContextMenuItem(Serial serial) : base(serial)
{
}
private bool _requiresNewPacket;
public ContextMenuItem(Serial serial, bool requiresNewPacket) : base(serial) =>
_requiresNewPacket = requiresNewPacket;
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
list.Add(new ContextMenuEntry(500000));
list.Add(new ContextMenuEntry(500001));
list.Add(new ContextMenuEntry(500002));
list.Add(new ContextMenuEntry(3000001));
list.Add(new ContextMenuEntry(3000002));
list.Add(new ContextMenuEntry(3000003));
if (_requiresNewPacket)
{
list.Add(new ContextMenuEntry(500000));
}
}
}
[Collection("Sequential Tests")]
public class MenuPacketTests : IClassFixture<ServerFixture>
{
[Fact]
@ -41,45 +46,13 @@ namespace Server.Tests.Network
}
);
var data = new DisplayItemListMenu(menu).Compile();
var expected = new DisplayItemListMenu(menu).Compile();
var question = menu.Question;
var questionLength = Math.Min(255, question.Length);
var entriesCount = 0;
var length = 11 + questionLength;
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendDisplayItemListMenu(menu);
foreach (var entry in menu.Entries)
{
length += 5 + entry.Name.Length;
if (entriesCount == 255)
{
break;
}
entriesCount++;
}
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x7C); // Packet ID
expectedData.Write(ref pos, (ushort)length);
expectedData.Write(ref pos, menu.Serial);
expectedData.Write(ref pos, (ushort)0x00);
expectedData.Write(ref pos, (byte)questionLength);
expectedData.WriteAscii(ref pos, question, 255);
expectedData.Write(ref pos, (byte)entriesCount);
for (var i = 0; i < entriesCount; i++)
{
var entry = menu.Entries[i];
expectedData.Write(ref pos, (ushort)entry.ItemID);
expectedData.Write(ref pos, (ushort)entry.Hue);
var name = entry.Name?.Trim() ?? "";
expectedData.Write(ref pos, (byte)Math.Min(255, name.Length));
expectedData.WriteAscii(ref pos, name, 255);
}
AssertThat.Equal(data, expectedData);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
@ -95,162 +68,40 @@ namespace Server.Tests.Network
}
);
var data = new DisplayQuestionMenu(menu).Compile();
var expected = new DisplayQuestionMenu(menu).Compile();
using var ns = PacketTestUtilities.CreateTestNetState();
ns.SendDisplayQuestionMenu(menu);
var question = menu.Question;
var questionLength = Math.Min(255, question.Length);
var answersCount = 0;
var length = 11 + questionLength;
foreach (var answer in menu.Answers)
{
length += 5 + answer.Length;
if (answersCount == 255)
{
break;
}
answersCount++;
}
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0x7C); // Packet ID
expectedData.Write(ref pos, (ushort)length);
expectedData.Write(ref pos, menu.Serial);
expectedData.Write(ref pos, (ushort)0x00);
expectedData.Write(ref pos, (byte)question.Length);
expectedData.WriteAscii(ref pos, question, 255);
expectedData.Write(ref pos, (byte)answersCount);
for (var i = 0; i < answersCount; i++)
{
var answer = menu.Answers[i];
#if NO_LOCAL_INIT
expectedData.Write(ref pos, 0);
#else
pos += 4;
#endif
expectedData.Write(ref pos, (byte)Math.Min(255, answer.Length));
expectedData.WriteAscii(ref pos, answer, 255);
}
AssertThat.Equal(data, expectedData);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
[Fact]
public void TestDisplayContextMenu()
[Theory]
[InlineData(false, false)]
[InlineData(false, true)]
[InlineData(true, false)]
[InlineData(true, true)]
public void TestDisplayContextMenu(bool newHaven, bool newPacket)
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var item = new ContextMenuItem(World.NewItem);
var item = new ContextMenuItem(World.NewItem, newPacket);
var menu = new ContextMenu(m, item);
var data = new DisplayContextMenu(menu).Compile();
var packet = newHaven && newPacket ? (Packet)new DisplayContextMenu(menu) : new DisplayContextMenuOld(menu);
var expected = packet.Compile();
var length = 12 + menu.Entries.Length * 8;
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, (ushort)0x14); // Command
expectedData.Write(ref pos, (ushort)0x02); // Subcommand
expectedData.Write(ref pos, menu.Target.Serial);
var entries = menu.Entries;
expectedData.Write(ref pos, (byte)entries.Length);
for (var i = 0; i < entries.Length; i++)
using var ns = PacketTestUtilities.CreateTestNetState();
if (newHaven)
{
var entry = entries[i];
expectedData.Write(ref pos, entry.Number);
expectedData.Write(ref pos, (ushort)i);
var flags = entry.Flags;
var range = entry.Range;
if (range == -1)
{
range = 18;
}
if (!(entry.Enabled && menu.From.InRange(item.GetWorldLocation(), range)))
{
flags |= CMEFlags.Disabled;
}
expectedData.Write(ref pos, (ushort)flags);
ns.ProtocolChanges |= ProtocolChanges.NewHaven;
}
AssertThat.Equal(data, expectedData);
}
ns.SendDisplayContextMenu(menu);
[Fact]
public void TestDisplayContextMenuOld()
{
var m = new Mobile(0x1);
m.DefaultMobileInit();
var item = new ContextMenuItem(World.NewItem);
var menu = new ContextMenu(m, item);
var data = new DisplayContextMenuOld(menu).Compile();
var length = 12 + menu.Entries.Sum(entry => 6 + ((entry.Color & 0xFFFF) != 0xFFFF ? 2 : 0));
Span<byte> expectedData = stackalloc byte[length];
var pos = 0;
expectedData.Write(ref pos, (byte)0xBF); // Packet ID
expectedData.Write(ref pos, (ushort)length); // Length
expectedData.Write(ref pos, (ushort)0x14); // Command
expectedData.Write(ref pos, (ushort)0x01); // Subcommand
expectedData.Write(ref pos, menu.Target.Serial);
var entries = menu.Entries;
expectedData.Write(ref pos, (byte)entries.Length);
for (var i = 0; i < entries.Length; i++)
{
var entry = entries[i];
expectedData.Write(ref pos, (ushort)i);
expectedData.Write(ref pos, (ushort)(entry.Number - 3000000));
var flags = entry.Flags;
var color = entry.Color & 0xFFFF;
if (color != 0xFFFF)
{
flags |= CMEFlags.Colored;
}
var range = entry.Range;
if (range == -1)
{
range = 18;
}
if (!(entry.Enabled && menu.From.InRange(item.GetWorldLocation(), range)))
{
flags |= CMEFlags.Disabled;
}
expectedData.Write(ref pos, (ushort)flags);
if ((flags & CMEFlags.Colored) != 0)
{
expectedData.Write(ref pos, (ushort)color);
}
}
AssertThat.Equal(data, expectedData);
var result = ns.SendPipe.Reader.TryRead();
AssertThat.Equal(result.Buffer[0].AsSpan(0), expected);
}
}
}

View file

@ -1,19 +1,3 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: MenuPackets.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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using Server.ContextMenus;
using Server.Menus;
using Server.Menus.ItemLists;
@ -21,16 +5,6 @@ using Server.Menus.Questions;
namespace Server.Network
{
[Flags]
public enum CMEFlags
{
None = 0x00,
Disabled = 0x01,
Arrow = 0x02,
Highlighted = 0x04,
Colored = 0x20
}
public sealed class DisplayItemListMenu : Packet
{
public DisplayItemListMenu(ItemListMenu menu) : base(0x7C)
@ -167,7 +141,7 @@ namespace Server.Network
if (range == -1)
{
range = 18;
range = Core.GlobalUpdateRange;
}
var flags = e.Flags;
@ -218,7 +192,7 @@ namespace Server.Network
if (range == -1)
{
range = 18;
range = Core.GlobalUpdateRange;
}
var flags = e.Flags;

View file

@ -55,7 +55,7 @@ namespace Server.Menus.ItemLists
public void SendTo(NetState state)
{
state.AddMenu(this);
state.Send(new DisplayItemListMenu(this));
state.SendDisplayItemListMenu(this);
}
}
}

View file

@ -37,7 +37,7 @@ namespace Server.Menus.Questions
public void SendTo(NetState state)
{
state.AddMenu(this);
state.Send(new DisplayQuestionMenu(this));
state.SendDisplayQuestionMenu(this);
}
}
}

View file

@ -1075,19 +1075,7 @@ namespace Server
set
{
m_ContextMenu = value;
if (m_ContextMenu != null && m_NetState != null)
{
// Old packet is preferred until assistants catch up
if (m_NetState.NewHaven && m_ContextMenu.RequiresNewPacket)
{
Send(new DisplayContextMenu(m_ContextMenu));
}
else
{
Send(new DisplayContextMenuOld(m_ContextMenu));
}
}
m_NetState.SendDisplayContextMenu(m_ContextMenu);
}
}

View file

@ -0,0 +1,219 @@
using System;
using System.Buffers;
using System.IO;
using Server.ContextMenus;
using Server.Menus.ItemLists;
using Server.Menus.Questions;
namespace Server.Network
{
[Flags]
public enum CMEFlags
{
None = 0x00,
Disabled = 0x01,
Arrow = 0x02,
Highlighted = 0x04,
Colored = 0x20
}
public static class OutgoingMenuPackets
{
public static void SendDisplayItemListMenu(this NetState ns, ItemListMenu menu)
{
if (ns == null || menu == null || !ns.GetSendBuffer(out var buffer))
{
return;
}
var writer = new CircularBufferWriter(buffer);
writer.Write((byte)0x7C); // Packet ID
writer.Seek(2, SeekOrigin.Current); // Length
writer.Write(menu.Serial);
writer.Write((ushort)0);
var question = menu.Question?.Trim();
if (question == null)
{
writer.Write((byte)0);
}
else
{
var questionLength = question.Length;
writer.Write((byte)questionLength);
writer.WriteAscii(question);
}
var entries = menu.Entries;
int entriesLength = (byte)entries.Length;
writer.Write((byte)entriesLength);
for (var i = 0; i < entriesLength; ++i)
{
var e = entries[i];
writer.Write((ushort)e.ItemID);
writer.Write((short)e.Hue);
var name = e.Name?.Trim();
if (name == null)
{
writer.Write((byte)0);
}
else
{
var nameLength = name.Length;
writer.Write((byte)nameLength);
writer.WriteAscii(name);
}
}
var length = writer.Position;
writer.Seek(1, SeekOrigin.Begin);
writer.Write((ushort)length);
ns.Send(ref buffer, length);
}
public static void SendDisplayQuestionMenu(this NetState ns, QuestionMenu menu)
{
if (ns == null || menu == null || !ns.GetSendBuffer(out var buffer))
{
return;
}
var writer = new CircularBufferWriter(buffer);
writer.Write((byte)0x7C); // Packet ID
writer.Seek(2, SeekOrigin.Current); // Length
writer.Write(menu.Serial);
writer.Write((ushort)0);
var question = menu.Question?.Trim();
if (question == null)
{
writer.Write((byte)0);
}
else
{
var questionLength = question.Length;
writer.Write((byte)questionLength);
writer.WriteAscii(question);
}
var answers = menu.Answers;
int answersLength = (byte)answers.Length;
writer.Write((byte)answersLength);
for (var i = 0; i < answersLength; ++i)
{
writer.Write(0);
var answer = answers[i]?.Trim();
if (answer == null)
{
writer.Write((byte)0);
}
else
{
var nameLength = answer.Length;
writer.Write((byte)nameLength);
writer.WriteAscii(answer);
}
}
var length = writer.Position;
writer.Seek(1, SeekOrigin.Begin);
writer.Write((ushort)length);
ns.Send(ref buffer, length);
}
public static void SendDisplayContextMenu(this NetState ns, ContextMenu menu)
{
if (ns == null || menu == null || !ns.GetSendBuffer(out var buffer))
{
return;
}
var newCommand = ns.NewHaven && menu.RequiresNewPacket;
var entries = menu.Entries;
var entriesLength = (byte)entries.Length;
var writer = new CircularBufferWriter(buffer);
writer.Write((byte)0xBF); // Packet ID
writer.Seek(2, SeekOrigin.Current); // Length
writer.Write((short)0x14); // Subpacket
writer.Write((short)(newCommand ? 0x02 : 0x01)); // Command
var target = menu.Target;
writer.Write(target.Serial);
writer.Write(entriesLength);
var p = target switch
{
Mobile _ => target.Location,
Item item => item.GetWorldLocation(),
_ => Point3D.Zero
};
for (var i = 0; i < entriesLength; ++i)
{
var e = entries[i];
var range = e.Range;
if (range == -1)
{
range = Core.GlobalUpdateRange;
}
var flags = e.Flags;
if (!(e.Enabled && menu.From.InRange(p, range)))
{
flags |= CMEFlags.Disabled;
}
if (newCommand)
{
writer.Write(e.Number);
writer.Write((short)i);
writer.Write((short)flags);
}
else
{
writer.Write((short)i);
writer.Write((ushort)(e.Number - 3000000));
var color = e.Color & 0xFFFF;
if (color != 0xFFFF)
{
flags |= CMEFlags.Colored;
}
writer.Write((short)flags);
if ((flags & CMEFlags.Colored) != 0)
{
writer.Write((short)color);
}
}
}
var length = writer.Position;
writer.Seek(1, SeekOrigin.Begin);
writer.Write((ushort)length);
ns.Send(ref buffer, length);
}
}
}