Updates Packets & Randomizer (#43)

* Fixes a bug with the main loop. Removes unnecessary optimizations for RDRand.

* WIP - Rewriting packets.

* Cleanup and updates README

* Converts more packets

* Fixes header

* Converts Effects packets.

* Forgot the send command

* Adds the start of containers packets.

* Adds message packets with caching

* Extends the send methods

* Starts to add Acquire methods

* Converted the packets

* Cleanup

* Cleanup

* Adds more packets

* Adds more packets

* Fixes clearing arrays from pool. Adds Mobile Incoming

* Converts more packets.

* Moves more packets

* Moves more packets

* Test compression

* Merges

* Migrating to an idiomatic syntax that also supports compression, and proxying.

* Optimize the stack alloc. Changes attributes

* Converted container packets

* Visual Studio doesn't auto save files. I am still getting used to subpar IDEs.

* Adds static packet caching. Will profile later. Converts more packets

* Fixes packets and changes how compression is configured

* WIP - Adds basics for Gumps. Not finished though.

* Fix file

* Fixes formatting

* Changed SpanWriter to be more idiomatic.

* Fixes Span vs RawSpan and missing stackallocs

* Converts over some more gumps

* WIP - Deletes 32bit support.

* Drops RDRand32 support.

* Fixes gump compilation

* Converting gump components

* Removes old huffman compression function

* Revert signature for backwards compatibility

* WIP - Converting more gump components

* Creates ArraySet for the strings. Updates AppendTo to reference that.

* Converts the maining gump components

* Cleans up gump components

* Cleans up directives

* Cleans up ArraySet and moves it. Adds null-coalescing-assignment

* Cleanup, Target Packets, and C# 8 changes.

* Removed OPL Packet

* World packets

* Fixing packet uses

* Cleans up code. Fixes packet uses in various places.

* More cleanup for packets

* Code cleanup

* Converts more packet uses and cleans up more code

* More code cleanup

* Finishes fixing the packets in Item

* Updates secure trade packets

* Updates core and gets it to compile.

* Moved packets to scripts. Fixed account handler use of packets

* Code cleanup

* Updates chat packets

* Code cleanuo

* Adds party packets, but need to implement them.

* Party packets WIP

* Finishes party packets

* Rearrange movement namespaces and classes

* Finishes plant packets

* Code Formatting

* Fixes moving effects

* Code cleanup, eliminates equipinfo

* Adds more packets. Fixes bugs with various packets.

* Finishes mahjon packets

* Fixes mahjong packet

* Code cleanup

* Finishes mahjong packets

* Adds Map packets

* Add multifacet maps and charts

* Cleans up some packets with UTF8

* Optimizes packets

* Cleans up more packets. Moves the MessageHelper

* Removes assistant support. Removes extended protocol. Incorporates MapUO packets as normal packets.

* Updates protocol extensions packet receiver

* Fixes a few bugs. Fixes a few more packets.

* Code cleanup and fixing more packets

* Fixes packet effects

* Cleaned up more code

* Buff Icon cleanup

* Removed unused constructors

* Code Cleanup. Adds BoatHS Packets

* Moves house files. Updates house foundation packets.

* Deployment cleanup

* Fixes:

* Code cleanup

* More code cleanup

* More code cleanup

* Cleaned up BaseHouse

* Enforces styling

* Converts foreach to linq where possible.

* Dont need that

* Goals/Readme updates

* Removes 32bit support at the highest level. Turns on HRT by default.

* Code cleanup. Fixes extended features packet.

* Fixes various bugs

* Code cleanup

* Code cleanup

* Code cleanup. Fixes gump X/Y assignment.

* Code cleanup using |= operator

* More code cleanup

* Cleanup

* Fixes spacing issues. Thanks Visual Studio. You suck.

* Compiler error

* Fixes NPE from RunUO 2.7

* Renames ScriptCompiler to AssemblyHandler. Fixes packets. Updates README

* Fixes more packets. Stupid trailing nulls.

* Fixes various bugs.

* Fixes for gumps

* Fixes more gump stuff. Going to split it out later since it is getting insane

* Recoded the gump writing

* WIP

* *Added output path of scripts project to dev branch
*Activated debugging in code
This commit is contained in:
Kamron Batman 2019-11-11 03:40:16 -08:00 committed by 3HMonkey
parent 7aa8fd3df1
commit 179cb50557
588 changed files with 10843 additions and 16177 deletions

View file

@ -0,0 +1,325 @@
using System;
using Server.Buffers;
namespace Server.Network
{
public enum DeleteResultType : byte
{
PasswordInvalid,
CharNotExist,
CharBeingPlayed,
CharTooYoung,
CharQueued,
BadRequest
}
public enum PMMessage : byte
{
CharNoExist = 1,
CharExists = 2,
CharInWorld = 5,
LoginSyncError = 6,
IdleWarning = 7
}
public enum ALRReason : byte
{
Invalid = 0x00,
InUse = 0x01,
Blocked = 0x02,
BadPass = 0x03,
Idle = 0xFE,
BadComm = 0xFF
}
public static partial class Packets
{
public static void SendDeleteResult(NetState ns, DeleteResultType res)
{
if (ns == null)
return;
Span<byte> span = ns.SendPipe.Writer.GetSpan(2);
span[0] = 0x85; // Packet ID
span[1] = (byte)res;
// Not compressed with Huffman
_ = ns.Flush(2);
}
public static void SendPopupMessage(NetState ns, PMMessage msg)
{
ns?.Send(stackalloc byte[]
{
0x53, // Packet ID
(byte)msg
});
}
public static void SendAccountLoginRej(NetState ns, ALRReason reason)
{
if (ns == null)
return;
Span<byte> span = ns.SendPipe.Writer.GetSpan(2);
span[0] = 0x82; // Packet ID
span[1] = (byte)reason;
// Not compressed with Huffman
_ = ns.Flush(2);
}
public static void SendSupportedFeatures(NetState ns)
{
if (ns == null)
return;
int length = ns.ExtendedSupportedFeatures ? 5 : 3;
Span<byte> bytes = stackalloc byte[length];
SpanWriter w = new SpanWriter(bytes);
w.Write((byte)0xB9); // Packet ID
FeatureFlags flags = ExpansionInfo.CoreExpansion.SupportedFeatures;
if (ns.Account?.Limit >= 6)
{
flags &= ~FeatureFlags.UOTD;
flags |= FeatureFlags.LiveAccount |
(ns.Account.Limit > 6 ? FeatureFlags.SeventhCharacterSlot : FeatureFlags.SixthCharacterSlot);
}
if (ns.ExtendedSupportedFeatures)
w.Write((uint)flags);
else
w.Write((ushort)flags);
ns.Send(w.RawSpan);
}
public static void SendCharacterList(NetState ns, CityInfo[] info)
{
if (ns == null)
return;
if (ns.NewCharacterList)
SendCharacterListNew(ns, ns.Account, info);
else
SendCharacterListOld(ns, ns.Account, info);
}
public static void SendCharacterListNew(NetState ns, IAccount a, CityInfo[] info)
{
if (ns == null)
return;
int highSlot = -1;
for (int i = 0; i < a.Length; ++i)
if (a[i] != null)
highSlot = i;
int count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5);
int length = 11 + count * 60 + info.Length * 89;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xA9); // Packet ID
w.Write((short)length); // Length
w.Write((byte)count);
for (int i = 0; i < count; ++i)
if (a[i] != null)
{
w.WriteAsciiFixed(a[i].Name, 30);
w.Position += 30;
}
else
w.Position += 60;
w.Write((byte)info.Length);
for (int i = 0; i < info.Length; ++i)
{
CityInfo ci = info[i];
w.Write((byte)i);
w.WriteAsciiFixed(ci.City, 32);
w.WriteAsciiFixed(ci.Building, 32);
w.Write(ci.X);
w.Write(ci.Y);
w.Write(ci.Z);
w.Write(ci.Map.MapID);
w.Write(ci.Description);
w.Position += 4; // w.Write(0);
}
CharacterListFlags 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
w.Write((int)flags);
w.Write((short)-1);
ns.Send(w.Span);
// TODO: Razor support?
}
public static void SendCharacterListOld(NetState ns, IAccount a, CityInfo[] info)
{
if (ns == null)
return;
int highSlot = -1;
for (int i = 0; i < a.Length; ++i)
if (a[i] != null)
highSlot = i;
int count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5);
int length = 9 + count * 60 + info.Length * 63;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xA9); // Packet ID
w.Write((short)length); // Length
w.Write((byte)count);
for (int i = 0; i < count; ++i)
if (a[i] != null)
{
w.WriteAsciiFixed(a[i].Name, 30);
w.Position += 30;
}
else
w.Position += 60;
w.Write((byte)info.Length);
for (int i = 0; i < info.Length; ++i)
{
CityInfo ci = info[i];
w.Write((byte)i);
w.WriteAsciiFixed(ci.City, 31);
w.WriteAsciiFixed(ci.Building, 31);
}
CharacterListFlags 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
w.Write((int)flags);
ns.Send(w.RawSpan);
// TODO: Razor support?
}
public static void SendCharacterListUpdate(NetState ns, IAccount a)
{
if (ns == null)
return;
int highSlot = -1;
for (int i = 0; i < a.Length; ++i)
if (a[i] != null)
highSlot = i;
int count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5);
int length = 4 + count * 60;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x86); // Packet ID
w.Write((short)length); // Length
w.Write((byte)count);
for (int i = 0; i < count; ++i)
{
Mobile m = a[i];
if (m != null)
{
w.WriteAsciiFixed(m.Name, 30);
w.Position += 30;
}
else
w.Position += 60;
}
ns.Send(w.RawSpan);
}
public static void SendPlayServerAck(NetState ns, ServerInfo si)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(ns.SendPipe.Writer.GetSpan(11));
w.Write((byte)0x8C); // Packet ID
int addr = Utility.GetAddressValue(si.Address.Address);
w.Write((byte)addr);
w.Write((byte)(addr >> 8));
w.Write((byte)(addr >> 16));
w.Write((byte)(addr >> 24));
w.Write((short)si.Address.Port);
w.Write(ns.m_AuthID);
// Not compressed with Huffman
_ = ns.Flush(11);
}
public static void SendAccountLoginAck(NetState ns, ServerInfo[] info)
{
if (ns == null)
return;
int length = 6 + info.Length * 40;
SpanWriter w = new SpanWriter(ns.SendPipe.Writer.GetSpan(length));
w.Write((byte)0xA8); // Packet ID
w.Write((short)length);
w.Write((byte)0x5D); // Unknown
w.Write((ushort)info.Length);
for (int i = 0; i < info.Length; ++i)
{
ServerInfo si = info[i];
w.Write((ushort)i);
w.WriteAsciiFixed(si.Name, 32, true);
w.Write((byte)si.FullPercent);
w.Write((sbyte)si.TimeZone);
w.Write(Utility.GetAddressValue(si.Address.Address));
}
// Not compressed with Huffman
_ = ns.Flush(length);
}
}
}

View file

@ -0,0 +1,69 @@
using Server.Buffers;
namespace Server.Network
{
public static partial class Packets
{
public static void SendSetArrow(NetState ns, short x, short y)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[6]);
w.Write((byte)0xBA); // Packet ID
w.Write((byte)1);
w.Write(x);
w.Write(y);
ns.Send(w.RawSpan);
}
private static byte[] _cancelArrowPacket;
public static void SendCancelArrow(NetState ns)
{
ns?.Send(_cancelArrowPacket ??= new byte[]
{
0xBA, // Packet ID
0x00,
0xFF,
0xFF,
0xFF,
0xFF
});
}
public static void SendSetArrowHS(NetState ns, Serial s, short x, short y)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[10]);
w.Write((byte)0xBA); // Packet ID
w.Write((byte)1);
w.Write(x);
w.Write(y);
w.Write(s);
ns.Send(w.RawSpan);
}
public static void SendCancelArrowHS(NetState ns, Serial s, short x, short y)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[10]);
w.Write((byte)0xBA); // Packet ID
w.Position++;
w.Write(x);
w.Write(y);
w.Write(s);
ns.Send(w.RawSpan);
}
}
}

View file

@ -0,0 +1,168 @@
using Server.Buffers;
namespace Server.Network
{
public static partial class Packets
{
public static class AttributeNormalizer
{
public static int Maximum { get; set; } = 25;
public static bool Enabled { get; set; } = true;
public static void Write(SpanWriter w, int cur, int max)
{
if (Enabled && max != 0)
{
w.Write((short)Maximum);
w.Write((short)(cur * Maximum / max));
}
else
{
w.Write((short)max);
w.Write((short)cur);
}
}
public static void WriteReverse(SpanWriter w, int cur, int max)
{
if (Enabled && max != 0)
{
w.Write((short)(cur * Maximum / max));
w.Write((short)Maximum);
}
else
{
w.Write((short)cur);
w.Write((short)max);
}
}
}
public static void SendMobileHits(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0xA1); // Packet ID
w.Write(m.Serial);
w.Write((short)m.HitsMax);
w.Write((short)m.Hits);
ns.Send(w.RawSpan);
}
public static void SendNormalizedMobileHits(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0xA1); // Packet ID
w.Write(m.Serial);
AttributeNormalizer.Write(w, m.Hits, m.HitsMax);
ns.Send(w.RawSpan);
}
public static void SendMobileMana(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0xA2); // Packet ID
w.Write(m.Serial);
w.Write((short)m.ManaMax);
w.Write((short)m.Mana);
ns.Send(w.RawSpan);
}
public static void SendNormalizedMobileMana(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0xA2); // Packet ID
w.Write(m.Serial);
AttributeNormalizer.Write(w, m.Mana, m.ManaMax);
ns.Send(w.RawSpan);
}
public static void SendMobileStam(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0xA3); // Packet ID
w.Write(m.Serial);
w.Write((short)m.StamMax);
w.Write((short)m.Stam);
ns.Send(w.RawSpan);
}
public static void SendNormalizedMobileStam(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0xA3); // Packet ID
w.Write(m.Serial);
AttributeNormalizer.Write(w, m.Stam, m.StamMax);
ns.Send(w.RawSpan);
}
public static void SendMobileAttributes(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[17]);
w.Write((byte)0x2D); // Packet ID
w.Write(m.Serial);
w.Write((short)m.HitsMax);
w.Write((short)m.Hits);
w.Write((short)m.ManaMax);
w.Write((short)m.Mana);
w.Write((short)m.StamMax);
w.Write((short)m.Stam);
ns.Send(w.RawSpan);
}
public static void SendNormalizedMobileAttributes(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[17]);
w.Write((byte)0x2D); // Packet ID
w.Write(m.Serial);
AttributeNormalizer.Write(w, m.Hits, m.HitsMax);
AttributeNormalizer.Write(w, m.Mana, m.ManaMax);
AttributeNormalizer.Write(w, m.Stam, m.StamMax);
ns.Send(w.RawSpan);
}
}
}

View file

@ -0,0 +1,217 @@
using System;
using System.Collections.Generic;
using Server.Buffers;
namespace Server.Network
{
public static partial class Packets
{
public static void SendDisplayContainer(NetState ns, Serial s, short gumpId = -1)
{
if (ns == null)
return;
if (ns.HighSeas)
SendDisplayContainerHS(ns, s, gumpId);
else
SendDisplayContainerOld(ns, s, gumpId);
}
public static void SendDisplayContainerOld(NetState ns, Serial s, short gumpId = -1)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[7]);
w.Write((byte)0x24); // Packet ID
w.Write(s);
w.Write(gumpId);
ns.Send(w.RawSpan);
}
public static void SendDisplayContainerHS(NetState ns, Serial s, short gumpId = -1)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0x24); // Packet ID
w.Write(s);
w.Write(gumpId);
w.Write((short)0x7D);
ns.Send(w.RawSpan);
}
public static void SendContainerContentUpdate(NetState ns, Item item)
{
if (ns == null)
return;
if (ns.ContainerGridLines)
SendContainerContentUpdateNew(ns, item);
else
SendContainerContentUpdateOld(ns, item);
}
public static void SendContainerContentUpdateOld(NetState ns, Item item)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[20]);
w.Write((byte)0x25); // Packet ID
Serial parentSerial;
if (item.Parent is Item parentItem)
parentSerial = parentItem.Serial;
else
{
Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)");
parentSerial = Serial.Zero;
}
w.Write(item.Serial);
w.Write((ushort)item.ItemID);
w.Position++; // w.Write((byte)0); // signed, itemID offset
w.Write((ushort)item.Amount);
w.Write((short)item.X);
w.Write((short)item.Y);
w.Write(parentSerial);
w.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue));
ns.Send(w.RawSpan);
}
public static void SendContainerContentUpdateNew(NetState ns, Item item)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[21]);
w.Write((byte)0x25); // Packet ID
Serial parentSerial;
if (item.Parent is Item parentItem)
parentSerial = parentItem.Serial;
else
{
Console.WriteLine("Warning: ContainerContentUpdate on item with !(parent is Item)");
parentSerial = Serial.Zero;
}
w.Write(item.Serial);
w.Write((ushort)item.ItemID);
w.Position++; // signed, itemID offset
w.Write((ushort)item.Amount);
w.Write((short)item.X);
w.Write((short)item.Y);
w.Position++; // Grid Location?
w.Write(parentSerial);
w.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue));
ns.Send(w.RawSpan);
}
public static void SendContainerContent(NetState ns, Mobile beholder, Item beheld)
{
if (ns == null)
return;
if (ns.ContainerGridLines)
SendContainerContentNew(ns, beholder, beheld);
else
SendContainerContentOld(ns, beholder, beheld);
}
public static void SendContainerContentOld(NetState ns, Mobile beholder, Item beheld)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[5 + beheld.Items.Count * 19]);
w.Write((byte)0x3C); // Packet ID
w.Position += 4; // Dynamic Length, Item Count
List<Item> items = beheld.Items;
int count = items.Count;
ushort written = 0;
for (int i = 0; i < count; ++i)
{
Item child = items[i];
if (!child.Deleted && beholder.CanSee(child))
{
Point3D loc = child.Location;
w.Write(child.Serial);
w.Write((ushort)child.ItemID);
w.Position++; // signed, itemID offset
w.Write((ushort)child.Amount);
w.Write((short)loc.m_X);
w.Write((short)loc.m_Y);
w.Write(beheld.Serial);
w.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue));
++written;
}
}
w.Position = 1;
w.Write((ushort)w.WrittenCount);
w.Write(written);
ns.Send(w.Span);
}
public static void SendContainerContentNew(NetState ns, Mobile beholder, Item beheld)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[5 + beheld.Items.Count * 20]);
w.Write((byte)0x3C); // Packet ID
w.Position += 4; // Dynamic Length, Item Count
List<Item> items = beheld.Items;
int count = items.Count;
ushort written = 0;
for (int i = 0; i < count; ++i)
{
Item child = items[i];
if (!child.Deleted && beholder.CanSee(child))
{
Point3D loc = child.Location;
w.Write(child.Serial);
w.Write((ushort)child.ItemID);
w.Position++; // signed, itemID offset
w.Write((ushort)child.Amount);
w.Write((short)loc.m_X);
w.Write((short)loc.m_Y);
w.Position++; // Grid Location?
w.Write(beheld.Serial);
w.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue));
++written;
}
}
w.Position = 1;
w.Write((ushort)w.WrittenCount);
w.Write(written);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,61 @@
using Server.Buffers;
namespace Server.Network
{
public static partial class Packets
{
public static void SendDamage(NetState ns, Serial m, int amount)
{
if (ns == null)
return;
if (ns.DamagePacket)
SendDamageNew(ns, m, amount);
else
SendDamageOld(ns, m, amount);
}
public static void SendDamageOld(NetState ns, Serial m, int amount)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[11]);
w.Write((byte)0xBF); // Extended Packet ID
w.Write((ushort)11); // Length
w.Write((short)0x22);
w.Write((byte)1);
w.Write(m);
if (amount > 255)
amount = 255;
else if (amount < 0)
amount = 0;
w.Write((byte)amount);
ns.Send(w.Span);
}
public static void SendDamageNew(NetState ns, Serial m, int amount)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0xBF); // Extended Packet ID
w.Write((ushort)9); // Length
w.Write(m);
if (amount > 0xFFFF)
amount = 0xFFFF;
else if (amount < 0)
amount = 0;
w.Write((ushort)amount);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,283 @@
using Server.Buffers;
namespace Server.Network
{
public enum EffectType : byte
{
Moving,
Lightning,
FixedXYZ,
FixedFrom,
Screen
}
public enum ScreenEffectType : short
{
FadeOut,
FadeIn,
LightFlash,
FadeInOut,
DarkFlash
}
public static partial class Packets
{
private static readonly int ParticleEffectPacketLength = 49;
private static readonly int HuedEffectPacketLength = 36;
private static readonly int OldEffectPacketLength = 28;
public static void SendParticalEffect(NetState ns, EffectType type, Serial from, Serial to,
int itemId, IPoint3D fromPoint, IPoint3D toPoint, int speed, int duration, bool fixedDirection,
bool explode, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, Serial serial,
int layer, int unknown)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[ParticleEffectPacketLength]);
w.Write((byte)0xC7); // Packet ID
w.Write((byte)type);
w.Write(from);
w.Write(to);
w.Write((short)itemId);
w.Write((short)fromPoint.X);
w.Write((short)fromPoint.Y);
w.Write((sbyte)fromPoint.Z);
w.Write((short)toPoint.X);
w.Write((short)toPoint.Y);
w.Write((sbyte)toPoint.Z);
w.Write((byte)speed);
w.Write((byte)duration);
w.Position += 2;
// w.Write((byte)0);
// w.Write((byte)0);
w.Write(fixedDirection);
w.Write(explode);
w.Write(hue);
w.Write(renderMode);
w.Write((short)effect);
w.Write((short)explodeEffect);
w.Write((short)explodeSound);
w.Write(serial);
w.Write((byte)layer);
w.Write((short)unknown);
ns.Send(w.RawSpan);
}
public static void SendHuedEffect(NetState ns, EffectType type, Serial from, Serial to, int itemId,
IPoint3D fromPoint, IPoint3D toPoint, int speed, int duration, bool fixedDirection, bool explode, int hue,
int renderMode)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[HuedEffectPacketLength]);
w.Write((byte)0xC0); // Packet ID
w.Write((byte)type);
w.Write(from);
w.Write(to);
w.Write((short)itemId);
w.Write((short)fromPoint.X);
w.Write((short)fromPoint.Y);
w.Write((sbyte)fromPoint.Z);
w.Write((short)toPoint.X);
w.Write((short)toPoint.Y);
w.Write((sbyte)toPoint.Z);
w.Write((byte)speed);
w.Write((byte)duration);
w.Position += 2;
// w.Write((byte)0);
// w.Write((byte)0);
w.Write(fixedDirection);
w.Write(explode);
w.Write(hue);
w.Write(renderMode);
ns.Send(w.RawSpan);
}
public static void SendScreenEffect(NetState ns, ScreenEffectType screen)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[HuedEffectPacketLength]);
w.Write((byte)0xC0); // Packet ID
w.Write((byte)0x04);
w.Position += 8;
w.Write((short)screen);
ns.Send(w.RawSpan);
}
public static void SendScreenOldEffect(NetState ns, ScreenEffectType screen)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[OldEffectPacketLength]);
w.Write((byte)0x70); // Packet ID
w.Write((byte)0x04);
w.Position += 8;
w.Write((short)screen);
ns.Send(w.RawSpan);
}
public static void SendBoltEffect(NetState ns, IEntity target)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[HuedEffectPacketLength]);
w.Write((byte)0xC0); // Packet ID
w.Write((byte)0x01);
w.Write(target.Serial);
w.Position += 6;
w.Write((short)target.X);
w.Write((short)target.Y);
w.Write((sbyte)target.Z);
w.Write((short)target.X);
w.Write((short)target.Y);
w.Write((sbyte)target.Z);
ns.Send(w.RawSpan);
}
public static void SendOldBoltEffect(NetState ns, IEntity target)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[OldEffectPacketLength]);
w.Write((byte)0x70); // Packet ID
w.Write((byte)0x01);
w.Write(target.Serial);
w.Position += 6;
w.Write((short)target.X);
w.Write((short)target.Y);
w.Write((sbyte)target.Z);
w.Write((short)target.X);
w.Write((short)target.Y);
w.Write((sbyte)target.Z);
ns.Send(w.RawSpan);
}
public static void SendEffect(NetState ns, EffectType type, Serial from, Serial to, int itemId,
IPoint3D fromPoint, IPoint3D toPoint, int speed, int duration, bool fixedDirection, bool explode)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[HuedEffectPacketLength]);
w.Write((byte)0xC0); // Packet ID
w.Write((byte)type);
w.Write(from);
w.Write(to);
w.Write((short)itemId);
w.Write((short)fromPoint.X);
w.Write((short)fromPoint.Y);
w.Write((sbyte)fromPoint.Z);
w.Write((short)toPoint.X);
w.Write((short)toPoint.Y);
w.Write((sbyte)toPoint.Z);
w.Write((byte)speed);
w.Write((byte)duration);
w.Position += 2;
// w.Write((byte)0);
// w.Write((byte)0);
w.Write(fixedDirection);
w.Write(explode);
ns.Send(w.RawSpan);
}
public static void SendOldEffect(NetState ns, EffectType type, Serial from, Serial to, int itemId,
IPoint3D fromPoint, IPoint3D toPoint, int speed, int duration, bool fixedDirection, bool explode)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[OldEffectPacketLength]);
w.Write((byte)0x70); // Packet ID
w.Write((byte)type);
w.Write(from);
w.Write(to);
w.Write((short)itemId);
w.Write((short)fromPoint.X);
w.Write((short)fromPoint.Y);
w.Write((sbyte)fromPoint.Z);
w.Write((short)toPoint.X);
w.Write((short)toPoint.Y);
w.Write((sbyte)toPoint.Z);
w.Write((byte)speed);
w.Write((byte)duration);
w.Position += 2;
// w.Write((byte)0);
// w.Write((byte)0);
w.Write(fixedDirection);
w.Write(explode);
ns.Send(w.RawSpan);
}
public static void SendLocationParticleEffect(NetState ns, IEntity e, int itemId, int speed, int duration, int hue, int renderMode,
int effect, int unknown)
{
SendParticalEffect(ns, EffectType.FixedXYZ, e.Serial, Serial.Zero, itemId, e.Location, e.Location, speed, duration,
true, false, hue, renderMode, effect, 1, 0, e.Serial, 255, unknown);
}
public static void SendLocationHuedEffect(NetState ns, IPoint3D p, int itemId, int speed, int duration, int hue, int renderMode)
{
SendHuedEffect(ns, EffectType.FixedXYZ, Serial.Zero, Serial.Zero, itemId, p, p, speed, duration, true, false,
hue, renderMode);
}
public static void SendMovingParticleEffect(NetState ns, IEntity from, IEntity to, int itemId, int speed, int duration, bool fixedDirection,
bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, EffectLayer layer,
int unknown)
{
SendParticalEffect(ns, EffectType.Moving, from.Serial, to.Serial, itemId, from.Location, to.Location, speed,
duration, fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, Serial.Zero,
(int)layer, unknown);
}
public static void SendMovingHuedEffect(NetState ns, IEntity from, IEntity to, int itemId, int speed, int duration, bool fixedDirection,
bool explodes, int hue, int renderMode)
{
SendHuedEffect(ns, EffectType.Moving, from.Serial, to.Serial, itemId, from.Location,
to.Location, speed, duration, fixedDirection, explodes, hue, renderMode);
}
public static void SendMovingHuedEffect(NetState ns, IPoint3D from, IPoint3D to, int itemId, int speed, int duration, bool fixedDirection,
bool explodes, int hue, int renderMode)
{
SendHuedEffect(ns, EffectType.Moving, Serial.Zero, Serial.Zero, itemId, from,
to, speed, duration, fixedDirection, explodes, hue, renderMode);
}
public static void SendTargetParticleEffect(NetState ns, IEntity e, int itemId, int speed, int duration, int hue, int renderMode,
int effect, int layer, int unknown)
{
SendParticalEffect(ns, EffectType.FixedFrom, e.Serial, Serial.Zero, itemId, e.Location, e.Location,
speed, duration, true, false, hue, renderMode, effect, 1, 0, e.Serial, layer, unknown);
}
public static void SendTargetHuedEffect(NetState ns, IEntity e, int itemId, int speed, int duration, int hue, int renderMode)
{
SendHuedEffect(ns, EffectType.FixedFrom, e.Serial, Serial.Zero, itemId, e.Location, e.Location, speed, duration,
true, false, hue, renderMode);
}
}
}

View file

@ -0,0 +1,94 @@
using System.Buffers;
using System.Collections.Generic;
using Server.Buffers;
using Server.Gumps;
namespace Server.Network
{
public interface IGumpWriter
{
int TextEntries { get; set; }
int Switches { get; set; }
ArrayBufferWriter<byte> Layout { get; }
int Intern(string value);
void WriteStrings(List<string> strings);
void Flush(NetState ns);
}
public static partial class Packets
{
public static void SendCloseGump(NetState ns, int typeId, int buttonId)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[13]);
w.Write((byte)0xBF); // Packet ID
w.Write((short)13); // Length
w.Write((short)0x04); // Command
w.Write(typeId);
w.Write(buttonId);
ns.Send(w.Span);
}
public static void SendDisplayGump(NetState ns, Gump g, string layout, string[] text)
{
if (ns == null)
return;
layout ??= "";
// Assume no longer than 128 characters per text element
SpanWriter w = new SpanWriter(stackalloc byte[20 + layout.Length + 1 + (258 * text?.Length ?? 0)]);
w.Write((byte)0xB0); // Packet ID
w.Position += 2; // Dynamic Length
w.Write(g.Serial);
w.Write(g.TypeID);
w.Write(g.X);
w.Write(g.Y);
w.Write((ushort)(layout.Length + 1));
w.WriteAsciiNull(layout);
w.Write((ushort)(text?.Length ?? 0));
for (int i = 0; i < text?.Length; ++i)
{
string v = text[i] ?? "";
ushort length = (ushort)v.Length;
w.Write(length);
w.WriteBigUni(v);
}
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendDisplaySignGump(NetState ns, Serial serial, int gumpId, string unknown, string caption)
{
if (ns == null)
return;
int length = 14 + caption.Length + unknown.Length;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x8B); // Packet ID
w.Write((short)length);
w.Write(serial);
w.Write((short)gumpId);
w.Write((short)unknown.Length);
w.WriteAsciiFixed(unknown, unknown.Length);
w.Write((short)(caption.Length + 1));
w.WriteAsciiFixed(caption, caption.Length);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,330 @@
using Server.Buffers;
using Server.Items;
namespace Server.Network
{
public enum LRReason : byte
{
CannotLift,
OutOfRange,
OutOfSight,
TryToSteal,
AreHolding,
Unspecific
}
public static partial class Packets
{
public static void SendWorldItem(NetState ns, Item item)
{
if (ns == null)
return;
if (ns.HighSeas)
SendWorldItemHS(ns, item);
else if (ns.StygianAbyss)
SendWorldItemSA(ns, item);
else
SendWorldItemOld(ns, item);
}
public static void SendWorldItemOld(NetState ns, Item item)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[20]);
w.Write((byte)0x1A); // Packet ID
w.Position += 2; // Dynamic Length
uint serial = item.Serial.Value;
int itemId = item.ItemID & 0x3FFF;
int amount = item.Amount;
Point3D loc = item.Location;
int x = loc.m_X;
int y = loc.m_Y;
int hue = item.Hue;
int flags = item.GetPacketFlags();
int direction = (int)item.Direction;
if (amount != 0)
serial |= 0x80000000;
else
serial &= 0x7FFFFFFF;
w.Write(serial);
if (item is BaseMulti)
w.Write((short)(itemId | 0x4000));
else
w.Write((short)itemId);
if (amount != 0)
w.Write((short)amount);
x &= 0x7FFF;
if (direction != 0) x |= 0x8000;
w.Write((short)x);
y &= 0x3FFF;
if (hue != 0) y |= 0x8000;
if (flags != 0) y |= 0x4000;
w.Write((short)y);
if (direction != 0)
w.Write((byte)direction);
w.Write((sbyte)loc.m_Z);
if (hue != 0)
w.Write((ushort)hue);
if (flags != 0)
w.Write((byte)flags);
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendWorldItemSA(NetState ns, Item item)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[24]);
w.Write((byte)0xF3); // Packet ID
w.Write((short)0x01);
int itemId = item.ItemID;
if (item is BaseMulti)
{
w.Write((byte)0x02);
w.Write(item.Serial);
itemId &= 0x3FFF;
w.Write((short)itemId);
w.Position++; // w.Write((byte)0);
}
else
{
w.Position++; // w.Write((byte)0);
w.Write(item.Serial);
itemId &= 0x7FFF;
w.Write((short)itemId);
w.Write((byte)item.Direction);
}
short amount = (short)item.Amount;
w.Write(amount);
w.Write(amount);
Point3D loc = item.Location;
w.Write((short)(loc.m_X & 0x7FFF));
w.Write((short)(loc.m_Y & 0x3FFF));
w.Write((sbyte)loc.m_Z);
w.Write((byte)item.Light);
w.Write((short)item.Hue);
w.Write((byte)item.GetPacketFlags());
ns.Send(w.Span);
}
public static void SendWorldItemHS(NetState ns, Item item)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[26]);
w.Write((byte)0xF3); // Packet ID
w.Write((short)0x01);
int itemId = item.ItemID;
if (item is BaseMulti)
{
w.Write((byte)0x02);
w.Write(item.Serial);
itemId &= 0x3FFF;
w.Write((short)itemId);
w.Position++; // w.Write((byte)0);
}
else
{
w.Position++; // w.Write((byte)0);
w.Write(item.Serial);
itemId &= 0x7FFF;
w.Write((short)itemId);
w.Write((byte)item.Direction);
}
short amount = (short)item.Amount;
w.Write(amount);
w.Write(amount);
Point3D loc = item.Location;
w.Write((short)(loc.m_X & 0x7FFF));
w.Write((short)(loc.m_Y & 0x3FFF));
w.Write((sbyte)loc.m_Z);
w.Write((byte)item.Light);
w.Write((short)item.Hue);
w.Write((byte)item.GetPacketFlags());
w.Position += 2;
ns.Send(w.Span);
}
public static void SendLiftRej(NetState ns, LRReason reason)
{
ns?.Send(stackalloc byte[]
{
0x27, // Packet ID
(byte)reason
});
}
public static void SendSpellbookContent(NetState ns, Serial s, int count, int offset, ulong content)
{
if (ns == null)
return;
if (ObjectPropertyList.Enabled && ns.NewSpellbook)
SendSpellbookContentNew(ns, s, count, offset, content);
else if (ns.ContainerGridLines)
SendSpellbookContent6017(ns, s, count, offset, content);
else
SendSpellbookContentOld(ns, s, count, offset, content);
}
public static void SendSpellbookContentOld(NetState ns, Serial s, int count, int offset, ulong content)
{
if (ns == null)
return;
int length = 5 + count * 19;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x3C); // Packet ID
w.Write((short)length); // Length
// This should always be the same as written
w.Write((ushort)count);
ulong mask = 1;
for (int i = 0; i < 64; ++i, mask <<= 1)
if ((content & mask) != 0)
{
w.Write(0x7FFFFFFF - i);
w.Position += 3;
w.Write((ushort)(i + offset));
w.Position += 4;
w.Write(s);
w.Position += 2;
}
ns.Send(w.Span);
}
public static void SendSpellbookContent6017(NetState ns, Serial s, int count, int offset, ulong content)
{
if (ns == null)
return;
int length = 5 + count * 20;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x3C); // Packet ID
w.Write((short)length); // Length
// This should always be the same as written
w.Write((ushort)count);
ulong mask = 1;
for (int i = 0; i < 64; ++i, mask <<= 1)
if ((content & mask) != 0)
{
w.Write(0x7FFFFFFF - i);
w.Position += 3;
w.Write((ushort)(i + offset));
w.Position += 5; // Grid Location?
w.Write(s);
w.Position += 2;
}
ns.Send(w.Span);
}
public static void SendSpellbookContentNew(NetState ns, Serial s, int graphic, int offset, ulong content)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[23]);
w.Write((byte)0x3C); // Packet ID
w.Write((short)23); // Length
w.Write((short)0x1B);
w.Write((short)0x01);
w.Write(s);
w.Write((short)graphic);
w.Write((short)offset);
for (int i = 0; i < 8; ++i)
w.Write((byte)(content >> (i * 8)));
ns.Send(w.Span);
}
public static void SendDragEffect(NetState ns, IEntity src, IEntity trg, int itemID, int hue, int amount)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[26]);
w.Write((byte)0x23); // Packet ID
w.Write((short)itemID);
w.Position++; // w.Write((byte)0);
w.Write((short)hue);
w.Write((short)amount);
w.Write(src.Serial);
w.Write((short)src.X);
w.Write((short)src.Y);
w.Write((sbyte)src.Z);
w.Write(trg.Serial);
w.Write((short)trg.X);
w.Write((short)trg.Y);
w.Write((sbyte)trg.Z);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,72 @@
using Server.Buffers;
namespace Server.Network
{
public static partial class Packets
{
private static byte[] m_MapPatchesPacket;
public static void SendMapPatches(NetState ns)
{
if (ns == null)
return;
if (m_MapPatchesPacket == null)
{
SpanWriter w = new SpanWriter(m_MapPatchesPacket = new byte[57]);
w.Write((byte)0xBF); // Extended Packet ID
w.Write((ushort)57); // Dynamic Length
w.Write((short)0x18); // Map Patches
w.Write(6); // Map count
w.Write(Map.Felucca.Tiles.Patch.StaticBlocks);
w.Write(Map.Felucca.Tiles.Patch.LandBlocks);
w.Write(Map.Trammel.Tiles.Patch.StaticBlocks);
w.Write(Map.Trammel.Tiles.Patch.LandBlocks);
w.Write(Map.Ilshenar.Tiles.Patch.StaticBlocks);
w.Write(Map.Ilshenar.Tiles.Patch.LandBlocks);
w.Write(Map.Malas.Tiles.Patch.StaticBlocks);
w.Write(Map.Malas.Tiles.Patch.LandBlocks);
w.Write(Map.Tokuno.Tiles.Patch.StaticBlocks);
w.Write(Map.Tokuno.Tiles.Patch.LandBlocks);
w.Write(Map.TerMur.Tiles.Patch.StaticBlocks);
w.Write(Map.TerMur.Tiles.Patch.LandBlocks);
}
ns.Send(m_MapPatchesPacket);
}
private static readonly byte[][][] _seasonChangePackets = {
new byte[2][], new byte[2][], new byte[2][], new byte[2][], new byte[2][]
};
public static void SendSeasonChange(NetState ns, byte season, byte playSound)
{
ns?.Send(_seasonChangePackets[season][playSound] ??= new byte[]
{
0xBC, // Packet ID
season,
playSound
});
}
public static void SendMapChange(NetState ns, Map map)
{
ns?.Send(stackalloc byte[6]
{
0xBF, // Extended Packet ID
0x00,
0x06, // Length
0x00,
0x08, // Command
(byte)(map?.MapID ?? 0)
});
}
}
}

View file

@ -0,0 +1,235 @@
using System;
using Server.Buffers;
using Server.ContextMenus;
using Server.Menus;
namespace Server.Network
{
[Flags]
public enum CMEFlags
{
None = 0x00,
Disabled = 0x01,
Arrow = 0x02,
Highlighted = 0x04,
Colored = 0x20
}
public static partial class Packets
{
public static void SendDisplayItemListMenu(NetState ns, ItemListMenu menu)
{
if (ns == null)
return;
// 10 + 128 + (255 * (128 + 5))
SpanWriter w = new SpanWriter(stackalloc byte[138 + menu.Entries.Length * 133]);
w.Write((byte)0x7C); // Packet ID
w.Position += 2; // Dynamic Length
w.Write(menu.Serial);
w.Position += 2; // w.Write((short)0);
string question = menu.Question;
if (question == null)
w.Position++; // w.Write((byte)0);
else
{
int questionLength = question.Length;
w.Write((byte)questionLength);
w.WriteAsciiFixed(question, questionLength);
}
ItemListEntry[] entries = menu.Entries;
int entriesLength = (byte)entries.Length;
w.Write((byte)entriesLength);
for (int i = 0; i < entriesLength; ++i)
{
ItemListEntry e = entries[i];
w.Write((ushort)e.ItemID);
w.Write((short)e.Hue);
string name = e.Name;
if (name == null)
w.Position++; // w.Write((byte)0);
else
{
int nameLength = name.Length;
w.Write((byte)nameLength);
w.WriteAsciiFixed(name, nameLength);
}
}
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendDisplayQuestionMenu(NetState ns, QuestionMenu menu)
{
if (ns == null)
return;
// 10 + 128 + (255 * (128 + 5))
SpanWriter w = new SpanWriter(stackalloc byte[138 + menu.Answers.Length * 133]);
w.Write((byte)0x7C); // Packet ID
w.Position += 2; // Dynamic Length
w.Write(menu.Serial);
w.Position += 2; // w.Write((short)0);
string question = menu.Question;
if (question == null)
w.Position++; // w.Write((byte)0);
else
{
int questionLength = question.Length;
w.Write((byte)questionLength);
w.WriteAsciiFixed(question, questionLength);
}
string[] answers = menu.Answers;
int answersLength = (byte)answers.Length;
w.Write((byte)answersLength);
for (int i = 0; i < answersLength; ++i)
{
w.Position += 4; // w.Write(0);
string answer = answers[i];
if (answer == null)
w.Position++; // w.Write((byte)0);
else
{
int answerLength = answer.Length;
w.Write((byte)answerLength);
w.WriteAsciiFixed(answer, answerLength);
}
}
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendDisplayContextMenu(NetState ns, ContextMenu menu)
{
if (ns == null)
return;
ContextMenuEntry[] entries = menu.Entries;
int length = 12 + entries.Length * 8;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xBF); // Packet ID
w.Write((short)length); // Length
w.Write((short)0x14); // Command
w.Write((short)0x02);
IEntity target = menu.Target as IEntity;
w.Write(target?.Serial ?? Serial.MinusOne);
w.Write((byte)entries.Length);
Point3D p;
if (target is Mobile)
p = target.Location;
else if (target is Item item)
p = item.GetWorldLocation();
else
p = Point3D.Zero;
for (int i = 0; i < entries.Length; ++i)
{
ContextMenuEntry e = entries[i];
w.Write(e.Number);
w.Write((short)i);
int range = e.Range;
if (range == -1)
range = 18;
w.Write((short)(e.Flags | (e.Enabled && menu.From.InRange(p, range) ? CMEFlags.None : CMEFlags.Disabled)));
}
ns.Send(w.Span);
}
public static void SendDisplayContextMenuOld(NetState ns, ContextMenu menu)
{
if (ns == null)
return;
ContextMenuEntry[] entries = menu.Entries;
SpanWriter w = new SpanWriter(stackalloc byte[12 + entries.Length * 8]);
w.Write((byte)0xBF); // Packet ID
w.Position += 2; // Dynamic Length
w.Write((short)0x14); // Command
w.Write((short)0x01); // Old!
IEntity target = menu.Target as IEntity;
w.Write(target?.Serial ?? Serial.MinusOne);
w.Write((byte)entries.Length);
Point3D p;
if (target is Mobile)
p = target.Location;
else if (target is Item item)
p = item.GetWorldLocation();
else
p = Point3D.Zero;
for (int i = 0; i < entries.Length; ++i)
{
ContextMenuEntry e = entries[i];
w.Write((short)i);
w.Write((ushort)(e.Number - 3000000));
int range = e.Range;
if (range == -1)
range = 18;
CMEFlags flags = e.Flags | (e.Enabled && menu.From.InRange(p, range) ? CMEFlags.None : CMEFlags.Disabled);
int color = e.Color & 0xFFFF;
if (color != 0xFFFF)
flags |= CMEFlags.Colored;
w.Write((short)flags);
if ((flags & CMEFlags.Colored) != 0)
w.Write((short)color);
}
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using Server.Buffers;
namespace Server.Network
{
[Flags]
public enum AffixType : byte
{
Append = 0x00,
Prepend = 0x01,
System = 0x02
}
public static partial class Packets
{
private static readonly Dictionary<int, byte[]> _messageLocalizedPackets = new Dictionary<int, byte[]>();
public static void SendMessageLocalized(NetState ns, int number)
{
if (ns == null)
return;
if (_messageLocalizedPackets.TryGetValue(number, out byte[] packet))
{
ns.Send(packet);
return;
}
SpanWriter w = new SpanWriter(packet = new byte[50]);
w.Write((byte)0xC1); // Packet ID
w.Write((short)50); // Length
w.Write(Serial.MinusOne);
w.Write((short)-1);
w.Position++; // w.Write((byte)MessageType.Regular);
w.Write((short)0x3B2);
w.Write((short)3);
w.Write(number);
w.WriteAsciiFixed("System", 30);
_messageLocalizedPackets[number] = packet;
ns.Send(packet);
}
public static void SendMessageLocalized(NetState ns, Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "",
string args = "")
{
if (ns == null)
return;
int length = 50 + args.Length * 2;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xC1); // Packet ID
w.Write((short)length); // Length
if (hue == 0) hue = 0x3B2;
w.Write(serial);
w.Write((short)graphic);
w.Write((byte)type);
w.Write((short)hue);
w.Write((short)font);
w.Write(number);
w.WriteAsciiFixed(name ?? "", 30);
w.WriteLittleUniNull(args);
ns.Send(w.Span);
}
public static void SendMessageLocalizedAffix(NetState ns, Serial serial, int graphic, MessageType type, int hue, int font, int number,
string name = "", AffixType affixType = AffixType.System, string affix = "", string args = "")
{
if (ns == null)
return;
affix ??= "";
args ??= "";
if (hue == 0) hue = 0x3B2;
int length = 52 + affix.Length + args.Length * 2;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xCC); // Packet ID
w.Write((short)length); // Length
w.Write(serial);
w.Write((short)graphic);
w.Write((byte)type);
w.Write((short)hue);
w.Write((short)font);
w.Write(number);
w.Write((byte)affixType);
w.WriteAsciiFixed(name ?? "", 30);
w.WriteAsciiNull(affix);
w.WriteBigUniNull(args); // Not little endian? Ugh
ns.Send(w.Span);
}
// TODO: Handle IEnumerable<NetState>
public static void SendAsciiMessage(NetState ns, Serial serial, int graphic, MessageType type, int hue, int font, string name, string text)
{
if (ns == null)
return;
text ??= "";
if (hue == 0) hue = 0x3B2;
int length = 45 + text.Length;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x1C); // Packet ID
w.Write((short)length); // Length
w.Write(serial);
w.Write((short)graphic);
w.Write((byte)type);
w.Write((short)hue);
w.Write((short)font);
w.WriteAsciiFixed(name ?? "", 30);
w.WriteAsciiNull(text);
ns.Send(w.Span);
}
// TODO: Handle IEnumerable<NetState>
public static void SendUnicodeMessage(NetState ns, Serial serial, int graphic, MessageType type, int hue, int font, string lang, string name,
string text)
{
if (ns == null)
return;
if (string.IsNullOrEmpty(lang)) lang = "ENU";
name ??= "";
text ??= "";
if (hue == 0) hue = 0x3B2;
int length = 50 + text.Length * 2;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xAE); // Packet ID
w.Write((short)length); // Length
w.Write(serial);
w.Write((short)graphic);
w.Write((byte)type);
w.Write((short)hue);
w.Write((short)font);
w.WriteAsciiFixed(lang, 4);
w.WriteAsciiFixed(name, 30);
w.WriteBigUniNull(text);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,959 @@
using System;
using System.Collections.Generic;
using Server.Buffers;
using Server.Items;
namespace Server.Network
{
public static partial class Packets
{
public static void SendDeathAnimation(NetState ns, Serial killed, Serial corpse)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[13]);
w.Write((byte)0xAF); // Packet ID
w.Write(killed);
w.Write(corpse);
w.Position++; w.Write(0);
ns.Send(w.Span);
}
public static void SendStatLockInfo(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[12]);
w.Write((byte)0xBF); // Extended Packet ID
w.Write((short)12); // Length
w.Write((short)0x19); // Subcommand
w.Write((byte)2);
w.Write(m.Serial);
w.Write((short)((int)m.StrLock << 4 | (int)m.DexLock << 2 | (int)m.IntLock));
ns.Send(w.Span);
}
public static void SendBondStatus(NetState ns, Serial m, bool bonded)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[11]);
w.Write((byte)0xBF); // Extended Packet ID
w.Write((short)11); // Length
w.Write((short)0x19); // Command
w.Position++; // w.Write((byte)0); // Subcommand
w.Write(m);
w.Write(bonded);
ns.Send(w.Span);
}
public static void SendPersonalLightLevel(NetState ns, Serial m, sbyte level)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[6]);
w.Write((byte)0x4E); // Packet ID
w.Write(m);
w.Write(level);
ns.Send(w.Span);
}
public static void SendPersonalLightLevelZero(NetState ns, Serial m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[6]);
w.Write((byte)0x4E); // Packet ID
w.Write(m);
w.Position++; // level 0
ns.Send(w.Span);
}
public static void SendEquipUpdate(NetState ns, Item item)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[15]);
w.Write((byte)0x2E); // Packet ID
Serial parentSerial = Serial.Zero;
int hue = item.Hue;
if (item.Parent is Mobile parent)
{
parentSerial = parent.Serial;
if (parent.SolidHueOverride >= 0)
hue = parent.SolidHueOverride;
}
else
Console.WriteLine("Warning: EquipUpdate on item with an invalid parent");
w.Write(item.Serial);
w.Write((short)item.ItemID);
w.Write((short)item.Layer);
w.Write(parentSerial);
w.Write((short)hue);
ns.Send(w.Span);
}
public static void SendSwing(NetState ns, Serial attacker, Serial defender)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[10]);
w.Write((byte)0x2F); // Packet ID
w.Position++; // ?
w.Write(attacker);
w.Write(defender);
ns.Send(w.Span);
}
public static void SendMobileMoving(NetState ns, Mobile m, int noto)
{
if (ns == null)
return;
if (ns.StygianAbyss)
SendMobileMovingNew(ns, m, noto);
else
SendMobileMovingOld(ns, m, noto);
}
public static void SendMobileMovingNew(NetState ns, Mobile m, int noto)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[17]);
w.Write((byte)0x77); // Packet ID
Point3D loc = m.Location;
w.Write(m.Serial);
w.Write((short)m.Body);
w.Write((short)loc.m_X);
w.Write((short)loc.m_Y);
w.Write((sbyte)loc.m_Z);
w.Write((byte)m.Direction);
w.Write((short)(m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue));
w.Write((byte)m.GetPacketFlags());
w.Write((byte)noto);
ns.Send(w.Span);
}
public static void SendMobileMovingOld(NetState ns, Mobile m, int noto)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[17]);
w.Write((byte)0x77); // Packet ID
Point3D loc = m.Location;
w.Write(m.Serial);
w.Write((short)m.Body);
w.Write((short)loc.m_X);
w.Write((short)loc.m_Y);
w.Write((sbyte)loc.m_Z);
w.Write((byte)m.Direction);
w.Write((short)(m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue));
w.Write((byte)m.GetOldPacketFlags());
w.Write((byte)noto);
ns.Send(w.Span);
}
public static void SendDisplayPaperdoll(NetState ns, Mobile m, string text, bool canLift)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[66]);
w.Write((byte)0x88); // Packet ID
byte flags = 0x00;
if (m.Warmode)
flags |= 0x01;
if (canLift)
flags |= 0x02;
w.Write(m.Serial);
w.WriteAsciiFixed(text, 60);
w.Write(flags);
ns.Send(w.Span);
}
public static void SendMobileName(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[37]);
w.Write((byte)0x98); // Packet ID
w.Write((ushort)37); // Dynamic Length
w.Write(m.Serial);
w.WriteAsciiFixed(m.Name ?? "", 30);
ns.Send(w.Span);
}
public static void SendMobileAnimation(NetState ns, Mobile m, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[14]);
w.Write((byte)0x6E); // Packet ID
w.Write(m.Serial);
w.Write((short)action);
w.Write((short)frameCount);
w.Write((short)repeatCount);
w.Write(!forward); // protocol has really "reverse" but I find this more intuitive
w.Write(repeat);
w.Write((byte)delay);
ns.Send(w.Span);
}
public static void SendNewMobileAnimation(NetState ns, Mobile m, int action, int frameCount, int delay)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[10]);
w.Write((byte)0xE2); // Packet ID
w.Write(m.Serial);
w.Write((short)action);
w.Write((short)frameCount);
w.Write((byte)delay);
ns.Send(w.Span);
}
public static void SendMobileStatusCompact(NetState ns, Mobile m, bool canBeRenamed = false)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[43]);
w.Write((byte)0x11); // Packet ID
w.Write((ushort)43); // Length
w.Write(m.Serial);
w.WriteAsciiFixed(m.Name ?? "", 30);
AttributeNormalizer.WriteReverse(w, m.Hits, m.HitsMax);
w.Write(canBeRenamed);
w.Position++; // type
ns.Send(w.Span);
}
public static void SendMobileStatusExtended(NetState to, Mobile m)
{
SendMobileStatusExtended(to, m, m.NetState);
}
public static void SendMobileStatusExtended(NetState to, Mobile m, NetState ns)
{
if (to == null)
return;
byte type;
short length;
if (Core.HS && ns?.ExtendedStatus == true)
{
type = 6;
length = 121;
}
else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true)
{
type = 5;
length = 91;
}
else if (Core.AOS)
{
type = 4;
length = 88;
}
else
{
type = 3;
length = 70;
}
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x11); // Packet ID
w.Write(length); // Length
w.Write(m.Serial);
w.WriteAsciiFixed(m.Name ?? "", 30);
w.Write((short)m.Hits);
w.Write((short)m.HitsMax);
w.Write(m.CanBeRenamedBy(m));
w.Write(type);
w.Write(m.Female);
w.Write((short)m.Str);
w.Write((short)m.Dex);
w.Write((short)m.Int);
w.Write((short)m.Stam);
w.Write((short)m.StamMax);
w.Write((short)m.Mana);
w.Write((short)m.ManaMax);
w.Write(m.TotalGold);
w.Write((short)(Core.AOS ? m.PhysicalResistance : (int)(m.ArmorRating + 0.5)));
w.Write((short)(Mobile.BodyWeight + m.TotalWeight));
if (type >= 5)
{
w.Write((short)m.MaxWeight);
w.Write((byte)(m.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but...
}
w.Write((short)m.StatCap);
w.Write((byte)m.Followers);
w.Write((byte)m.FollowersMax);
if (type >= 4)
{
w.Write((short)m.FireResistance); // Fire
w.Write((short)m.ColdResistance); // Cold
w.Write((short)m.PoisonResistance); // Poison
w.Write((short)m.EnergyResistance); // Energy
w.Write((short)m.Luck); // Luck
IWeapon weapon = m.Weapon;
if (weapon != null)
{
weapon.GetStatusDamage(m, out int min, out int max);
w.Write((short)min); // Damage min
w.Write((short)max); // Damage max
}
else
w.Position += 4; // Damage min, Damage max
w.Write(m.TithingPoints);
}
if (type >= 6)
for (int i = 0; i < 15; ++i)
w.Write((short)m.GetAOSStatus(i));
to.Send(w.Span);
}
public static void SendMobileStatus(NetState to, Mobile beholder, Mobile beheld)
{
SendMobileStatus(to, beholder, beheld, beheld.NetState);
}
public static void SendMobileStatus(NetState to, Mobile beholder, Mobile beheld, NetState ns)
{
if (to == null)
return;
int type;
short length;
if (beholder != beheld)
{
type = 0;
length = 43;
}
else if (Core.HS && ns?.ExtendedStatus == true)
{
type = 6;
length = 121;
}
else if (Core.ML && ns?.SupportsExpansion(Expansion.ML) == true)
{
type = 5;
length = 91;
}
else if (Core.AOS)
{
type = 4;
length = 88;
}
else
{
type = 3;
length = 70;
}
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x11); // Packet ID
w.Write((ushort)length); // Length
w.Write(beheld.Serial);
w.WriteAsciiFixed(beheld.Name ?? "", 30);
if (beholder == beheld)
{
w.Write((short)beheld.Hits);
w.Write((short)beheld.HitsMax);
}
else
AttributeNormalizer.WriteReverse(w, beheld.Hits, beheld.HitsMax);
w.Write(beheld.CanBeRenamedBy(beholder));
w.Write((byte)type);
if (type <= 0)
return;
w.Write(beheld.Female);
w.Write((short)beheld.Str);
w.Write((short)beheld.Dex);
w.Write((short)beheld.Int);
w.Write((short)beheld.Stam);
w.Write((short)beheld.StamMax);
w.Write((short)beheld.Mana);
w.Write((short)beheld.ManaMax);
w.Write(beheld.TotalGold);
w.Write((short)(Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5)));
w.Write((short)(Mobile.BodyWeight + beheld.TotalWeight));
if (type >= 5)
{
w.Write((short)beheld.MaxWeight);
w.Write((byte)(beheld.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but...
}
w.Write((short)beheld.StatCap);
w.Write((byte)beheld.Followers);
w.Write((byte)beheld.FollowersMax);
if (type >= 4)
{
w.Write((short)beheld.FireResistance); // Fire
w.Write((short)beheld.ColdResistance); // Cold
w.Write((short)beheld.PoisonResistance); // Poison
w.Write((short)beheld.EnergyResistance); // Energy
w.Write((short)beheld.Luck); // Luck
IWeapon weapon = beheld.Weapon;
if (weapon != null)
{
weapon.GetStatusDamage(beheld, out int min, out int max);
w.Write((short)min); // Damage min
w.Write((short)max); // Damage max
}
else
w.Position += 2; // Damage min, Damage max
w.Write(beheld.TithingPoints);
}
if (type >= 6)
for (int i = 0; i < 15; ++i)
w.Write((short)beheld.GetAOSStatus(i));
to.Send(w.Span);
}
public static void SendHealthbarPoison(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[12]);
w.Write((byte)0x17); // Packet ID
w.Write((ushort)12); // Length
w.Write(m.Serial);
w.Write((short)1);
w.Write((short)1);
Poison p = m.Poison;
if (p != null)
w.Write((byte)(p.Level + 1));
else
w.Position++;
ns.Send(w.Span);
}
public static void SendHealthbarYellow(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[12]);
w.Write((byte)0x17); // Packet ID
w.Write((ushort)12); // Length
w.Write(m.Serial);
w.Write((short)1);
w.Write((short)2);
w.Write(m.Blessed || m.YellowHealthbar);
ns.Send(w.Span);
}
public static void SendMobileUpdate(NetState ns, Mobile m)
{
if (ns == null)
return;
if (ns.StygianAbyss)
SendMobileUpdateSA(ns, m);
else
SendMobileUpdateOld(ns, m);
}
public static void SendMobileUpdateSA(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[19]);
w.Write((byte)0x20); // Packet ID
int hue = m.Hue;
if (m.SolidHueOverride >= 0)
hue = m.SolidHueOverride;
w.Write(m.Serial);
w.Write((short)m.Body);
w.Position++; // w.Write((byte)0);
w.Write((short)hue);
w.Write((byte)m.GetPacketFlags());
w.Write((short)m.X);
w.Write((short)m.Y);
w.Position += 2; // w.Write((short)0);
w.Write((byte)m.Direction);
w.Write((sbyte)m.Z);
ns.Send(w.Span);
}
public static void SendMobileUpdateOld(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[19]);
w.Write((byte)0x20); // Packet ID
int hue = m.Hue;
if (m.SolidHueOverride >= 0)
hue = m.SolidHueOverride;
w.Write(m.Serial);
w.Write((short)m.Body);
w.Position++; // w.Write((byte)0);
w.Write((short)hue);
w.Write((byte)m.GetOldPacketFlags());
w.Write((short)m.X);
w.Write((short)m.Y);
w.Position += 2; // w.Write((short)0);
w.Write((byte)m.Direction);
w.Write((sbyte)m.Z);
ns.Send(w.Span);
}
public static void SendMobileIncoming(NetState ns, Mobile beholder, Mobile beheld)
{
if (ns == null)
return;
if (ns.NewMobileIncoming)
SendMobileIncomingNew(ns, beholder, beheld);
else if (ns.StygianAbyss)
SendMobileIncomingSA(ns, beholder, beheld);
else
SendMobileIncomingOld(ns, beholder, beheld);
}
public static void SendMobileIncomingNew(NetState ns, Mobile beholder, Mobile beheld)
{
if (ns == null)
return;
Span<bool> dupedLayers = stackalloc bool[256];
List<Item> eq = beheld.Items;
int count = eq.Count;
if (beheld.HairItemID > 0)
count++;
if (beheld.FacialHairItemID > 0)
count++;
SpanWriter w = new SpanWriter(stackalloc byte[23 + count * 9]);
w.Write((byte)0x78); // Packet ID
w.Position += 2; // Dynamic Length
int hue = beheld.Hue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
w.Write(beheld.Serial);
w.Write((short)beheld.Body);
w.Write((short)beheld.X);
w.Write((short)beheld.Y);
w.Write((sbyte)beheld.Z);
w.Write((byte)beheld.Direction);
w.Write((short)hue);
w.Write((byte)beheld.GetPacketFlags());
w.Write(Notoriety.Compute(beholder, beheld));
for (int i = 0; i < eq.Count; ++i)
{
Item item = eq[i];
byte layer = (byte)item.Layer;
if (!item.Deleted && beholder.CanSee(item) && !dupedLayers[layer])
{
dupedLayers[layer] = true;
hue = item.Hue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
w.Write(item.Serial);
w.Write((ushort)(item.ItemID & 0xFFFF));
w.Write(layer);
w.Write((short)hue);
}
}
if (beheld.HairItemID > 0 && !dupedLayers[(int)Layer.Hair])
{
hue = beheld.HairHue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
w.Write(HairInfo.FakeSerial(beheld.Serial));
w.Write((ushort)(beheld.HairItemID & 0xFFFF));
w.Write((byte)Layer.Hair);
w.Write((short)hue);
}
if (beheld.FacialHairItemID > 0 && !dupedLayers[(int)Layer.FacialHair])
{
hue = beheld.FacialHairHue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
w.Write(FacialHairInfo.FakeSerial(beheld.Serial));
w.Write((ushort)(beheld.FacialHairItemID & 0xFFFF));
w.Write((byte)Layer.FacialHair);
w.Write((short)hue);
}
w.Position += 4; // w.Write(0)
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendMobileIncomingSA(NetState ns, Mobile beholder, Mobile beheld)
{
if (ns == null)
return;
Span<bool> dupedLayers = stackalloc bool[256];
List<Item> eq = beheld.Items;
int count = eq.Count;
if (beheld.HairItemID > 0)
count++;
if (beheld.FacialHairItemID > 0)
count++;
SpanWriter w = new SpanWriter(stackalloc byte[23 + count * 9]);
w.Write((byte)0x78); // Packet ID
w.Position += 2; // Dynamic Length
int hue = beheld.Hue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
w.Write(beheld.Serial);
w.Write((short)beheld.Body);
w.Write((short)beheld.X);
w.Write((short)beheld.Y);
w.Write((sbyte)beheld.Z);
w.Write((byte)beheld.Direction);
w.Write((short)hue);
w.Write((byte)beheld.GetPacketFlags());
w.Write(Notoriety.Compute(beholder, beheld));
for (int i = 0; i < eq.Count; ++i)
{
Item item = eq[i];
byte layer = (byte)item.Layer;
if (!item.Deleted && beholder.CanSee(item) && !dupedLayers[layer])
{
dupedLayers[layer] = true;
hue = item.Hue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
int itemId = item.ItemID & 0x7FFF;
bool writeHue = hue != 0;
if (writeHue)
itemId |= 0x8000;
w.Write(item.Serial);
w.Write((ushort)itemId);
w.Write(layer);
if (writeHue)
w.Write((short)hue);
}
}
if (beheld.HairItemID > 0 && !dupedLayers[(int)Layer.Hair])
{
hue = beheld.HairHue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
int itemId = beheld.HairItemID & 0x7FFF;
bool writeHue = hue != 0;
if (writeHue)
itemId |= 0x8000;
w.Write(HairInfo.FakeSerial(beheld.Serial));
w.Write((ushort)itemId);
w.Write((byte)Layer.Hair);
if (writeHue)
w.Write((short)hue);
}
if (beheld.FacialHairItemID > 0 && !dupedLayers[(int)Layer.FacialHair])
{
hue = beheld.FacialHairHue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
int itemId = beheld.FacialHairItemID & 0x7FFF;
bool writeHue = hue != 0;
if (writeHue)
itemId |= 0x8000;
w.Write(FacialHairInfo.FakeSerial(beheld.Serial));
w.Write((ushort)itemId);
w.Write((byte)Layer.FacialHair);
if (writeHue)
w.Write((short)hue);
}
w.Position += 4; // w.Write(0)
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendMobileIncomingOld(NetState ns, Mobile beholder, Mobile beheld)
{
if (ns == null)
return;
Span<bool> dupedLayers = stackalloc bool[256];
List<Item> eq = beheld.Items;
int count = eq.Count;
if (beheld.HairItemID > 0)
count++;
if (beheld.FacialHairItemID > 0)
count++;
SpanWriter w = new SpanWriter(stackalloc byte[23 + count * 9]);
w.Write((byte)0x78); // Packet ID
w.Position += 2; // Dynamic Length
int hue = beheld.Hue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
w.Write(beheld.Serial);
w.Write((short)beheld.Body);
w.Write((short)beheld.X);
w.Write((short)beheld.Y);
w.Write((sbyte)beheld.Z);
w.Write((byte)beheld.Direction);
w.Write((short)hue);
w.Write((byte)beheld.GetOldPacketFlags());
w.Write(Notoriety.Compute(beholder, beheld));
for (int i = 0; i < eq.Count; ++i)
{
Item item = eq[i];
byte layer = (byte)item.Layer;
if (!item.Deleted && beholder.CanSee(item) && !dupedLayers[layer])
{
dupedLayers[layer] = true;
hue = item.Hue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
int itemId = item.ItemID & 0x7FFF;
bool writeHue = hue != 0;
if (writeHue)
itemId |= 0x8000;
w.Write(item.Serial);
w.Write((ushort)itemId);
w.Write(layer);
if (writeHue)
w.Write((short)hue);
}
}
if (beheld.HairItemID > 0 && !dupedLayers[(int)Layer.Hair])
{
hue = beheld.HairHue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
int itemId = beheld.HairItemID & 0x7FFF;
bool writeHue = hue != 0;
if (writeHue)
itemId |= 0x8000;
w.Write(HairInfo.FakeSerial(beheld.Serial));
w.Write((ushort)itemId);
w.Write((byte)Layer.Hair);
if (writeHue)
w.Write((short)hue);
}
if (beheld.FacialHairItemID > 0 && !dupedLayers[(int)Layer.FacialHair])
{
hue = beheld.FacialHairHue;
if (beheld.SolidHueOverride >= 0)
hue = beheld.SolidHueOverride;
int itemId = beheld.FacialHairItemID & 0x7FFF;
bool writeHue = hue != 0;
if (writeHue)
itemId |= 0x8000;
w.Write(FacialHairInfo.FakeSerial(beheld.Serial));
w.Write((ushort)itemId);
w.Write((byte)Layer.FacialHair);
if (writeHue)
w.Write((short)hue);
}
w.Position += 4; // w.Write(0)
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendFollowMessage(NetState ns, Serial s1, Serial s2)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0x15); // Packet ID
w.Write(s1);
w.Write(s2);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,550 @@
using System;
using Server.Buffers;
namespace Server.Network
{
public static partial class Packets
{
public static void SendObjectHelpResponse(NetState ns, Serial e, string text)
{
if (ns == null)
return;
text ??= "";
int length = 9 + text.Length * 2;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte) 0xB7); // Extended Packet ID
w.Write((ushort) length); // Length
w.Write(e);
w.WriteBigUniNull(text);
ns.Send(w.Span);
}
public static void SendChangeUpdateRange(NetState ns, byte range = 18)
{
ns?.Send(stackalloc byte[]
{
0xC8, // Packet ID
range
});
}
public static void SendChangeCombatant(NetState ns, Serial combatant)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[5]);
w.Write((byte)0xAA); // Packet ID
w.Write(combatant);
ns.Send(w.Span);
}
public static void SendDisplayHuePicker(NetState ns, Serial s, int itemId)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0x95); // Packet ID
w.Write(s);
w.Position += 2; // w.Write((short)0);
w.Write((short)itemId);
ns.Send(w.Span);
}
public static void SendUnicodePrompt(NetState ns, Serial message)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[21]);
w.Write((byte)0xC2); // Packet ID
w.Write((short)21); // Length
w.Write(message); // Should be Player serial?
w.Write(message);
w.Position += 10;
ns.Send(w.Span);
}
public static void SendDeathStatus_Dead(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0x2C, // Packet ID
0x00
});
}
public static void SendDeathStatus_Alive(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0x2C, // Packet ID
0x02 // Why not 1?
});
}
private static readonly byte[][] _speedControlPackets = new byte[3][];
public static void SendSpeedControlDisabled(NetState ns)
{
ns?.Send(_speedControlPackets[0] ??= new byte[]
{
0xBF, // Extended Packet ID
0x00,
0x06, // Length
0x26,
0x00 // Disabled
});
}
public static void SendSpeedControlMount(NetState ns)
{
ns?.Send(_speedControlPackets[1] ??= new byte[]
{
0xBF, // Extended Packet ID
0x00,
0x06, // Length
0x26,
0x01 // Mount
});
}
public static void SendSpeedControlWalk(NetState ns)
{
ns?.Send(_speedControlPackets[2] ??= new byte[]
{
0xBF, // Extended Packet ID
0x00,
0x06, // Length
0x26,
0x02 // Walk
});
}
public static void SendToggleSpecialAbility(NetState ns, short ability, bool active)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[8]);
w.Write((byte)0xBF); // Packet ID
w.Write((short)8); // Length
w.Write((short)0x25); // Command
w.Write(ability);
w.Write(active);
ns.Send(w.Span);
}
public static void SendGlobalLightLevel(NetState ns, sbyte level)
{
ns?.Send(stackalloc byte[]
{
0x4F, // Packet ID
(byte)level
});
}
public static void SendLogoutAck(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0xD1, // Packet ID
0x01
});
}
public static void SendWeather(NetState ns, byte type, byte density, sbyte temperature)
{
ns?.Send(stackalloc byte[]
{
0x65, // Packet ID
type,
density,
(byte)temperature
});
}
public static void SendPlayerMove(NetState ns, Direction d)
{
ns?.Send(stackalloc byte[]
{
0x97, // Packet ID
(byte)d
});
}
public static void SendClientVersionReq(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0xBD, // Packet ID
0x00,
0x03
});
}
public static void SendSetWarMode(NetState ns, bool warmode)
{
if (warmode)
SendInWarMode(ns);
else
SendInPeaceMode(ns);
}
public static void SendInWarMode(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0x72, // Packet ID
0x01, // War mode
0x00,
0x32, // ?
0x00
});
}
public static void SendInPeaceMode(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0x72, // Packet ID
0x00, // Peace mode
0x00,
0x32, // ?
0x00
});
}
public static void SendRemoveEntity(NetState ns, Serial entity)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[5]);
w.Write((byte)0x1D); // Packet ID
w.Write(entity);
ns.Send(w.Span);
}
public static void SendServerChange(NetState ns, Mobile m, Map map)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[16]);
w.Write((byte)0x76); // Packet ID
w.Write((short)m.X);
w.Write((short)m.Y);
w.Write((short)m.Z);
w.Position += 5;
w.Write((short)map.Width);
w.Write((short)map.Height);
ns.Send(w.Span);
}
public static void SendSkillsUpdate(NetState ns, Skills skills)
{
if (ns == null)
return;
int length = 6 + skills.Length * 9;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x3A); // Packet ID
w.Write((short)length); // Length
w.Write((byte)0x02); // type: absolute, capped
for (int i = 0; i < skills.Length; ++i)
{
Skill s = skills[i];
double v = s.NonRacialValue;
int uv = (int)(v * 10);
if (uv < 0)
uv = 0;
else if (uv >= 0x10000)
uv = 0xFFFF;
w.Write((ushort)(s.Info.SkillID + 1));
w.Write((ushort)uv);
w.Write((ushort)s.BaseFixedPoint);
w.Write((byte)s.Lock);
w.Write((ushort)s.CapFixedPoint);
}
w.Position += 2;
ns.Send(w.Span);
}
public static void SendSkillChange(NetState ns, Skill skill)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[13]);
w.Write((byte)0x3A); // Packet ID
w.Write((short)13); // Length
double v = skill.NonRacialValue;
int uv = (int)(v * 10);
if (uv < 0)
uv = 0;
else if (uv >= 0x10000)
uv = 0xFFFF;
w.Write((byte)0xDF); // type: delta, capped
w.Write((ushort)skill.Info.SkillID);
w.Write((ushort)uv);
w.Write((ushort)skill.BaseFixedPoint);
w.Write((byte)skill.Lock);
w.Write((ushort)skill.CapFixedPoint);
ns.Send(w.Span);
}
public static void SendLaunchBrowser(NetState ns, string url)
{
if (ns == null)
return;
url ??= "";
int length = 4 + url.Length;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xA5); // Packet ID
w.Write((short)length); // Length
w.WriteAsciiNull(url);
ns.Send(w.Span);
}
// TODO: Optimize for IEnumerable<NetState>
public static void SendPlaySound(NetState ns, int soundId, IPoint3D target)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[12]);
w.Write((byte)0x54); // Packet ID
w.Write((byte)1); // flags
w.Write((short)soundId);
w.Position += 2; // volume?
w.Write((short)target.X);
w.Write((short)target.Y);
w.Write((short)target.Z);
ns.Send(w.Span);
}
public static void SendPlayRepeatingSound(NetState ns, int soundId, IPoint3D target)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[12]);
w.Write((byte)0x54); // Packet ID
w.Position++; // flags
w.Write((short)soundId);
w.Position += 2; // volume?
w.Write((short)target.X);
w.Write((short)target.Y);
w.Write((short)target.Z);
ns.Send(w.Span);
}
public static void SendPlayMusic(NetState ns, MusicName music)
{
ushort value = (ushort)music;
ns?.Send(stackalloc byte[]
{
0x6D, // Packet ID
(byte)(value >> 8),
(byte)(value & 0xFF)
});
}
public static void SendStopMusic(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0x6D, // Packet ID
0x1F,
0xFF
});
}
public static void SendScrollMessage(NetState ns, int type, int tip, string text)
{
if (ns == null)
return;
text ??= "";
int length = 10 + text.Length;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xA6); // Packet ID
w.Write((short)length); // Length
w.Write((byte)type);
w.Write(tip);
w.Write((ushort)text.Length);
w.WriteAsciiFixed(text, text.Length);
ns.Send(w.Span);
}
public static void SendCurrentTime(NetState ns)
{
if (ns == null)
return;
// TODO: Don't call UtcNow so readily.
DateTime now = DateTime.UtcNow;
ns.Send(stackalloc byte[]
{
0x5B, // Packet ID
(byte)now.Hour,
(byte)now.Minute,
(byte)now.Second
});
}
public static void SendPathfindMessage(NetState ns, IPoint3D p)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[7]);
w.Write((byte)0x38); // Packet ID
w.Write((short)p.X);
w.Write((short)p.Y);
w.Write((short)p.Z);
ns.Send(w.Span);
}
public static void SendPingAck(NetState ns, byte ping)
{
ns?.Send(stackalloc byte[2]
{
0x73, // Packet ID
ping
});
}
public static void SendMovementRej(NetState ns, int seq, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[8]);
w.Write((byte)0x21); // Packet ID
w.Write((byte)seq);
w.Write((short)m.X);
w.Write((short)m.Y);
w.Write((byte)m.Direction);
w.Write((sbyte)m.Z);
ns.Send(w.Span);
}
public static void SendMovementAck(NetState ns, Mobile m)
{
if (ns == null)
return;
SendMovementAck(ns, ns.Sequence, Notoriety.Compute(m, m));
}
public static void SendMovementAck(NetState ns, byte seq, byte noto)
{
ns?.Send(stackalloc byte[3]
{
0x22,
seq,
noto
});
}
public static void SendLoginConfirm(NetState ns, Mobile m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[37]);
w.Write((byte)0x1B); // Packet ID
w.Write(m.Serial);
w.Position++;
w.Write((short)m.Body);
w.Write((short)m.X);
w.Write((short)m.Y);
w.Write((short)m.Z);
w.Write((byte)m.Direction);
w.Position++;
w.Write(-1);
Map map = m.Map;
if (map == null || map == Map.Internal)
map = m.LogoutMap;
w.Position += 4;
w.Write((short)(map?.Width ?? 6144));
w.Write((short)(map?.Height ?? 4096));
w.Position += 9;
ns.Send(w.Span);
}
public static void SendLoginComplete(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0x55 // Packet ID
});
}
public static void SendClearWeaponAbility(NetState ns)
{
ns?.Send(stackalloc byte[]
{
0xBF, // Extended Packet ID
0x00,
0x05, // Length
0x21, // Command
0x00
});
}
}
}

View file

@ -0,0 +1,29 @@
using Server.Buffers;
namespace Server.Network
{
public static partial class Packets
{
public static void SendDisplayProfile(NetState ns, Serial m, string header, string body, string footer)
{
if (ns == null)
return;
header ??= "";
body ??= "";
footer ??= "";
int length = 12 + header.Length + footer.Length * 2 + body.Length * 2;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0xB8); // Packet ID
w.Write((short)length); // Length
w.Write(m);
w.WriteAsciiNull(header);
w.WriteBigUniNull(footer);
w.WriteBigUniNull(body);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,81 @@
using Server.Buffers;
using Server.Targeting;
namespace Server.Network
{
public static partial class Packets
{
public static void SendMultiTargetReqHS(NetState ns, MultiTarget t)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[30]);
w.Write((byte)0x99); // Packet ID
w.Write(t.AllowGround);
w.Write(t.TargetID);
w.Write((byte)t.Flags);
w.Position += 11;
w.Write((short)t.MultiID);
w.Write((short)t.Offset.X);
w.Write((short)t.Offset.Y);
w.Write((short)t.Offset.Z);
// 4 bytes unknown?
ns.Send(w.Span);
}
public static void SendMultiTargetReq(NetState ns, MultiTarget t)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[26]);
w.Write((byte)0x99); // Packet ID
w.Write(t.AllowGround);
w.Write(t.TargetID);
w.Write((byte)t.Flags);
w.Position += 14;
w.Write((short)t.MultiID);
w.Write((short)t.Offset.X);
w.Write((short)t.Offset.Y);
w.Write((short)t.Offset.Z);
ns.Send(w.Span);
}
private static byte[] _targetReqPacket;
public static void SendCancelTarget(NetState ns)
{
if (ns == null)
return;
if (_targetReqPacket == null)
{
_targetReqPacket = new byte[19];
_targetReqPacket[0] = 0x6C; // Packet ID
_targetReqPacket[6] = 0x03; // ?
}
ns.Send(_targetReqPacket);
}
public static void SendTargetReq(NetState ns, Target t)
{
if (ns == null || t == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[19]);
w.Write((byte)0x6C); // Packet ID
w.Write(t.AllowGround);
w.Write(t.TargetID);
w.Write((byte)t.Flags);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,139 @@
using Server.Buffers;
namespace Server.Network
{
public enum TradeFlag : byte
{
Display = 0x0,
Close = 0x1,
Update = 0x2,
UpdateGold = 0x3,
UpdateLedger = 0x4
}
public static partial class Packets
{
public static void SendDisplaySecureTrade(NetState ns, Serial them, Serial firstCont, Serial secondCont, string name = "")
{
if (ns == null)
return;
name ??= "";
int length = 17 + name.Length;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x6F); // Packet ID
w.Write((short)length); // Length
w.Position++; // Display
w.Write(them);
w.Write(firstCont);
w.Write(secondCont);
w.Write(name.Length > 0);
w.WriteAscii(name);
ns.Send(w.Span);
}
public static void SendCloseSecureTrade(NetState ns, Serial cont)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[8]);
w.Write((byte)0x6F); // Packet ID
w.Write((ushort)8); // Length
w.Write((byte)1); // Close
w.Write(cont);
ns.Send(w.Span);
}
public static void SendUpdateSecureTrade(NetState ns, Serial cont, bool fromAccepted, bool toAccepted)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[10]);
w.Write((byte)0x6F); // Packet ID
w.Write((ushort)10); // Length
w.Write((byte)TradeFlag.Update);
w.Write(cont);
w.Write(fromAccepted);
w.Write(toAccepted);
ns.Send(w.Span);
}
public static void SendUpdateSecureTrade(NetState ns, Serial cont, TradeFlag flag, int first, int second)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[16]);
w.Write((byte)0x6F); // Packet ID
w.Write((ushort)16); // Length
w.Write((byte)flag);
w.Write(cont);
w.Write(first);
w.Write(second);
ns.Send(w.Span);
}
public static void SendSecureTradeEquip(NetState ns, Item item, Serial m)
{
if (ns == null)
return;
if (ns.ContainerGridLines)
SendSecureTradeEquipNew(ns, item, m);
else
SendSecureTradeEquipOld(ns, item, m);
}
public static void SendSecureTradeEquipOld(NetState ns, Item item, Serial m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[20]);
w.Write((byte)0x25); // Packet ID
w.Write(item.Serial);
w.Write((short)item.ItemID);
w.Position++; // Write((byte)0)
w.Write((short)item.Amount);
w.Write((short)item.X);
w.Write((short)item.Y);
w.Write(m);
w.Write((short)item.Hue);
ns.Send(w.Span);
}
public static void SendSecureTradeEquipNew(NetState ns, Item item, Serial m)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[21]);
w.Write((byte)0x25); // Packet ID
w.Write(item.Serial);
w.Write((short)item.ItemID);
w.Position++; // Write((byte)0)
w.Write((short)item.Amount);
w.Write((short)item.X);
w.Write((short)item.Y);
w.Write(m);
w.Position++; // Write((byte)0) Grid Location?
w.Write((short)item.Hue);
ns.Send(w.Span);
}
}
}

View file

@ -0,0 +1,207 @@
using System.Collections.Generic;
using Server.Buffers;
using Server.Items;
namespace Server.Network
{
public static partial class Packets
{
public static void SendVendorBuyContent(NetState ns, IList<BuyItemState> list)
{
if (ns == null)
return;
if (ns.ContainerGridLines)
SendVendorBuyContentNew(ns, list);
else
SendVendorBuyContentOld(ns, list);
}
public static void SendVendorBuyContentOld(NetState ns, IList<BuyItemState> list)
{
if (ns == null)
return;
int length = 5 + list.Count * 19;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x3C); // Packet ID
w.Write((ushort)length); // Length
w.Write((short)list.Count);
//The client sorts these by their X/Y value.
//OSI sends these in weird order. X/Y highest to lowest and serial lowest to highest
//These are already sorted by serial (done by the vendor class) but we have to send them by x/y
//(the x74 packet is sent in 'correct' order.)
for (int i = list.Count - 1; i >= 0; --i)
{
BuyItemState bis = list[i];
w.Write(bis.MySerial);
w.Write((ushort)bis.ItemID);
w.Position++; // Write((byte)0); itemid offset
w.Write((ushort)bis.Amount);
w.Write((short)(i + 1)); //x
w.Write((short)1); //y
w.Write(bis.ContainerSerial);
w.Write((ushort)bis.Hue);
}
ns.Send(w.RawSpan);
}
public static void SendVendorBuyContentNew(NetState ns, IList<BuyItemState> list)
{
if (ns == null)
return;
int length = 5 + list.Count * 20;
SpanWriter w = new SpanWriter(stackalloc byte[length]);
w.Write((byte)0x3C); // Packet ID
w.Write((ushort)length); // Length
w.Write((short)list.Count);
//The client sorts these by their X/Y value.
//OSI sends these in weird order. X/Y highest to lowest and serial lowest to highest
//These are already sorted by serial (done by the vendor class) but we have to send them by x/y
//(the x74 packet is sent in 'correct' order.)
for (int i = list.Count - 1; i >= 0; --i)
{
BuyItemState bis = list[i];
w.Write(bis.MySerial);
w.Write((ushort)bis.ItemID);
w.Position++; // Write((byte)0); itemid offset
w.Write((ushort)bis.Amount);
w.Write((short)(i + 1)); //x
w.Write((short)1); //y
w.Position++; // Write((byte)0); Grid Location?
w.Write(bis.ContainerSerial);
w.Write((ushort)bis.Hue);
}
ns.Send(w.RawSpan);
}
public static void SendDisplayBuyList(NetState ns, Serial vendor)
{
if (ns == null)
return;
if (ns.HighSeas)
SendDisplayBuyListHS(ns, vendor);
else
SendDisplayBuyListOld(ns, vendor);
}
public static void SendDisplayBuyListOld(NetState ns, Serial vendor)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[7]);
w.Write((byte)0x24); // Packet ID
w.Write(vendor);
w.Write((short)0x30); // buy window id?
ns.Send(w.RawSpan);
}
public static void SendDisplayBuyListHS(NetState ns, Serial vendor)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[9]);
w.Write((byte)0x24); // Packet ID
w.Write(vendor);
w.Write((short)0x30); // buy window id?
//w.Write((short)0x00); // Unknown
ns.Send(w.RawSpan);
}
public static void SendVendorBuyList(NetState ns, Mobile vendor, IList<BuyItemState> list)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[8 + 135 * list.Count]);
w.Write((byte)0x74); // Packet ID
w.Position += 2; // Dynamic Length
w.Write(!(vendor.FindItemOnLayer(Layer.ShopBuy) is Container BuyPack) ? Serial.MinusOne : BuyPack.Serial);
w.Write((byte)list.Count);
for (int i = 0; i < list.Count; ++i)
{
BuyItemState bis = list[i];
w.Write(bis.Price);
string desc = bis.Description ?? "";
w.Write((byte)(desc.Length + 1));
w.WriteAsciiNull(desc);
}
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendVendorSellList(NetState ns, Serial shopkeeper, IList<SellItemState> list)
{
if (ns == null)
return;
ushort count = (ushort)list.Count;
SpanWriter w = new SpanWriter(stackalloc byte[9 + 140 * count]);
w.Write((byte)0x9E); // Packet ID
w.Position += 2; //( Dynamic Length
w.Write(shopkeeper);
w.Write(count);
for (int i = 0; i < count; ++i)
{
SellItemState state = list[i];
w.Write(state.Item.Serial);
w.Write((ushort)state.Item.ItemID);
w.Write((ushort)state.Item.Hue);
w.Write((ushort)state.Item.Amount);
w.Write((ushort)state.Price);
string name = state.Item.Name;
if (name == null || (name = name.Trim()).Length <= 0)
name = state.Name ?? "";
w.Write((ushort)name.Length);
w.WriteAsciiFixed(name, (ushort)name.Length);
}
w.Position = 1;
w.Write((ushort)w.WrittenCount);
ns.Send(w.Span);
}
public static void SendEndVendorBuyOrSell(NetState ns, Serial vendor)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[8]);
w.Write((byte)0x3B); // Packet ID
w.Write(vendor);
ns.Send(w.RawSpan);
}
}
}

View file

@ -0,0 +1,78 @@
using Server.Buffers;
using Server.Items;
namespace Server.Network
{
public static partial class Packets
{
public static void SendHairEquipUpdate(NetState ns, Mobile parent)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[15]);
w.Write((byte)0x2E); // Packet ID
int hue = parent.HairHue;
if (parent.SolidHueOverride >= 0)
hue = parent.SolidHueOverride;
w.Write(HairInfo.FakeSerial(parent.Serial));
w.Write((short)parent.HairItemID);
w.Write((short)Layer.Hair);
w.Write(parent.Serial);
w.Write((short)hue);
ns.Send(w.Span);
}
public static void SendFacialHairEquipUpdate(NetState ns, Mobile parent)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[15]);
w.Write((byte)0x2E); // Packet ID
int hue = parent.FacialHairHue;
if (parent.SolidHueOverride >= 0)
hue = parent.SolidHueOverride;
w.Write(FacialHairInfo.FakeSerial(parent.Serial));
w.Write((short)parent.FacialHairItemID);
w.Write((short)Layer.Hair);
w.Write(parent.Serial);
w.Write((short)hue);
ns.Send(w.Span);
}
public static void SendRemoveHair(NetState ns, Mobile parent)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[5]);
w.Write((byte)0x1D); // Packet ID
w.Write(HairInfo.FakeSerial(parent.Serial));
ns.Send(w.Span);
}
public static void SendRemoveFacialHair(NetState ns, Mobile parent)
{
if (ns == null)
return;
SpanWriter w = new SpanWriter(stackalloc byte[5]);
w.Write((byte)0x1D); // Packet ID
w.Write(FacialHairInfo.FakeSerial(parent.Serial));
ns.Send(w.Span);
}
}
}