fix(network): Adds ConnectUO (#551)
- [X] Adds ability to create new protocol extension packets - [X] Adds freeshard protocol extension support (packet 0xF1) - [X] Adds ConnectUO support - [X] Adds arbitrary read for `CircularBufferReader` and `SpanReader`
This commit is contained in:
parent
76970325a3
commit
5e3b42bf9c
11 changed files with 381 additions and 162 deletions
|
|
@ -10,7 +10,7 @@ namespace Server.Tests
|
||||||
public void TestGetBytes(string input, byte[] bytes)
|
public void TestGetBytes(string input, byte[] bytes)
|
||||||
{
|
{
|
||||||
Span<byte> outputBytes = stackalloc byte[input.Length / 2];
|
Span<byte> outputBytes = stackalloc byte[input.Length / 2];
|
||||||
HexStringConverter.GetBytes(input, outputBytes);
|
input.GetBytes(outputBytes);
|
||||||
|
|
||||||
Assert.Equal(bytes, outputBytes.ToArray());
|
Assert.Equal(bytes, outputBytes.ToArray());
|
||||||
Assert.Equal(input, bytes.ToHexString());
|
Assert.Equal(input, bytes.ToHexString());
|
||||||
|
|
|
||||||
|
|
@ -374,5 +374,21 @@ namespace Server.Network
|
||||||
SeekOrigin.End => Length + offset,
|
SeekOrigin.End => Length + offset,
|
||||||
_ => Position + offset // Current
|
_ => Position + offset // Current
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool Read(Span<byte> bytes)
|
||||||
|
{
|
||||||
|
if (bytes.Length < Length)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_first.Length > 0 && !_first.TryCopyTo(bytes))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _second.Length <= 0 || _second.TryCopyTo(bytes.Slice(_first.Length));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -278,5 +278,16 @@ namespace System.Buffers
|
||||||
_ => offset // Begin
|
_ => offset // Begin
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool Read(Span<byte> bytes)
|
||||||
|
{
|
||||||
|
if (bytes.Length < Length)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _buffer.TryCopyTo(bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ namespace Server.Text
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe void GetBytes(string str, Span<byte> bytes)
|
public static unsafe void GetBytes(this string str, Span<byte> bytes)
|
||||||
{
|
{
|
||||||
fixed (char* strP = str)
|
fixed (char* strP = str)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ namespace Server.Accounting.Security
|
||||||
public bool ValidatePassword(string encryptedPassword, string plainPassword)
|
public bool ValidatePassword(string encryptedPassword, string plainPassword)
|
||||||
{
|
{
|
||||||
Span<byte> encryptedBytes = stackalloc byte[m_OutputSize];
|
Span<byte> encryptedBytes = stackalloc byte[m_OutputSize];
|
||||||
HexStringConverter.GetBytes(encryptedPassword, encryptedBytes);
|
encryptedPassword.GetBytes(encryptedBytes);
|
||||||
|
|
||||||
var iterations = BinaryPrimitives.ReadUInt16LittleEndian(encryptedBytes.SliceToLength(2));
|
var iterations = BinaryPrimitives.ReadUInt16LittleEndian(encryptedBytes.SliceToLength(2));
|
||||||
var salt = encryptedBytes.Slice(2, m_SaltSize);
|
var salt = encryptedBytes.Slice(2, m_SaltSize);
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ namespace Server.Gumps
|
||||||
AddLabel(150, 270, LabelHue, Core.ScriptItems.ToString());
|
AddLabel(150, 270, LabelHue, Core.ScriptItems.ToString());
|
||||||
|
|
||||||
AddLabel(20, 290, LabelHue, "Uptime:");
|
AddLabel(20, 290, LabelHue, "Uptime:");
|
||||||
AddLabel(150, 290, LabelHue, FormatTimeSpan(Core.Now - Clock.ServerStart));
|
AddLabel(150, 290, LabelHue, FormatTimeSpan(TimeSpan.FromMilliseconds(Core.TickCount)));
|
||||||
|
|
||||||
AddLabel(20, 310, LabelHue, "Memory:");
|
AddLabel(20, 310, LabelHue, "Memory:");
|
||||||
AddLabel(150, 310, LabelHue, FormatByteAmount(GC.GetTotalMemory(false)));
|
AddLabel(150, 310, LabelHue, FormatByteAmount(GC.GetTotalMemory(false)));
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Server.Items
|
||||||
|
|
||||||
public static DateTime ServerStart { get; private set; }
|
public static DateTime ServerStart { get; private set; }
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Configure()
|
||||||
{
|
{
|
||||||
ServerStart = Core.Now;
|
ServerStart = Core.Now;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
122
Projects/UOContent/Network/ConnectUO.cs
Normal file
122
Projects/UOContent/Network/ConnectUO.cs
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*************************************************************************
|
||||||
|
* ModernUO *
|
||||||
|
* Copyright (C) 2019-2021 - ModernUO Development Team *
|
||||||
|
* Email: hi@modernuo.com *
|
||||||
|
* File: ConnectUO.cs *
|
||||||
|
* *
|
||||||
|
* This program is free software: you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using Server.Accounting;
|
||||||
|
using Server.Text;
|
||||||
|
|
||||||
|
namespace Server.Network
|
||||||
|
{
|
||||||
|
public static class ConnectUO
|
||||||
|
{
|
||||||
|
public enum ConnectUOServerType
|
||||||
|
{
|
||||||
|
RunUO,
|
||||||
|
ServUO,
|
||||||
|
UOX3,
|
||||||
|
POL,
|
||||||
|
Sphere,
|
||||||
|
ModernUO
|
||||||
|
}
|
||||||
|
|
||||||
|
public const byte ConnectUOProtocolVersion = 0;
|
||||||
|
private const int _connectUOTokenLength = 32;
|
||||||
|
private const ConnectUOServerType _serverType = ConnectUOServerType.ModernUO;
|
||||||
|
private static byte[] _token;
|
||||||
|
|
||||||
|
public static void Configure()
|
||||||
|
{
|
||||||
|
var enabled = ServerConfiguration.GetOrUpdateSetting("connectuo.enabled", true);
|
||||||
|
var token = ServerConfiguration.GetOrUpdateSetting("connectuo.token", null);
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(token))
|
||||||
|
{
|
||||||
|
if (token.Length != _connectUOTokenLength * 2)
|
||||||
|
{
|
||||||
|
throw new Exception("Invalid length for ConnectUO token");
|
||||||
|
}
|
||||||
|
|
||||||
|
_token = GC.AllocateUninitializedArray<byte>(_connectUOTokenLength);
|
||||||
|
token.GetBytes(_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Utility.PushColor(ConsoleColor.Red);
|
||||||
|
Console.WriteLine("ConnectUO token could not be parsed");
|
||||||
|
Console.WriteLine("Make sure modernuo.json is properly configured");
|
||||||
|
Utility.PopColor();
|
||||||
|
_token = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeshardProtocol.Register(0xC0, false, PollInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PollInfo(NetState ns, CircularBufferReader reader, ref int packetLength)
|
||||||
|
{
|
||||||
|
var version = reader.ReadByte();
|
||||||
|
|
||||||
|
if (_token != null)
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
byte* tok = stackalloc byte[_token.Length];
|
||||||
|
var span = new Span<byte>(tok, _token.Length);
|
||||||
|
reader.Read(span);
|
||||||
|
|
||||||
|
if (!span.SequenceEqual(_token))
|
||||||
|
{
|
||||||
|
ns.Disconnect("Invalid token sent for ConnectUO");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ns.WriteConsole($"ConnectUO (v{version}) is requesting stats.");
|
||||||
|
if (version > ConnectUOProtocolVersion)
|
||||||
|
{
|
||||||
|
Utility.PushColor(ConsoleColor.Yellow);
|
||||||
|
ns.WriteConsole("Warning! ConnectUO (v{version}) is newer than what is supported.");
|
||||||
|
Utility.PopColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ns.SendServerPollInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendServerPollInfo(this NetState ns)
|
||||||
|
{
|
||||||
|
if (ns == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var writer = new SpanWriter(stackalloc byte[15]);
|
||||||
|
writer.Write((byte)0xC0); // Packet ID
|
||||||
|
writer.Write(17); // Length
|
||||||
|
writer.Write(ConnectUOProtocolVersion); // Version
|
||||||
|
writer.Write((byte)_serverType);
|
||||||
|
writer.Write((int)(Core.TickCount / 1000));
|
||||||
|
writer.Write(Accounts.Count); // Shame if you modify this!
|
||||||
|
writer.Write(TcpServer.Instances.Count); // Shame if you modify this!
|
||||||
|
|
||||||
|
ns.Send(writer.Span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Projects/UOContent/Network/FreeshardProtocol.cs
Normal file
31
Projects/UOContent/Network/FreeshardProtocol.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*************************************************************************
|
||||||
|
* ModernUO *
|
||||||
|
* Copyright (C) 2019-2021 - ModernUO Development Team *
|
||||||
|
* Email: hi@modernuo.com *
|
||||||
|
* File: FreeshardProtocol.cs *
|
||||||
|
* *
|
||||||
|
* This program is free software: you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
namespace Server.Network
|
||||||
|
{
|
||||||
|
public static class FreeshardProtocol
|
||||||
|
{
|
||||||
|
private static PacketHandler[] _handlers;
|
||||||
|
|
||||||
|
[CallPriority(10)]
|
||||||
|
public static void Configure()
|
||||||
|
{
|
||||||
|
_handlers = ProtocolExtensions.Register(0xF1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Register(int cmd, bool ingame, OnPacketReceive onReceive) =>
|
||||||
|
_handlers[cmd] = new PacketHandler(cmd, 0, ingame, onReceive);
|
||||||
|
}
|
||||||
|
}
|
||||||
164
Projects/UOContent/Network/MapUO.cs
Normal file
164
Projects/UOContent/Network/MapUO.cs
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*************************************************************************
|
||||||
|
* ModernUO *
|
||||||
|
* Copyright (C) 2019-2021 - ModernUO Development Team *
|
||||||
|
* Email: hi@modernuo.com *
|
||||||
|
* File: MapUO.cs *
|
||||||
|
* *
|
||||||
|
* This program is free software: you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.IO;
|
||||||
|
using Server.Engines.PartySystem;
|
||||||
|
using Server.Guilds;
|
||||||
|
|
||||||
|
namespace Server.Network
|
||||||
|
{
|
||||||
|
public static class MapUO
|
||||||
|
{
|
||||||
|
private static PacketHandler[] _handlers;
|
||||||
|
|
||||||
|
public static void Configure()
|
||||||
|
{
|
||||||
|
_handlers = ProtocolExtensions.Register(0xF0);
|
||||||
|
|
||||||
|
Register(0x00, true, QueryPartyMemberLocations);
|
||||||
|
Register(0x01, true, QueryGuildMemberLocations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Register(int cmd, bool ingame, OnPacketReceive onReceive) =>
|
||||||
|
_handlers[cmd] = new PacketHandler(cmd, 0, ingame, onReceive);
|
||||||
|
|
||||||
|
public static void QueryGuildMemberLocations(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||||
|
{
|
||||||
|
Mobile from = state.Mobile;
|
||||||
|
|
||||||
|
state.SendGuildMemberLocations(from, from.Guild as Guild, reader.ReadBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void QueryPartyMemberLocations(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||||
|
{
|
||||||
|
Mobile from = state.Mobile;
|
||||||
|
var party = Party.Get(from);
|
||||||
|
|
||||||
|
if (party != null)
|
||||||
|
{
|
||||||
|
state.SendPartyMemberLocations(from, party);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendGuildMemberLocations(this NetState ns, Mobile from, Guild guild, bool sendLocations)
|
||||||
|
{
|
||||||
|
if (ns == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = guild?.Members.Count ?? 0;
|
||||||
|
var maxLength = 9 + (count > 1 ? (count - 1) * (sendLocations ? 10 : 4) : 0);
|
||||||
|
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||||
|
writer.Write((byte)0xF0); // Packet ID
|
||||||
|
writer.Seek(2, SeekOrigin.Current);
|
||||||
|
writer.Write((byte)0x02); // Command
|
||||||
|
writer.Write(count > 0 && sendLocations);
|
||||||
|
|
||||||
|
bool sendPacket = false;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var m = guild!.Members[i];
|
||||||
|
|
||||||
|
if (m?.NetState == null || m == from)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendLocations && Utility.InUpdateRange(from, m) && from.CanSee(m))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPacket = true;
|
||||||
|
writer.Write(m.Serial);
|
||||||
|
|
||||||
|
if (sendLocations)
|
||||||
|
{
|
||||||
|
writer.Write((short)m.X);
|
||||||
|
writer.Write((short)m.Y);
|
||||||
|
writer.Write((byte)(m.Map?.MapID ?? 0));
|
||||||
|
|
||||||
|
if (m.Alive)
|
||||||
|
{
|
||||||
|
writer.Write((byte)(m.Hits * 100 / Math.Max(m.HitsMax, 1)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.Write((byte)0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendPacket)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write(0);
|
||||||
|
writer.WritePacketLength();
|
||||||
|
ns.Send(writer.Span);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendPartyMemberLocations(this NetState ns, Mobile from, Party party)
|
||||||
|
{
|
||||||
|
if (ns == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = party?.Members.Count ?? 0;
|
||||||
|
var maxLength = 9 + (count > 1 ? (count - 1) * 9 : 0);
|
||||||
|
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||||
|
writer.Write((byte)0xF0); // Packet ID
|
||||||
|
writer.Seek(2, SeekOrigin.Current);
|
||||||
|
writer.Write((byte)0x01); // Command
|
||||||
|
|
||||||
|
bool sendPacket = false;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var pmi = party!.Members[i];
|
||||||
|
Mobile mob = pmi?.Mobile;
|
||||||
|
|
||||||
|
if (mob?.NetState == null || mob == from)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utility.InUpdateRange(from, mob) && from.CanSee(mob))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPacket = true;
|
||||||
|
writer.Write(mob.Serial);
|
||||||
|
writer.Write((short)mob.X);
|
||||||
|
writer.Write((short)mob.Y);
|
||||||
|
writer.Write((byte)(mob.Map?.MapID ?? 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendPacket)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write(0);
|
||||||
|
writer.WritePacketLength();
|
||||||
|
ns.Send(writer.Span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,179 +1,54 @@
|
||||||
using System;
|
/*************************************************************************
|
||||||
using System.Buffers;
|
* ModernUO *
|
||||||
using System.IO;
|
* Copyright (C) 2019-2021 - ModernUO Development Team *
|
||||||
using Server.Engines.PartySystem;
|
* Email: hi@modernuo.com *
|
||||||
using Server.Guilds;
|
* File: ProtocolExtension.cs *
|
||||||
|
* *
|
||||||
|
* This program is free software: you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
namespace Server.Network
|
namespace Server.Network
|
||||||
{
|
{
|
||||||
public static class ProtocolExtensions
|
public static class ProtocolExtensions
|
||||||
{
|
{
|
||||||
private static readonly PacketHandler[] _handlers = new PacketHandler[0x100];
|
public static PacketHandler[] Register(byte packetId)
|
||||||
|
|
||||||
public static void Configure()
|
|
||||||
{
|
{
|
||||||
IncomingPackets.Register( 0xF0, 0, false, DecodeBundledPacket);
|
var packetHandlers = new PacketHandler[0x100];
|
||||||
Register(0x00, true, QueryPartyMemberLocations);
|
|
||||||
Register(0x01, true, QueryGuildMemberLocations);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Register(int packetID, bool ingame, OnPacketReceive onReceive)
|
void DecodeBundledPacket(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||||
{
|
|
||||||
_handlers[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PacketHandler GetHandler(int packetID) =>
|
|
||||||
packetID >= 0 && packetID < _handlers.Length ? _handlers[packetID] : null;
|
|
||||||
|
|
||||||
public static void DecodeBundledPacket(NetState state, CircularBufferReader reader, ref int packetLength)
|
|
||||||
{
|
|
||||||
int packetID = reader.ReadByte();
|
|
||||||
|
|
||||||
PacketHandler ph = GetHandler(packetID);
|
|
||||||
|
|
||||||
if (ph == null)
|
|
||||||
{
|
{
|
||||||
return;
|
int cmd = reader.ReadByte();
|
||||||
}
|
|
||||||
|
|
||||||
if (ph.Ingame && state.Mobile == null)
|
PacketHandler ph = cmd >= 0 && cmd < packetHandlers.Length ? packetHandlers[cmd] : null;
|
||||||
{
|
|
||||||
state.WriteConsole("Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile", packetID);
|
|
||||||
state.Disconnect("Sent in-game packet before being attached to a mobile.");
|
|
||||||
}
|
|
||||||
else if (ph.Ingame && state.Mobile.Deleted)
|
|
||||||
{
|
|
||||||
state.Disconnect(string.Empty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ph.OnReceive(state, reader, ref packetLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void QueryGuildMemberLocations(NetState state, CircularBufferReader reader, ref int packetLength)
|
if (ph == null)
|
||||||
{
|
|
||||||
Mobile from = state.Mobile;
|
|
||||||
|
|
||||||
state.SendGuildMemberLocations(from, from.Guild as Guild, reader.ReadBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void QueryPartyMemberLocations(NetState state, CircularBufferReader reader, ref int packetLength)
|
|
||||||
{
|
|
||||||
Mobile from = state.Mobile;
|
|
||||||
var party = Party.Get(from);
|
|
||||||
|
|
||||||
if (party != null)
|
|
||||||
{
|
|
||||||
state.SendPartyMemberLocations(from, party);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SendGuildMemberLocations(this NetState ns, Mobile from, Guild guild, bool sendLocations)
|
|
||||||
{
|
|
||||||
if (ns == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = guild?.Members.Count ?? 0;
|
|
||||||
var maxLength = 9 + (count > 1 ? (count - 1) * (sendLocations ? 10 : 4) : 0);
|
|
||||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
|
||||||
writer.Write((byte)0xF0); // Packet ID
|
|
||||||
writer.Seek(2, SeekOrigin.Current);
|
|
||||||
writer.Write((byte)0x02); // Command
|
|
||||||
writer.Write(count > 0 && sendLocations);
|
|
||||||
|
|
||||||
bool sendPacket = false;
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var m = guild!.Members[i];
|
|
||||||
|
|
||||||
if (m?.NetState == null || m == from)
|
|
||||||
{
|
{
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sendLocations && Utility.InUpdateRange(from, m) && from.CanSee(m))
|
if (ph.Ingame && state.Mobile == null)
|
||||||
{
|
{
|
||||||
continue;
|
state.WriteConsole("Sent in-game packet (0x{0:X2}x{1:X2}) before having been attached to a mobile", packetId, cmd);
|
||||||
|
state.Disconnect("Sent in-game packet before being attached to a mobile.");
|
||||||
}
|
}
|
||||||
|
else if (ph.Ingame && state.Mobile.Deleted)
|
||||||
sendPacket = true;
|
|
||||||
writer.Write(m.Serial);
|
|
||||||
|
|
||||||
if (sendLocations)
|
|
||||||
{
|
{
|
||||||
writer.Write((short)m.X);
|
state.Disconnect(string.Empty);
|
||||||
writer.Write((short)m.Y);
|
}
|
||||||
writer.Write((byte)(m.Map?.MapID ?? 0));
|
else
|
||||||
|
{
|
||||||
if (m.Alive)
|
ph.OnReceive(state, reader, ref packetLength);
|
||||||
{
|
|
||||||
writer.Write((byte)(m.Hits * 100 / Math.Max(m.HitsMax, 1)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
writer.Write((byte)0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sendPacket)
|
IncomingPackets.Register(packetId, 0, false, DecodeBundledPacket);
|
||||||
{
|
return packetHandlers;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(0);
|
|
||||||
writer.WritePacketLength();
|
|
||||||
ns.Send(writer.Span);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SendPartyMemberLocations(this NetState ns, Mobile from, Party party)
|
|
||||||
{
|
|
||||||
if (ns == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = party?.Members.Count ?? 0;
|
|
||||||
var maxLength = 9 + (count > 1 ? (count - 1) * 9 : 0);
|
|
||||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
|
||||||
writer.Write((byte)0xF0); // Packet ID
|
|
||||||
writer.Seek(2, SeekOrigin.Current);
|
|
||||||
writer.Write((byte)0x01); // Command
|
|
||||||
|
|
||||||
bool sendPacket = false;
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var pmi = party!.Members[i];
|
|
||||||
Mobile mob = pmi?.Mobile;
|
|
||||||
|
|
||||||
if (mob?.NetState == null || mob == from)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Utility.InUpdateRange(from, mob) && from.CanSee(mob))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPacket = true;
|
|
||||||
writer.Write(mob.Serial);
|
|
||||||
writer.Write((short)mob.X);
|
|
||||||
writer.Write((short)mob.Y);
|
|
||||||
writer.Write((byte)(mob.Map?.MapID ?? 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sendPacket)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(0);
|
|
||||||
writer.WritePacketLength();
|
|
||||||
ns.Send(writer.Span);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue