Revert "Updates Packets & Randomizer (#43)" (#61)

This reverts commit 179cb50557.
This commit is contained in:
Kamron Batman 2019-11-11 08:46:11 -08:00 committed by GitHub
parent 179cb50557
commit c2ecc76457
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
588 changed files with 16260 additions and 10926 deletions

View file

@ -76,7 +76,7 @@ namespace Server.Network
DisplayListener();
while (!Core.Closing)
while (true)
{
Socket s;
try
@ -90,7 +90,7 @@ namespace Server.Network
}
if (VerifySocket(s))
new NetState(s, pump);
_ = new NetState(s, pump);
else
Release(s);
}
@ -105,9 +105,12 @@ namespace Server.Network
{
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in adapters)
foreach (UnicastIPAddressInformation unicast in adapter.GetIPProperties().UnicastAddresses)
if (ipep.AddressFamily == unicast.Address.AddressFamily)
Console.WriteLine("Listening: {0}:{1}", unicast.Address, ipep.Port);
{
IPInterfaceProperties properties = adapter.GetIPProperties();
foreach (UnicastIPAddressInformation unicast in properties.UnicastAddresses)
if (ipep.AddressFamily == unicast.Address.AddressFamily)
Console.WriteLine("Listening: {0}:{1}", unicast.Address, ipep.Port);
}
}
else
{

View file

@ -26,13 +26,14 @@ using System.IO;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Server.Accounting;
using Server.Diagnostics;
using Server.Gumps;
using Server.HuePickers;
using Server.Items;
using Server.Menus;
using NetworkPackets = Server.Network.Packets;
namespace Server.Network
{
@ -92,6 +93,7 @@ namespace Server.Network
{
private string m_ToString;
private ClientVersion m_Version;
private SendQueue<Packet> m_SendQueue = new SendQueue<Packet>();
public DateTime ConnectedOn { get; }
@ -109,8 +111,6 @@ namespace Server.Network
private static AsyncState m_AsyncState = m_ResumeState;
public bool UseCompression { get; set; }
public IPacketEncoder PacketEncoder { get; set; }
public static NetStateCreatedCallback CreatedCallback { get; set; }
@ -158,8 +158,7 @@ namespace Server.Network
ProtocolChanges = ProtocolChanges.Version500a;
else if (value >= m_Version407a)
ProtocolChanges = ProtocolChanges.Version407a;
else if (value >= m_Version400a)
ProtocolChanges = ProtocolChanges.Version400a;
else if (value >= m_Version400a) ProtocolChanges = ProtocolChanges.Version400a;
}
}
@ -273,7 +272,9 @@ namespace Server.Network
public Socket Socket { get; private set; }
public byte Sequence { get; set; }
public bool CompressionEnabled { get; set; }
public int Sequence { get; set; }
public List<Gump> Gumps { get; private set; }
@ -299,7 +300,8 @@ namespace Server.Network
public void AddMenu(IMenu menu)
{
Menus ??= new List<IMenu>();
if (Menus == null)
Menus = new List<IMenu>();
if (Menus.Count < MenuCap)
Menus.Add(menu);
@ -327,7 +329,8 @@ namespace Server.Network
public void AddHuePicker(HuePicker huePicker)
{
HuePickers ??= new List<HuePicker>();
if (HuePickers == null)
HuePickers = new List<HuePicker>();
if (HuePickers.Count < HuePickerCap)
HuePickers.Add(huePicker);
@ -355,7 +358,8 @@ namespace Server.Network
public void AddGump(Gump gump)
{
Gumps ??= new List<Gump>();
if (Gumps == null)
Gumps = new List<Gump>();
if (Gumps.Count < GumpCap)
Gumps.Add(gump);
@ -383,8 +387,8 @@ namespace Server.Network
public void LaunchBrowser(string url)
{
NetworkPackets.SendMessageLocalized(this, Serial.MinusOne, -1, MessageType.Label, 0x35, 3, 501231);
NetworkPackets.SendLaunchBrowser(this, url);
Send(new MessageLocalized(Serial.MinusOne, -1, MessageType.Label, 0x35, 3, 501231, "", ""));
Send(new LaunchBrowser(url));
}
public CityInfo[] CityInfo { get; set; }
@ -442,6 +446,17 @@ namespace Server.Network
m_AsyncState = Interlocked.Exchange(ref m_AsyncState, m_ResumeState);
}
public virtual void Send(Packet p)
{
if (Socket == null || BlockAllPackets)
{
p.OnSend();
return;
}
m_SendQueue.Enqueue(p);
}
public bool CheckEncrypted(int packetID)
{
if (!SentFirstPacket && packetID != 0xF0 && packetID != 0xF1 && packetID != 0xCF && packetID != 0x80 &&
@ -455,35 +470,32 @@ namespace Server.Network
return false;
}
public Pipe RecvdPipe { get; private set; }
public Pipe SendPipe { get; private set; }
private Pipe m_RecvdPipe;
private async Task Start(MessagePump pump)
{
RecvdPipe = new Pipe();
SendPipe = new Pipe();
m_RecvdPipe = new Pipe();
Running = true;
await Task.WhenAll(HandlePackets(pump), ProcessSends(), ProcessRecvs());
await Task.WhenAll(ProcessSends(), ProcessRecvs(), HandlePackets(pump)).ConfigureAwait(false);
}
private async Task ProcessRecvs()
{
PipeWriter w = RecvdPipe.Writer;
PipeWriter w = m_RecvdPipe.Writer;
while (!Core.Closing)
while (true)
{
if (m_AsyncState.Paused)
{
Interlocked.Exchange(ref m_NextCheckActivity, Core.TickCount + 90000);
await Timer.Pause(50);
await Timer.Pause(50).ConfigureAwait(false);
continue;
}
try
{
Memory<byte> memory = w.GetMemory();
int bytesRead = await Socket.ReceiveAsync(memory, SocketFlags.None);
int bytesRead = await Socket.ReceiveAsync(memory, SocketFlags.None).ConfigureAwait(false);
if (bytesRead == 0)
break;
@ -491,7 +503,7 @@ namespace Server.Network
w.Advance(bytesRead);
FlushResult result = await w.FlushAsync();
FlushResult result = await w.FlushAsync().ConfigureAwait(false);
if (result.IsCompleted || result.IsCanceled)
break;
}
@ -505,7 +517,7 @@ namespace Server.Network
Dispose();
}
private async Task HandlePackets(MessagePump pump)
private async Task ProcessSends()
{
while (true)
try
@ -514,31 +526,26 @@ namespace Server.Network
if (p == null)
break;
long pos = PacketHandlers.ProcessPacket(pump, this, seq);
ReadOnlyMemory<byte> buffer = p.Compile(CompressionEnabled, out int length);
if (pos <= 0)
break;
if (buffer.Length <= 0 || length <= 0)
{
p.OnSend();
return;
}
pr.AdvanceTo(seq.GetPosition(pos, seq.Start));
buffer = buffer.Slice(0, length);
if (result.IsCompleted || result.IsCanceled)
break;
}
PacketSendProfile prof = null;
pr.Complete();
}
if (Core.Profiling)
prof = PacketSendProfile.Acquire(p.GetType());
public async Task Flush(int bytesWritten)
{
if (IsDisposing || BlockAllPackets)
return;
prof?.Start();
SendPipe.Writer.Advance(bytesWritten);
FlushResult result = await SendPipe.Writer.FlushAsync();
await Socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false);
if (result.IsCompleted || result.IsCanceled)
Dispose();
}
p.OnSend();
prof?.Finish(length);
}
@ -555,44 +562,29 @@ namespace Server.Network
}
}
private async Task ProcessSends()
private async Task HandlePackets(MessagePump pump)
{
PipeReader pr = SendPipe.Reader;
PipeReader pr = m_RecvdPipe.Reader;
while (!Core.Closing)
while (true)
{
if (m_AsyncState.Paused)
{
await Timer.Pause(50);
continue;
}
ReadResult result = await pr.ReadAsync();
ReadOnlySequence<byte> seq = result.Buffer;
if (seq.Length == 0)
break;
if (!SequenceMarshal.TryGetReadOnlyMemory(seq, out ReadOnlyMemory<byte> memory))
long pos = PacketHandlers.ProcessPacket(pump, this, seq);
if (pos <= 0)
break;
try
{
int bytesWritten = await Socket.SendAsync(memory, SocketFlags.None);
pr.AdvanceTo(seq.GetPosition(bytesWritten));
}
catch (SocketException ex)
{
Console.WriteLine(ex);
TraceException(ex);
break;
}
pr.AdvanceTo(seq.GetPosition(pos, seq.Start));
if (result.IsCompleted || result.IsCanceled)
break;
}
Dispose();
pr.Complete();
}
public PacketHandler GetHandler(int packetID) =>
@ -647,17 +639,17 @@ namespace Server.Network
Task.Run(async () =>
{
RecvdPipe.Reader.CancelPendingRead();
RecvdPipe.Reader.Complete();
await SendPipe.Writer.FlushAsync();
SendPipe.Writer.Complete();
m_RecvdPipe.Reader.CancelPendingRead();
m_RecvdPipe.Reader.Complete();
await m_RecvdPipe.Writer.FlushAsync().ConfigureAwait(false);
m_RecvdPipe.Writer.Complete();
try { Socket.Shutdown(SocketShutdown.Both); } catch (Exception ex) { TraceException(ex); }
try { Socket.Close(); } catch (Exception ex) { TraceException(ex); }
Socket = null;
RecvdPipe = null;
SendPipe = null;
m_RecvdPipe = null;
m_SendQueue = null;
m_Disposed.Enqueue(this);
});
}

View file

@ -0,0 +1,267 @@
/***************************************************************************
* Packet.cs
* -------------------
* begin : August 2, 2019
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Diagnostics;
using System;
using System.Buffers;
using System.Diagnostics;
using System.IO;
namespace Server.Network
{
public abstract class Packet
{
private const int CompressorBufferSize = 0x10000;
private const int BufferSize = 4096;
private byte[] m_CompiledBuffer;
private int m_CompiledLength;
private int m_Length;
private State m_State;
protected PacketWriter m_Stream;
protected Packet(int packetID)
{
PacketID = packetID;
if (Core.Profiling)
{
PacketSendProfile prof = PacketSendProfile.Acquire(GetType());
prof.Increment();
}
}
protected Packet(int packetID, int length)
{
PacketID = packetID;
m_Length = length;
m_Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
m_Stream.Write((byte)packetID);
if (Core.Profiling)
{
PacketSendProfile prof = PacketSendProfile.Acquire(GetType());
prof.Increment();
}
}
public int PacketID { get; }
public PacketWriter UnderlyingStream => m_Stream;
public void EnsureCapacity(int length)
{
m_Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
m_Stream.Write((byte)PacketID);
m_Stream.Write((short)0);
}
public static Packet SetStatic(Packet p)
{
p.SetStatic();
return p;
}
public static Packet Acquire(Packet p)
{
p.Acquire();
return p;
}
public static void Release(ref Packet p)
{
p?.Release();
p = null;
}
public static void Release(Packet p)
{
p?.Release();
}
public void SetStatic()
{
m_State |= State.Static | State.Acquired;
}
public void Acquire()
{
m_State |= State.Acquired;
}
public void OnSend()
{
Core.Set(); // Is this still needed if this is done async?
if ((m_State & (State.Acquired | State.Static)) == 0)
Free();
}
private void Free()
{
if (m_CompiledBuffer == null)
return;
if ((m_State & State.Buffered) != 0)
ArrayPool<byte>.Shared.Return(m_CompiledBuffer);
m_State &= ~(State.Static | State.Acquired | State.Buffered);
m_CompiledBuffer = null;
}
public void Release()
{
if ((m_State & State.Acquired) != 0)
Free();
}
public byte[] Compile(bool compress, out int length)
{
lock (this)
{
if (m_CompiledBuffer == null)
{
if ((m_State & State.Accessed) == 0)
{
m_State |= State.Accessed;
}
else
{
if ((m_State & State.Warned) == 0)
{
m_State |= State.Warned;
try
{
using StreamWriter op = new StreamWriter("net_opt.log", true);
op.WriteLine("Redundant compile for packet {0}, use Acquire() and Release()", GetType());
op.WriteLine(new StackTrace());
}
catch
{
// ignored
}
}
m_CompiledBuffer = new byte[0];
m_CompiledLength = 0;
length = m_CompiledLength;
return m_CompiledBuffer;
}
InternalCompile(compress);
}
length = m_CompiledLength;
return m_CompiledBuffer;
}
}
private void InternalCompile(bool compress)
{
if (m_Length == 0)
{
long streamLen = m_Stream.Length;
m_Stream.Seek(1, SeekOrigin.Begin);
m_Stream.Write((ushort)streamLen);
}
else if (m_Stream.Length != m_Length)
{
int diff = (int)m_Stream.Length - m_Length;
Console.WriteLine("Packet: 0x{0:X2}: Bad packet length! ({1}{2} bytes)", PacketID, diff >= 0 ? "+" : "",
diff);
}
MemoryStream ms = m_Stream.UnderlyingStream;
m_CompiledBuffer = ms.GetBuffer();
int length = (int)ms.Length;
if (compress)
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(CompressorBufferSize);
Compression.Compress(m_CompiledBuffer, 0, length, buffer, out length);
if (length <= 0)
{
Console.WriteLine("Warning: Compression buffer overflowed on packet 0x{0:X2} ('{1}') (length={2})",
PacketID, GetType().Name, length);
using StreamWriter op = new StreamWriter("compression_overflow.log", true);
op.WriteLine("{0} Warning: Compression buffer overflowed on packet 0x{1:X2} ('{2}') (length={3})",
DateTime.UtcNow, PacketID, GetType().Name, length);
op.WriteLine(new StackTrace());
}
else
{
m_CompiledLength = length;
if ((m_State & State.Static) != 0)
{
m_CompiledBuffer = new byte[length];
Buffer.BlockCopy(buffer, 0, m_CompiledBuffer, 0, length);
ArrayPool<byte>.Shared.Return(buffer);
}
else
{
m_CompiledBuffer = buffer;
m_State |= State.Buffered;
}
}
}
else if (length > 0)
{
byte[] old = m_CompiledBuffer;
m_CompiledLength = length;
if ((m_State & State.Static) != 0)
m_CompiledBuffer = new byte[length];
else
{
m_CompiledBuffer = ArrayPool<byte>.Shared.Rent(length);
m_State |= State.Buffered;
}
Buffer.BlockCopy(old, 0, m_CompiledBuffer, 0, length);
}
PacketWriter.ReleaseInstance(m_Stream);
m_Stream = null;
}
[Flags]
private enum State
{
Inactive = 0x00,
Static = 0x01,
Acquired = 0x02,
Accessed = 0x04,
Buffered = 0x08,
Warned = 0x10
}
}
}

View file

@ -26,6 +26,7 @@ using Server.Accounting;
using Server.ContextMenus;
using Server.Diagnostics;
using Server.Gumps;
using Server.HuePickers;
using Server.Items;
using Server.Menus;
using Server.Prompts;
@ -55,6 +56,11 @@ namespace Server.Network
public static class PacketHandlers
{
public delegate void PlayCharCallback(NetState state, bool val);
private const int BadFood = unchecked((int)0xBAADF00D);
private const int BadUOTD = unchecked((int)0xFFCEFFCE);
private const int m_AuthIDWindowSize = 128;
private static PacketHandler[] m_6017Handlers;
@ -103,32 +109,43 @@ namespace Server.Network
Register(0x01, 5, false, Disconnect);
Register(0x02, 7, true, MovementReq);
Register(0x03, 0, true, AsciiSpeech);
Register(0x04, 2, true, GodModeRequest);
Register(0x05, 5, true, AttackReq);
Register(0x06, 5, true, UseReq);
Register(0x07, 7, true, LiftReq);
Register(0x08, 14, true, DropReq);
Register(0x09, 5, true, LookReq);
Register(0x0A, 11, true, Edit);
Register(0x12, 0, true, TextCommand);
Register(0x13, 10, true, EquipReq);
Register(0x14, 6, true, ChangeZ);
Register(0x22, 3, true, Resynchronize);
Register(0x2C, 2, true, DeathStatusResponse);
Register(0x34, 10, true, MobileQuery);
Register(0x3A, 0, true, ChangeSkillLock);
Register(0x3B, 0, true, VendorBuyReply);
Register(0x47, 11, true, NewTerrain);
Register(0x48, 73, true, NewAnimData);
Register(0x58, 106, true, NewRegion);
Register(0x5D, 73, false, PlayCharacter);
Register(0x61, 9, true, DeleteStatic);
Register(0x6C, 19, true, TargetResponse);
Register(0x6F, 0, true, SecureTrade);
Register(0x72, 5, true, SetWarMode);
Register(0x73, 2, false, PingReq);
Register(0x75, 35, true, RenameRequest);
Register(0x79, 9, true, ResourceQuery);
Register(0x7E, 2, true, GodviewQuery);
Register(0x7D, 13, true, MenuResponse);
Register(0x80, 62, false, AccountLogin);
Register(0x83, 39, false, DeleteCharacter);
Register(0x91, 65, false, GameLogin);
Register(0x95, 9, true, HuePickerResponse);
Register(0x96, 0, true, GameCentralMoniter);
Register(0x98, 0, true, MobileNameRequest);
Register(0x9A, 0, true, AsciiPromptResponse);
Register(0x9B, 258, true, HelpRequest);
Register(0x9D, 51, true, GMSingle);
Register(0x9F, 0, true, VendorSellReply);
Register(0xA0, 3, false, PlayServer);
Register(0xA4, 149, false, SystemInfo);
@ -144,6 +161,8 @@ namespace Server.Network
Register(0xBF, 0, true, ExtendedCommand);
Register(0xC2, 0, true, UnicodePromptResponse);
Register(0xC8, 2, true, SetUpdateRange);
Register(0xC9, 6, true, TripTime);
Register(0xCA, 6, true, UTripTime);
Register(0xCF, 0, false, AccountLogin);
Register(0xD0, 0, true, ConfigurationFile);
Register(0xD1, 2, true, LogoutReq);
@ -195,7 +214,9 @@ namespace Server.Network
public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive)
{
Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive);
if (m_6017Handlers[packetID] == null)
m_6017Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
}
public static PacketHandler GetHandler(int packetID) => Handlers[packetID];
@ -321,8 +342,6 @@ namespace Server.Network
return -1;
}
// Console.WriteLine("Packet Handler {0:X}", handler.PacketID);
long packetLength = handler.Length;
if (handler.Length <= 0 && r.Length >= 3)
{
@ -394,9 +413,9 @@ namespace Server.Network
public static void EncodedCommand(NetState state, PacketReader pvSrc)
{
IEntity e = World.FindEntity(pvSrc.ReadUInt32());
int packetId = pvSrc.ReadUInt16();
int packetID = pvSrc.ReadUInt16();
EncodedPacketHandler ph = GetEncodedHandler(packetId);
EncodedPacketHandler ph = GetEncodedHandler(packetID);
if (ph != null)
{
@ -404,7 +423,7 @@ namespace Server.Network
{
Console.WriteLine(
"Client: {0}: Sent ingame packet (0xD7x{1:X2}) before having been attached to a mobile", state,
packetId);
packetID);
state.Dispose();
}
else if (ph.Ingame && state.Mobile.Deleted)
@ -517,30 +536,37 @@ namespace Server.Network
if (vendor == null)
return;
if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10) || flag != 0x02)
if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
{
Packets.SendEndVendorBuyOrSell(state, vendor.Serial);
state.Send(new EndVendorBuy(vendor));
return;
}
msgSize -= 1 + 2 + 4 + 1;
if (msgSize / 7 > 100)
return;
List<BuyItemResponse> buyList = new List<BuyItemResponse>(msgSize / 7);
while (msgSize > 0)
if (flag == 0x02)
{
pvSrc.ReadByte(); // layer
Serial serial = pvSrc.ReadUInt32();
int amount = pvSrc.ReadInt16();
msgSize -= 1 + 2 + 4 + 1;
buyList.Add(new BuyItemResponse(serial, amount));
msgSize -= 7;
if (msgSize / 7 > 100)
return;
List<BuyItemResponse> buyList = new List<BuyItemResponse>(msgSize / 7);
while (msgSize > 0)
{
byte layer = pvSrc.ReadByte();
Serial serial = pvSrc.ReadUInt32();
int amount = pvSrc.ReadInt16();
buyList.Add(new BuyItemResponse(serial, amount));
msgSize -= 7;
}
if (buyList.Count > 0 && vendor is IVendor v && v.OnBuyItems(state.Mobile, buyList))
state.Send(new EndVendorBuy(vendor));
}
else
{
state.Send(new EndVendorBuy(vendor));
}
if (buyList.Count > 0 && vendor is IVendor v && v.OnBuyItems(state.Mobile, buyList))
Packets.SendEndVendorBuyOrSell(state, vendor.Serial);
}
public static void VendorSellReply(NetState state, PacketReader pvSrc)
@ -552,7 +578,7 @@ namespace Server.Network
if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
{
Packets.SendEndVendorBuyOrSell(state, vendor.Serial);
state.Send(new EndVendorSell(vendor));
return;
}
@ -564,14 +590,14 @@ namespace Server.Network
for (int i = 0; i < count; i++)
{
Item item = World.FindItem(pvSrc.ReadUInt32());
int amount = pvSrc.ReadInt16();
int Amount = pvSrc.ReadInt16();
if (item != null && amount > 0)
sellList.Add(new SellItemResponse(item, amount));
if (item != null && Amount > 0)
sellList.Add(new SellItemResponse(item, Amount));
}
if (sellList.Count > 0 && vendor is IVendor v && v.OnSellItems(state.Mobile, sellList))
Packets.SendEndVendorBuyOrSell(state, vendor.Serial);
state.Send(new EndVendorSell(vendor));
}
}
@ -583,6 +609,39 @@ namespace Server.Network
EventSink.InvokeDeleteRequest(new DeleteRequestEventArgs(state, index));
}
public static void ResourceQuery(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
{
}
}
public static void GameCentralMoniter(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
{
int type = pvSrc.ReadByte();
int num1 = pvSrc.ReadInt32();
Console.WriteLine("God Client: {0}: Game central moniter", state);
Console.WriteLine(" - Type: {0}", type);
Console.WriteLine(" - Number: {0}", num1);
pvSrc.Trace(state);
}
}
public static void GodviewQuery(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state)) Console.WriteLine("God Client: {0}: Godview query 0x{1:X}", state, pvSrc.ReadByte());
}
public static void GMSingle(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
pvSrc.Trace(state);
}
public static void DeathStatusResponse(NetState state, PacketReader pvSrc)
{
// Ignored
@ -618,7 +677,7 @@ namespace Server.Network
Mobile m = World.FindMobile(pvSrc.ReadUInt32());
if (m != null && Utility.InUpdateRange(state.Mobile, m) && state.Mobile.CanSee(m))
Packets.SendMobileName(state, m);
state.Send(new MobileName(m));
}
public static void RequestScrollWindow(NetState state, PacketReader pvSrc)
@ -655,6 +714,34 @@ namespace Server.Network
}
}
public static void TripTime(NetState state, PacketReader pvSrc)
{
int unk1 = pvSrc.ReadByte();
int unk2 = pvSrc.ReadInt32();
state.Send(new TripTimeResponse(unk1));
}
public static void UTripTime(NetState state, PacketReader pvSrc)
{
int unk1 = pvSrc.ReadByte();
int unk2 = pvSrc.ReadInt32();
state.Send(new UTripTimeResponse(unk1));
}
public static void ChangeZ(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
{
int x = pvSrc.ReadInt16();
int y = pvSrc.ReadInt16();
int z = pvSrc.ReadSByte();
Console.WriteLine("God Client: {0}: Change Z ({1}, {2}, {3})", state, x, y, z);
}
}
public static void SystemInfo(NetState state, PacketReader pvSrc)
{
int v1 = pvSrc.ReadByte();
@ -671,6 +758,83 @@ namespace Server.Network
int v8 = pvSrc.ReadInt32();
}
public static void Edit(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
{
int type = pvSrc.ReadByte(); // 10 = static, 7 = npc, 4 = dynamic
int x = pvSrc.ReadInt16();
int y = pvSrc.ReadInt16();
int id = pvSrc.ReadInt16();
int z = pvSrc.ReadSByte();
int hue = pvSrc.ReadUInt16();
Console.WriteLine("God Client: {0}: Edit {6} ({1}, {2}, {3}) 0x{4:X} (0x{5:X})", state, x, y, z, id, hue,
type);
}
}
public static void DeleteStatic(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
{
int x = pvSrc.ReadInt16();
int y = pvSrc.ReadInt16();
int z = pvSrc.ReadInt16();
int id = pvSrc.ReadUInt16();
Console.WriteLine("God Client: {0}: Delete Static ({1}, {2}, {3}) 0x{4:X}", state, x, y, z, id);
}
}
public static void NewAnimData(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
{
Console.WriteLine("God Client: {0}: New tile animation", state);
pvSrc.Trace(state);
}
}
public static void NewTerrain(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
{
int x = pvSrc.ReadInt16();
int y = pvSrc.ReadInt16();
int id = pvSrc.ReadUInt16();
int width = pvSrc.ReadInt16();
int height = pvSrc.ReadInt16();
Console.WriteLine("God Client: {0}: New Terrain ({1}, {2})+({3}, {4}) 0x{5:X4}", state, x, y, width, height,
id);
}
}
public static void NewRegion(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
{
string name = pvSrc.ReadString(40);
int unk = pvSrc.ReadInt32();
int x = pvSrc.ReadInt16();
int y = pvSrc.ReadInt16();
int width = pvSrc.ReadInt16();
int height = pvSrc.ReadInt16();
int zStart = pvSrc.ReadInt16();
int zEnd = pvSrc.ReadInt16();
string desc = pvSrc.ReadString(40);
int soundFX = pvSrc.ReadInt16();
int music = pvSrc.ReadInt16();
int nightFX = pvSrc.ReadInt16();
int dungeon = pvSrc.ReadByte();
int light = pvSrc.ReadInt16();
Console.WriteLine("God Client: {0}: New Region '{1}' ('{2}')", state, name, desc);
}
}
public static void AccountID(NetState state, PacketReader pvSrc)
{
}
@ -806,6 +970,12 @@ namespace Server.Network
}
}
public static void GodModeRequest(NetState state, PacketReader pvSrc)
{
if (VerifyGC(state))
state.Send(new GodModeReply(pvSrc.ReadBoolean()));
}
public static void AsciiPromptResponse(NetState state, PacketReader pvSrc)
{
uint serial = pvSrc.ReadUInt32();
@ -1030,12 +1200,14 @@ namespace Server.Network
public static void LogoutReq(NetState state, PacketReader pvSrc)
{
Packets.SendLogoutAck(state);
state.Send(new LogoutAck());
}
public static void ChangeSkillLock(NetState state, PacketReader pvSrc)
{
state.Mobile.Skills[pvSrc.ReadInt16()]?.SetLockNoRelay((SkillLock)pvSrc.ReadByte());
Skill s = state.Mobile.Skills[pvSrc.ReadInt16()];
s?.SetLockNoRelay((SkillLock)pvSrc.ReadByte());
}
public static void HelpRequest(NetState state, PacketReader pvSrc)
@ -1046,13 +1218,13 @@ namespace Server.Network
public static void TargetResponse(NetState state, PacketReader pvSrc)
{
int type = pvSrc.ReadByte();
int targetId = pvSrc.ReadInt32();
int targetID = pvSrc.ReadInt32();
int flags = pvSrc.ReadByte();
Serial serial = pvSrc.ReadUInt32();
int x = pvSrc.ReadInt16(), y = pvSrc.ReadInt16(), z = pvSrc.ReadInt16();
int graphic = pvSrc.ReadUInt16();
if (targetId == unchecked((int)0xDEADBEEF))
if (targetID == unchecked((int)0xDEADBEEF))
return;
Mobile from = state.Mobile;
@ -1072,7 +1244,7 @@ namespace Server.Network
// User pressed escape
t.Cancel(from, TargetCancelType.Canceled);
}
else if (t.TargetID != targetId)
else if (t.TargetID != targetID)
{
// Sanity, prevent fake target
}
@ -1095,28 +1267,42 @@ namespace Server.Network
t.Cancel(from, TargetCancelType.Canceled);
return;
}
StaticTile[] tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis);
if (state.HighSeas)
else
{
ItemData id = TileData.ItemTable[graphic & TileData.MaxItemValue];
if (id.Surface) z -= id.Height;
}
StaticTile[] tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis);
if (tiles.All(tile => tile.Z != z || tile.ID != graphic))
{
t.Cancel(from, TargetCancelType.Canceled);
return;
}
bool valid = false;
toTarget = new StaticTarget(new Point3D(x, y, z), graphic);
if (state.HighSeas)
{
ItemData id = TileData.ItemTable[graphic & TileData.MaxItemValue];
if (id.Surface) z -= id.Height;
}
for (int i = 0; !valid && i < tiles.Length; ++i)
if (tiles[i].Z == z && tiles[i].ID == graphic)
valid = true;
if (!valid)
{
t.Cancel(from, TargetCancelType.Canceled);
return;
}
else
{
toTarget = new StaticTarget(new Point3D(x, y, z), graphic);
}
}
}
}
else if (serial.IsMobile)
{
toTarget = World.FindMobile(serial);
}
else if (serial.IsItem)
{
toTarget = World.FindItem(serial);
}
else
{
t.Cancel(from, TargetCancelType.Canceled);
@ -1252,8 +1438,13 @@ namespace Server.Network
{
Mobile m = state.Mobile;
Packets.SendMobileUpdate(state, m);
Packets.SendMobileIncoming(state, m, m);
if (state.StygianAbyss)
state.Send(new MobileUpdate(m));
else
state.Send(new MobileUpdateOld(m));
state.Send(MobileIncoming.Create(state, m, m));
m.SendEverything();
state.Sequence = 0;
@ -1305,24 +1496,24 @@ namespace Server.Network
for (int i = 0; i < count; ++i)
{
int speechId;
int speechID;
if ((i & 1) == 0)
{
hold <<= 8;
hold |= pvSrc.ReadByte();
speechId = hold;
speechID = hold;
hold = 0;
}
else
{
value = pvSrc.ReadInt16();
speechId = (value & 0xFFF0) >> 4;
speechID = (value & 0xFFF0) >> 4;
hold = value & 0xF;
}
if (!keyList.Contains(speechId))
keyList.Add(speechId);
if (!keyList.Contains(speechID))
keyList.Add(speechID);
}
text = pvSrc.ReadUTF8StringSafe();
@ -1359,7 +1550,9 @@ namespace Server.Network
uint value = pvSrc.ReadUInt32();
if ((value & ~0x7FFFFFFF) != 0)
{
from.OnPaperdollRequest();
}
else
{
Serial s = value;
@ -1383,7 +1576,9 @@ namespace Server.Network
from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
}
else
{
from.SendActionMessage();
}
}
public static void LookReq(NetState state, PacketReader pvSrc)
@ -1399,7 +1594,9 @@ namespace Server.Network
if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
{
if (SingleClickProps)
{
m.OnAosSingleClick(from);
}
else
{
if (from.Region.OnSingleClick(from, m))
@ -1415,7 +1612,9 @@ namespace Server.Network
Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
{
if (SingleClickProps)
{
item.OnAosSingleClick(from);
}
else if (from.Region.OnSingleClick(from, item))
{
if (item.Parent is Item item1)
@ -1429,25 +1628,25 @@ namespace Server.Network
public static void PingReq(NetState state, PacketReader pvSrc)
{
Packets.SendPingAck(state, pvSrc.ReadByte());
state.Send(PingAck.Instantiate(pvSrc.ReadByte()));
}
public static void SetUpdateRange(NetState state, PacketReader pvSrc)
{
Packets.SendChangeUpdateRange(state);
state.Send(ChangeUpdateRange.Instantiate(18));
}
public static void MovementReq(NetState state, PacketReader pvSrc)
{
Direction dir = (Direction)pvSrc.ReadByte();
byte seq = pvSrc.ReadByte();
pvSrc.ReadInt32(); // key
int seq = pvSrc.ReadByte();
int key = pvSrc.ReadInt32();
Mobile m = state.Mobile;
if (state.Sequence == 0 && seq != 0 || !m.Move(dir))
{
Packets.SendMovementRej(state, seq, m);
state.Send(new MovementRej(seq, m));
state.Sequence = 0;
m.ClearFastwalkStack();
@ -1456,7 +1655,7 @@ namespace Server.Network
{
++seq;
if (seq == 0)
if (seq == 256)
seq = 1;
state.Sequence = seq;
@ -1489,7 +1688,7 @@ namespace Server.Network
{
int packetID = pvSrc.ReadUInt16();
// Console.WriteLine("Extended Packet: {0:X}", packetID);
Console.WriteLine("Extended Packet: {0:X}", packetID);
PacketHandler ph = GetExtendedHandler(packetID);
if (ph == null)
@ -1863,6 +2062,13 @@ namespace Server.Network
if (m != null)
switch (type)
{
case 0x00: // Unknown, sent by godclient
{
if (VerifyGC(state))
Console.WriteLine("God Client: {0}: Query 0x{1:X2} on {2} '{3}'", state, type, m.Serial, m.Name);
break;
}
case 0x04: // Stats
{
m.OnStatsQuery(from);
@ -1887,17 +2093,47 @@ namespace Server.Network
string name = pvSrc.ReadString(30);
pvSrc.ReadInt16(); // Unknown
pvSrc.Seek(2, SeekOrigin.Current);
int flags = pvSrc.ReadInt32();
pvSrc.Seek(24, SeekOrigin.Current);
/* if (FeatureProtection.DisabledFeatures != 0 && ThirdPartyAuthCallback != null)
{
bool authOK = false;
ulong razorFeatures = ((ulong)pvSrc.ReadUInt32() << 32) | pvSrc.ReadUInt32();
if (razorFeatures == (ulong)FeatureProtection.DisabledFeatures)
{
bool doesNotMatch = false;
for (int i = 0; !doesNotMatch && i < m_ThirdPartyAuthKey.Length; i++)
doesNotMatch = pvSrc.ReadByte() != m_ThirdPartyAuthKey[i];
if (!doesNotMatch)
authOK = true;
}
else
{
pvSrc.Seek(16, SeekOrigin.Current);
}
ThirdPartyAuthCallback(state, authOK);
}*/
/* else
{*/
pvSrc.Seek(24, SeekOrigin.Current);
/* }*/
/* if (ThirdPartyHackedCallback != null)
{
pvSrc.Seek(-2, SeekOrigin.Current);
if (pvSrc.ReadUInt16() == 0xDEAD)
ThirdPartyHackedCallback(state, true);
}*/
if (!state.Running)
return;
int charSlot = pvSrc.ReadInt32();
// TODO: Handle IP address.
int clientIP = pvSrc.ReadInt32();
IAccount a = state.Account;
@ -1916,7 +2152,7 @@ namespace Server.Network
if (check != null && check.Map != Map.Internal && check != m)
{
Console.WriteLine("Login: {0}: Account in use", state);
Packets.SendPopupMessage(state, PMMessage.CharInWorld);
state.Send(new PopupMessage(PMMessage.CharInWorld));
return;
}
}
@ -1931,9 +2167,11 @@ namespace Server.Network
// TODO: Make this wait one tick so we don't have to call it unnecessarily
NetState.ProcessDisposedQueue();
Packets.SendClientVersionReq(state);
state.Send(new ClientVersionReq());
state.BlockAllPackets = true;
state.Flags = (ClientFlags)flags;
state.Mobile = m;
@ -1945,38 +2183,93 @@ namespace Server.Network
public static void DoLogin(NetState state, Mobile m)
{
Packets.SendLoginConfirm(state, m);
state.Send(new LoginConfirm(m));
if (m.Map != null)
Packets.SendMapChange(state, m.Map);
state.Send(new MapChange(m));
Packets.SendMapPatches(state);
Packets.SendSeasonChange(state, m.GetSeason(), 1);
Packets.SendSupportedFeatures(state);
state.Send(new MapPatches());
state.Send(SeasonChange.Instantiate(m.GetSeason(), true));
state.Send(SupportedFeatures.Instantiate(state));
state.Sequence = 0;
// TODO: Verify if we need this twice
Packets.SendMobileUpdate(state, m);
m.CheckLightLevels(true);
Packets.SendMobileUpdate(state, m);
if (state.NewMobileIncoming)
{
state.Send(new MobileUpdate(m));
state.Send(new MobileUpdate(m));
Packets.SendMobileIncoming(state, m, m);
Packets.SendMobileStatus(state, m, m);
Packets.SendSetWarMode(state, m.Warmode);
m.CheckLightLevels(true);
m.SendEverything();
state.Send(new MobileUpdate(m));
Packets.SendSupportedFeatures(state);
Packets.SendMobileUpdate(state, m);
Packets.SendMobileStatus(state, m, m);
Packets.SendSetWarMode(state, m.Warmode);
Packets.SendMobileIncoming(state, m, m);
state.Send(new MobileIncoming(m, m));
//state.Send( new MobileAttributes( m ) );
state.Send(new MobileStatus(m, m));
state.Send(Network.SetWarMode.Instantiate(m.Warmode));
Packets.SendLoginComplete(state);
Packets.SendCurrentTime(state);
Packets.SendMapChange(state, m.Map);
Packets.SendSeasonChange(state, m.GetSeason(), 1);
m.SendEverything();
state.Send(SupportedFeatures.Instantiate(state));
state.Send(new MobileUpdate(m));
//state.Send( new MobileAttributes( m ) );
state.Send(new MobileStatus(m, m));
state.Send(Network.SetWarMode.Instantiate(m.Warmode));
state.Send(new MobileIncoming(m, m));
}
else if (state.StygianAbyss)
{
state.Send(new MobileUpdate(m));
state.Send(new MobileUpdate(m));
m.CheckLightLevels(true);
state.Send(new MobileUpdate(m));
state.Send(new MobileIncomingSA(m, m));
//state.Send( new MobileAttributes( m ) );
state.Send(new MobileStatus(m, m));
state.Send(Network.SetWarMode.Instantiate(m.Warmode));
m.SendEverything();
state.Send(SupportedFeatures.Instantiate(state));
state.Send(new MobileUpdate(m));
//state.Send( new MobileAttributes( m ) );
state.Send(new MobileStatus(m, m));
state.Send(Network.SetWarMode.Instantiate(m.Warmode));
state.Send(new MobileIncomingSA(m, m));
}
else
{
state.Send(new MobileUpdateOld(m));
state.Send(new MobileUpdateOld(m));
m.CheckLightLevels(true);
state.Send(new MobileUpdateOld(m));
state.Send(new MobileIncomingOld(m, m));
//state.Send( new MobileAttributes( m ) );
state.Send(new MobileStatus(m, m));
state.Send(Network.SetWarMode.Instantiate(m.Warmode));
m.SendEverything();
state.Send(SupportedFeatures.Instantiate(state));
state.Send(new MobileUpdateOld(m));
//state.Send( new MobileAttributes( m ) );
state.Send(new MobileStatus(m, m));
state.Send(Network.SetWarMode.Instantiate(m.Warmode));
state.Send(new MobileIncomingOld(m, m));
}
state.Send(LoginComplete.Instance);
state.Send(new CurrentTime());
state.Send(SeasonChange.Instantiate(m.GetSeason(), true));
state.Send(new MapChange(m));
EventSink.InvokeLogin(new LoginEventArgs(m));
@ -2041,14 +2334,20 @@ namespace Server.Network
race = Race.Races[raceID];
}
else
{
race = Race.Races[(byte)(genderRace / 2)];
}
if (race == null)
race = Race.DefaultRace;
race ??= Race.DefaultRace;
CityInfo[] info = state.CityInfo;
IAccount a = state.Account;
if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length)
{
state.Dispose();
}
else
{
// Check if anyone is using this account
@ -2059,7 +2358,7 @@ namespace Server.Network
if (check != null && check.Map != Map.Internal)
{
Console.WriteLine("Login: {0}: Account in use", state);
Packets.SendPopupMessage(state, PMMessage.CharInWorld);
state.Send(new PopupMessage(PMMessage.CharInWorld));
return;
}
}
@ -2084,7 +2383,7 @@ namespace Server.Network
race
);
Packets.SendClientVersionReq(state);
state.Send(new ClientVersionReq());
state.BlockAllPackets = true;
@ -2154,8 +2453,10 @@ namespace Server.Network
bool female = genderRace % 2 != 0;
byte raceId = (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1);
Race race = Race.Races[raceId] ?? Race.DefaultRace;
Race race;
byte raceID = (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1);
race = Race.Races[raceID] ?? Race.DefaultRace;
CityInfo[] info = state.CityInfo;
IAccount a = state.Account;
@ -2174,7 +2475,7 @@ namespace Server.Network
if (check != null && check.Map != Map.Internal)
{
Console.WriteLine("Login: {0}: Account in use", state);
Packets.SendPopupMessage(state, PMMessage.CharInWorld);
state.Send(new PopupMessage(PMMessage.CharInWorld));
return;
}
}
@ -2200,20 +2501,25 @@ namespace Server.Network
race
);
Packets.SendClientVersionReq(state);
state.Send(new ClientVersionReq());
state.BlockAllPackets = true;
EventSink.InvokeCharacterCreated(args);
Mobile m = args.Mobile;
if (m != null)
{
state.BlockAllPackets = true;
state.Mobile = m;
m.NetState = state;
new LoginTimer(state, m).Start();
}
else
{
state.BlockAllPackets = false;
state.Dispose();
}
}
}
@ -2224,11 +2530,11 @@ namespace Server.Network
int oldestID = 0;
DateTime oldest = DateTime.MaxValue;
foreach (var (key, value) in m_AuthIDWindow)
if (value.Age < oldest)
foreach (KeyValuePair<int, AuthIDPersistence> kvp in m_AuthIDWindow)
if (kvp.Value.Age < oldest)
{
oldestID = key;
oldest = value.Age;
oldestID = kvp.Key;
oldest = kvp.Value.Age;
}
m_AuthIDWindow.Remove(oldestID);
@ -2258,13 +2564,13 @@ namespace Server.Network
}
state.SentFirstPacket = true;
state.UseCompression = true;
int authID = pvSrc.ReadInt32();
if (m_AuthIDWindow.TryGetValue(authID, out AuthIDPersistence ap))
{
m_AuthIDWindow.Remove(authID);
state.Version = ap.Version;
}
else if (ClientVerification)
@ -2298,12 +2604,19 @@ namespace Server.Network
if (e.Accepted)
{
state.CityInfo = e.CityInfo;
state.CompressionEnabled = true;
Packets.SendSupportedFeatures(state);
Packets.SendCharacterList(state, state.CityInfo);
state.Send(SupportedFeatures.Instantiate(state));
if (state.NewCharacterList)
state.Send(new CharacterList(state.Account, state.CityInfo));
else
state.Send(new CharacterListOld(state.Account, state.CityInfo));
}
else
{
state.Dispose();
}
}
public static void PlayServer(NetState state, PacketReader pvSrc)
@ -2313,15 +2626,17 @@ namespace Server.Network
IAccount a = state.Account;
if (info == null || a == null || index < 0 || index >= info.Length)
{
state.Dispose();
}
else
{
ServerInfo si = info[index];
state.m_AuthID = GenerateAuthID(state);
state.m_AuthID = PlayServerAck.m_AuthID = GenerateAuthID(state);
state.SentFirstPacket = false;
Packets.SendPlayServerAck(state, si);
state.Send(new PlayServerAck(si));
}
}
@ -2419,13 +2734,13 @@ namespace Server.Network
state.ServerInfo = info;
Packets.SendAccountLoginAck(state, info);
state.Send(new AccountLoginAck(info));
}
}
public static void AccountLogin_ReplyRej(NetState state, ALRReason reason)
{
Packets.SendAccountLoginRej(state, reason);
state.Send(new AccountLoginRej(reason));
state.Dispose();
}
@ -2443,10 +2758,7 @@ namespace Server.Network
protected override void OnTick()
{
if (m_State == null)
{
Stop();
return;
}
if (m_State.Version != null)
{

View file

@ -104,206 +104,31 @@ namespace Server.Network
public sbyte ReadSByte() => (sbyte)ReadByte();
public bool ReadBoolean() => ReadByte() != 0;
public bool ReadBoolean() => ReadByte() > 0;
public bool IsSafeChar(int c) => c >= 0x20 && c < 0xFFFE;
public string ReadUTF8StringSafe()
public string ReadUnicodeStringLE()
{
StringBuilder sb = new StringBuilder();
bool end = false;
while (!(end |= m_Reader.End))
{
ReadOnlySpan<byte> span = m_Reader.UnreadSpan;
int count = 0;
bool safe = true;
while (count < span.Length)
{
byte chr = span[count];
if (chr == 0)
{
end = true;
break;
}
if (!IsSafeChar(chr))
{
safe = false;
break;
}
count++;
}
sb.Append(Encoding.UTF8.GetString(span.Slice(0, count)));
m_Reader.Advance(count + (safe ? 1 : 0));
}
while (m_Reader.TryReadLittleEndian(out short c) && c != 0)
sb.Append((char)c);
return sb.ToString();
}
public string ReadUTF8String()
public string ReadUnicodeStringLE(int fixedLength)
{
StringBuilder sb = new StringBuilder();
bool end = false;
while (!(end |= m_Reader.End))
{
ReadOnlySpan<byte> span = m_Reader.UnreadSpan;
int count = 0;
while (count < span.Length)
{
if (span[count] == 0)
{
end = true;
break;
}
while (fixedLength-- > 0 && m_Reader.TryReadLittleEndian(out short c) && c != 0)
sb.Append((char)c);
count++;
}
sb.Append(Encoding.UTF8.GetString(span.Slice(0, count)));
m_Reader.Advance(count);
}
if (fixedLength > 0)
m_Reader.Advance(fixedLength);
return sb.ToString();
}
public string ReadString()
{
StringBuilder sb = new StringBuilder();
bool end = false;
while (!(end |= m_Reader.End))
{
ReadOnlySpan<byte> span = m_Reader.UnreadSpan;
int count = 0;
while (count < span.Length)
{
if (span[count] == 0)
{
end = true;
break;
}
count++;
}
sb.Append(Encoding.ASCII.GetString(span.Slice(0, count)));
m_Reader.Advance(count);
}
return sb.ToString();
}
public string ReadString(int size)
{
StringBuilder sb = new StringBuilder();
bool end = false;
while (!(end |= m_Reader.End) && size > 0)
{
ReadOnlySpan<byte> span = m_Reader.UnreadSpan;
int count = 0;
while (count < span.Length && size > 0)
{
if (span[count] == 0)
{
end = true;
break;
}
count++;
size--;
}
sb.Append(Encoding.ASCII.GetString(span.Slice(0, count)));
m_Reader.Advance(count);
}
if (size > 0)
m_Reader.Advance(size);
return sb.ToString();
}
public bool IsSafeChar(int c) => c >= 0x20 && c < 0xFFFE;
public string ReadUTF8StringSafe(int fixedLength)
{
StringBuilder sb = new StringBuilder();
bool end = false;
while (!(end |= m_Reader.End))
{
ReadOnlySpan<byte> span = m_Reader.UnreadSpan;
int count = 0;
bool safe = true;
while (count < span.Length)
{
byte chr = span[count];
if (chr == 0)
{
end = true;
break;
}
if (!IsSafeChar(chr))
{
safe = false;
break;
}
count++;
}
sb.Append(Encoding.ASCII.GetString(span.Slice(0, count)));
m_Reader.Advance(count + (safe ? 1 : 0));
}
return sb.ToString();
}
public string ReadStringSafe(int size)
{
StringBuilder sb = new StringBuilder();
bool end = false;
while (!(end |= m_Reader.End) && size > 0)
{
ReadOnlySpan<byte> span = m_Reader.UnreadSpan;
int count = 0;
bool safe = true;
while (count < span.Length && size > 0)
{
byte chr = span[count];
if (chr == 0)
{
end = true;
break;
}
if (!IsSafeChar(chr))
{
safe = false;
break;
}
count++;
size--;
}
sb.Append(Encoding.ASCII.GetString(span.Slice(0, count)));
m_Reader.Advance(size + (safe ? 1 : 0));
}
public string ReadUTF8String() =>
Utility.UTF8.GetString(
m_Reader.TryReadTo(out ReadOnlySpan<byte> span, (byte)'\0', true) ? span :
m_Reader.Sequence.Slice(m_Reader.Position, m_Reader.Remaining).ToArray()
);
public string ReadUnicodeStringLESafe(int fixedLength)
{
StringBuilder sb = new StringBuilder();
@ -329,6 +154,17 @@ namespace Server.Network
return sb.ToString();
}
public string ReadUnicodeStringSafe()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryReadBigEndian(out short c) && c != 0)
if (IsSafeChar(c))
sb.Append((char)c);
return sb.ToString();
}
public string ReadUnicodeString()
{
StringBuilder sb = new StringBuilder();
@ -339,24 +175,72 @@ namespace Server.Network
return sb.ToString();
}
public string ReadUnicodeString(int fixedLength)
public bool IsSafeChar(int c) => c >= 0x20 && c < 0xFFFE;
public string ReadUTF8StringSafe(int fixedLength)
{
StringBuilder sb = new StringBuilder();
string s;
while (fixedLength-- > 0 && m_Reader.TryReadBigEndian(out short c) && c != 0)
sb.Append((char)c);
if (m_Reader.TryReadTo(out ReadOnlySpan<byte> span, (byte)'\0', true))
s = Utility.UTF8.GetString(span.Length > fixedLength ? span.Slice(0, fixedLength) : span);
else
{
long size = Math.Min(m_Reader.Remaining, fixedLength);
s = Utility.UTF8.GetString(m_Reader.Sequence.Slice(m_Reader.Position, size).ToArray());
m_Reader.Advance(size);
}
if (fixedLength > 0)
m_Reader.Advance(fixedLength * 2);
StringBuilder sb = new StringBuilder(s.Length);
for (int i = 0; i < s.Length; ++i)
if (IsSafeChar(s[i]))
sb.Append(s[i]);
return sb.ToString();
}
public string ReadUnicodeStringSafe()
public string ReadUTF8StringSafe()
{
string s;
if (m_Reader.TryReadTo(out ReadOnlySpan<byte> span, (byte)'\0', true))
s = Utility.UTF8.GetString(span);
else
{
s = Utility.UTF8.GetString(m_Reader.Sequence.Slice(m_Reader.Position, m_Reader.Remaining).ToArray());
m_Reader.Advance(m_Reader.Remaining);
}
StringBuilder sb = new StringBuilder(s.Length);
for (int i = 0; i < s.Length; ++i)
if (IsSafeChar(s[i]))
sb.Append(s[i]);
return sb.ToString();
}
public string ReadUTF8String() =>
Utility.UTF8.GetString(
m_Reader.TryReadTo(out ReadOnlySpan<byte> span, (byte)'\0', true) ? span :
m_Reader.Sequence.Slice(m_Reader.Position, m_Reader.Remaining).ToArray()
);
public string ReadString()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryReadBigEndian(out short c) && c != 0)
while (m_Reader.TryRead(out byte c))
sb.Append((char)c);
return sb.ToString();
}
public string ReadStringSafe()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryRead(out byte c))
if (IsSafeChar(c))
sb.Append((char)c);
@ -376,5 +260,45 @@ namespace Server.Network
return sb.ToString();
}
public string ReadUnicodeString(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryReadBigEndian(out short c) && c != 0)
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength * 2);
return sb.ToString();
}
public string ReadStringSafe(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryRead(out byte c) && c != 0)
if (IsSafeChar(c))
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength);
return sb.ToString();
}
public string ReadString(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryRead(out byte c) && c != 0)
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength);
return sb.ToString();
}
}
}

View file

@ -0,0 +1,346 @@
/***************************************************************************
* PacketWriter.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
namespace Server.Network
{
/// <summary>
/// Provides functionality for writing primitive binary data.
/// </summary>
public class PacketWriter
{
private static ConcurrentQueue<PacketWriter> m_Pool = new ConcurrentQueue<PacketWriter>();
/// <summary>
/// Internal format buffer.
/// </summary>
private byte[] m_Buffer = new byte[4];
private int m_Capacity;
/// <summary>
/// Instantiates a new PacketWriter instance with a given capacity.
/// </summary>
/// <param name="capacity">Initial capacity for the internal stream.</param>
public PacketWriter(int capacity = 32)
{
UnderlyingStream = new MemoryStream(capacity);
m_Capacity = capacity;
}
/// <summary>
/// Gets the total stream length.
/// </summary>
public long Length => UnderlyingStream.Length;
/// <summary>
/// Gets or sets the current stream position.
/// </summary>
public long Position
{
get => UnderlyingStream.Position;
set => UnderlyingStream.Position = value;
}
/// <summary>
/// The internal stream used by this PacketWriter instance.
/// </summary>
public MemoryStream UnderlyingStream { get; private set; }
public static PacketWriter CreateInstance(int capacity = 32)
{
if (m_Pool.TryDequeue(out PacketWriter pw))
{
pw.m_Capacity = capacity;
pw.UnderlyingStream.SetLength(0);
return pw;
}
return new PacketWriter(capacity);
}
public static void ReleaseInstance(PacketWriter pw)
{
m_Pool.Enqueue(pw);
}
/// <summary>
/// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1.
/// </summary>
public void Write(bool value)
{
UnderlyingStream.WriteByte((byte)(value ? 1 : 0));
}
/// <summary>
/// Writes a 1-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write(byte value)
{
UnderlyingStream.WriteByte(value);
}
/// <summary>
/// Writes a 1-byte signed integer value to the underlying stream.
/// </summary>
public void Write(sbyte value)
{
UnderlyingStream.WriteByte((byte)value);
}
/// <summary>
/// Writes a 2-byte signed integer value to the underlying stream.
/// </summary>
public void Write(short value)
{
m_Buffer[0] = (byte)(value >> 8);
m_Buffer[1] = (byte)value;
UnderlyingStream.Write(m_Buffer, 0, 2);
}
/// <summary>
/// Writes a 2-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write(ushort value)
{
m_Buffer[0] = (byte)(value >> 8);
m_Buffer[1] = (byte)value;
UnderlyingStream.Write(m_Buffer, 0, 2);
}
/// <summary>
/// Writes a 4-byte signed integer value to the underlying stream.
/// </summary>
public void Write(int value)
{
m_Buffer[0] = (byte)(value >> 24);
m_Buffer[1] = (byte)(value >> 16);
m_Buffer[2] = (byte)(value >> 8);
m_Buffer[3] = (byte)value;
UnderlyingStream.Write(m_Buffer, 0, 4);
}
/// <summary>
/// Writes a 4-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write(uint value)
{
m_Buffer[0] = (byte)(value >> 24);
m_Buffer[1] = (byte)(value >> 16);
m_Buffer[2] = (byte)(value >> 8);
m_Buffer[3] = (byte)value;
UnderlyingStream.Write(m_Buffer, 0, 4);
}
/// <summary>
/// Writes a sequence of bytes to the underlying stream
/// </summary>
public void Write(byte[] buffer, int offset, int size)
{
UnderlyingStream.Write(buffer, offset, size);
}
/// <summary>
/// Writes a fixed-length ASCII-encoded string value to the underlying stream. To fit (size), the string content is either
/// truncated or padded with null characters.
/// </summary>
public void WriteAsciiFixed(string value, int size)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteAsciiFixed() with null value");
value = string.Empty;
}
int length = value.Length;
UnderlyingStream.SetLength(UnderlyingStream.Length + size);
if (length >= size)
{
UnderlyingStream.Position +=
Encoding.ASCII.GetBytes(value, 0, size, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
Encoding.ASCII.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += size;
}
}
/// <summary>
/// Writes a dynamic-length ASCII-encoded string value to the underlying stream, followed by a 1-byte null character.
/// </summary>
public void WriteAsciiNull(string value)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteAsciiNull() with null value");
value = string.Empty;
}
int length = value.Length;
UnderlyingStream.SetLength(UnderlyingStream.Length + length + 1);
Encoding.ASCII.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += length + 1;
}
/// <summary>
/// Writes a dynamic-length little-endian unicode string value to the underlying stream, followed by a 2-byte null character.
/// </summary>
public void WriteLittleUniNull(string value)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteLittleUniNull() with null value");
value = string.Empty;
}
int length = value.Length;
UnderlyingStream.SetLength(UnderlyingStream.Length + (length + 1) * 2);
UnderlyingStream.Position += Encoding.Unicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += 2;
}
/// <summary>
/// Writes a fixed-length little-endian unicode string value to the underlying stream. To fit (size), the string content is
/// either truncated or padded with null characters.
/// </summary>
public void WriteLittleUniFixed(string value, int size)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteLittleUniFixed() with null value");
value = string.Empty;
}
int length = value.Length;
size *= 2;
UnderlyingStream.SetLength(UnderlyingStream.Length + size);
if (length * 2 >= size)
{
UnderlyingStream.Position +=
Encoding.Unicode.GetBytes(value, 0, size / 2, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
Encoding.Unicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += size;
}
}
/// <summary>
/// Writes a dynamic-length big-endian unicode string value to the underlying stream, followed by a 2-byte null character.
/// </summary>
public void WriteBigUniNull(string value)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteBigUniNull() with null value");
value = string.Empty;
}
int length = value.Length;
UnderlyingStream.SetLength(UnderlyingStream.Length + (length + 1) * 2);
UnderlyingStream.Position +=
Encoding.BigEndianUnicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += 2;
}
/// <summary>
/// Writes a fixed-length big-endian unicode string value to the underlying stream. To fit (size), the string content is
/// either truncated or padded with null characters.
/// </summary>
public void WriteBigUniFixed(string value, int size)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteBigUniFixed() with null value");
value = string.Empty;
}
int length = value.Length;
size *= 2;
UnderlyingStream.SetLength(UnderlyingStream.Length + size);
if (length * 2 >= size )
{
UnderlyingStream.Position +=
Encoding.BigEndianUnicode.GetBytes(value, 0, size / 2, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
Encoding.BigEndianUnicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += size;
}
}
/// <summary>
/// Fills the stream from the current position up to (capacity) with 0x00's
/// </summary>
public void Fill()
{
Fill(m_Capacity - UnderlyingStream.Length);
}
/// <summary>
/// Writes a number of 0x00 byte values to the underlying stream.
/// </summary>
public void Fill(long length)
{
if (UnderlyingStream.Position == UnderlyingStream.Length)
{
UnderlyingStream.SetLength(UnderlyingStream.Length + length);
UnderlyingStream.Seek(0, SeekOrigin.End);
}
else
{
UnderlyingStream.Write(new byte[length], 0, (int)length);
}
}
/// <summary>
/// Offsets the current position from an origin.
/// </summary>
public long Seek(long offset, SeekOrigin origin) => UnderlyingStream.Seek(offset, origin);
/// <summary>
/// Gets the entire stream content as a byte array.
/// </summary>
public byte[] ToArray() => UnderlyingStream.ToArray();
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,325 +0,0 @@
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

@ -1,69 +0,0 @@
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

@ -1,168 +0,0 @@
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

@ -1,217 +0,0 @@
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

@ -1,61 +0,0 @@
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

@ -1,283 +0,0 @@
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

@ -1,94 +0,0 @@
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

@ -1,330 +0,0 @@
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

@ -1,72 +0,0 @@
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

@ -1,235 +0,0 @@
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

@ -1,161 +0,0 @@
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

@ -1,959 +0,0 @@
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

@ -1,550 +0,0 @@
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

@ -1,29 +0,0 @@
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

@ -1,81 +0,0 @@
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

@ -1,139 +0,0 @@
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

@ -1,207 +0,0 @@
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

@ -1,78 +0,0 @@
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);
}
}
}

View file

@ -0,0 +1,44 @@
/***************************************************************************
* SendQueue.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace Server.Network
{
public class SendQueue<T>
{
private BlockingCollection<T> m_Queue = new BlockingCollection<T>(new ConcurrentQueue<T>());
public void Enqueue(T t)
{
m_Queue.Add(t);
}
public Task<T> DequeueAsync()
{
TaskCompletionSource<T> taskCompletion = new TaskCompletionSource<T>();
Task.Run(() => taskCompletion.SetResult(Dequeue()));
return taskCompletion.Task;
}
public T Dequeue() => m_Queue.Take();
}
}

View file

@ -1,24 +0,0 @@
using System;
using System.Net;
namespace Server.Network
{
public sealed class ServerInfo
{
public ServerInfo(string name, int fullPercent, TimeZoneInfo tz, IPEndPoint address)
{
Name = name;
FullPercent = fullPercent;
TimeZone = tz.GetUtcOffset(DateTime.Now).Hours;
Address = address;
}
public string Name { get; set; }
public int FullPercent { get; set; }
public int TimeZone { get; set; }
public IPEndPoint Address { get; set; }
}
}

View file

@ -34,7 +34,7 @@ namespace Server.Network
public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
{
if (MemoryMarshal.TryGetArray(memory, out ArraySegment<byte> result))
if (MemoryMarshal.TryGetArray(memory, out var result))
return result;
throw new InvalidOperationException("Buffer backed by array was expected");
@ -42,7 +42,7 @@ namespace Server.Network
public static ArraySegment<byte> GetArray(this ReadOnlySequence<byte> memory)
{
if (SequenceMarshal.TryGetArray(memory, out ArraySegment<byte> result))
if (SequenceMarshal.TryGetArray(memory, out var result))
return result;
throw new InvalidOperationException("Buffer backed by array was expected");

View file

@ -0,0 +1,135 @@
/***************************************************************************
* StaticPacketHandlers.cs
* -------------------
* begin : March 15, 2019
* copyright : (C) The ModernUO Team
* email : hi@modernuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Concurrent;
namespace Server.Network
{
public static class StaticPacketHandlers
{
private static ConcurrentDictionary<IPropertyListObject,OPLInfo> OPLInfoPackets = new ConcurrentDictionary<IPropertyListObject,OPLInfo>();
private static ConcurrentDictionary<IPropertyListObject,ObjectPropertyList> ObjectPropertyListPackets = new ConcurrentDictionary<IPropertyListObject,ObjectPropertyList>();
private static ConcurrentDictionary<IEntity,RemoveEntity> RemoveEntityPackets = new ConcurrentDictionary<IEntity,RemoveEntity>();
private static ConcurrentDictionary<Item,WorldItem> WorldItemPackets = new ConcurrentDictionary<Item,WorldItem>();
private static ConcurrentDictionary<Item,WorldItemSA> WorldItemSAPackets = new ConcurrentDictionary<Item,WorldItemSA>();
private static ConcurrentDictionary<Item,WorldItemHS> WorldItemHSPackets = new ConcurrentDictionary<Item,WorldItemHS>();
public static OPLInfo GetOPLInfoPacket(IPropertyListObject obj)
{
return OPLInfoPackets.GetOrAdd(obj, value =>
{
OPLInfo packet = new OPLInfo(value.PropertyList);
packet.SetStatic();
return packet;
});
}
public static OPLInfo FreeOPLInfoPacket(IPropertyListObject obj)
{
if (OPLInfoPackets.TryRemove(obj, out OPLInfo p))
Packet.Release(p);
return p;
}
public static ObjectPropertyList GetOPLPacket(IPropertyListObject obj)
{
return ObjectPropertyListPackets.GetOrAdd(obj, value =>
{
ObjectPropertyList list = new ObjectPropertyList(value);
value.GetProperties(list);
if (value is Item item)
item.AppendChildProperties(list);
list.Terminate();
list.SetStatic();
return list;
});
}
public static ObjectPropertyList FreeOPLPacket(IPropertyListObject obj)
{
if (ObjectPropertyListPackets.TryRemove(obj, out ObjectPropertyList list))
Packet.Release(list);
return list;
}
public static RemoveEntity GetRemoveEntityPacket(IEntity entity)
{
return RemoveEntityPackets.GetOrAdd(entity, value =>
{
RemoveEntity packet = new RemoveEntity(value);
packet.SetStatic();
return packet;
});
}
public static void FreeRemoveItemPacket(IEntity entity)
{
if (RemoveEntityPackets.TryRemove(entity, out RemoveEntity p))
Packet.Release(p);
}
public static WorldItem GetWorldItemPacket(Item item)
{
return WorldItemPackets.GetOrAdd(item, value =>
{
WorldItem packet = new WorldItem(value);
packet.SetStatic();
return packet;
});
}
public static WorldItemSA GetWorldItemSAPacket(Item item)
{
return WorldItemSAPackets.GetOrAdd(item, value =>
{
WorldItemSA packet = new WorldItemSA(value);
packet.SetStatic();
return packet;
});
}
public static WorldItemHS GetWorldItemHSPacket(Item item)
{
return WorldItemHSPackets.GetOrAdd(item, value =>
{
WorldItemHS packet = new WorldItemHS(value);
packet.SetStatic();
return packet;
});
}
public static void FreeWorldItemPackets(Item item)
{
if (WorldItemPackets.TryRemove(item, out WorldItem wi))
Packet.Release(wi);
if (WorldItemSAPackets.TryRemove(item, out WorldItemSA wisa))
Packet.Release(wisa);
if (WorldItemHSPackets.TryRemove(item, out WorldItemHS wihs))
Packet.Release(wihs);
}
}
}