fix: Stops creating blocked packets entirely (#944)
Optimizes larger servers where users are logging in and packets are being created for no reason.
This commit is contained in:
parent
5db8b1354c
commit
941452de4a
58 changed files with 5510 additions and 5537 deletions
|
|
@ -4139,7 +4139,7 @@ namespace Server
|
|||
|
||||
var ns = from.NetState;
|
||||
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Server
|
|||
|
||||
public static void SendHairEquipUpdatePacket(this NetState ns, Mobile m, uint hairSerial, int itemId, int hue, Layer layer)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ namespace Server
|
|||
|
||||
public static void SendRemoveHairPacket(this NetState ns, uint hairSerial)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8317,7 +8317,7 @@ namespace Server
|
|||
{
|
||||
var ns = m_NetState;
|
||||
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ namespace Server.Network
|
|||
|
||||
public void Send(ReadOnlySpan<byte> span)
|
||||
{
|
||||
if (span == null || Connection == null || BlockAllPackets)
|
||||
if (span == null || this.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,526 +18,525 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using CV = Server.ClientVersion;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingAccountPackets
|
||||
{
|
||||
public static class IncomingAccountPackets
|
||||
private const int m_AuthIDWindowSize = 128;
|
||||
private static readonly Dictionary<int, AuthIDPersistence> m_AuthIDWindow =
|
||||
new(m_AuthIDWindowSize);
|
||||
|
||||
internal struct AuthIDPersistence
|
||||
{
|
||||
private const int m_AuthIDWindowSize = 128;
|
||||
private static readonly Dictionary<int, AuthIDPersistence> m_AuthIDWindow =
|
||||
new(m_AuthIDWindowSize);
|
||||
public DateTime Age;
|
||||
public readonly ClientVersion Version;
|
||||
|
||||
internal struct AuthIDPersistence
|
||||
public AuthIDPersistence(ClientVersion v)
|
||||
{
|
||||
public DateTime Age;
|
||||
public readonly ClientVersion Version;
|
||||
Age = Core.Now;
|
||||
Version = v;
|
||||
}
|
||||
}
|
||||
|
||||
public AuthIDPersistence(ClientVersion v)
|
||||
{
|
||||
Age = Core.Now;
|
||||
Version = v;
|
||||
}
|
||||
public static void Configure()
|
||||
{
|
||||
IncomingPackets.Register(0x00, 104, false, CreateCharacter);
|
||||
IncomingPackets.Register(0x5D, 73, false, PlayCharacter);
|
||||
IncomingPackets.Register(0x80, 62, false, AccountLogin);
|
||||
IncomingPackets.Register(0x83, 39, false, DeleteCharacter);
|
||||
IncomingPackets.Register(0x91, 65, false, GameLogin);
|
||||
IncomingPackets.Register(0xA0, 3, false, PlayServer);
|
||||
IncomingPackets.Register(0xBB, 9, false, AccountID);
|
||||
IncomingPackets.Register(0xBD, 0, false, ClientVersion);
|
||||
IncomingPackets.Register(0xBE, 0, true, AssistVersion);
|
||||
IncomingPackets.Register(0xCF, 0, false, AccountLogin);
|
||||
IncomingPackets.Register(0xE1, 0, false, ClientType);
|
||||
IncomingPackets.Register(0xEF, 21, false, LoginServerSeed);
|
||||
IncomingPackets.Register(0xF8, 106, false, CreateCharacter);
|
||||
}
|
||||
|
||||
public static void CreateCharacter(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.Seek(9, SeekOrigin.Current);
|
||||
/*
|
||||
var unk1 = reader.ReadInt32();
|
||||
var unk2 = reader.ReadInt32();
|
||||
int unk3 = reader.ReadByte();
|
||||
*/
|
||||
var name = reader.ReadAscii(30);
|
||||
|
||||
reader.Seek(2, SeekOrigin.Current);
|
||||
var flags = reader.ReadInt32();
|
||||
reader.Seek(8, SeekOrigin.Current);
|
||||
int prof = reader.ReadByte();
|
||||
reader.Seek(15, SeekOrigin.Current);
|
||||
|
||||
int genderRace = reader.ReadByte();
|
||||
|
||||
var stats = new StatNameValue[]
|
||||
{
|
||||
new(StatType.Str, reader.ReadByte()),
|
||||
new(StatType.Dex, reader.ReadByte()),
|
||||
new(StatType.Int, reader.ReadByte())
|
||||
};
|
||||
|
||||
var skills = new SkillNameValue[state.NewCharacterCreation ? 4 : 3];
|
||||
skills[0] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte());
|
||||
skills[1] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte());
|
||||
skills[2] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte());
|
||||
if (state.NewCharacterCreation)
|
||||
{
|
||||
skills[3] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte());
|
||||
}
|
||||
|
||||
public static void Configure()
|
||||
int hue = reader.ReadUInt16();
|
||||
int hairVal = reader.ReadInt16();
|
||||
int hairHue = reader.ReadInt16();
|
||||
int hairValf = reader.ReadInt16();
|
||||
int hairHuef = reader.ReadInt16();
|
||||
reader.ReadByte();
|
||||
int cityIndex = reader.ReadByte();
|
||||
reader.Seek(8, SeekOrigin.Current);
|
||||
/*
|
||||
var charSlot = reader.ReadInt32();
|
||||
var clientIP = reader.ReadInt32();
|
||||
*/
|
||||
int shirtHue = reader.ReadInt16();
|
||||
int pantsHue = reader.ReadInt16();
|
||||
|
||||
/*
|
||||
Pre-7.0.0.0:
|
||||
0x00, 0x01 -> Human Male, Human Female
|
||||
0x02, 0x03 -> Elf Male, Elf Female
|
||||
|
||||
Post-7.0.0.0:
|
||||
0x00, 0x01
|
||||
0x02, 0x03 -> Human Male, Human Female
|
||||
0x04, 0x05 -> Elf Male, Elf Female
|
||||
0x05, 0x06 -> Gargoyle Male, Gargoyle Female
|
||||
*/
|
||||
|
||||
var female = genderRace % 2 != 0;
|
||||
|
||||
var raceID = state.StygianAbyss ? (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1) : (byte)(genderRace / 2);
|
||||
Race race = Race.Races[raceID] ?? Race.DefaultRace;
|
||||
|
||||
var info = state.CityInfo;
|
||||
var a = state.Account;
|
||||
|
||||
if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length)
|
||||
{
|
||||
IncomingPackets.Register(0x00, 104, false, CreateCharacter);
|
||||
IncomingPackets.Register(0x5D, 73, false, PlayCharacter);
|
||||
IncomingPackets.Register(0x80, 62, false, AccountLogin);
|
||||
IncomingPackets.Register(0x83, 39, false, DeleteCharacter);
|
||||
IncomingPackets.Register(0x91, 65, false, GameLogin);
|
||||
IncomingPackets.Register(0xA0, 3, false, PlayServer);
|
||||
IncomingPackets.Register(0xBB, 9, false, AccountID);
|
||||
IncomingPackets.Register(0xBD, 0, false, ClientVersion);
|
||||
IncomingPackets.Register(0xBE, 0, true, AssistVersion);
|
||||
IncomingPackets.Register(0xCF, 0, false, AccountLogin);
|
||||
IncomingPackets.Register(0xE1, 0, false, ClientType);
|
||||
IncomingPackets.Register(0xEF, 21, false, LoginServerSeed);
|
||||
IncomingPackets.Register(0xF8, 106, false, CreateCharacter);
|
||||
state.Disconnect("Invalid city selected during character creation.");
|
||||
return;
|
||||
}
|
||||
|
||||
public static void CreateCharacter(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
// Check if anyone is using this account
|
||||
for (var i = 0; i < a.Length; ++i)
|
||||
{
|
||||
reader.Seek(9, SeekOrigin.Current);
|
||||
/*
|
||||
var unk1 = reader.ReadInt32();
|
||||
var unk2 = reader.ReadInt32();
|
||||
int unk3 = reader.ReadByte();
|
||||
*/
|
||||
var name = reader.ReadAscii(30);
|
||||
var check = a[i];
|
||||
|
||||
reader.Seek(2, SeekOrigin.Current);
|
||||
var flags = reader.ReadInt32();
|
||||
reader.Seek(8, SeekOrigin.Current);
|
||||
int prof = reader.ReadByte();
|
||||
reader.Seek(15, SeekOrigin.Current);
|
||||
|
||||
int genderRace = reader.ReadByte();
|
||||
|
||||
var stats = new StatNameValue[]
|
||||
if (check != null && check.Map != Map.Internal)
|
||||
{
|
||||
new(StatType.Str, reader.ReadByte()),
|
||||
new(StatType.Dex, reader.ReadByte()),
|
||||
new(StatType.Int, reader.ReadByte())
|
||||
};
|
||||
|
||||
var skills = new SkillNameValue[state.NewCharacterCreation ? 4 : 3];
|
||||
skills[0] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte());
|
||||
skills[1] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte());
|
||||
skills[2] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte());
|
||||
if (state.NewCharacterCreation)
|
||||
{
|
||||
skills[3] = new SkillNameValue((SkillName)reader.ReadByte(), reader.ReadByte());
|
||||
}
|
||||
|
||||
int hue = reader.ReadUInt16();
|
||||
int hairVal = reader.ReadInt16();
|
||||
int hairHue = reader.ReadInt16();
|
||||
int hairValf = reader.ReadInt16();
|
||||
int hairHuef = reader.ReadInt16();
|
||||
reader.ReadByte();
|
||||
int cityIndex = reader.ReadByte();
|
||||
reader.Seek(8, SeekOrigin.Current);
|
||||
/*
|
||||
var charSlot = reader.ReadInt32();
|
||||
var clientIP = reader.ReadInt32();
|
||||
*/
|
||||
int shirtHue = reader.ReadInt16();
|
||||
int pantsHue = reader.ReadInt16();
|
||||
|
||||
/*
|
||||
Pre-7.0.0.0:
|
||||
0x00, 0x01 -> Human Male, Human Female
|
||||
0x02, 0x03 -> Elf Male, Elf Female
|
||||
|
||||
Post-7.0.0.0:
|
||||
0x00, 0x01
|
||||
0x02, 0x03 -> Human Male, Human Female
|
||||
0x04, 0x05 -> Elf Male, Elf Female
|
||||
0x05, 0x06 -> Gargoyle Male, Gargoyle Female
|
||||
*/
|
||||
|
||||
var female = genderRace % 2 != 0;
|
||||
|
||||
var raceID = state.StygianAbyss ? (byte)(genderRace < 4 ? 0 : genderRace / 2 - 1) : (byte)(genderRace / 2);
|
||||
Race race = Race.Races[raceID] ?? Race.DefaultRace;
|
||||
|
||||
var info = state.CityInfo;
|
||||
var a = state.Account;
|
||||
|
||||
if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length)
|
||||
{
|
||||
state.Disconnect("Invalid city selected during character creation.");
|
||||
state.LogInfo("Account in use");
|
||||
state.SendPopupMessage(PMMessage.CharInWorld);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if anyone is using this account
|
||||
for (var i = 0; i < a.Length; ++i)
|
||||
{
|
||||
var check = a[i];
|
||||
|
||||
if (check != null && check.Map != Map.Internal)
|
||||
{
|
||||
state.LogInfo("Account in use");
|
||||
state.SendPopupMessage(PMMessage.CharInWorld);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state.Flags = (ClientFlags)flags;
|
||||
|
||||
var args = new CharacterCreatedEventArgs(
|
||||
state,
|
||||
a,
|
||||
name,
|
||||
female,
|
||||
hue,
|
||||
stats,
|
||||
info[cityIndex],
|
||||
skills,
|
||||
shirtHue,
|
||||
pantsHue,
|
||||
hairVal,
|
||||
hairHue,
|
||||
hairValf,
|
||||
hairHuef,
|
||||
prof,
|
||||
race
|
||||
);
|
||||
|
||||
state.SendClientVersionRequest();
|
||||
|
||||
state.BlockAllPackets = true;
|
||||
|
||||
EventSink.InvokeCharacterCreated(args);
|
||||
|
||||
var m = args.Mobile;
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
state.Mobile = m;
|
||||
m.NetState = state;
|
||||
new LoginTimer(state, m).Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
state.BlockAllPackets = false;
|
||||
state.Disconnect("Character creation blocked.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteCharacter(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
state.Flags = (ClientFlags)flags;
|
||||
|
||||
var args = new CharacterCreatedEventArgs(
|
||||
state,
|
||||
a,
|
||||
name,
|
||||
female,
|
||||
hue,
|
||||
stats,
|
||||
info[cityIndex],
|
||||
skills,
|
||||
shirtHue,
|
||||
pantsHue,
|
||||
hairVal,
|
||||
hairHue,
|
||||
hairValf,
|
||||
hairHuef,
|
||||
prof,
|
||||
race
|
||||
);
|
||||
|
||||
state.SendClientVersionRequest();
|
||||
|
||||
state.BlockAllPackets = true;
|
||||
|
||||
EventSink.InvokeCharacterCreated(args);
|
||||
|
||||
var m = args.Mobile;
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
reader.Seek(30, SeekOrigin.Current);
|
||||
var index = reader.ReadInt32();
|
||||
|
||||
EventSink.InvokeDeleteRequest(state, index);
|
||||
}
|
||||
|
||||
public static void AccountID(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
}
|
||||
|
||||
public static void AssistVersion(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var unk = reader.ReadInt32();
|
||||
var av = reader.ReadAscii();
|
||||
}
|
||||
|
||||
public static void ClientVersion(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var version = state.Version = new CV(reader.ReadAscii());
|
||||
|
||||
EventSink.InvokeClientVersionReceived(state, version);
|
||||
}
|
||||
|
||||
public static void ClientType(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.ReadUInt16();
|
||||
|
||||
int type = reader.ReadUInt16();
|
||||
var version = state.Version = new CV(reader.ReadAscii());
|
||||
|
||||
EventSink.InvokeClientVersionReceived(state, version);
|
||||
}
|
||||
|
||||
public static void PlayCharacter(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.Seek(4, SeekOrigin.Current); // 0xEDEDEDED
|
||||
|
||||
reader.Seek(30, SeekOrigin.Current); // var name = reader.ReadAscii(30);
|
||||
|
||||
reader.Seek(2, SeekOrigin.Current);
|
||||
|
||||
var flags = reader.ReadInt32();
|
||||
|
||||
reader.Seek(24, SeekOrigin.Current);
|
||||
|
||||
var charSlot = reader.ReadInt32();
|
||||
reader.Seek(4, SeekOrigin.Current); // var clientIP = reader.ReadInt32();
|
||||
|
||||
var a = state.Account;
|
||||
|
||||
if (a == null || charSlot < 0 || charSlot >= a.Length)
|
||||
{
|
||||
state.Disconnect("Invalid character slot selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
var m = a[charSlot];
|
||||
|
||||
// Check if anyone is using this account
|
||||
for (var i = 0; i < a.Length; ++i)
|
||||
{
|
||||
var check = a[i];
|
||||
|
||||
if (check != null && check.Map != Map.Internal && check != m)
|
||||
{
|
||||
state.LogInfo("Account in use");
|
||||
state.SendPopupMessage(PMMessage.CharInWorld);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
state.Disconnect("Empty character slot selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
m.NetState?.Disconnect("Character selected for a player already logged in.");
|
||||
|
||||
state.SendClientVersionRequest();
|
||||
|
||||
state.BlockAllPackets = true;
|
||||
|
||||
state.Flags = (ClientFlags)flags;
|
||||
|
||||
state.Mobile = m;
|
||||
m.NetState = state;
|
||||
|
||||
new LoginTimer(state, m).Start();
|
||||
}
|
||||
|
||||
public static void DoLogin(this NetState state, Mobile m)
|
||||
else
|
||||
{
|
||||
state.SendLoginConfirmation(m);
|
||||
state.BlockAllPackets = false;
|
||||
state.Disconnect("Character creation blocked.");
|
||||
}
|
||||
}
|
||||
|
||||
state.SendMapChange(m.Map);
|
||||
public static void DeleteCharacter(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.Seek(30, SeekOrigin.Current);
|
||||
var index = reader.ReadInt32();
|
||||
|
||||
state.SendMapPatches();
|
||||
EventSink.InvokeDeleteRequest(state, index);
|
||||
}
|
||||
|
||||
state.SendSeasonChange((byte)m.GetSeason(), true);
|
||||
public static void AccountID(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
}
|
||||
|
||||
public static void AssistVersion(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var unk = reader.ReadInt32();
|
||||
var av = reader.ReadAscii();
|
||||
}
|
||||
|
||||
public static void ClientVersion(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var version = state.Version = new CV(reader.ReadAscii());
|
||||
|
||||
EventSink.InvokeClientVersionReceived(state, version);
|
||||
}
|
||||
|
||||
public static void ClientType(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.ReadUInt16();
|
||||
|
||||
int type = reader.ReadUInt16();
|
||||
var version = state.Version = new CV(reader.ReadAscii());
|
||||
|
||||
EventSink.InvokeClientVersionReceived(state, version);
|
||||
}
|
||||
|
||||
public static void PlayCharacter(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.Seek(4, SeekOrigin.Current); // 0xEDEDEDED
|
||||
|
||||
reader.Seek(30, SeekOrigin.Current); // var name = reader.ReadAscii(30);
|
||||
|
||||
reader.Seek(2, SeekOrigin.Current);
|
||||
|
||||
var flags = reader.ReadInt32();
|
||||
|
||||
reader.Seek(24, SeekOrigin.Current);
|
||||
|
||||
var charSlot = reader.ReadInt32();
|
||||
reader.Seek(4, SeekOrigin.Current); // var clientIP = reader.ReadInt32();
|
||||
|
||||
var a = state.Account;
|
||||
|
||||
if (a == null || charSlot < 0 || charSlot >= a.Length)
|
||||
{
|
||||
state.Disconnect("Invalid character slot selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
var m = a[charSlot];
|
||||
|
||||
// Check if anyone is using this account
|
||||
for (var i = 0; i < a.Length; ++i)
|
||||
{
|
||||
var check = a[i];
|
||||
|
||||
if (check != null && check.Map != Map.Internal && check != m)
|
||||
{
|
||||
state.LogInfo("Account in use");
|
||||
state.SendPopupMessage(PMMessage.CharInWorld);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
state.Disconnect("Empty character slot selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
m.NetState?.Disconnect("Character selected for a player already logged in.");
|
||||
|
||||
state.SendClientVersionRequest();
|
||||
|
||||
state.BlockAllPackets = true;
|
||||
|
||||
state.Flags = (ClientFlags)flags;
|
||||
|
||||
state.Mobile = m;
|
||||
m.NetState = state;
|
||||
|
||||
new LoginTimer(state, m).Start();
|
||||
}
|
||||
|
||||
public static void DoLogin(this NetState state, Mobile m)
|
||||
{
|
||||
state.SendLoginConfirmation(m);
|
||||
|
||||
state.SendMapChange(m.Map);
|
||||
|
||||
state.SendMapPatches();
|
||||
|
||||
state.SendSeasonChange((byte)m.GetSeason(), true);
|
||||
|
||||
state.SendSupportedFeature();
|
||||
|
||||
state.Sequence = 0;
|
||||
|
||||
state.SendMobileUpdate(m);
|
||||
state.SendMobileUpdate(m);
|
||||
|
||||
m.CheckLightLevels(true);
|
||||
|
||||
state.SendMobileUpdate(m);
|
||||
|
||||
state.SendMobileIncoming(m, m);
|
||||
|
||||
state.SendMobileStatus(m);
|
||||
state.SendSetWarMode(m.Warmode);
|
||||
|
||||
m.SendEverything();
|
||||
|
||||
state.SendSupportedFeature();
|
||||
state.SendMobileUpdate(m);
|
||||
|
||||
state.SendMobileStatus(m);
|
||||
state.SendSetWarMode(m.Warmode);
|
||||
state.SendMobileIncoming(m, m);
|
||||
|
||||
state.SendLoginComplete();
|
||||
state.SendCurrentTime();
|
||||
state.SendSeasonChange((byte)m.GetSeason(), true);
|
||||
state.SendMapChange(m.Map);
|
||||
|
||||
state.SendPlayMusic(m.Region.Music);
|
||||
|
||||
EventSink.InvokeLogin(m);
|
||||
}
|
||||
|
||||
private static int GenerateAuthID(this NetState state)
|
||||
{
|
||||
if (m_AuthIDWindow.Count == m_AuthIDWindowSize)
|
||||
{
|
||||
var oldestID = 0;
|
||||
var oldest = DateTime.MaxValue;
|
||||
|
||||
foreach (var (key, authId) in m_AuthIDWindow)
|
||||
{
|
||||
if (authId.Age < oldest)
|
||||
{
|
||||
oldestID = key;
|
||||
oldest = authId.Age;
|
||||
}
|
||||
}
|
||||
|
||||
m_AuthIDWindow.Remove(oldestID);
|
||||
}
|
||||
|
||||
int authID;
|
||||
|
||||
do
|
||||
{
|
||||
authID = Utility.Random(1, int.MaxValue - 1);
|
||||
|
||||
if (Utility.RandomBool())
|
||||
{
|
||||
authID |= 1 << 31;
|
||||
}
|
||||
} while (m_AuthIDWindow.ContainsKey(authID));
|
||||
|
||||
m_AuthIDWindow[authID] = new AuthIDPersistence(state.Version);
|
||||
|
||||
return authID;
|
||||
}
|
||||
|
||||
public static void GameLogin(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
// TODO: Connection throttling
|
||||
|
||||
if (state.SentFirstPacket)
|
||||
{
|
||||
state.Disconnect("Duplicate game login packet received.");
|
||||
return;
|
||||
}
|
||||
|
||||
state.SentFirstPacket = true;
|
||||
|
||||
var authID = reader.ReadInt32();
|
||||
|
||||
if (!m_AuthIDWindow.TryGetValue(authID, out var ap))
|
||||
{
|
||||
state.LogInfo("Invalid client detected, disconnecting...");
|
||||
state.Disconnect("Unable to find auth id.");
|
||||
}
|
||||
|
||||
if (state._authId != 0 && authID != state._authId || state._authId == 0 && authID != state._seed)
|
||||
{
|
||||
state.LogInfo("Invalid client detected, disconnecting...");
|
||||
state.Disconnect("Invalid auth id in game login packet.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_AuthIDWindow.Remove(authID);
|
||||
state.Version = ap.Version;
|
||||
|
||||
var username = reader.ReadAscii(30);
|
||||
var password = reader.ReadAscii(30);
|
||||
|
||||
var e = new GameLoginEventArgs(state, username, password);
|
||||
|
||||
EventSink.InvokeGameLogin(e);
|
||||
|
||||
if (e.Accepted)
|
||||
{
|
||||
state.CityInfo = e.CityInfo;
|
||||
|
||||
// Comment out these lines to turn off huffman compression
|
||||
state.CompressionEnabled = true;
|
||||
state.PacketEncoder ??= NetworkCompression.Compress;
|
||||
|
||||
state.SendSupportedFeature();
|
||||
state.SendCharacterList();
|
||||
}
|
||||
else
|
||||
{
|
||||
state.Disconnect("Login rejected by GameLogin packet handler.");
|
||||
}
|
||||
}
|
||||
|
||||
state.Sequence = 0;
|
||||
public static void PlayServer(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int index = reader.ReadInt16();
|
||||
var info = state.ServerInfo;
|
||||
var a = state.Account;
|
||||
|
||||
state.SendMobileUpdate(m);
|
||||
state.SendMobileUpdate(m);
|
||||
if (info == null || a == null || index < 0 || index >= info.Length)
|
||||
{
|
||||
state.Disconnect("Invalid server selected.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var si = info[index];
|
||||
|
||||
m.CheckLightLevels(true);
|
||||
state._authId = GenerateAuthID(state);
|
||||
|
||||
state.SendMobileUpdate(m);
|
||||
state.SentFirstPacket = false;
|
||||
state.SendPlayServerAck(si, state._authId);
|
||||
}
|
||||
}
|
||||
|
||||
state.SendMobileIncoming(m, m);
|
||||
public static void LoginServerSeed(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
state._seed = reader.ReadInt32();
|
||||
state.Seeded = true;
|
||||
|
||||
state.SendMobileStatus(m);
|
||||
state.SendSetWarMode(m.Warmode);
|
||||
|
||||
m.SendEverything();
|
||||
|
||||
state.SendSupportedFeature();
|
||||
state.SendMobileUpdate(m);
|
||||
|
||||
state.SendMobileStatus(m);
|
||||
state.SendSetWarMode(m.Warmode);
|
||||
state.SendMobileIncoming(m, m);
|
||||
|
||||
state.SendLoginComplete();
|
||||
state.SendCurrentTime();
|
||||
state.SendSeasonChange((byte)m.GetSeason(), true);
|
||||
state.SendMapChange(m.Map);
|
||||
|
||||
state.SendPlayMusic(m.Region.Music);
|
||||
|
||||
EventSink.InvokeLogin(m);
|
||||
if (state._seed == 0)
|
||||
{
|
||||
state.LogInfo("Invalid client detected, disconnecting");
|
||||
state.Disconnect("Duplicate seed sent.");
|
||||
return;
|
||||
}
|
||||
|
||||
private static int GenerateAuthID(this NetState state)
|
||||
var clientMaj = reader.ReadInt32();
|
||||
var clientMin = reader.ReadInt32();
|
||||
var clientRev = reader.ReadInt32();
|
||||
var clientPat = reader.ReadInt32();
|
||||
|
||||
state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat);
|
||||
}
|
||||
|
||||
public static void AccountLogin(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
// TODO: Throttle Connection
|
||||
|
||||
if (state.SentFirstPacket)
|
||||
{
|
||||
if (m_AuthIDWindow.Count == m_AuthIDWindowSize)
|
||||
{
|
||||
var oldestID = 0;
|
||||
var oldest = DateTime.MaxValue;
|
||||
|
||||
foreach (var (key, authId) in m_AuthIDWindow)
|
||||
{
|
||||
if (authId.Age < oldest)
|
||||
{
|
||||
oldestID = key;
|
||||
oldest = authId.Age;
|
||||
}
|
||||
}
|
||||
|
||||
m_AuthIDWindow.Remove(oldestID);
|
||||
}
|
||||
|
||||
int authID;
|
||||
|
||||
do
|
||||
{
|
||||
authID = Utility.Random(1, int.MaxValue - 1);
|
||||
|
||||
if (Utility.RandomBool())
|
||||
{
|
||||
authID |= 1 << 31;
|
||||
}
|
||||
} while (m_AuthIDWindow.ContainsKey(authID));
|
||||
|
||||
m_AuthIDWindow[authID] = new AuthIDPersistence(state.Version);
|
||||
|
||||
return authID;
|
||||
state.Disconnect("Duplicate account login packet sent.");
|
||||
return;
|
||||
}
|
||||
|
||||
public static void GameLogin(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
state.SentFirstPacket = true;
|
||||
|
||||
var username = reader.ReadAscii(30);
|
||||
var password = reader.ReadAscii(30);
|
||||
|
||||
var accountLoginEventArgs = new AccountLoginEventArgs(state, username, password);
|
||||
|
||||
EventSink.InvokeAccountLogin(accountLoginEventArgs);
|
||||
|
||||
if (accountLoginEventArgs.Accepted)
|
||||
{
|
||||
// TODO: Connection throttling
|
||||
var serverListEventArgs = new ServerListEventArgs(state, state.Account);
|
||||
|
||||
if (state.SentFirstPacket)
|
||||
EventSink.InvokeServerList(serverListEventArgs);
|
||||
|
||||
if (serverListEventArgs.Rejected)
|
||||
{
|
||||
state.Disconnect("Duplicate game login packet received.");
|
||||
return;
|
||||
}
|
||||
|
||||
state.SentFirstPacket = true;
|
||||
|
||||
var authID = reader.ReadInt32();
|
||||
|
||||
if (!m_AuthIDWindow.TryGetValue(authID, out var ap))
|
||||
{
|
||||
state.LogInfo("Invalid client detected, disconnecting...");
|
||||
state.Disconnect("Unable to find auth id.");
|
||||
}
|
||||
|
||||
if (state._authId != 0 && authID != state._authId || state._authId == 0 && authID != state._seed)
|
||||
{
|
||||
state.LogInfo("Invalid client detected, disconnecting...");
|
||||
state.Disconnect("Invalid auth id in game login packet.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_AuthIDWindow.Remove(authID);
|
||||
state.Version = ap.Version;
|
||||
|
||||
var username = reader.ReadAscii(30);
|
||||
var password = reader.ReadAscii(30);
|
||||
|
||||
var e = new GameLoginEventArgs(state, username, password);
|
||||
|
||||
EventSink.InvokeGameLogin(e);
|
||||
|
||||
if (e.Accepted)
|
||||
{
|
||||
state.CityInfo = e.CityInfo;
|
||||
|
||||
// Comment out these lines to turn off huffman compression
|
||||
state.CompressionEnabled = true;
|
||||
state.PacketEncoder ??= NetworkCompression.Compress;
|
||||
|
||||
state.SendSupportedFeature();
|
||||
state.SendCharacterList();
|
||||
state.Account = null;
|
||||
AccountLogin_ReplyRej(state, ALRReason.BadComm);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.Disconnect("Login rejected by GameLogin packet handler.");
|
||||
state.ServerInfo = serverListEventArgs.Servers.ToArray();
|
||||
state.SendAccountLoginAck();
|
||||
}
|
||||
}
|
||||
|
||||
public static void PlayServer(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
else
|
||||
{
|
||||
int index = reader.ReadInt16();
|
||||
var info = state.ServerInfo;
|
||||
var a = state.Account;
|
||||
AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason);
|
||||
}
|
||||
}
|
||||
|
||||
if (info == null || a == null || index < 0 || index >= info.Length)
|
||||
{
|
||||
state.Disconnect("Invalid server selected.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var si = info[index];
|
||||
private static void AccountLogin_ReplyRej(this NetState state, ALRReason reason)
|
||||
{
|
||||
state.SendAccountLoginRejected(reason);
|
||||
state.Disconnect($"Account login rejected due to {reason}");
|
||||
}
|
||||
|
||||
state._authId = GenerateAuthID(state);
|
||||
private class LoginTimer : Timer
|
||||
{
|
||||
private readonly Mobile _mobile;
|
||||
private readonly NetState _state;
|
||||
|
||||
state.SentFirstPacket = false;
|
||||
state.SendPlayServerAck(si, state._authId);
|
||||
}
|
||||
public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromMilliseconds(64), TimeSpan.FromMilliseconds(64))
|
||||
{
|
||||
_state = state;
|
||||
_mobile = m;
|
||||
}
|
||||
|
||||
public static void LoginServerSeed(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
protected override void OnTick()
|
||||
{
|
||||
state._seed = reader.ReadInt32();
|
||||
state.Seeded = true;
|
||||
|
||||
if (state._seed == 0)
|
||||
if (_state != null)
|
||||
{
|
||||
state.LogInfo("Invalid client detected, disconnecting");
|
||||
state.Disconnect("Duplicate seed sent.");
|
||||
return;
|
||||
}
|
||||
|
||||
var clientMaj = reader.ReadInt32();
|
||||
var clientMin = reader.ReadInt32();
|
||||
var clientRev = reader.ReadInt32();
|
||||
var clientPat = reader.ReadInt32();
|
||||
|
||||
state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat);
|
||||
}
|
||||
|
||||
public static void AccountLogin(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
// TODO: Throttle Connection
|
||||
|
||||
if (state.SentFirstPacket)
|
||||
{
|
||||
state.Disconnect("Duplicate account login packet sent.");
|
||||
return;
|
||||
}
|
||||
|
||||
state.SentFirstPacket = true;
|
||||
|
||||
var username = reader.ReadAscii(30);
|
||||
var password = reader.ReadAscii(30);
|
||||
|
||||
var accountLoginEventArgs = new AccountLoginEventArgs(state, username, password);
|
||||
|
||||
EventSink.InvokeAccountLogin(accountLoginEventArgs);
|
||||
|
||||
if (accountLoginEventArgs.Accepted)
|
||||
{
|
||||
var serverListEventArgs = new ServerListEventArgs(state, state.Account);
|
||||
|
||||
EventSink.InvokeServerList(serverListEventArgs);
|
||||
|
||||
if (serverListEventArgs.Rejected)
|
||||
if (_state.Account == null)
|
||||
{
|
||||
state.Account = null;
|
||||
AccountLogin_ReplyRej(state, ALRReason.BadComm);
|
||||
_state.Disconnect("Account was deleted during the login process.");
|
||||
}
|
||||
else
|
||||
else if (_mobile == null)
|
||||
{
|
||||
state.ServerInfo = serverListEventArgs.Servers.ToArray();
|
||||
state.SendAccountLoginAck();
|
||||
_state.Disconnect("Player was deleted during the login process.");
|
||||
}
|
||||
else if (_state.Version != null)
|
||||
{
|
||||
_state.BlockAllPackets = false;
|
||||
DoLogin(_state, _mobile);
|
||||
}
|
||||
else // Waiting to receive the client version before we continue the login process
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AccountLogin_ReplyRej(state, accountLoginEventArgs.RejectReason);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AccountLogin_ReplyRej(this NetState state, ALRReason reason)
|
||||
{
|
||||
state.SendAccountLoginRejected(reason);
|
||||
state.Disconnect($"Account login rejected due to {reason}");
|
||||
}
|
||||
|
||||
private class LoginTimer : Timer
|
||||
{
|
||||
private readonly Mobile _mobile;
|
||||
private readonly NetState _state;
|
||||
|
||||
public LoginTimer(NetState state, Mobile m) : base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
|
||||
{
|
||||
_state = state;
|
||||
_mobile = m;
|
||||
}
|
||||
|
||||
protected override void OnTick()
|
||||
{
|
||||
if (_state != null)
|
||||
{
|
||||
if (_state.Account == null)
|
||||
{
|
||||
_state.Disconnect("Account was deleted during the login process.");
|
||||
}
|
||||
else if (_mobile == null)
|
||||
{
|
||||
_state.Disconnect("Player was deleted during the login process.");
|
||||
}
|
||||
else if (_state.Version != null)
|
||||
{
|
||||
_state.BlockAllPackets = false;
|
||||
DoLogin(_state, _mobile);
|
||||
}
|
||||
else // Waiting to receive the client version before we continue the login process
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Stop();
|
||||
}
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,97 +13,160 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingEntityPackets
|
||||
{
|
||||
public static class IncomingEntityPackets
|
||||
public static bool SingleClickProps { get; set; }
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
public static bool SingleClickProps { get; set; }
|
||||
IncomingPackets.Register(0x06, 5, true, UseReq);
|
||||
IncomingPackets.Register(0x09, 5, true, LookReq);
|
||||
IncomingPackets.Register(0xB6, 9, true, ObjectHelpRequest);
|
||||
IncomingPackets.Register(0xD6, 0, true, BatchQueryProperties);
|
||||
}
|
||||
|
||||
public static void Configure()
|
||||
public static void ObjectHelpRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
int unk = reader.ReadByte();
|
||||
var lang = reader.ReadAscii(3);
|
||||
|
||||
if (serial.IsItem)
|
||||
{
|
||||
IncomingPackets.Register(0x06, 5, true, UseReq);
|
||||
IncomingPackets.Register(0x09, 5, true, LookReq);
|
||||
IncomingPackets.Register(0xB6, 9, true, ObjectHelpRequest);
|
||||
IncomingPackets.Register(0xD6, 0, true, BatchQueryProperties);
|
||||
}
|
||||
var item = World.FindItem(serial);
|
||||
|
||||
public static void ObjectHelpRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
int unk = reader.ReadByte();
|
||||
var lang = reader.ReadAscii(3);
|
||||
|
||||
if (serial.IsItem)
|
||||
if (item != null && from.Map == item.Map && Utility.InUpdateRange(item.GetWorldLocation(), from.Location) &&
|
||||
from.CanSee(item))
|
||||
{
|
||||
var item = World.FindItem(serial);
|
||||
|
||||
if (item != null && from.Map == item.Map && Utility.InUpdateRange(item.GetWorldLocation(), from.Location) &&
|
||||
from.CanSee(item))
|
||||
{
|
||||
item.OnHelpRequest(from);
|
||||
}
|
||||
}
|
||||
else if (serial.IsMobile)
|
||||
{
|
||||
var m = World.FindMobile(serial);
|
||||
|
||||
if (m != null && from.Map == m.Map && Utility.InUpdateRange(m.Location, from.Location) && from.CanSee(m))
|
||||
{
|
||||
m.OnHelpRequest(m);
|
||||
}
|
||||
item.OnHelpRequest(from);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UseReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
else if (serial.IsMobile)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
var m = World.FindMobile(serial);
|
||||
|
||||
if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0)
|
||||
if (m != null && from.Map == m.Map && Utility.InUpdateRange(m.Location, from.Location) && from.CanSee(m))
|
||||
{
|
||||
var value = reader.ReadUInt32();
|
||||
m.OnHelpRequest(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((value & ~0x7FFFFFFF) != 0)
|
||||
{
|
||||
from.OnPaperdollRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial s = (Serial)value;
|
||||
public static void UseReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (s.IsMobile)
|
||||
{
|
||||
var m = World.FindMobile(s);
|
||||
if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0)
|
||||
{
|
||||
var value = reader.ReadUInt32();
|
||||
|
||||
if (m?.Deleted == false)
|
||||
{
|
||||
from.Use(m);
|
||||
}
|
||||
}
|
||||
else if (s.IsItem)
|
||||
{
|
||||
var item = World.FindItem(s);
|
||||
|
||||
if (item?.Deleted == false)
|
||||
{
|
||||
from.Use(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
|
||||
if ((value & ~0x7FFFFFFF) != 0)
|
||||
{
|
||||
from.OnPaperdollRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendActionMessage();
|
||||
Serial s = (Serial)value;
|
||||
|
||||
if (s.IsMobile)
|
||||
{
|
||||
var m = World.FindMobile(s);
|
||||
|
||||
if (m?.Deleted == false)
|
||||
{
|
||||
from.Use(m);
|
||||
}
|
||||
}
|
||||
else if (s.IsItem)
|
||||
{
|
||||
var item = World.FindItem(s);
|
||||
|
||||
if (item?.Deleted == false)
|
||||
{
|
||||
from.Use(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendActionMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public static void LookReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
Serial s = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (s.IsMobile)
|
||||
{
|
||||
var m = World.FindMobile(s);
|
||||
|
||||
if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location))
|
||||
{
|
||||
if (SingleClickProps)
|
||||
{
|
||||
m.OnAosSingleClick(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (from.Region.OnSingleClick(from, m))
|
||||
{
|
||||
m.OnSingleClick(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void LookReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
else if (s.IsItem)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
var item = World.FindItem(s);
|
||||
|
||||
if (item?.Deleted == false && from.CanSee(item) &&
|
||||
Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
|
||||
{
|
||||
if (SingleClickProps)
|
||||
{
|
||||
item.OnAosSingleClick(from);
|
||||
}
|
||||
else if (from.Region.OnSingleClick(from, item))
|
||||
{
|
||||
if (item.Parent is Item parentItem)
|
||||
{
|
||||
parentItem.OnSingleClickContained(from, item);
|
||||
}
|
||||
|
||||
item.OnSingleClick(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void BatchQueryProperties(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
if (!ObjectPropertyList.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var from = state.Mobile;
|
||||
|
||||
var length = reader.Remaining;
|
||||
|
||||
if (length % 4 != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (reader.Remaining > 0)
|
||||
{
|
||||
Serial s = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (s.IsMobile)
|
||||
|
|
@ -112,17 +175,7 @@ namespace Server.Network
|
|||
|
||||
if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location))
|
||||
{
|
||||
if (SingleClickProps)
|
||||
{
|
||||
m.OnAosSingleClick(from);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (from.Region.OnSingleClick(from, m))
|
||||
{
|
||||
m.OnSingleClick(from);
|
||||
}
|
||||
}
|
||||
m.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
else if (s.IsItem)
|
||||
|
|
@ -132,61 +185,7 @@ namespace Server.Network
|
|||
if (item?.Deleted == false && from.CanSee(item) &&
|
||||
Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
|
||||
{
|
||||
if (SingleClickProps)
|
||||
{
|
||||
item.OnAosSingleClick(from);
|
||||
}
|
||||
else if (from.Region.OnSingleClick(from, item))
|
||||
{
|
||||
if (item.Parent is Item parentItem)
|
||||
{
|
||||
parentItem.OnSingleClickContained(from, item);
|
||||
}
|
||||
|
||||
item.OnSingleClick(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void BatchQueryProperties(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
if (!ObjectPropertyList.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var from = state.Mobile;
|
||||
|
||||
var length = reader.Remaining;
|
||||
|
||||
if (length % 4 != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (reader.Remaining > 0)
|
||||
{
|
||||
Serial s = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (s.IsMobile)
|
||||
{
|
||||
var m = World.FindMobile(s);
|
||||
|
||||
if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location))
|
||||
{
|
||||
m.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
else if (s.IsItem)
|
||||
{
|
||||
var item = World.FindItem(s);
|
||||
|
||||
if (item?.Deleted == false && from.CanSee(item) &&
|
||||
Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
|
||||
{
|
||||
item.SendPropertiesTo(from);
|
||||
}
|
||||
item.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,502 +16,501 @@
|
|||
using System.Collections.Generic;
|
||||
using Server.ContextMenus;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingExtendedCommandPackets
|
||||
{
|
||||
public static class IncomingExtendedCommandPackets
|
||||
private static readonly PacketHandler[] m_ExtendedHandlersLow = new PacketHandler[0x100];
|
||||
private static readonly Dictionary<int, PacketHandler> m_ExtendedHandlersHigh = new();
|
||||
|
||||
// TODO: Change to outside configuration
|
||||
public static int[] ValidAnimations { get; set; } =
|
||||
{
|
||||
private static readonly PacketHandler[] m_ExtendedHandlersLow = new PacketHandler[0x100];
|
||||
private static readonly Dictionary<int, PacketHandler> m_ExtendedHandlersHigh = new();
|
||||
6, 21, 32, 33,
|
||||
100, 101, 102, 103,
|
||||
104, 105, 106, 107,
|
||||
108, 109, 110, 111,
|
||||
112, 113, 114, 115,
|
||||
116, 117, 118, 119,
|
||||
120, 121, 123, 124,
|
||||
125, 126, 127, 128
|
||||
};
|
||||
|
||||
// TODO: Change to outside configuration
|
||||
public static int[] ValidAnimations { get; set; } =
|
||||
public static void Configure()
|
||||
{
|
||||
IncomingPackets.Register(0xBF, 0, true, ExtendedCommand);
|
||||
|
||||
RegisterExtended(0x05, false, ScreenSize);
|
||||
RegisterExtended(0x06, true, PartyMessage);
|
||||
RegisterExtended(0x09, true, DisarmRequest);
|
||||
RegisterExtended(0x0A, true, StunRequest);
|
||||
RegisterExtended(0x0B, false, Language);
|
||||
RegisterExtended(0x0C, true, CloseStatus);
|
||||
RegisterExtended(0x0E, true, Animate);
|
||||
RegisterExtended(0x0F, false, Empty); // What's this?
|
||||
RegisterExtended(0x10, true, QueryProperties);
|
||||
RegisterExtended(0x13, true, ContextMenuRequest);
|
||||
RegisterExtended(0x15, true, ContextMenuResponse);
|
||||
RegisterExtended(0x1A, true, StatLockChange);
|
||||
RegisterExtended(0x1C, true, CastSpell);
|
||||
RegisterExtended(0x24, false, UnhandledBF);
|
||||
RegisterExtended(0x2C, true, BandageTarget);
|
||||
RegisterExtended(0x2D, true, TargetedSpell);
|
||||
RegisterExtended(0x2E, true, TargetedSkillUse);
|
||||
RegisterExtended(0x30, true, TargetByResourceMacro);
|
||||
RegisterExtended(0x32, true, ToggleFlying);
|
||||
}
|
||||
|
||||
private static void UnhandledBF(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
}
|
||||
|
||||
public static void Empty(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
}
|
||||
|
||||
public static void RegisterExtended(int packetID, bool ingame, OnPacketReceive onReceive)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
6, 21, 32, 33,
|
||||
100, 101, 102, 103,
|
||||
104, 105, 106, 107,
|
||||
108, 109, 110, 111,
|
||||
112, 113, 114, 115,
|
||||
116, 117, 118, 119,
|
||||
120, 121, 123, 124,
|
||||
125, 126, 127, 128
|
||||
};
|
||||
|
||||
public static void Configure()
|
||||
m_ExtendedHandlersLow[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
|
||||
}
|
||||
else
|
||||
{
|
||||
IncomingPackets.Register(0xBF, 0, true, ExtendedCommand);
|
||||
m_ExtendedHandlersHigh[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
|
||||
}
|
||||
}
|
||||
|
||||
RegisterExtended(0x05, false, ScreenSize);
|
||||
RegisterExtended(0x06, true, PartyMessage);
|
||||
RegisterExtended(0x09, true, DisarmRequest);
|
||||
RegisterExtended(0x0A, true, StunRequest);
|
||||
RegisterExtended(0x0B, false, Language);
|
||||
RegisterExtended(0x0C, true, CloseStatus);
|
||||
RegisterExtended(0x0E, true, Animate);
|
||||
RegisterExtended(0x0F, false, Empty); // What's this?
|
||||
RegisterExtended(0x10, true, QueryProperties);
|
||||
RegisterExtended(0x13, true, ContextMenuRequest);
|
||||
RegisterExtended(0x15, true, ContextMenuResponse);
|
||||
RegisterExtended(0x1A, true, StatLockChange);
|
||||
RegisterExtended(0x1C, true, CastSpell);
|
||||
RegisterExtended(0x24, false, UnhandledBF);
|
||||
RegisterExtended(0x2C, true, BandageTarget);
|
||||
RegisterExtended(0x2D, true, TargetedSpell);
|
||||
RegisterExtended(0x2E, true, TargetedSkillUse);
|
||||
RegisterExtended(0x30, true, TargetByResourceMacro);
|
||||
RegisterExtended(0x32, true, ToggleFlying);
|
||||
public static PacketHandler GetExtendedHandler(int packetID)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
return m_ExtendedHandlersLow[packetID];
|
||||
}
|
||||
|
||||
private static void UnhandledBF(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
m_ExtendedHandlersHigh.TryGetValue(packetID, out var handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static void RemoveExtendedHandler(int packetID)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
m_ExtendedHandlersLow[packetID] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ExtendedHandlersHigh.Remove(packetID);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExtendedCommand(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int packetId = reader.ReadUInt16();
|
||||
|
||||
var ph = GetExtendedHandler(packetId);
|
||||
|
||||
if (ph == null)
|
||||
{
|
||||
reader.Trace(state);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void Empty(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
}
|
||||
|
||||
public static void RegisterExtended(int packetID, bool ingame, OnPacketReceive onReceive)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
m_ExtendedHandlersLow[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ExtendedHandlersHigh[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
|
||||
}
|
||||
}
|
||||
|
||||
public static PacketHandler GetExtendedHandler(int packetID)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
return m_ExtendedHandlersLow[packetID];
|
||||
}
|
||||
|
||||
m_ExtendedHandlersHigh.TryGetValue(packetID, out var handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static void RemoveExtendedHandler(int packetID)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
m_ExtendedHandlersLow[packetID] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ExtendedHandlersHigh.Remove(packetID);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExtendedCommand(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int packetId = reader.ReadUInt16();
|
||||
|
||||
var ph = GetExtendedHandler(packetId);
|
||||
|
||||
if (ph == null)
|
||||
{
|
||||
reader.Trace(state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ph.Ingame && state.Mobile?.Deleted != false)
|
||||
{
|
||||
if (state.Mobile == null)
|
||||
{
|
||||
state.LogInfo(
|
||||
"Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile",
|
||||
packetId
|
||||
);
|
||||
}
|
||||
|
||||
state.Disconnect($"Sent in-game packet(0xBFx{packetId:X2}) but mobile is deleted.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ph.OnReceive(state, reader, ref packetLength);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ScreenSize(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var width = reader.ReadInt32();
|
||||
var unk = reader.ReadInt32();
|
||||
}
|
||||
|
||||
// TODO: Move out of the core
|
||||
public static void PartyMessage(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
if (ph.Ingame && state.Mobile?.Deleted != false)
|
||||
{
|
||||
if (state.Mobile == null)
|
||||
{
|
||||
return;
|
||||
state.LogInfo(
|
||||
"Sent in-game packet (0xBFx{0:X2}) before having been attached to a mobile",
|
||||
packetId
|
||||
);
|
||||
}
|
||||
|
||||
switch (reader.ReadByte())
|
||||
state.Disconnect($"Sent in-game packet(0xBFx{packetId:X2}) but mobile is deleted.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ph.OnReceive(state, reader, ref packetLength);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ScreenSize(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var width = reader.ReadInt32();
|
||||
var unk = reader.ReadInt32();
|
||||
}
|
||||
|
||||
// TODO: Move out of the core
|
||||
public static void PartyMessage(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
if (state.Mobile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (reader.ReadByte())
|
||||
{
|
||||
case 0x01:
|
||||
PartyMessage_AddMember(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x02:
|
||||
PartyMessage_RemoveMember(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x03:
|
||||
PartyMessage_PrivateMessage(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x04:
|
||||
PartyMessage_PublicMessage(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x06:
|
||||
PartyMessage_SetCanLoot(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x08:
|
||||
PartyMessage_Accept(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x09:
|
||||
PartyMessage_Decline(state, reader, ref packetLength);
|
||||
break;
|
||||
default:
|
||||
reader.Trace(state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnAdd(state.Mobile);
|
||||
}
|
||||
|
||||
public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32()));
|
||||
}
|
||||
|
||||
public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnPrivateMessage(
|
||||
state.Mobile,
|
||||
World.FindMobile((Serial)reader.ReadUInt32()),
|
||||
reader.ReadBigUniSafe()
|
||||
);
|
||||
}
|
||||
|
||||
public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe());
|
||||
}
|
||||
|
||||
public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean());
|
||||
}
|
||||
|
||||
public static void PartyMessage_Accept(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32()));
|
||||
}
|
||||
|
||||
public static void PartyMessage_Decline(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32()));
|
||||
}
|
||||
|
||||
public static void Animate(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var action = reader.ReadInt32();
|
||||
|
||||
var ok = false;
|
||||
|
||||
for (var i = 0; !ok && i < ValidAnimations.Length; ++i)
|
||||
{
|
||||
ok = action == ValidAnimations[i];
|
||||
}
|
||||
|
||||
if (ok && from.Alive && from.Body.IsHuman && !from.Mounted)
|
||||
{
|
||||
from.Animate(action, 7, 1, true, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CastSpell(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Item spellbook = reader.ReadInt16() == 1 ? World.FindItem((Serial)reader.ReadUInt32()) : null;
|
||||
|
||||
var spellID = reader.ReadInt16() - 1;
|
||||
EventSink.InvokeCastSpellRequest(from, spellID, spellbook);
|
||||
}
|
||||
|
||||
public static void ToggleFlying(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
state.Mobile?.ToggleFlying();
|
||||
}
|
||||
|
||||
public static void StunRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EventSink.InvokeStunRequest(from);
|
||||
}
|
||||
|
||||
public static void DisarmRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EventSink.InvokeDisarmRequest(from);
|
||||
}
|
||||
|
||||
public static void StatLockChange(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int stat = reader.ReadByte();
|
||||
int lockValue = reader.ReadByte();
|
||||
|
||||
if (lockValue > 2)
|
||||
{
|
||||
lockValue = 0;
|
||||
}
|
||||
|
||||
switch (stat)
|
||||
{
|
||||
case 0:
|
||||
from.StrLock = (StatLockType)lockValue;
|
||||
break;
|
||||
case 1:
|
||||
from.DexLock = (StatLockType)lockValue;
|
||||
break;
|
||||
case 2:
|
||||
from.IntLock = (StatLockType)lockValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CloseStatus(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
}
|
||||
|
||||
public static void Language(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
from.Language = reader.ReadAscii(4);
|
||||
}
|
||||
|
||||
public static void QueryProperties(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
if (!ObjectPropertyList.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var from = state.Mobile;
|
||||
|
||||
Serial s = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (s.IsMobile)
|
||||
{
|
||||
var m = World.FindMobile(s);
|
||||
|
||||
if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location))
|
||||
{
|
||||
case 0x01:
|
||||
PartyMessage_AddMember(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x02:
|
||||
PartyMessage_RemoveMember(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x03:
|
||||
PartyMessage_PrivateMessage(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x04:
|
||||
PartyMessage_PublicMessage(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x06:
|
||||
PartyMessage_SetCanLoot(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x08:
|
||||
PartyMessage_Accept(state, reader, ref packetLength);
|
||||
break;
|
||||
case 0x09:
|
||||
PartyMessage_Decline(state, reader, ref packetLength);
|
||||
break;
|
||||
default:
|
||||
reader.Trace(state);
|
||||
break;
|
||||
m.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
else if (s.IsItem)
|
||||
{
|
||||
PartyCommands.Handler?.OnAdd(state.Mobile);
|
||||
}
|
||||
var item = World.FindItem(s);
|
||||
|
||||
public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32()));
|
||||
}
|
||||
|
||||
public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnPrivateMessage(
|
||||
state.Mobile,
|
||||
World.FindMobile((Serial)reader.ReadUInt32()),
|
||||
reader.ReadBigUniSafe()
|
||||
);
|
||||
}
|
||||
|
||||
public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe());
|
||||
}
|
||||
|
||||
public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean());
|
||||
}
|
||||
|
||||
public static void PartyMessage_Accept(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32()));
|
||||
}
|
||||
|
||||
public static void PartyMessage_Decline(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile((Serial)reader.ReadUInt32()));
|
||||
}
|
||||
|
||||
public static void Animate(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
if (item?.Deleted == false && from.CanSee(item) &&
|
||||
Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var action = reader.ReadInt32();
|
||||
|
||||
var ok = false;
|
||||
|
||||
for (var i = 0; !ok && i < ValidAnimations.Length; ++i)
|
||||
{
|
||||
ok = action == ValidAnimations[i];
|
||||
}
|
||||
|
||||
if (ok && from.Alive && from.Body.IsHuman && !from.Mounted)
|
||||
{
|
||||
from.Animate(action, 7, 1, true, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CastSpell(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Item spellbook = reader.ReadInt16() == 1 ? World.FindItem((Serial)reader.ReadUInt32()) : null;
|
||||
|
||||
var spellID = reader.ReadInt16() - 1;
|
||||
EventSink.InvokeCastSpellRequest(from, spellID, spellbook);
|
||||
}
|
||||
|
||||
public static void ToggleFlying(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
state.Mobile?.ToggleFlying();
|
||||
}
|
||||
|
||||
public static void StunRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EventSink.InvokeStunRequest(from);
|
||||
}
|
||||
|
||||
public static void DisarmRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EventSink.InvokeDisarmRequest(from);
|
||||
}
|
||||
|
||||
public static void StatLockChange(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int stat = reader.ReadByte();
|
||||
int lockValue = reader.ReadByte();
|
||||
|
||||
if (lockValue > 2)
|
||||
{
|
||||
lockValue = 0;
|
||||
}
|
||||
|
||||
switch (stat)
|
||||
{
|
||||
case 0:
|
||||
from.StrLock = (StatLockType)lockValue;
|
||||
break;
|
||||
case 1:
|
||||
from.DexLock = (StatLockType)lockValue;
|
||||
break;
|
||||
case 2:
|
||||
from.IntLock = (StatLockType)lockValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CloseStatus(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
}
|
||||
|
||||
public static void Language(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
from.Language = reader.ReadAscii(4);
|
||||
}
|
||||
|
||||
public static void QueryProperties(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
if (!ObjectPropertyList.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var from = state.Mobile;
|
||||
|
||||
Serial s = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (s.IsMobile)
|
||||
{
|
||||
var m = World.FindMobile(s);
|
||||
|
||||
if (m != null && from.CanSee(m) && Utility.InUpdateRange(from.Location, m.Location))
|
||||
{
|
||||
m.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
else if (s.IsItem)
|
||||
{
|
||||
var item = World.FindItem(s);
|
||||
|
||||
if (item?.Deleted == false && from.CanSee(item) &&
|
||||
Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
|
||||
{
|
||||
item.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ContextMenuResponse(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = from.ContextMenu;
|
||||
|
||||
from.ContextMenu = null;
|
||||
|
||||
if (menu != null && from == menu.From)
|
||||
{
|
||||
var entity = World.FindEntity((Serial)reader.ReadUInt32());
|
||||
|
||||
if (entity != null && entity == menu.Target && from.CanSee(entity))
|
||||
{
|
||||
Point3D p;
|
||||
|
||||
if (entity is Mobile)
|
||||
{
|
||||
p = entity.Location;
|
||||
}
|
||||
else if (entity is Item item)
|
||||
{
|
||||
p = item.GetWorldLocation();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int index = reader.ReadUInt16();
|
||||
|
||||
if (index >= 0 && index < menu.Entries.Length)
|
||||
{
|
||||
var e = menu.Entries[index];
|
||||
|
||||
var range = e.Range;
|
||||
|
||||
if (range == -1)
|
||||
{
|
||||
range = 18;
|
||||
}
|
||||
|
||||
if (e.Enabled && from.InRange(p, range))
|
||||
{
|
||||
e.OnClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ContextMenuRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
var target = World.FindEntity((Serial)reader.ReadUInt32());
|
||||
|
||||
if (from != null && target != null && from.Map == target.Map && from.CanSee(target))
|
||||
{
|
||||
var item = target as Item;
|
||||
|
||||
var checkLocation = item?.GetWorldLocation() ?? target.Location;
|
||||
if (!(Utility.InUpdateRange(from.Location, checkLocation) && from.CheckContextMenuDisplay(target)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var c = new ContextMenu(from, target);
|
||||
|
||||
if (c.Entries.Length > 0)
|
||||
{
|
||||
if (item?.RootParent is Mobile mobile && mobile != from && mobile.AccessLevel >= from.AccessLevel)
|
||||
{
|
||||
for (var i = 0; i < c.Entries.Length; ++i)
|
||||
{
|
||||
if (!c.Entries[i].NonLocalUse)
|
||||
{
|
||||
c.Entries[i].Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
from.ContextMenu = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void BandageTarget(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0)
|
||||
{
|
||||
var bandage = World.FindItem((Serial)reader.ReadUInt32());
|
||||
|
||||
if (bandage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var target = World.FindMobile((Serial)reader.ReadUInt32());
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EventSink.InvokeBandageTargetRequest(from, bandage, target);
|
||||
|
||||
from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendActionMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public static void TargetedSpell(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var spellId = (short)(reader.ReadInt16() - 1); // zero based;
|
||||
|
||||
EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), spellId);
|
||||
}
|
||||
|
||||
public static void TargetedSkillUse(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var skillId = reader.ReadInt16();
|
||||
|
||||
EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), skillId);
|
||||
}
|
||||
|
||||
public static void TargetByResourceMacro(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (serial.IsItem)
|
||||
{
|
||||
EventSink.InvokeTargetByResourceMacro(state.Mobile, World.FindItem(serial), reader.ReadInt16());
|
||||
item.SendPropertiesTo(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ContextMenuResponse(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = from.ContextMenu;
|
||||
|
||||
from.ContextMenu = null;
|
||||
|
||||
if (menu != null && from == menu.From)
|
||||
{
|
||||
var entity = World.FindEntity((Serial)reader.ReadUInt32());
|
||||
|
||||
if (entity != null && entity == menu.Target && from.CanSee(entity))
|
||||
{
|
||||
Point3D p;
|
||||
|
||||
if (entity is Mobile)
|
||||
{
|
||||
p = entity.Location;
|
||||
}
|
||||
else if (entity is Item item)
|
||||
{
|
||||
p = item.GetWorldLocation();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int index = reader.ReadUInt16();
|
||||
|
||||
if (index >= 0 && index < menu.Entries.Length)
|
||||
{
|
||||
var e = menu.Entries[index];
|
||||
|
||||
var range = e.Range;
|
||||
|
||||
if (range == -1)
|
||||
{
|
||||
range = 18;
|
||||
}
|
||||
|
||||
if (e.Enabled && from.InRange(p, range))
|
||||
{
|
||||
e.OnClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ContextMenuRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
var target = World.FindEntity((Serial)reader.ReadUInt32());
|
||||
|
||||
if (from != null && target != null && from.Map == target.Map && from.CanSee(target))
|
||||
{
|
||||
var item = target as Item;
|
||||
|
||||
var checkLocation = item?.GetWorldLocation() ?? target.Location;
|
||||
if (!(Utility.InUpdateRange(from.Location, checkLocation) && from.CheckContextMenuDisplay(target)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var c = new ContextMenu(from, target);
|
||||
|
||||
if (c.Entries.Length > 0)
|
||||
{
|
||||
if (item?.RootParent is Mobile mobile && mobile != from && mobile.AccessLevel >= from.AccessLevel)
|
||||
{
|
||||
for (var i = 0; i < c.Entries.Length; ++i)
|
||||
{
|
||||
if (!c.Entries[i].NonLocalUse)
|
||||
{
|
||||
c.Entries[i].Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
from.ContextMenu = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void BandageTarget(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (from.AccessLevel >= AccessLevel.Counselor || Core.TickCount - from.NextActionTime >= 0)
|
||||
{
|
||||
var bandage = World.FindItem((Serial)reader.ReadUInt32());
|
||||
|
||||
if (bandage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var target = World.FindMobile((Serial)reader.ReadUInt32());
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EventSink.InvokeBandageTargetRequest(from, bandage, target);
|
||||
|
||||
from.NextActionTime = Core.TickCount + Mobile.ActionDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
from.SendActionMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public static void TargetedSpell(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var spellId = (short)(reader.ReadInt16() - 1); // zero based;
|
||||
|
||||
EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), spellId);
|
||||
}
|
||||
|
||||
public static void TargetedSkillUse(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var skillId = reader.ReadInt16();
|
||||
|
||||
EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity((Serial)reader.ReadUInt32()), skillId);
|
||||
}
|
||||
|
||||
public static void TargetByResourceMacro(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (serial.IsItem)
|
||||
{
|
||||
EventSink.InvokeTargetByResourceMacro(state.Mobile, World.FindItem(serial), reader.ReadInt16());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,18 +13,17 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
namespace Server.Network
|
||||
{
|
||||
public static class IncomingHousePackets
|
||||
{
|
||||
public static void Configure()
|
||||
{
|
||||
IncomingPackets.Register(0xFB, 2, false, ShowPublicHouseContent);
|
||||
}
|
||||
namespace Server.Network;
|
||||
|
||||
public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var showPublicHouseContent = reader.ReadBoolean();
|
||||
}
|
||||
public static class IncomingHousePackets
|
||||
{
|
||||
public static void Configure()
|
||||
{
|
||||
IncomingPackets.Register(0xFB, 2, false, ShowPublicHouseContent);
|
||||
}
|
||||
|
||||
public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var showPublicHouseContent = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,159 +17,158 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingItemPackets
|
||||
{
|
||||
public static class IncomingItemPackets
|
||||
public static void Configure()
|
||||
{
|
||||
public static void Configure()
|
||||
IncomingPackets.Register(0x07, 7, true, LiftReq);
|
||||
IncomingPackets.Register(0x08, 15, true, DropReq);
|
||||
IncomingPackets.Register(0x13, 10, true, EquipReq);
|
||||
IncomingPackets.Register(0xEC, 0, false, EquipMacro);
|
||||
IncomingPackets.Register(0xED, 0, false, UnequipMacro);
|
||||
}
|
||||
|
||||
public static void LiftReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
int amount = reader.ReadUInt16();
|
||||
var item = World.FindItem(serial);
|
||||
|
||||
state.Mobile.Lift(item, amount, out _, out _);
|
||||
}
|
||||
|
||||
public static void EquipReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
var item = from.Holding;
|
||||
|
||||
var valid = item != null && item.HeldBy == from && item.Map == Map.Internal;
|
||||
|
||||
from.Holding = null;
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
IncomingPackets.Register(0x07, 7, true, LiftReq);
|
||||
IncomingPackets.Register(0x08, 15, true, DropReq);
|
||||
IncomingPackets.Register(0x13, 10, true, EquipReq);
|
||||
IncomingPackets.Register(0xEC, 0, false, EquipMacro);
|
||||
IncomingPackets.Register(0xED, 0, false, UnequipMacro);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void LiftReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
int amount = reader.ReadUInt16();
|
||||
var item = World.FindItem(serial);
|
||||
reader.Seek(5, SeekOrigin.Current);
|
||||
var to = World.FindMobile((Serial)reader.ReadUInt32()) ?? from;
|
||||
|
||||
state.Mobile.Lift(item, amount, out _, out _);
|
||||
if (!to.AllowEquipFrom(from) || !to.EquipItem(item))
|
||||
{
|
||||
item.Bounce(from);
|
||||
}
|
||||
|
||||
public static void EquipReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
item.ClearBounce();
|
||||
}
|
||||
|
||||
public static void DropReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.ReadInt32(); // serial, ignored
|
||||
int x = reader.ReadInt16();
|
||||
int y = reader.ReadInt16();
|
||||
int z = reader.ReadSByte();
|
||||
if (state.ContainerGridLines)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
var item = from.Holding;
|
||||
|
||||
var valid = item != null && item.HeldBy == from && item.Map == Map.Internal;
|
||||
|
||||
from.Holding = null;
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
reader.Seek(5, SeekOrigin.Current);
|
||||
var to = World.FindMobile((Serial)reader.ReadUInt32()) ?? from;
|
||||
|
||||
if (!to.AllowEquipFrom(from) || !to.EquipItem(item))
|
||||
{
|
||||
item.Bounce(from);
|
||||
}
|
||||
|
||||
item.ClearBounce();
|
||||
}
|
||||
|
||||
public static void DropReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.ReadInt32(); // serial, ignored
|
||||
int x = reader.ReadInt16();
|
||||
int y = reader.ReadInt16();
|
||||
int z = reader.ReadSByte();
|
||||
if (state.ContainerGridLines)
|
||||
{
|
||||
reader.ReadByte(); // Grid Location?
|
||||
}
|
||||
else
|
||||
{
|
||||
packetLength -= 1;
|
||||
}
|
||||
|
||||
Serial dest = (Serial)reader.ReadUInt32();
|
||||
|
||||
var loc = new Point3D(x, y, z);
|
||||
|
||||
var from = state.Mobile;
|
||||
|
||||
if (dest.IsMobile)
|
||||
{
|
||||
from.Drop(World.FindMobile(dest), loc);
|
||||
}
|
||||
else if (dest.IsItem)
|
||||
{
|
||||
var item = World.FindItem(dest);
|
||||
|
||||
if (item is BaseMulti multi && multi.AllowsRelativeDrop)
|
||||
{
|
||||
loc.m_X += multi.X;
|
||||
loc.m_Y += multi.Y;
|
||||
from.Drop(loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Drop(item, loc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Drop(loc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DropReq6017(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.ReadInt32(); // serial, ignored
|
||||
int x = reader.ReadInt16();
|
||||
int y = reader.ReadInt16();
|
||||
int z = reader.ReadSByte();
|
||||
reader.ReadByte(); // Grid Location?
|
||||
Serial dest = (Serial)reader.ReadUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
packetLength -= 1;
|
||||
}
|
||||
|
||||
var loc = new Point3D(x, y, z);
|
||||
Serial dest = (Serial)reader.ReadUInt32();
|
||||
|
||||
var from = state.Mobile;
|
||||
var loc = new Point3D(x, y, z);
|
||||
|
||||
if (dest.IsMobile)
|
||||
var from = state.Mobile;
|
||||
|
||||
if (dest.IsMobile)
|
||||
{
|
||||
from.Drop(World.FindMobile(dest), loc);
|
||||
}
|
||||
else if (dest.IsItem)
|
||||
{
|
||||
var item = World.FindItem(dest);
|
||||
|
||||
if (item is BaseMulti multi && multi.AllowsRelativeDrop)
|
||||
{
|
||||
from.Drop(World.FindMobile(dest), loc);
|
||||
}
|
||||
else if (dest.IsItem)
|
||||
{
|
||||
var item = World.FindItem(dest);
|
||||
|
||||
if (item is BaseMulti multi && multi.AllowsRelativeDrop)
|
||||
{
|
||||
loc.m_X += multi.X;
|
||||
loc.m_Y += multi.Y;
|
||||
from.Drop(loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Drop(item, loc);
|
||||
}
|
||||
loc.m_X += multi.X;
|
||||
loc.m_Y += multi.Y;
|
||||
from.Drop(loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Drop(loc);
|
||||
from.Drop(item, loc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EquipMacro(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
else
|
||||
{
|
||||
int count = reader.ReadByte();
|
||||
var serialList = new List<Serial>(count);
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
serialList.Add((Serial)reader.ReadUInt32());
|
||||
}
|
||||
|
||||
EventSink.InvokeEquipMacro(state.Mobile, serialList);
|
||||
}
|
||||
|
||||
public static void UnequipMacro(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int count = reader.ReadByte();
|
||||
var layers = new List<Layer>(count);
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
layers.Add((Layer)reader.ReadUInt16());
|
||||
}
|
||||
|
||||
EventSink.InvokeUnequipMacro(state.Mobile, layers);
|
||||
from.Drop(loc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DropReq6017(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
reader.ReadInt32(); // serial, ignored
|
||||
int x = reader.ReadInt16();
|
||||
int y = reader.ReadInt16();
|
||||
int z = reader.ReadSByte();
|
||||
reader.ReadByte(); // Grid Location?
|
||||
Serial dest = (Serial)reader.ReadUInt32();
|
||||
|
||||
var loc = new Point3D(x, y, z);
|
||||
|
||||
var from = state.Mobile;
|
||||
|
||||
if (dest.IsMobile)
|
||||
{
|
||||
from.Drop(World.FindMobile(dest), loc);
|
||||
}
|
||||
else if (dest.IsItem)
|
||||
{
|
||||
var item = World.FindItem(dest);
|
||||
|
||||
if (item is BaseMulti multi && multi.AllowsRelativeDrop)
|
||||
{
|
||||
loc.m_X += multi.X;
|
||||
loc.m_Y += multi.Y;
|
||||
from.Drop(loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Drop(item, loc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Drop(loc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EquipMacro(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int count = reader.ReadByte();
|
||||
var serialList = new List<Serial>(count);
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
serialList.Add((Serial)reader.ReadUInt32());
|
||||
}
|
||||
|
||||
EventSink.InvokeEquipMacro(state.Mobile, serialList);
|
||||
}
|
||||
|
||||
public static void UnequipMacro(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int count = reader.ReadByte();
|
||||
var layers = new List<Layer>(count);
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
layers.Add((Layer)reader.ReadUInt16());
|
||||
}
|
||||
|
||||
EventSink.InvokeUnequipMacro(state.Mobile, layers);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,146 +15,145 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
[Flags]
|
||||
public enum MessageType
|
||||
{
|
||||
[Flags]
|
||||
public enum MessageType
|
||||
Regular = 0x00,
|
||||
System = 0x01,
|
||||
Emote = 0x02,
|
||||
Label = 0x06,
|
||||
Focus = 0x07,
|
||||
Whisper = 0x08,
|
||||
Yell = 0x09,
|
||||
Spell = 0x0A,
|
||||
|
||||
Guild = 0x0D,
|
||||
Alliance = 0x0E,
|
||||
Command = 0x0F,
|
||||
|
||||
Encoded = 0xC0
|
||||
}
|
||||
|
||||
public static class IncomingMessagePackets
|
||||
{
|
||||
private static readonly KeywordList m_KeywordList = new();
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
Regular = 0x00,
|
||||
System = 0x01,
|
||||
Emote = 0x02,
|
||||
Label = 0x06,
|
||||
Focus = 0x07,
|
||||
Whisper = 0x08,
|
||||
Yell = 0x09,
|
||||
Spell = 0x0A,
|
||||
|
||||
Guild = 0x0D,
|
||||
Alliance = 0x0E,
|
||||
Command = 0x0F,
|
||||
|
||||
Encoded = 0xC0
|
||||
IncomingPackets.Register(0x03, 0, true, AsciiSpeech);
|
||||
IncomingPackets.Register(0xAD, 0, true, UnicodeSpeech);
|
||||
}
|
||||
|
||||
public static class IncomingMessagePackets
|
||||
public static void AsciiSpeech(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
private static readonly KeywordList m_KeywordList = new();
|
||||
var from = state.Mobile;
|
||||
|
||||
public static void Configure()
|
||||
if (from == null)
|
||||
{
|
||||
IncomingPackets.Register(0x03, 0, true, AsciiSpeech);
|
||||
IncomingPackets.Register(0xAD, 0, true, UnicodeSpeech);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void AsciiSpeech(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
var type = (MessageType)reader.ReadByte();
|
||||
int hue = reader.ReadInt16();
|
||||
reader.ReadInt16(); // font
|
||||
var text = reader.ReadAsciiSafe().Trim();
|
||||
|
||||
if (text.Length is <= 0 or > 128)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var type = (MessageType)reader.ReadByte();
|
||||
int hue = reader.ReadInt16();
|
||||
reader.ReadInt16(); // font
|
||||
var text = reader.ReadAsciiSafe().Trim();
|
||||
|
||||
if (text.Length is <= 0 or > 128)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Enum.IsDefined(typeof(MessageType), type))
|
||||
{
|
||||
type = MessageType.Regular;
|
||||
}
|
||||
|
||||
from.DoSpeech(text, Array.Empty<int>(), type, Utility.ClipDyedHue(hue));
|
||||
return;
|
||||
}
|
||||
|
||||
public static void UnicodeSpeech(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
if (!Enum.IsDefined(typeof(MessageType), type))
|
||||
{
|
||||
var from = state.Mobile;
|
||||
type = MessageType.Regular;
|
||||
}
|
||||
|
||||
if (from == null)
|
||||
from.DoSpeech(text, Array.Empty<int>(), type, Utility.ClipDyedHue(hue));
|
||||
}
|
||||
|
||||
public static void UnicodeSpeech(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var type = (MessageType)reader.ReadByte();
|
||||
int hue = reader.ReadInt16();
|
||||
reader.ReadInt16(); // font
|
||||
var lang = reader.ReadAscii(4);
|
||||
string text;
|
||||
|
||||
var isEncoded = (type & MessageType.Encoded) != 0;
|
||||
int[] keywords;
|
||||
|
||||
if (isEncoded)
|
||||
{
|
||||
int value = reader.ReadInt16();
|
||||
var count = (value & 0xFFF0) >> 4;
|
||||
var hold = value & 0xF;
|
||||
|
||||
if (count is < 0 or > 50)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var type = (MessageType)reader.ReadByte();
|
||||
int hue = reader.ReadInt16();
|
||||
reader.ReadInt16(); // font
|
||||
var lang = reader.ReadAscii(4);
|
||||
string text;
|
||||
var keyList = m_KeywordList;
|
||||
|
||||
var isEncoded = (type & MessageType.Encoded) != 0;
|
||||
int[] keywords;
|
||||
|
||||
if (isEncoded)
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
int value = reader.ReadInt16();
|
||||
var count = (value & 0xFFF0) >> 4;
|
||||
var hold = value & 0xF;
|
||||
int speechID;
|
||||
|
||||
if (count is < 0 or > 50)
|
||||
if ((i & 1) == 0)
|
||||
{
|
||||
return;
|
||||
hold <<= 8;
|
||||
hold |= reader.ReadByte();
|
||||
speechID = hold;
|
||||
hold = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = reader.ReadInt16();
|
||||
speechID = (value & 0xFFF0) >> 4;
|
||||
hold = value & 0xF;
|
||||
}
|
||||
|
||||
var keyList = m_KeywordList;
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
if (!keyList.Contains(speechID))
|
||||
{
|
||||
int speechID;
|
||||
|
||||
if ((i & 1) == 0)
|
||||
{
|
||||
hold <<= 8;
|
||||
hold |= reader.ReadByte();
|
||||
speechID = hold;
|
||||
hold = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = reader.ReadInt16();
|
||||
speechID = (value & 0xFFF0) >> 4;
|
||||
hold = value & 0xF;
|
||||
}
|
||||
|
||||
if (!keyList.Contains(speechID))
|
||||
{
|
||||
keyList.Add(speechID);
|
||||
}
|
||||
keyList.Add(speechID);
|
||||
}
|
||||
|
||||
text = reader.ReadUTF8Safe();
|
||||
|
||||
keywords = keyList.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
text = reader.ReadBigUniSafe();
|
||||
|
||||
keywords = Array.Empty<int>();
|
||||
}
|
||||
|
||||
text = text.Trim();
|
||||
text = reader.ReadUTF8Safe();
|
||||
|
||||
if (text.Length is <= 0 or > 128)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
type &= ~MessageType.Encoded;
|
||||
|
||||
if (!Enum.IsDefined(typeof(MessageType), type))
|
||||
{
|
||||
type = MessageType.Regular;
|
||||
}
|
||||
|
||||
from.Language = lang;
|
||||
from.DoSpeech(text, keywords, type, Utility.ClipDyedHue(hue));
|
||||
keywords = keyList.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
text = reader.ReadBigUniSafe();
|
||||
|
||||
keywords = Array.Empty<int>();
|
||||
}
|
||||
|
||||
text = text.Trim();
|
||||
|
||||
if (text.Length is <= 0 or > 128)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
type &= ~MessageType.Encoded;
|
||||
|
||||
if (!Enum.IsDefined(typeof(MessageType), type))
|
||||
{
|
||||
type = MessageType.Regular;
|
||||
}
|
||||
|
||||
from.Language = lang;
|
||||
from.DoSpeech(text, keywords, type, Utility.ClipDyedHue(hue));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,152 +15,151 @@
|
|||
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingMobilePackets
|
||||
{
|
||||
public static class IncomingMobilePackets
|
||||
public static void Configure()
|
||||
{
|
||||
public static void Configure()
|
||||
IncomingPackets.Register(0x75, 35, true, RenameRequest);
|
||||
IncomingPackets.Register(0x98, 0, true, MobileNameRequest);
|
||||
IncomingPackets.Register(0xB8, 0, true, ProfileReq);
|
||||
IncomingPackets.Register(0x6F, 0, true, SecureTrade);
|
||||
}
|
||||
|
||||
public static void RenameRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
var targ = World.FindMobile((Serial)reader.ReadUInt32());
|
||||
|
||||
if (targ != null)
|
||||
{
|
||||
IncomingPackets.Register(0x75, 35, true, RenameRequest);
|
||||
IncomingPackets.Register(0x98, 0, true, MobileNameRequest);
|
||||
IncomingPackets.Register(0xB8, 0, true, ProfileReq);
|
||||
IncomingPackets.Register(0x6F, 0, true, SecureTrade);
|
||||
EventSink.InvokeRenameRequest(from, targ, reader.ReadAsciiSafe());
|
||||
}
|
||||
}
|
||||
|
||||
public static void MobileNameRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var m = World.FindMobile((Serial)reader.ReadUInt32());
|
||||
|
||||
if (m != null && Utility.InUpdateRange(state.Mobile.Location, m.Location) && state.Mobile.CanSee(m))
|
||||
{
|
||||
state.SendMobileName(m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ProfileReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int type = reader.ReadByte();
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
var beholder = state.Mobile;
|
||||
var beheld = World.FindMobile(serial);
|
||||
|
||||
if (beheld == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public static void RenameRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
switch (type)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
var targ = World.FindMobile((Serial)reader.ReadUInt32());
|
||||
case 0x00: // display request
|
||||
{
|
||||
EventSink.InvokeProfileRequest(beholder, beheld);
|
||||
|
||||
if (targ != null)
|
||||
{
|
||||
EventSink.InvokeRenameRequest(from, targ, reader.ReadAsciiSafe());
|
||||
}
|
||||
}
|
||||
|
||||
public static void MobileNameRequest(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var m = World.FindMobile((Serial)reader.ReadUInt32());
|
||||
|
||||
if (m != null && Utility.InUpdateRange(state.Mobile.Location, m.Location) && state.Mobile.CanSee(m))
|
||||
{
|
||||
state.SendMobileName(m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ProfileReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int type = reader.ReadByte();
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
var beholder = state.Mobile;
|
||||
var beheld = World.FindMobile(serial);
|
||||
|
||||
if (beheld == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0x00: // display request
|
||||
{
|
||||
EventSink.InvokeProfileRequest(beholder, beheld);
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x01: // edit request
|
||||
{
|
||||
reader.ReadInt16(); // Skip
|
||||
int length = reader.ReadUInt16();
|
||||
|
||||
if (length > 511)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var text = reader.ReadBigUni(length);
|
||||
|
||||
EventSink.InvokeChangeProfileRequest(beholder, beheld, text);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SecureTrade(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
switch (reader.ReadByte())
|
||||
{
|
||||
case 1: // Cancel
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (World.FindItem(serial) is SecureTradeContainer cont && cont.Trade != null &&
|
||||
(cont.Trade.From.Mobile == state.Mobile || cont.Trade.To.Mobile == state.Mobile))
|
||||
{
|
||||
cont.Trade.Cancel();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: // Check
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (World.FindItem(serial) is SecureTradeContainer cont)
|
||||
{
|
||||
var trade = cont.Trade;
|
||||
|
||||
var value = reader.ReadInt32() != 0;
|
||||
|
||||
if (trade != null)
|
||||
{
|
||||
if (trade.From.Mobile == state.Mobile)
|
||||
{
|
||||
trade.From.Accepted = value;
|
||||
trade.Update();
|
||||
}
|
||||
else if (trade.To.Mobile == state.Mobile)
|
||||
{
|
||||
trade.To.Accepted = value;
|
||||
trade.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: // Update Gold
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (World.FindItem(serial) is SecureTradeContainer cont)
|
||||
{
|
||||
var gold = reader.ReadInt32();
|
||||
var plat = reader.ReadInt32();
|
||||
|
||||
var trade = cont.Trade;
|
||||
|
||||
if (trade != null)
|
||||
{
|
||||
if (trade.From.Mobile == state.Mobile)
|
||||
{
|
||||
trade.From.Gold = gold;
|
||||
trade.From.Plat = plat;
|
||||
trade.UpdateFromCurrency();
|
||||
}
|
||||
else if (trade.To.Mobile == state.Mobile)
|
||||
{
|
||||
trade.To.Gold = gold;
|
||||
trade.To.Plat = plat;
|
||||
trade.UpdateToCurrency();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 0x01: // edit request
|
||||
{
|
||||
reader.ReadInt16(); // Skip
|
||||
int length = reader.ReadUInt16();
|
||||
|
||||
if (length > 511)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var text = reader.ReadBigUni(length);
|
||||
|
||||
EventSink.InvokeChangeProfileRequest(beholder, beheld, text);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SecureTrade(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
switch (reader.ReadByte())
|
||||
{
|
||||
case 1: // Cancel
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (World.FindItem(serial) is SecureTradeContainer cont && cont.Trade != null &&
|
||||
(cont.Trade.From.Mobile == state.Mobile || cont.Trade.To.Mobile == state.Mobile))
|
||||
{
|
||||
cont.Trade.Cancel();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2: // Check
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (World.FindItem(serial) is SecureTradeContainer cont)
|
||||
{
|
||||
var trade = cont.Trade;
|
||||
|
||||
var value = reader.ReadInt32() != 0;
|
||||
|
||||
if (trade != null)
|
||||
{
|
||||
if (trade.From.Mobile == state.Mobile)
|
||||
{
|
||||
trade.From.Accepted = value;
|
||||
trade.Update();
|
||||
}
|
||||
else if (trade.To.Mobile == state.Mobile)
|
||||
{
|
||||
trade.To.Accepted = value;
|
||||
trade.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: // Update Gold
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
|
||||
if (World.FindItem(serial) is SecureTradeContainer cont)
|
||||
{
|
||||
var gold = reader.ReadInt32();
|
||||
var plat = reader.ReadInt32();
|
||||
|
||||
var trade = cont.Trade;
|
||||
|
||||
if (trade != null)
|
||||
{
|
||||
if (trade.From.Mobile == state.Mobile)
|
||||
{
|
||||
trade.From.Gold = gold;
|
||||
trade.From.Plat = plat;
|
||||
trade.UpdateFromCurrency();
|
||||
}
|
||||
else if (trade.To.Mobile == state.Mobile)
|
||||
{
|
||||
trade.To.Gold = gold;
|
||||
trade.To.Plat = plat;
|
||||
trade.UpdateToCurrency();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,88 +13,49 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingMovementPackets
|
||||
{
|
||||
public static class IncomingMovementPackets
|
||||
public static void Configure()
|
||||
{
|
||||
public static void Configure()
|
||||
IncomingPackets.Register(0x02, 7, true, MovementReq);
|
||||
// Not used by OSI, and interferes with ClassicUO/Razor protocol extensions
|
||||
// IncomingPackets.Register(0xF0, 0, true, NewMovementReq);
|
||||
// IncomingPackets.Register(0xF1, 9, true, TimeSyncReq);
|
||||
}
|
||||
|
||||
public static void NewMovementReq(NetState ns, CircularBufferReader reader)
|
||||
{
|
||||
var from = ns.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
IncomingPackets.Register(0x02, 7, true, MovementReq);
|
||||
// Not used by OSI, and interferes with ClassicUO/Razor protocol extensions
|
||||
// IncomingPackets.Register(0xF0, 0, true, NewMovementReq);
|
||||
// IncomingPackets.Register(0xF1, 9, true, TimeSyncReq);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void NewMovementReq(NetState ns, CircularBufferReader reader)
|
||||
var steps = reader.ReadByte();
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
var from = ns.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var steps = reader.ReadByte();
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
var t1 = reader.ReadUInt64(); // start time?
|
||||
var t2 = reader.ReadUInt64(); // end time?
|
||||
int seq = reader.ReadByte();
|
||||
var dir = (Direction)reader.ReadByte();
|
||||
var mode = reader.ReadInt32(); // 1 = walk, 2 = run
|
||||
if (mode == 2)
|
||||
{
|
||||
dir |= Direction.Running;
|
||||
}
|
||||
|
||||
// Location
|
||||
reader.ReadInt32(); // x
|
||||
reader.ReadInt32(); // y
|
||||
reader.ReadInt32(); // z
|
||||
|
||||
if (ns.Sequence == 0 && seq != 0 || !from.Move(dir))
|
||||
{
|
||||
ns.SendMovementRej(seq, from);
|
||||
ns.Sequence = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++seq;
|
||||
|
||||
if (seq == 256)
|
||||
{
|
||||
seq = 1;
|
||||
}
|
||||
|
||||
ns.Sequence = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void TimeSyncReq(NetState ns, CircularBufferReader reader)
|
||||
{
|
||||
reader.ReadUInt64(); // Client Time?
|
||||
|
||||
ns.SendTimeSyncResponse();
|
||||
}
|
||||
|
||||
public static void MovementReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dir = (Direction)reader.ReadByte();
|
||||
var t1 = reader.ReadUInt64(); // start time?
|
||||
var t2 = reader.ReadUInt64(); // end time?
|
||||
int seq = reader.ReadByte();
|
||||
var key = reader.ReadUInt32();
|
||||
|
||||
if (state.Sequence == 0 && seq != 0 || !from.Move(dir))
|
||||
var dir = (Direction)reader.ReadByte();
|
||||
var mode = reader.ReadInt32(); // 1 = walk, 2 = run
|
||||
if (mode == 2)
|
||||
{
|
||||
state.SendMovementRej(seq, from);
|
||||
state.Sequence = 0;
|
||||
dir |= Direction.Running;
|
||||
}
|
||||
|
||||
// Location
|
||||
reader.ReadInt32(); // x
|
||||
reader.ReadInt32(); // y
|
||||
reader.ReadInt32(); // z
|
||||
|
||||
if (ns.Sequence == 0 && seq != 0 || !from.Move(dir))
|
||||
{
|
||||
ns.SendMovementRej(seq, from);
|
||||
ns.Sequence = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -105,8 +66,46 @@ namespace Server.Network
|
|||
seq = 1;
|
||||
}
|
||||
|
||||
state.Sequence = seq;
|
||||
ns.Sequence = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void TimeSyncReq(NetState ns, CircularBufferReader reader)
|
||||
{
|
||||
reader.ReadUInt64(); // Client Time?
|
||||
|
||||
ns.SendTimeSyncResponse();
|
||||
}
|
||||
|
||||
public static void MovementReq(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var from = state.Mobile;
|
||||
|
||||
if (from == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dir = (Direction)reader.ReadByte();
|
||||
int seq = reader.ReadByte();
|
||||
var key = reader.ReadUInt32();
|
||||
|
||||
if (state.Sequence == 0 && seq != 0 || !from.Move(dir))
|
||||
{
|
||||
state.SendMovementRej(seq, from);
|
||||
state.Sequence = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++seq;
|
||||
|
||||
if (seq == 256)
|
||||
{
|
||||
seq = 1;
|
||||
}
|
||||
|
||||
state.Sequence = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,91 +16,90 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingPackets
|
||||
{
|
||||
public static class IncomingPackets
|
||||
private static readonly PacketHandler[] m_6017Handlers = new PacketHandler[0x100];
|
||||
|
||||
private static readonly EncodedPacketHandler[] m_EncodedHandlersLow = new EncodedPacketHandler[0x100];
|
||||
|
||||
private static readonly Dictionary<int, EncodedPacketHandler> m_EncodedHandlersHigh =
|
||||
new();
|
||||
|
||||
public static PacketHandler[] Handlers { get; } = new PacketHandler[0x100];
|
||||
|
||||
public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive)
|
||||
{
|
||||
private static readonly PacketHandler[] m_6017Handlers = new PacketHandler[0x100];
|
||||
Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
|
||||
m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive);
|
||||
}
|
||||
|
||||
private static readonly EncodedPacketHandler[] m_EncodedHandlersLow = new EncodedPacketHandler[0x100];
|
||||
public static PacketHandler GetHandler(int packetID) => Handlers[packetID];
|
||||
|
||||
private static readonly Dictionary<int, EncodedPacketHandler> m_EncodedHandlersHigh =
|
||||
new();
|
||||
|
||||
public static PacketHandler[] Handlers { get; } = new PacketHandler[0x100];
|
||||
|
||||
public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive)
|
||||
public static void RegisterEncoded(int packetID, bool ingame, OnEncodedPacketReceive onReceive)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
|
||||
m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive);
|
||||
m_EncodedHandlersLow[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
|
||||
}
|
||||
|
||||
public static PacketHandler GetHandler(int packetID) => Handlers[packetID];
|
||||
|
||||
public static void RegisterEncoded(int packetID, bool ingame, OnEncodedPacketReceive onReceive)
|
||||
else
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
m_EncodedHandlersLow[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_EncodedHandlersHigh[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
|
||||
}
|
||||
}
|
||||
|
||||
public static EncodedPacketHandler GetEncodedHandler(int packetID)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
return m_EncodedHandlersLow[packetID];
|
||||
}
|
||||
|
||||
m_EncodedHandlersHigh.TryGetValue(packetID, out var handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static void RemoveEncodedHandler(int packetID)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
m_EncodedHandlersLow[packetID] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_EncodedHandlersHigh.Remove(packetID);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterThrottler(int packetID, ThrottlePacketCallback t)
|
||||
{
|
||||
var ph = GetHandler(packetID);
|
||||
|
||||
if (ph != null)
|
||||
{
|
||||
ph.ThrottleCallback = t;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsInfoPacket(byte packetId)
|
||||
{
|
||||
// These packets can arrive at any time during the login process. They're just informational.
|
||||
return packetId switch
|
||||
{
|
||||
0x01 => true, // Disconnect
|
||||
0x73 => true, // Ping
|
||||
0xA4 => true, // SystemInfo
|
||||
0xB1 => true, // Gump Response
|
||||
0xBB => true, // Account ID
|
||||
0xBD => true, // Client Version
|
||||
0xBE => true, // Assist Version
|
||||
0xD9 => true, // Hardware Info
|
||||
0xDD => true, // Gumps (Packed)
|
||||
0xE1 => true, // Client Type
|
||||
0xF4 => true, // CrashReport
|
||||
_ => false
|
||||
};
|
||||
m_EncodedHandlersHigh[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
|
||||
}
|
||||
}
|
||||
|
||||
public static EncodedPacketHandler GetEncodedHandler(int packetID)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
return m_EncodedHandlersLow[packetID];
|
||||
}
|
||||
|
||||
m_EncodedHandlersHigh.TryGetValue(packetID, out var handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static void RemoveEncodedHandler(int packetID)
|
||||
{
|
||||
if (packetID >= 0 && packetID < 0x100)
|
||||
{
|
||||
m_EncodedHandlersLow[packetID] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_EncodedHandlersHigh.Remove(packetID);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterThrottler(int packetID, ThrottlePacketCallback t)
|
||||
{
|
||||
var ph = GetHandler(packetID);
|
||||
|
||||
if (ph != null)
|
||||
{
|
||||
ph.ThrottleCallback = t;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsInfoPacket(byte packetId)
|
||||
{
|
||||
// These packets can arrive at any time during the login process. They're just informational.
|
||||
return packetId switch
|
||||
{
|
||||
0x01 => true, // Disconnect
|
||||
0x73 => true, // Ping
|
||||
0xA4 => true, // SystemInfo
|
||||
0xB1 => true, // Gump Response
|
||||
0xBB => true, // Account ID
|
||||
0xBD => true, // Client Version
|
||||
0xBE => true, // Assist Version
|
||||
0xD9 => true, // Hardware Info
|
||||
0xDD => true, // Gumps (Packed)
|
||||
0xE1 => true, // Client Type
|
||||
0xF4 => true, // CrashReport
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -16,130 +16,129 @@
|
|||
using Server.Diagnostics;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingTargetingPackets
|
||||
{
|
||||
public static class IncomingTargetingPackets
|
||||
public static void Configure()
|
||||
{
|
||||
public static void Configure()
|
||||
IncomingPackets.Register(0x6C, 19, true, TargetResponse);
|
||||
}
|
||||
|
||||
public static void TargetResponse(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
int type = reader.ReadByte();
|
||||
var targetID = reader.ReadInt32();
|
||||
int flags = reader.ReadByte();
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
int x = reader.ReadInt16();
|
||||
int y = reader.ReadInt16();
|
||||
reader.ReadByte();
|
||||
int z = reader.ReadSByte();
|
||||
int graphic = reader.ReadUInt16();
|
||||
|
||||
if (targetID == unchecked((int)0xDEADBEEF))
|
||||
{
|
||||
IncomingPackets.Register(0x6C, 19, true, TargetResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void TargetResponse(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
var from = state.Mobile;
|
||||
|
||||
var t = from.Target;
|
||||
|
||||
if (t == null)
|
||||
{
|
||||
int type = reader.ReadByte();
|
||||
var targetID = reader.ReadInt32();
|
||||
int flags = reader.ReadByte();
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
int x = reader.ReadInt16();
|
||||
int y = reader.ReadInt16();
|
||||
reader.ReadByte();
|
||||
int z = reader.ReadSByte();
|
||||
int graphic = reader.ReadUInt16();
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetID == unchecked((int)0xDEADBEEF))
|
||||
var prof = TargetProfile.Acquire(t.GetType());
|
||||
prof?.Start();
|
||||
|
||||
try
|
||||
{
|
||||
if (x == -1 && y == -1 && !serial.IsValid)
|
||||
{
|
||||
return;
|
||||
// User pressed escape
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
}
|
||||
|
||||
var from = state.Mobile;
|
||||
|
||||
var t = from.Target;
|
||||
|
||||
if (t == null)
|
||||
else if (t.TargetID != targetID)
|
||||
{
|
||||
return;
|
||||
// Sanity, prevent fake target
|
||||
}
|
||||
|
||||
var prof = TargetProfile.Acquire(t.GetType());
|
||||
prof?.Start();
|
||||
|
||||
try
|
||||
else
|
||||
{
|
||||
if (x == -1 && y == -1 && !serial.IsValid)
|
||||
{
|
||||
// User pressed escape
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
}
|
||||
else if (t.TargetID != targetID)
|
||||
{
|
||||
// Sanity, prevent fake target
|
||||
}
|
||||
else
|
||||
{
|
||||
object toTarget;
|
||||
object toTarget;
|
||||
|
||||
if (type == 1)
|
||||
if (type == 1)
|
||||
{
|
||||
if (graphic == 0)
|
||||
{
|
||||
if (graphic == 0)
|
||||
toTarget = new LandTarget(new Point3D(x, y, z), from.Map);
|
||||
}
|
||||
else
|
||||
{
|
||||
var map = from.Map;
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
toTarget = new LandTarget(new Point3D(x, y, z), from.Map);
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var map = from.Map;
|
||||
var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis);
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
var valid = false;
|
||||
|
||||
if (state.HighSeas)
|
||||
{
|
||||
var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
|
||||
if (id.Surface)
|
||||
{
|
||||
z -= id.Height;
|
||||
}
|
||||
}
|
||||
|
||||
for (var 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
|
||||
{
|
||||
var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis);
|
||||
|
||||
var valid = false;
|
||||
|
||||
if (state.HighSeas)
|
||||
{
|
||||
var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
|
||||
if (id.Surface)
|
||||
{
|
||||
z -= id.Height;
|
||||
}
|
||||
}
|
||||
|
||||
for (var 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);
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
t.Invoke(from, toTarget);
|
||||
}
|
||||
else if (serial.IsMobile)
|
||||
{
|
||||
toTarget = World.FindMobile(serial);
|
||||
}
|
||||
else if (serial.IsItem)
|
||||
{
|
||||
toTarget = World.FindItem(serial);
|
||||
}
|
||||
else
|
||||
{
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
return;
|
||||
}
|
||||
|
||||
t.Invoke(from, toTarget);
|
||||
}
|
||||
finally
|
||||
{
|
||||
prof?.Finish();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
prof?.Finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,96 +15,95 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class IncomingVendorPackets
|
||||
{
|
||||
public static class IncomingVendorPackets
|
||||
public static void Configure()
|
||||
{
|
||||
public static void Configure()
|
||||
IncomingPackets.Register(0x3B, 0, true, VendorBuyReply);
|
||||
IncomingPackets.Register(0x9F, 0, true, VendorSellReply);
|
||||
}
|
||||
|
||||
public static void VendorBuyReply(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var vendor = World.FindMobile((Serial)reader.ReadUInt32());
|
||||
|
||||
if (vendor == null)
|
||||
{
|
||||
IncomingPackets.Register(0x3B, 0, true, VendorBuyReply);
|
||||
IncomingPackets.Register(0x9F, 0, true, VendorSellReply);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void VendorBuyReply(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
var flag = reader.ReadByte();
|
||||
|
||||
if (!vendor.Deleted && Utility.InRange(vendor.Location, state.Mobile.Location, 10) && flag == 0x02)
|
||||
{
|
||||
var vendor = World.FindMobile((Serial)reader.ReadUInt32());
|
||||
var msgSize = packetLength - 8; // Remaining bytes
|
||||
|
||||
if (vendor == null)
|
||||
if (msgSize / 7 > 100)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var flag = reader.ReadByte();
|
||||
|
||||
if (!vendor.Deleted && Utility.InRange(vendor.Location, state.Mobile.Location, 10) && flag == 0x02)
|
||||
var buyList = new List<BuyItemResponse>(msgSize / 7);
|
||||
while (msgSize > 0)
|
||||
{
|
||||
var msgSize = packetLength - 8; // Remaining bytes
|
||||
|
||||
if (msgSize / 7 > 100)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var buyList = new List<BuyItemResponse>(msgSize / 7);
|
||||
while (msgSize > 0)
|
||||
{
|
||||
var layer = reader.ReadByte();
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
int amount = reader.ReadInt16();
|
||||
|
||||
buyList.Add(new BuyItemResponse(serial, amount));
|
||||
msgSize -= 7;
|
||||
}
|
||||
|
||||
if (buyList.Count <= 0 || (vendor as IVendor)?.OnBuyItems(state.Mobile, buyList) != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state.SendEndVendorBuy(vendor.Serial);
|
||||
}
|
||||
|
||||
public static void VendorSellReply(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
var vendor = World.FindMobile(serial);
|
||||
|
||||
if (vendor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (vendor.Deleted || !Utility.InRange(vendor.Location, state.Mobile.Location, 10))
|
||||
{
|
||||
state.SendEndVendorSell(vendor.Serial);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = reader.ReadUInt16();
|
||||
|
||||
if (count >= 100 || reader.Remaining != count * 6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sellList = new List<SellItemResponse>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = World.FindItem((Serial)reader.ReadUInt32());
|
||||
var layer = reader.ReadByte();
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
int amount = reader.ReadInt16();
|
||||
|
||||
if (item != null && amount > 0)
|
||||
{
|
||||
sellList.Add(new SellItemResponse(item, amount));
|
||||
}
|
||||
buyList.Add(new BuyItemResponse(serial, amount));
|
||||
msgSize -= 7;
|
||||
}
|
||||
|
||||
if (sellList.Count > 0 && vendor is IVendor v && v.OnSellItems(state.Mobile, sellList))
|
||||
if (buyList.Count <= 0 || (vendor as IVendor)?.OnBuyItems(state.Mobile, buyList) != true)
|
||||
{
|
||||
state.SendEndVendorSell(vendor.Serial);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state.SendEndVendorBuy(vendor.Serial);
|
||||
}
|
||||
|
||||
public static void VendorSellReply(NetState state, CircularBufferReader reader, ref int packetLength)
|
||||
{
|
||||
var serial = (Serial)reader.ReadUInt32();
|
||||
var vendor = World.FindMobile(serial);
|
||||
|
||||
if (vendor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (vendor.Deleted || !Utility.InRange(vendor.Location, state.Mobile.Location, 10))
|
||||
{
|
||||
state.SendEndVendorSell(vendor.Serial);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = reader.ReadUInt16();
|
||||
|
||||
if (count >= 100 || reader.Remaining != count * 6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sellList = new List<SellItemResponse>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = World.FindItem((Serial)reader.ReadUInt32());
|
||||
int amount = reader.ReadInt16();
|
||||
|
||||
if (item != null && amount > 0)
|
||||
{
|
||||
sellList.Add(new SellItemResponse(item, amount));
|
||||
}
|
||||
}
|
||||
|
||||
if (sellList.Count > 0 && vendor is IVendor v && v.OnSellItems(state.Mobile, sellList))
|
||||
{
|
||||
state.SendEndVendorSell(vendor.Serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,436 +19,435 @@ using System.Buffers;
|
|||
using System.Runtime.CompilerServices;
|
||||
using Server.Accounting;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public enum ALRReason : byte
|
||||
{
|
||||
public enum ALRReason : byte
|
||||
{
|
||||
Invalid = 0,
|
||||
InUse = 1,
|
||||
Blocked = 2,
|
||||
BadPass = 3,
|
||||
Idle = 254,
|
||||
BadComm = 255
|
||||
}
|
||||
Invalid = 0,
|
||||
InUse = 1,
|
||||
Blocked = 2,
|
||||
BadPass = 3,
|
||||
Idle = 254,
|
||||
BadComm = 255
|
||||
}
|
||||
|
||||
public enum PMMessage : byte
|
||||
{
|
||||
None = 0,
|
||||
CharNoExist = 1,
|
||||
CharExists = 2,
|
||||
CharInWorld = 5,
|
||||
LoginSyncError = 6,
|
||||
IdleWarning = 7
|
||||
}
|
||||
public enum PMMessage : byte
|
||||
{
|
||||
None = 0,
|
||||
CharNoExist = 1,
|
||||
CharExists = 2,
|
||||
CharInWorld = 5,
|
||||
LoginSyncError = 6,
|
||||
IdleWarning = 7
|
||||
}
|
||||
|
||||
public enum DeleteResultType
|
||||
{
|
||||
PasswordInvalid,
|
||||
CharNotExist,
|
||||
CharBeingPlayed,
|
||||
CharTooYoung,
|
||||
CharQueued,
|
||||
BadRequest
|
||||
}
|
||||
public enum DeleteResultType
|
||||
{
|
||||
PasswordInvalid,
|
||||
CharNotExist,
|
||||
CharBeingPlayed,
|
||||
CharTooYoung,
|
||||
CharQueued,
|
||||
BadRequest
|
||||
}
|
||||
|
||||
public static class OutgoingAccountPackets
|
||||
{
|
||||
/**
|
||||
public static class OutgoingAccountPackets
|
||||
{
|
||||
/**
|
||||
* Packet: 0x81
|
||||
* Length: Up to 425 bytes
|
||||
*
|
||||
* Displays the list of characters during the login process.
|
||||
* Note: Currently Unused
|
||||
*/
|
||||
public static void SendChangeCharacter(this NetState ns, IAccount a)
|
||||
public static void SendChangeCharacter(this NetState ns, IAccount a)
|
||||
{
|
||||
if (ns == null || a == null)
|
||||
{
|
||||
if (ns == null || a == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var length = 5 + a.Length * 60;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
|
||||
writer.Write((byte)0x81); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((ushort)0); // Count & Placeholder
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (var i = 0; i < a.Length; ++i)
|
||||
{
|
||||
var m = a[i];
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
writer.Clear(60);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
|
||||
|
||||
count++;
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.Clear(30); // Password (empty)
|
||||
}
|
||||
}
|
||||
|
||||
var position = writer.Position;
|
||||
writer.Seek(3, SeekOrigin.Begin);
|
||||
writer.Write((byte)count);
|
||||
writer.Seek(position, SeekOrigin.Begin);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
var length = 5 + a.Length * 60;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
|
||||
writer.Write((byte)0x81); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((ushort)0); // Count & Placeholder
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (var i = 0; i < a.Length; ++i)
|
||||
{
|
||||
var m = a[i];
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
writer.Clear(60);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
|
||||
|
||||
count++;
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.Clear(30); // Password (empty)
|
||||
}
|
||||
}
|
||||
|
||||
var position = writer.Position;
|
||||
writer.Seek(3, SeekOrigin.Begin);
|
||||
writer.Write((byte)count);
|
||||
writer.Seek(position, SeekOrigin.Begin);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet: 0xBD
|
||||
* Length: 3 bytes
|
||||
*
|
||||
* Sends a requests for the client version
|
||||
*/
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendClientVersionRequest(this NetState ns) => ns?.Send(stackalloc byte[] { 0xBD, 0x00, 0x03 });
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendClientVersionRequest(this NetState ns) => ns?.Send(stackalloc byte[] { 0xBD, 0x00, 0x03 });
|
||||
|
||||
/**
|
||||
/**
|
||||
* Packet: 0x85
|
||||
* Length: 2 bytes
|
||||
*
|
||||
* Sends the result of a deletion request
|
||||
*/
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendCharacterDeleteResult(this NetState ns, DeleteResultType res) =>
|
||||
ns?.Send(stackalloc byte[] { 0x85, (byte)res });
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendCharacterDeleteResult(this NetState ns, DeleteResultType res) =>
|
||||
ns?.Send(stackalloc byte[] { 0x85, (byte)res });
|
||||
|
||||
/**
|
||||
/**
|
||||
* Packet: 0x53
|
||||
* Length: 2 bytes
|
||||
*
|
||||
* Sends a PopupMessage with a predetermined message
|
||||
*/
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendPopupMessage(this NetState ns, PMMessage msg) =>
|
||||
ns?.Send(stackalloc byte[] { 0x53, (byte)msg });
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendPopupMessage(this NetState ns, PMMessage msg) =>
|
||||
ns?.Send(stackalloc byte[] { 0x53, (byte)msg });
|
||||
|
||||
/**
|
||||
/**
|
||||
* Packet: 0xB9
|
||||
* Length: 3 or 5 bytes
|
||||
*
|
||||
* Sends support features based on the client version
|
||||
*/
|
||||
public static void SendSupportedFeature(this NetState ns)
|
||||
public static void SendSupportedFeature(this NetState ns)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
return;
|
||||
}
|
||||
|
||||
var flags = ExpansionInfo.CoreExpansion.SupportedFeatures;
|
||||
|
||||
if (ns.Account.Limit >= 6)
|
||||
{
|
||||
flags |= FeatureFlags.LiveAccount;
|
||||
flags &= ~FeatureFlags.UOTD;
|
||||
|
||||
if (ns.Account.Limit > 6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var flags = ExpansionInfo.CoreExpansion.SupportedFeatures;
|
||||
|
||||
if (ns.Account.Limit >= 6)
|
||||
{
|
||||
flags |= FeatureFlags.LiveAccount;
|
||||
flags &= ~FeatureFlags.UOTD;
|
||||
|
||||
if (ns.Account.Limit > 6)
|
||||
{
|
||||
flags |= FeatureFlags.SeventhCharacterSlot;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags |= FeatureFlags.SixthCharacterSlot;
|
||||
}
|
||||
}
|
||||
|
||||
var length = ns.ExtendedSupportedFeatures ? 5 : 3;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xB9); // Packet ID
|
||||
|
||||
if (ns.ExtendedSupportedFeatures)
|
||||
{
|
||||
writer.Write((uint)flags);
|
||||
flags |= FeatureFlags.SeventhCharacterSlot;
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((ushort)flags);
|
||||
flags |= FeatureFlags.SixthCharacterSlot;
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
/**
|
||||
var length = ns.ExtendedSupportedFeatures ? 5 : 3;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xB9); // Packet ID
|
||||
|
||||
if (ns.ExtendedSupportedFeatures)
|
||||
{
|
||||
writer.Write((uint)flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((ushort)flags);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet: 0x1B
|
||||
* Length: 37 bytes
|
||||
*
|
||||
* Sends login confirmation
|
||||
*/
|
||||
public static void SendLoginConfirmation(this NetState ns, Mobile m)
|
||||
public static void SendLoginConfirmation(this NetState ns, Mobile m)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[37]);
|
||||
writer.Write((byte)0x1B); // PacketID
|
||||
writer.Write(m.Serial);
|
||||
writer.Write(0);
|
||||
writer.Write((short)m.Body);
|
||||
writer.Write((short)m.X);
|
||||
writer.Write((short)m.Y);
|
||||
writer.Write((short)m.Z);
|
||||
writer.Write((byte)m.Direction);
|
||||
writer.Write((byte)0);
|
||||
writer.Write(-1);
|
||||
|
||||
writer.Write(0);
|
||||
|
||||
var map = m.Map;
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
map = m.LogoutMap;
|
||||
}
|
||||
|
||||
writer.Write((short)(map?.Width ?? Map.Felucca.Width));
|
||||
writer.Write((short)(map?.Height ?? Map.Felucca.Height));
|
||||
writer.Clear(writer.Capacity - writer.Position); // Remaining is zero
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
var writer = new SpanWriter(stackalloc byte[37]);
|
||||
writer.Write((byte)0x1B); // PacketID
|
||||
writer.Write(m.Serial);
|
||||
writer.Write(0);
|
||||
writer.Write((short)m.Body);
|
||||
writer.Write((short)m.X);
|
||||
writer.Write((short)m.Y);
|
||||
writer.Write((short)m.Z);
|
||||
writer.Write((byte)m.Direction);
|
||||
writer.Write((byte)0);
|
||||
writer.Write(-1);
|
||||
|
||||
writer.Write(0);
|
||||
|
||||
var map = m.Map;
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
map = m.LogoutMap;
|
||||
}
|
||||
|
||||
writer.Write((short)(map?.Width ?? Map.Felucca.Width));
|
||||
writer.Write((short)(map?.Height ?? Map.Felucca.Height));
|
||||
writer.Clear(writer.Capacity - writer.Position); // Remaining is zero
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet: 0x55
|
||||
* Length: 1 byte
|
||||
*
|
||||
* Sends login completion
|
||||
*/
|
||||
public static void SendLoginComplete(this NetState ns)
|
||||
{
|
||||
ns?.Send(stackalloc byte[] { 0x55 });
|
||||
}
|
||||
public static void SendLoginComplete(this NetState ns)
|
||||
{
|
||||
ns?.Send(stackalloc byte[] { 0x55 });
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Packet: 0x86
|
||||
* Length: Up to 424 bytes
|
||||
*
|
||||
* Sends updated character list
|
||||
*/
|
||||
public static void SendCharacterListUpdate(this NetState ns, IAccount a)
|
||||
public static void SendCharacterListUpdate(this NetState ns, IAccount a)
|
||||
{
|
||||
if (ns == null || a == null)
|
||||
{
|
||||
if (ns == null || a == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var highSlot = -1;
|
||||
|
||||
for (var i = a.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (a[i] != null)
|
||||
{
|
||||
highSlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5);
|
||||
var length = 4 + count * 60;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x86); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
|
||||
writer.Write((byte)count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var m = a[i];
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
writer.Clear(60);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.Clear(30); // password
|
||||
}
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
var highSlot = -1;
|
||||
|
||||
for (var i = a.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (a[i] != null)
|
||||
{
|
||||
highSlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var count = Math.Max(Math.Max(highSlot + 1, a.Limit), 5);
|
||||
var length = 4 + count * 60;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x86); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
|
||||
writer.Write((byte)count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var m = a[i];
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
writer.Clear(60);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.Clear(30); // password
|
||||
}
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet: 0xA9
|
||||
* Length: 1410 or more bytes
|
||||
*
|
||||
* Sends list of characters and starting cities.
|
||||
*/
|
||||
public static void SendCharacterList(this NetState ns)
|
||||
public static void SendCharacterList(this NetState ns)
|
||||
{
|
||||
var acct = ns?.Account;
|
||||
|
||||
if (acct == null)
|
||||
{
|
||||
var acct = ns?.Account;
|
||||
|
||||
if (acct == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var client70130 = ns.NewCharacterList;
|
||||
var textLength = client70130 ? 32 : 31;
|
||||
|
||||
var cityInfo = ns.CityInfo;
|
||||
|
||||
var highSlot = -1;
|
||||
|
||||
for (var i = acct.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (acct[i] != null)
|
||||
{
|
||||
highSlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var count = Math.Max(Math.Max(highSlot + 1, acct.Limit), 5);
|
||||
var length = (client70130 ?
|
||||
11 + (textLength * 2 + 25) * cityInfo.Length :
|
||||
9 + (textLength * 2 + 1) * cityInfo.Length) + count * 60;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xA9); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((byte)count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var m = acct[i];
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
writer.Clear(60);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.Clear(30); // password
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write((byte)cityInfo.Length);
|
||||
|
||||
for (int i = 0; i < cityInfo.Length; ++i)
|
||||
{
|
||||
var ci = cityInfo[i];
|
||||
|
||||
writer.Write((byte)i);
|
||||
writer.WriteAscii(ci.City, textLength);
|
||||
writer.WriteAscii(ci.Building, textLength);
|
||||
if (client70130)
|
||||
{
|
||||
writer.Write(ci.X);
|
||||
writer.Write(ci.Y);
|
||||
writer.Write(ci.Z);
|
||||
writer.Write(ci.Map?.MapID ?? 0);
|
||||
writer.Write(ci.Description);
|
||||
writer.Write(0);
|
||||
}
|
||||
}
|
||||
|
||||
var 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 (acct.Limit == 1)
|
||||
{
|
||||
flags |= CharacterListFlags.SlotLimit &
|
||||
CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character
|
||||
}
|
||||
|
||||
writer.Write((int)flags);
|
||||
if (client70130)
|
||||
{
|
||||
writer.Write((short)-1);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
var client70130 = ns.NewCharacterList;
|
||||
var textLength = client70130 ? 32 : 31;
|
||||
|
||||
var cityInfo = ns.CityInfo;
|
||||
|
||||
var highSlot = -1;
|
||||
|
||||
for (var i = acct.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (acct[i] != null)
|
||||
{
|
||||
highSlot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var count = Math.Max(Math.Max(highSlot + 1, acct.Limit), 5);
|
||||
var length = (client70130 ?
|
||||
11 + (textLength * 2 + 25) * cityInfo.Length :
|
||||
9 + (textLength * 2 + 1) * cityInfo.Length) + count * 60;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xA9); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((byte)count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var m = acct[i];
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
writer.Clear(60);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = (m.RawName?.Trim()).DefaultIfNullOrEmpty("-no name-");
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.Clear(30); // password
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write((byte)cityInfo.Length);
|
||||
|
||||
for (int i = 0; i < cityInfo.Length; ++i)
|
||||
{
|
||||
var ci = cityInfo[i];
|
||||
|
||||
writer.Write((byte)i);
|
||||
writer.WriteAscii(ci.City, textLength);
|
||||
writer.WriteAscii(ci.Building, textLength);
|
||||
if (client70130)
|
||||
{
|
||||
writer.Write(ci.X);
|
||||
writer.Write(ci.Y);
|
||||
writer.Write(ci.Z);
|
||||
writer.Write(ci.Map?.MapID ?? 0);
|
||||
writer.Write(ci.Description);
|
||||
writer.Write(0);
|
||||
}
|
||||
}
|
||||
|
||||
var 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 (acct.Limit == 1)
|
||||
{
|
||||
flags |= CharacterListFlags.SlotLimit &
|
||||
CharacterListFlags.OneCharacterSlot; // Limit Characters & One Character
|
||||
}
|
||||
|
||||
writer.Write((int)flags);
|
||||
if (client70130)
|
||||
{
|
||||
writer.Write((short)-1);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet: 0x82
|
||||
* Length: 2 bytes
|
||||
*
|
||||
* Sends a reason for rejecting the login
|
||||
*/
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendAccountLoginRejected(this NetState ns, ALRReason reason) =>
|
||||
ns?.Send(stackalloc byte[] { 0x82, (byte)reason });
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendAccountLoginRejected(this NetState ns, ALRReason reason) =>
|
||||
ns?.Send(stackalloc byte[] { 0x82, (byte)reason });
|
||||
|
||||
/**
|
||||
/**
|
||||
* Packet: 0xA8
|
||||
* Length: 6 + 40 bytes per server listing
|
||||
*
|
||||
* Sends login acknowledge with server listing
|
||||
*/
|
||||
public static void SendAccountLoginAck(this NetState ns)
|
||||
public static void SendAccountLoginAck(this NetState ns)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var info = ns.ServerInfo;
|
||||
var length = 6 + 40 * info.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xA8); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((byte)0x5D);
|
||||
writer.Write((ushort)info.Length);
|
||||
|
||||
for (var i = 0; i < info.Length; ++i)
|
||||
{
|
||||
var si = info[i];
|
||||
|
||||
writer.Write((ushort)i);
|
||||
writer.WriteAscii(si.Name, 32);
|
||||
writer.Write((byte)si.FullPercent);
|
||||
writer.Write((sbyte)si.TimeZone);
|
||||
// UO only supports IPv4
|
||||
writer.Write(si.RawAddress);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
var info = ns.ServerInfo;
|
||||
var length = 6 + 40 * info.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xA8); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((byte)0x5D);
|
||||
writer.Write((ushort)info.Length);
|
||||
|
||||
for (var i = 0; i < info.Length; ++i)
|
||||
{
|
||||
var si = info[i];
|
||||
|
||||
writer.Write((ushort)i);
|
||||
writer.WriteAscii(si.Name, 32);
|
||||
writer.Write((byte)si.FullPercent);
|
||||
writer.Write((sbyte)si.TimeZone);
|
||||
// UO only supports IPv4
|
||||
writer.Write(si.RawAddress);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet: 0x8C
|
||||
* Length: 11 bytes
|
||||
*
|
||||
* Sends acknowledge play server
|
||||
*/
|
||||
public static void SendPlayServerAck(this NetState ns, ServerInfo si, int authId)
|
||||
public static void SendPlayServerAck(this NetState ns, ServerInfo si, int authId)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[11]);
|
||||
writer.Write((byte)0x8C); // Packet ID
|
||||
|
||||
writer.WriteLE(si.RawAddress);
|
||||
writer.Write((short)si.Address.Port);
|
||||
writer.Write(authId);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[11]);
|
||||
writer.Write((byte)0x8C); // Packet ID
|
||||
|
||||
writer.WriteLE(si.RawAddress);
|
||||
writer.Write((short)si.Address.Port);
|
||||
writer.Write(authId);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,42 +16,41 @@
|
|||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingCombatPackets
|
||||
{
|
||||
public static class OutgoingCombatPackets
|
||||
public static void SendSwing(this NetState ns, Serial attacker, Serial defender)
|
||||
{
|
||||
public static void SendSwing(this NetState ns, Serial attacker, Serial defender)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[10]);
|
||||
writer.Write((byte)0x2F); // Packet ID
|
||||
writer.Write((byte)0);
|
||||
writer.Write(attacker);
|
||||
writer.Write(defender);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void SendSetWarMode(this NetState ns, bool warmode) =>
|
||||
ns?.Send(stackalloc byte[] { 0x72, *(byte*)&warmode, 0x00, 0x32, 0x00 });
|
||||
var writer = new SpanWriter(stackalloc byte[10]);
|
||||
writer.Write((byte)0x2F); // Packet ID
|
||||
writer.Write((byte)0);
|
||||
writer.Write(attacker);
|
||||
writer.Write(defender);
|
||||
|
||||
public static void SendChangeCombatant(this NetState ns, Serial combatant)
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void SendSetWarMode(this NetState ns, bool warmode) =>
|
||||
ns?.Send(stackalloc byte[] { 0x72, *(byte*)&warmode, 0x00, 0x32, 0x00 });
|
||||
|
||||
public static void SendChangeCombatant(this NetState ns, Serial combatant)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[5]);
|
||||
writer.Write((byte)0xAA); // Packet ID
|
||||
writer.Write(combatant);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[5]);
|
||||
writer.Write((byte)0xAA); // Packet ID
|
||||
writer.Write(combatant);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,194 +17,193 @@ using System;
|
|||
using System.Buffers;
|
||||
using System.IO;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingContainerPackets
|
||||
{
|
||||
public static class OutgoingContainerPackets
|
||||
public static void SendDisplaySpellbook(this NetState ns, Serial book) => ns.SendDisplayContainer(book, -1);
|
||||
|
||||
public static void SendSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content)
|
||||
{
|
||||
public static void SendDisplaySpellbook(this NetState ns, Serial book) => ns.SendDisplayContainer(book, -1);
|
||||
|
||||
public static void SendSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ObjectPropertyList.Enabled && ns.NewSpellbook)
|
||||
{
|
||||
ns.SendNewSpellbookContent(book, graphic, offset, content);
|
||||
}
|
||||
else
|
||||
{
|
||||
ns.SendOldSpellbookContent(book, offset, content);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendNewSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content)
|
||||
if (ObjectPropertyList.Enabled && ns.NewSpellbook)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[23]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)23); // Length
|
||||
writer.Write((short)0x1B); // Subpacket
|
||||
writer.Write((short)0x01); // Command
|
||||
|
||||
writer.Write(book);
|
||||
writer.Write((short)graphic);
|
||||
writer.Write((short)offset);
|
||||
|
||||
for (var i = 0; i < 8; ++i)
|
||||
{
|
||||
writer.Write((byte)(content >> (i * 8)));
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
ns.SendNewSpellbookContent(book, graphic, offset, content);
|
||||
}
|
||||
|
||||
public static void SendOldSpellbookContent(this NetState ns, Serial book, int offset, ulong content)
|
||||
else
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var count = content.NumberOfSetBits();
|
||||
var length = 5 + count * (ns.ContainerGridLines ? 20 : 19);
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x3C); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((ushort)count);
|
||||
|
||||
ulong mask = 1;
|
||||
for (var i = 0; i < 64; ++i, mask <<= 1)
|
||||
{
|
||||
if ((content & mask) != 0)
|
||||
{
|
||||
writer.Write(0x7FFFFFFF - i);
|
||||
writer.Write((ushort)0); // child ItemID
|
||||
writer.Write((byte)0); // ItemID offset
|
||||
writer.Write((ushort)(i + offset)); // Amount
|
||||
writer.Write(0); // X, Y
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((byte)0); // Grid Location
|
||||
}
|
||||
writer.Write(book);
|
||||
writer.Write((short)0); // Quest Hue
|
||||
}
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendDisplayContainer(this NetState ns, Serial cont, int gumpId)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]);
|
||||
writer.Write((byte)0x24); // Packet ID
|
||||
writer.Write(cont);
|
||||
writer.Write((ushort)gumpId);
|
||||
if (ns.HighSeas)
|
||||
{
|
||||
writer.Write((short)0x7D);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendContainerContentUpdate(this NetState ns, Item item)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]);
|
||||
writer.Write((byte)0x25); // Packet ID
|
||||
writer.Write(item.Serial);
|
||||
writer.Write((ushort)item.ItemID);
|
||||
writer.Write((byte)0); // signed, itemID offset
|
||||
writer.Write((ushort)Math.Min(item.Amount, ushort.MaxValue));
|
||||
writer.Write((short)item.X);
|
||||
writer.Write((short)item.Y);
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((byte)0); // Grid Location?
|
||||
}
|
||||
writer.Write(parentSerial);
|
||||
writer.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue));
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendContainerContent(this NetState ns, Mobile beholder, Item beheld)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var items = beheld.Items;
|
||||
var count = items.Count;
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[5 + items.Count * (ns.ContainerGridLines ? 20 : 19)]);
|
||||
writer.Write((byte)0x3C); // Packet ID
|
||||
writer.Seek(4, SeekOrigin.Current); // Length & written count
|
||||
|
||||
var written = 0;
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var child = items[i];
|
||||
|
||||
if (!child.Deleted && beholder.CanSee(child))
|
||||
{
|
||||
var loc = child.Location;
|
||||
|
||||
writer.Write(child.Serial);
|
||||
writer.Write((ushort)child.ItemID);
|
||||
writer.Write((byte)0); // signed, itemID offset
|
||||
writer.Write((ushort)Math.Min(child.Amount, ushort.MaxValue));
|
||||
writer.Write((short)loc.X);
|
||||
writer.Write((short)loc.Y);
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((byte)0); // Grid Location?
|
||||
}
|
||||
writer.Write(beheld.Serial);
|
||||
writer.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue));
|
||||
|
||||
++written;
|
||||
}
|
||||
}
|
||||
|
||||
writer.Seek(1, SeekOrigin.Begin);
|
||||
writer.Write((ushort)writer.BytesWritten);
|
||||
writer.Write((ushort)written);
|
||||
writer.Seek(0, SeekOrigin.End);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
ns.SendOldSpellbookContent(book, offset, content);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendNewSpellbookContent(this NetState ns, Serial book, int graphic, int offset, ulong content)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[23]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)23); // Length
|
||||
writer.Write((short)0x1B); // Subpacket
|
||||
writer.Write((short)0x01); // Command
|
||||
|
||||
writer.Write(book);
|
||||
writer.Write((short)graphic);
|
||||
writer.Write((short)offset);
|
||||
|
||||
for (var i = 0; i < 8; ++i)
|
||||
{
|
||||
writer.Write((byte)(content >> (i * 8)));
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendOldSpellbookContent(this NetState ns, Serial book, int offset, ulong content)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var count = content.NumberOfSetBits();
|
||||
var length = 5 + count * (ns.ContainerGridLines ? 20 : 19);
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x3C); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((ushort)count);
|
||||
|
||||
ulong mask = 1;
|
||||
for (var i = 0; i < 64; ++i, mask <<= 1)
|
||||
{
|
||||
if ((content & mask) != 0)
|
||||
{
|
||||
writer.Write(0x7FFFFFFF - i);
|
||||
writer.Write((ushort)0); // child ItemID
|
||||
writer.Write((byte)0); // ItemID offset
|
||||
writer.Write((ushort)(i + offset)); // Amount
|
||||
writer.Write(0); // X, Y
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((byte)0); // Grid Location
|
||||
}
|
||||
writer.Write(book);
|
||||
writer.Write((short)0); // Quest Hue
|
||||
}
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendDisplayContainer(this NetState ns, Serial cont, int gumpId)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]);
|
||||
writer.Write((byte)0x24); // Packet ID
|
||||
writer.Write(cont);
|
||||
writer.Write((ushort)gumpId);
|
||||
if (ns.HighSeas)
|
||||
{
|
||||
writer.Write((short)0x7D);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendContainerContentUpdate(this NetState ns, Item item)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]);
|
||||
writer.Write((byte)0x25); // Packet ID
|
||||
writer.Write(item.Serial);
|
||||
writer.Write((ushort)item.ItemID);
|
||||
writer.Write((byte)0); // signed, itemID offset
|
||||
writer.Write((ushort)Math.Min(item.Amount, ushort.MaxValue));
|
||||
writer.Write((short)item.X);
|
||||
writer.Write((short)item.Y);
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((byte)0); // Grid Location?
|
||||
}
|
||||
writer.Write(parentSerial);
|
||||
writer.Write((ushort)(item.QuestItem ? Item.QuestItemHue : item.Hue));
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendContainerContent(this NetState ns, Mobile beholder, Item beheld)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var items = beheld.Items;
|
||||
var count = items.Count;
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[5 + items.Count * (ns.ContainerGridLines ? 20 : 19)]);
|
||||
writer.Write((byte)0x3C); // Packet ID
|
||||
writer.Seek(4, SeekOrigin.Current); // Length & written count
|
||||
|
||||
var written = 0;
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var child = items[i];
|
||||
|
||||
if (!child.Deleted && beholder.CanSee(child))
|
||||
{
|
||||
var loc = child.Location;
|
||||
|
||||
writer.Write(child.Serial);
|
||||
writer.Write((ushort)child.ItemID);
|
||||
writer.Write((byte)0); // signed, itemID offset
|
||||
writer.Write((ushort)Math.Min(child.Amount, ushort.MaxValue));
|
||||
writer.Write((short)loc.X);
|
||||
writer.Write((short)loc.Y);
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((byte)0); // Grid Location?
|
||||
}
|
||||
writer.Write(beheld.Serial);
|
||||
writer.Write((ushort)(child.QuestItem ? Item.QuestItemHue : child.Hue));
|
||||
|
||||
++written;
|
||||
}
|
||||
}
|
||||
|
||||
writer.Seek(1, SeekOrigin.Begin);
|
||||
writer.Write((ushort)writer.BytesWritten);
|
||||
writer.Write((ushort)written);
|
||||
writer.Seek(0, SeekOrigin.End);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,36 +16,35 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingDamagePackets
|
||||
{
|
||||
public static class OutgoingDamagePackets
|
||||
public static void SendDamage(this NetState ns, Serial serial, int amount)
|
||||
{
|
||||
public static void SendDamage(this NetState ns, Serial serial, int amount)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.DamagePacket ? 7 : 11]);
|
||||
|
||||
if (ns.DamagePacket)
|
||||
{
|
||||
writer.Write((byte)0x0B); // Packet ID
|
||||
writer.Write(serial);
|
||||
writer.Write((ushort)Math.Clamp(amount, 0, 0xFFFF));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)11); // Length
|
||||
writer.Write((ushort)0x22);
|
||||
writer.Write((byte)1);
|
||||
writer.Write(serial);
|
||||
writer.Write((byte)Math.Clamp(amount, 0, 0xFF));
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.DamagePacket ? 7 : 11]);
|
||||
|
||||
if (ns.DamagePacket)
|
||||
{
|
||||
writer.Write((byte)0x0B); // Packet ID
|
||||
writer.Write(serial);
|
||||
writer.Write((ushort)Math.Clamp(amount, 0, 0xFFFF));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)11); // Length
|
||||
writer.Write((ushort)0x22);
|
||||
writer.Write((byte)1);
|
||||
writer.Write(serial);
|
||||
writer.Write((byte)Math.Clamp(amount, 0, 0xFF));
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,325 +16,324 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingEffectPackets
|
||||
{
|
||||
public static class OutgoingEffectPackets
|
||||
public const int SoundPacketLength = 12;
|
||||
public const int ParticleEffectLength = 49;
|
||||
public const int HuedEffectLength = 36;
|
||||
public const int BoltEffectLength = 36;
|
||||
|
||||
public static void SendSoundEffect(this NetState ns, int soundID, IPoint3D target)
|
||||
{
|
||||
public const int SoundPacketLength = 12;
|
||||
public const int ParticleEffectLength = 49;
|
||||
public const int HuedEffectLength = 36;
|
||||
public const int BoltEffectLength = 36;
|
||||
|
||||
public static void SendSoundEffect(this NetState ns, int soundID, IPoint3D target)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[SoundPacketLength].InitializePacket();
|
||||
CreateSoundEffect(buffer, soundID, target);
|
||||
|
||||
ns.Send(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void CreateSoundEffect(Span<byte> buffer, int soundID, IPoint3D target)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Span<byte> buffer = stackalloc byte[SoundPacketLength].InitializePacket();
|
||||
CreateSoundEffect(buffer, soundID, target);
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0x54); // Packet ID
|
||||
writer.Write((byte)1); // flags
|
||||
writer.Write((short)soundID);
|
||||
writer.Write((short)0); // volume
|
||||
writer.Write((short)target.X);
|
||||
writer.Write((short)target.Y);
|
||||
writer.Write((short)target.Z);
|
||||
ns.Send(buffer);
|
||||
}
|
||||
|
||||
public static void CreateSoundEffect(Span<byte> buffer, int soundID, IPoint3D target)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public static void CreateParticleEffect(
|
||||
Span<byte> buffer,
|
||||
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 (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0x54); // Packet ID
|
||||
writer.Write((byte)1); // flags
|
||||
writer.Write((short)soundID);
|
||||
writer.Write((short)0); // volume
|
||||
writer.Write((short)target.X);
|
||||
writer.Write((short)target.Y);
|
||||
writer.Write((short)target.Z);
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xC7); // Packet ID
|
||||
writer.Write((byte)type);
|
||||
writer.Write(from);
|
||||
writer.Write(to);
|
||||
writer.Write((short)itemID);
|
||||
writer.Write((short)fromPoint.X);
|
||||
writer.Write((short)fromPoint.Y);
|
||||
writer.Write((sbyte)fromPoint.Z);
|
||||
writer.Write((short)toPoint.X);
|
||||
writer.Write((short)toPoint.Y);
|
||||
writer.Write((sbyte)toPoint.Z);
|
||||
writer.Write((byte)speed);
|
||||
writer.Write((byte)duration);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((byte)0);
|
||||
writer.Write(fixedDirection);
|
||||
writer.Write(explode);
|
||||
writer.Write(hue);
|
||||
writer.Write(renderMode);
|
||||
writer.Write((short)effect);
|
||||
writer.Write((short)explodeEffect);
|
||||
writer.Write((short)explodeSound);
|
||||
writer.Write(serial);
|
||||
writer.Write((byte)layer);
|
||||
writer.Write((short)unknown);
|
||||
public static void CreateParticleEffect(
|
||||
Span<byte> buffer,
|
||||
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 (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public static void CreateTargetParticleEffect(
|
||||
Span<byte> buffer,
|
||||
IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int layer, int unknown
|
||||
) => CreateParticleEffect(
|
||||
buffer,
|
||||
EffectType.FixedFrom,
|
||||
e.Serial,
|
||||
Serial.Zero,
|
||||
itemID,
|
||||
e.Location,
|
||||
e.Location,
|
||||
speed,
|
||||
duration,
|
||||
true,
|
||||
false,
|
||||
hue,
|
||||
renderMode,
|
||||
effect,
|
||||
1,
|
||||
0,
|
||||
e.Serial,
|
||||
layer,
|
||||
unknown
|
||||
);
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xC7); // Packet ID
|
||||
writer.Write((byte)type);
|
||||
writer.Write(from);
|
||||
writer.Write(to);
|
||||
writer.Write((short)itemID);
|
||||
writer.Write((short)fromPoint.X);
|
||||
writer.Write((short)fromPoint.Y);
|
||||
writer.Write((sbyte)fromPoint.Z);
|
||||
writer.Write((short)toPoint.X);
|
||||
writer.Write((short)toPoint.Y);
|
||||
writer.Write((sbyte)toPoint.Z);
|
||||
writer.Write((byte)speed);
|
||||
writer.Write((byte)duration);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((byte)0);
|
||||
writer.Write(fixedDirection);
|
||||
writer.Write(explode);
|
||||
writer.Write(hue);
|
||||
writer.Write(renderMode);
|
||||
writer.Write((short)effect);
|
||||
writer.Write((short)explodeEffect);
|
||||
writer.Write((short)explodeSound);
|
||||
writer.Write(serial);
|
||||
writer.Write((byte)layer);
|
||||
writer.Write((short)unknown);
|
||||
}
|
||||
|
||||
public static void CreateLocationParticleEffect(
|
||||
Span<byte> buffer,
|
||||
IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int unknown
|
||||
) => CreateParticleEffect(
|
||||
buffer,
|
||||
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 CreateTargetParticleEffect(
|
||||
Span<byte> buffer,
|
||||
IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int layer, int unknown
|
||||
) => CreateParticleEffect(
|
||||
buffer,
|
||||
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 CreateMovingParticleEffect(
|
||||
Span<byte> buffer,
|
||||
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
|
||||
) => CreateParticleEffect(
|
||||
buffer,
|
||||
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 CreateLocationParticleEffect(
|
||||
Span<byte> buffer,
|
||||
IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int unknown
|
||||
) => CreateParticleEffect(
|
||||
buffer,
|
||||
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 CreateHuedEffect(
|
||||
Span<byte> buffer,
|
||||
EffectType type, Serial from, Serial to, int itemID, Point3D fromPoint, Point3D toPoint,
|
||||
int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode
|
||||
)
|
||||
public static void CreateMovingParticleEffect(
|
||||
Span<byte> buffer,
|
||||
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
|
||||
) => CreateParticleEffect(
|
||||
buffer,
|
||||
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 CreateHuedEffect(
|
||||
Span<byte> buffer,
|
||||
EffectType type, Serial from, Serial to, int itemID, Point3D fromPoint, Point3D toPoint,
|
||||
int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode
|
||||
)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xC0); // Packet ID
|
||||
writer.Write((byte)type);
|
||||
writer.Write(from);
|
||||
writer.Write(to);
|
||||
writer.Write((short)itemID);
|
||||
writer.Write((short)fromPoint.X);
|
||||
writer.Write((short)fromPoint.Y);
|
||||
writer.Write((sbyte)fromPoint.Z);
|
||||
writer.Write((short)toPoint.X);
|
||||
writer.Write((short)toPoint.Y);
|
||||
writer.Write((sbyte)toPoint.Z);
|
||||
writer.Write((byte)speed);
|
||||
writer.Write((byte)duration);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((byte)0);
|
||||
writer.Write(fixedDirection);
|
||||
writer.Write(explode);
|
||||
writer.Write(hue);
|
||||
writer.Write(renderMode);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void CreateTargetHuedEffect(
|
||||
Span<byte> buffer,
|
||||
IEntity e, int itemID, int speed, int duration, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.FixedFrom,
|
||||
e.Serial,
|
||||
Serial.Zero,
|
||||
itemID,
|
||||
e.Location,
|
||||
e.Location,
|
||||
speed,
|
||||
duration,
|
||||
true,
|
||||
false,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xC0); // Packet ID
|
||||
writer.Write((byte)type);
|
||||
writer.Write(from);
|
||||
writer.Write(to);
|
||||
writer.Write((short)itemID);
|
||||
writer.Write((short)fromPoint.X);
|
||||
writer.Write((short)fromPoint.Y);
|
||||
writer.Write((sbyte)fromPoint.Z);
|
||||
writer.Write((short)toPoint.X);
|
||||
writer.Write((short)toPoint.Y);
|
||||
writer.Write((sbyte)toPoint.Z);
|
||||
writer.Write((byte)speed);
|
||||
writer.Write((byte)duration);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((byte)0);
|
||||
writer.Write(fixedDirection);
|
||||
writer.Write(explode);
|
||||
writer.Write(hue);
|
||||
writer.Write(renderMode);
|
||||
}
|
||||
|
||||
public static void CreateLocationHuedEffect(
|
||||
Span<byte> buffer,
|
||||
Point3D p, int itemID, int speed, int duration, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.FixedXYZ,
|
||||
Serial.Zero,
|
||||
Serial.Zero,
|
||||
itemID,
|
||||
p,
|
||||
p,
|
||||
speed,
|
||||
duration,
|
||||
true,
|
||||
false,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
public static void CreateTargetHuedEffect(
|
||||
Span<byte> buffer,
|
||||
IEntity e, int itemID, int speed, int duration, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.FixedFrom,
|
||||
e.Serial,
|
||||
Serial.Zero,
|
||||
itemID,
|
||||
e.Location,
|
||||
e.Location,
|
||||
speed,
|
||||
duration,
|
||||
true,
|
||||
false,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
|
||||
public static void CreateMovingHuedEffect(
|
||||
Span<byte> buffer,
|
||||
IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection,
|
||||
bool explodes, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.Moving,
|
||||
from.Serial,
|
||||
to.Serial,
|
||||
itemID,
|
||||
from.Location,
|
||||
to.Location,
|
||||
speed,
|
||||
duration,
|
||||
fixedDirection,
|
||||
explodes,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
public static void CreateLocationHuedEffect(
|
||||
Span<byte> buffer,
|
||||
Point3D p, int itemID, int speed, int duration, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.FixedXYZ,
|
||||
Serial.Zero,
|
||||
Serial.Zero,
|
||||
itemID,
|
||||
p,
|
||||
p,
|
||||
speed,
|
||||
duration,
|
||||
true,
|
||||
false,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
|
||||
public static void CreateMovingHuedEffect(
|
||||
Span<byte> buffer,
|
||||
int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration,
|
||||
bool fixedDirection, bool explodes, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.Moving,
|
||||
Serial.Zero,
|
||||
Serial.Zero,
|
||||
itemID,
|
||||
fromLocation,
|
||||
toLocation,
|
||||
speed,
|
||||
duration,
|
||||
fixedDirection,
|
||||
explodes,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
public static void CreateMovingHuedEffect(
|
||||
Span<byte> buffer,
|
||||
IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection,
|
||||
bool explodes, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.Moving,
|
||||
from.Serial,
|
||||
to.Serial,
|
||||
itemID,
|
||||
from.Location,
|
||||
to.Location,
|
||||
speed,
|
||||
duration,
|
||||
fixedDirection,
|
||||
explodes,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
|
||||
public static void CreateMovingHuedEffect(
|
||||
Span<byte> buffer,
|
||||
Serial from, Serial to, int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration,
|
||||
bool fixedDirection, bool explodes, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.Moving,
|
||||
from,
|
||||
to,
|
||||
itemID,
|
||||
fromLocation,
|
||||
toLocation,
|
||||
speed,
|
||||
duration,
|
||||
fixedDirection,
|
||||
explodes,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
public static void CreateMovingHuedEffect(
|
||||
Span<byte> buffer,
|
||||
int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration,
|
||||
bool fixedDirection, bool explodes, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.Moving,
|
||||
Serial.Zero,
|
||||
Serial.Zero,
|
||||
itemID,
|
||||
fromLocation,
|
||||
toLocation,
|
||||
speed,
|
||||
duration,
|
||||
fixedDirection,
|
||||
explodes,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
|
||||
public static void CreateBoltEffect(Span<byte> buffer, IEntity target, int hue) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.Lightning,
|
||||
target.Serial,
|
||||
Serial.Zero,
|
||||
0,
|
||||
target.Location,
|
||||
target.Location,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
hue,
|
||||
0
|
||||
);
|
||||
public static void CreateMovingHuedEffect(
|
||||
Span<byte> buffer,
|
||||
Serial from, Serial to, int itemID, Point3D fromLocation, Point3D toLocation, int speed, int duration,
|
||||
bool fixedDirection, bool explodes, int hue, int renderMode
|
||||
) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.Moving,
|
||||
from,
|
||||
to,
|
||||
itemID,
|
||||
fromLocation,
|
||||
toLocation,
|
||||
speed,
|
||||
duration,
|
||||
fixedDirection,
|
||||
explodes,
|
||||
hue,
|
||||
renderMode
|
||||
);
|
||||
|
||||
public static void SendScreenEffect(this NetState ns, ScreenEffectType type)
|
||||
public static void CreateBoltEffect(Span<byte> buffer, IEntity target, int hue) => CreateHuedEffect(
|
||||
buffer,
|
||||
EffectType.Lightning,
|
||||
target.Serial,
|
||||
Serial.Zero,
|
||||
0,
|
||||
target.Location,
|
||||
target.Location,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
hue,
|
||||
0
|
||||
);
|
||||
|
||||
public static void SendScreenEffect(this NetState ns, ScreenEffectType type)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[28]);
|
||||
|
||||
writer.Write((byte)0x70); // Packet ID
|
||||
writer.Write((byte)0x4);
|
||||
writer.Clear(8);
|
||||
writer.Write((ushort)type);
|
||||
writer.Clear(16);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[28]);
|
||||
|
||||
writer.Write((byte)0x70); // Packet ID
|
||||
writer.Write((byte)0x4);
|
||||
writer.Clear(8);
|
||||
writer.Write((ushort)type);
|
||||
writer.Clear(16);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,135 +17,134 @@ using System;
|
|||
using System.Buffers;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingEntityPackets
|
||||
{
|
||||
public static class OutgoingEntityPackets
|
||||
public const int OPLPacketLength = 9;
|
||||
public const int RemoveEntityLength = 5;
|
||||
public const int MaxWorldEntityPacketLength = 26;
|
||||
|
||||
public static void CreateOPLInfo(Span<byte> buffer, Item item) =>
|
||||
CreateOPLInfo(buffer, item.Serial, item.PropertyList.Hash);
|
||||
|
||||
public static void CreateOPLInfo(Span<byte> buffer, Serial serial, int hash)
|
||||
{
|
||||
public const int OPLPacketLength = 9;
|
||||
public const int RemoveEntityLength = 5;
|
||||
public const int MaxWorldEntityPacketLength = 26;
|
||||
|
||||
public static void CreateOPLInfo(Span<byte> buffer, Item item) =>
|
||||
CreateOPLInfo(buffer, item.Serial, item.PropertyList.Hash);
|
||||
|
||||
public static void CreateOPLInfo(Span<byte> buffer, Serial serial, int hash)
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xDC); // Packet ID
|
||||
writer.Write(serial);
|
||||
writer.Write(hash);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendOPLInfo(this NetState ns, IPropertyListObject obj) =>
|
||||
ns.SendOPLInfo(obj.Serial, obj.PropertyList.Hash);
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xDC); // Packet ID
|
||||
writer.Write(serial);
|
||||
writer.Write(hash);
|
||||
}
|
||||
|
||||
public static void SendOPLInfo(this NetState ns, Serial serial, int hash)
|
||||
public static void SendOPLInfo(this NetState ns, IPropertyListObject obj) =>
|
||||
ns.SendOPLInfo(obj.Serial, obj.PropertyList.Hash);
|
||||
|
||||
public static void SendOPLInfo(this NetState ns, Serial serial, int hash)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[OPLPacketLength].InitializePacket();
|
||||
CreateOPLInfo(buffer, serial, hash);
|
||||
|
||||
ns.Send(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void CreateRemoveEntity(Span<byte> buffer, Serial serial)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Span<byte> buffer = stackalloc byte[OPLPacketLength].InitializePacket();
|
||||
CreateOPLInfo(buffer, serial, hash);
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0x1D); // Packet ID
|
||||
writer.Write(serial);
|
||||
ns.Send(buffer);
|
||||
}
|
||||
|
||||
public static void CreateRemoveEntity(Span<byte> buffer, Serial serial)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendRemoveEntity(this NetState ns, Serial serial)
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0x1D); // Packet ID
|
||||
writer.Write(serial);
|
||||
}
|
||||
|
||||
public static void SendRemoveEntity(this NetState ns, Serial serial)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[RemoveEntityLength].InitializePacket();
|
||||
CreateRemoveEntity(buffer, serial);
|
||||
|
||||
ns.Send(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
public static int CreateWorldEntity(Span<byte> buffer, IEntity entity, bool isHS)
|
||||
Span<byte> buffer = stackalloc byte[RemoveEntityLength].InitializePacket();
|
||||
CreateRemoveEntity(buffer, serial);
|
||||
|
||||
ns.Send(buffer);
|
||||
}
|
||||
|
||||
public static int CreateWorldEntity(Span<byte> buffer, IEntity entity, bool isHS)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xF3); // Packet ID
|
||||
writer.Write((short)0x1); // command
|
||||
|
||||
int type = 0;
|
||||
int gfx = 0;
|
||||
int amount = 1;
|
||||
int hue = 0;
|
||||
byte light = 0;
|
||||
int flags = 0;
|
||||
|
||||
if (entity is BaseMulti multi)
|
||||
{
|
||||
type = 2;
|
||||
gfx = multi.ItemID & (isHS ? 0xFFFF : 0x7FFF);
|
||||
hue = multi.Hue;
|
||||
amount = multi.Amount;
|
||||
}
|
||||
else if (entity is Item item)
|
||||
{
|
||||
// type = 3 if is damageable
|
||||
gfx = item.ItemID & (isHS ? 0xFFFF : 0x7FFF);
|
||||
hue = item.Hue;
|
||||
amount = item.Amount;
|
||||
light = (byte)item.Light;
|
||||
flags = item.GetPacketFlags();
|
||||
}
|
||||
else if (entity is Mobile mobile)
|
||||
{
|
||||
type = 1;
|
||||
gfx = mobile.Body;
|
||||
hue = mobile.Hue;
|
||||
flags = mobile.GetPacketFlags(true);
|
||||
}
|
||||
|
||||
writer.Write((byte)type);
|
||||
writer.Write(entity.Serial);
|
||||
writer.Write((ushort)gfx);
|
||||
writer.Write((byte)0);
|
||||
|
||||
writer.Write((short)amount); // Min
|
||||
writer.Write((short)amount); // Max
|
||||
|
||||
writer.Write((short)(entity.X & 0x7FFF));
|
||||
writer.Write((short)(entity.Y & 0x3FFF));
|
||||
writer.Write((sbyte)entity.Z);
|
||||
|
||||
writer.Write(light);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((byte)flags);
|
||||
|
||||
if (isHS)
|
||||
{
|
||||
writer.Write((short)0);
|
||||
}
|
||||
|
||||
return writer.Position;
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xF3); // Packet ID
|
||||
writer.Write((short)0x1); // command
|
||||
|
||||
int type = 0;
|
||||
int gfx = 0;
|
||||
int amount = 1;
|
||||
int hue = 0;
|
||||
byte light = 0;
|
||||
int flags = 0;
|
||||
|
||||
if (entity is BaseMulti multi)
|
||||
{
|
||||
type = 2;
|
||||
gfx = multi.ItemID & (isHS ? 0xFFFF : 0x7FFF);
|
||||
hue = multi.Hue;
|
||||
amount = multi.Amount;
|
||||
}
|
||||
else if (entity is Item item)
|
||||
{
|
||||
// type = 3 if is damageable
|
||||
gfx = item.ItemID & (isHS ? 0xFFFF : 0x7FFF);
|
||||
hue = item.Hue;
|
||||
amount = item.Amount;
|
||||
light = (byte)item.Light;
|
||||
flags = item.GetPacketFlags();
|
||||
}
|
||||
else if (entity is Mobile mobile)
|
||||
{
|
||||
type = 1;
|
||||
gfx = mobile.Body;
|
||||
hue = mobile.Hue;
|
||||
flags = mobile.GetPacketFlags(true);
|
||||
}
|
||||
|
||||
writer.Write((byte)type);
|
||||
writer.Write(entity.Serial);
|
||||
writer.Write((ushort)gfx);
|
||||
writer.Write((byte)0);
|
||||
|
||||
writer.Write((short)amount); // Min
|
||||
writer.Write((short)amount); // Max
|
||||
|
||||
writer.Write((short)(entity.X & 0x7FFF));
|
||||
writer.Write((short)(entity.Y & 0x3FFF));
|
||||
writer.Write((sbyte)entity.Z);
|
||||
|
||||
writer.Write(light);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((byte)flags);
|
||||
|
||||
if (isHS)
|
||||
{
|
||||
writer.Write((short)0);
|
||||
}
|
||||
|
||||
return writer.Position;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,108 +17,107 @@ using System;
|
|||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public class EquipInfoAttribute
|
||||
{
|
||||
public class EquipInfoAttribute
|
||||
public EquipInfoAttribute(int number, int charges = -1)
|
||||
{
|
||||
public EquipInfoAttribute(int number, int charges = -1)
|
||||
{
|
||||
Number = number;
|
||||
Charges = charges;
|
||||
}
|
||||
|
||||
public int Number { get; }
|
||||
|
||||
public int Charges { get; }
|
||||
Number = number;
|
||||
Charges = charges;
|
||||
}
|
||||
|
||||
public static class OutgoingEquipmentPackets
|
||||
public int Number { get; }
|
||||
|
||||
public int Charges { get; }
|
||||
}
|
||||
|
||||
public static class OutgoingEquipmentPackets
|
||||
{
|
||||
public static void SendDisplayEquipmentInfo(
|
||||
this NetState ns,
|
||||
Serial serial, int number, string crafterName, bool unidentified, List<EquipInfoAttribute> attrs
|
||||
)
|
||||
{
|
||||
public static void SendDisplayEquipmentInfo(
|
||||
this NetState ns,
|
||||
Serial serial, int number, string crafterName, bool unidentified, List<EquipInfoAttribute> attrs
|
||||
)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
crafterName = crafterName.DefaultIfNullOrEmpty("");
|
||||
|
||||
var length = 17 +
|
||||
(crafterName.Length > 0 ? 6 + crafterName.Length : 0) +
|
||||
(unidentified ? 4 : 0) +
|
||||
attrs.Count * 6;
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((ushort)0x10); // Subpacket
|
||||
writer.Write(serial);
|
||||
writer.Write(number);
|
||||
|
||||
if (crafterName.Length > 0)
|
||||
{
|
||||
writer.Write(-3); // crafted by
|
||||
|
||||
writer.Write((ushort)crafterName.Length);
|
||||
writer.WriteAscii(crafterName);
|
||||
}
|
||||
|
||||
if (unidentified)
|
||||
{
|
||||
writer.Write(-4);
|
||||
}
|
||||
|
||||
for (var i = 0; i < attrs.Count; ++i)
|
||||
{
|
||||
var attr = attrs[i];
|
||||
writer.Write(attr.Number);
|
||||
writer.Write((short)attr.Charges);
|
||||
}
|
||||
|
||||
writer.Write(-1);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendEquipUpdate(this NetState ns, Item item)
|
||||
crafterName = crafterName.DefaultIfNullOrEmpty("");
|
||||
|
||||
var length = 17 +
|
||||
(crafterName.Length > 0 ? 6 + crafterName.Length : 0) +
|
||||
(unidentified ? 4 : 0) +
|
||||
attrs.Count * 6;
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((ushort)0x10); // Subpacket
|
||||
writer.Write(serial);
|
||||
writer.Write(number);
|
||||
|
||||
if (crafterName.Length > 0)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
writer.Write(-3); // crafted by
|
||||
|
||||
Serial parentSerial;
|
||||
|
||||
var parent = item.Parent as Mobile;
|
||||
var hue = item.Hue;
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parentSerial = parent.Serial;
|
||||
|
||||
if (parent.SolidHueOverride >= 0)
|
||||
{
|
||||
hue = parent.SolidHueOverride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)");
|
||||
parentSerial = Serial.Zero;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[15]);
|
||||
writer.Write((byte)0x2E); // Packet ID
|
||||
writer.Write(item.Serial);
|
||||
writer.Write((short)item.ItemID);
|
||||
writer.Write((ushort)item.Layer);
|
||||
writer.Write(parentSerial);
|
||||
writer.Write((short)hue);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
writer.Write((ushort)crafterName.Length);
|
||||
writer.WriteAscii(crafterName);
|
||||
}
|
||||
|
||||
if (unidentified)
|
||||
{
|
||||
writer.Write(-4);
|
||||
}
|
||||
|
||||
for (var i = 0; i < attrs.Count; ++i)
|
||||
{
|
||||
var attr = attrs[i];
|
||||
writer.Write(attr.Number);
|
||||
writer.Write((short)attr.Charges);
|
||||
}
|
||||
|
||||
writer.Write(-1);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendEquipUpdate(this NetState ns, Item item)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Serial parentSerial;
|
||||
|
||||
var parent = item.Parent as Mobile;
|
||||
var hue = item.Hue;
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parentSerial = parent.Serial;
|
||||
|
||||
if (parent.SolidHueOverride >= 0)
|
||||
{
|
||||
hue = parent.SolidHueOverride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Warning: EquipUpdate on item with !(parent is Mobile)");
|
||||
parentSerial = Serial.Zero;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[15]);
|
||||
writer.Write((byte)0x2E); // Packet ID
|
||||
writer.Write(item.Serial);
|
||||
writer.Write((short)item.ItemID);
|
||||
writer.Write((ushort)item.Layer);
|
||||
writer.Write(parentSerial);
|
||||
writer.Write((short)hue);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,200 +22,199 @@ using Server.Collections;
|
|||
using Server.Gumps;
|
||||
using Server.Logging;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingGumpPackets
|
||||
{
|
||||
public static class OutgoingGumpPackets
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(OutgoingGumpPackets));
|
||||
|
||||
public static void SendCloseGump(this NetState ns, int typeId, int buttonId)
|
||||
{
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(OutgoingGumpPackets));
|
||||
|
||||
public static void SendCloseGump(this NetState ns, int typeId, int buttonId)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[13]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)13);
|
||||
|
||||
writer.Write((short)0x04);
|
||||
writer.Write(typeId);
|
||||
writer.Write(buttonId);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
|
||||
private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
|
||||
private static readonly byte[] _packBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
|
||||
private static readonly OrderedHashSet<string> _stringsList = new(32);
|
||||
var writer = new SpanWriter(stackalloc byte[13]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)13);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WritePacked(ReadOnlySpan<byte> span, ref SpanWriter writer)
|
||||
writer.Write((short)0x04);
|
||||
writer.Write(typeId);
|
||||
writer.Write(buttonId);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
private static readonly byte[] _layoutBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
|
||||
private static readonly byte[] _stringsBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
|
||||
private static readonly byte[] _packBuffer = GC.AllocateUninitializedArray<byte>(0x20000);
|
||||
private static readonly OrderedHashSet<string> _stringsList = new(32);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WritePacked(ReadOnlySpan<byte> span, ref SpanWriter writer)
|
||||
{
|
||||
var length = span.Length;
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
var length = span.Length;
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
writer.Write(0);
|
||||
return;
|
||||
}
|
||||
|
||||
var wantLength = Zlib.MaxPackSize(length);
|
||||
var packBuffer = _packBuffer;
|
||||
byte[] rentedBuffer = null;
|
||||
|
||||
if (wantLength > packBuffer.Length)
|
||||
{
|
||||
packBuffer = rentedBuffer = ArrayPool<byte>.Shared.Rent(wantLength);
|
||||
}
|
||||
|
||||
var packLength = wantLength;
|
||||
|
||||
var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default);
|
||||
|
||||
if (error != ZlibError.Okay)
|
||||
{
|
||||
logger.Warning($"Gump compression failed {error}");
|
||||
|
||||
writer.Write(4);
|
||||
writer.Write(0);
|
||||
return;
|
||||
}
|
||||
|
||||
writer.Write(4 + packLength);
|
||||
writer.Write(length);
|
||||
writer.Write(packBuffer.AsSpan(0, packLength));
|
||||
|
||||
if (rentedBuffer != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(rentedBuffer);
|
||||
}
|
||||
writer.Write(0);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries)
|
||||
var wantLength = Zlib.MaxPackSize(length);
|
||||
var packBuffer = _packBuffer;
|
||||
byte[] rentedBuffer = null;
|
||||
|
||||
if (wantLength > packBuffer.Length)
|
||||
{
|
||||
switches = 0;
|
||||
entries = 0;
|
||||
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var packed = ns.Unpack;
|
||||
|
||||
var layoutWriter = new SpanWriter(_layoutBuffer);
|
||||
|
||||
if (!gump.Draggable)
|
||||
{
|
||||
layoutWriter.Write(Gump.NoMove);
|
||||
}
|
||||
|
||||
if (!gump.Closable)
|
||||
{
|
||||
layoutWriter.Write(Gump.NoClose);
|
||||
}
|
||||
|
||||
if (!gump.Disposable)
|
||||
{
|
||||
layoutWriter.Write(Gump.NoDispose);
|
||||
}
|
||||
|
||||
if (!gump.Resizable)
|
||||
{
|
||||
layoutWriter.Write(Gump.NoResize);
|
||||
}
|
||||
|
||||
foreach (var entry in gump.Entries)
|
||||
{
|
||||
entry.AppendTo(ref layoutWriter, _stringsList, ref entries, ref switches);
|
||||
}
|
||||
|
||||
var stringsWriter = new SpanWriter(_stringsBuffer);
|
||||
|
||||
foreach (var str in _stringsList)
|
||||
{
|
||||
var s = str ?? "";
|
||||
stringsWriter.Write((ushort)s.Length);
|
||||
stringsWriter.WriteBigUni(s);
|
||||
}
|
||||
|
||||
int maxLength;
|
||||
if (packed)
|
||||
{
|
||||
var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten);
|
||||
var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten);
|
||||
maxLength = 40 + worstLayoutLength + worstStringsLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(maxLength);
|
||||
writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
|
||||
writer.Write(gump.Serial);
|
||||
writer.Write(gump.TypeID);
|
||||
writer.Write(gump.X);
|
||||
writer.Write(gump.Y);
|
||||
|
||||
if (packed)
|
||||
{
|
||||
layoutWriter.Write((byte)0); // Layout text terminator
|
||||
WritePacked(layoutWriter.Span, ref writer);
|
||||
|
||||
writer.Write(_stringsList.Count);
|
||||
WritePacked(stringsWriter.Span, ref writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((ushort)layoutWriter.BytesWritten);
|
||||
writer.Write(layoutWriter.Span);
|
||||
|
||||
writer.Write((ushort)_stringsList.Count);
|
||||
writer.Write(stringsWriter.Span);
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
|
||||
ns.Send(writer.Span);
|
||||
|
||||
layoutWriter.Dispose(); // Just in case
|
||||
stringsWriter.Dispose(); // Just in case
|
||||
|
||||
if (_stringsList.Count > 0)
|
||||
{
|
||||
_stringsList.Clear();
|
||||
}
|
||||
packBuffer = rentedBuffer = ArrayPool<byte>.Shared.Rent(wantLength);
|
||||
}
|
||||
|
||||
public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption)
|
||||
var packLength = wantLength;
|
||||
|
||||
var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default);
|
||||
|
||||
if (error != ZlibError.Okay)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
logger.Warning($"Gump compression failed {error}");
|
||||
|
||||
unknown ??= "";
|
||||
caption ??= "";
|
||||
writer.Write(4);
|
||||
writer.Write(0);
|
||||
return;
|
||||
}
|
||||
|
||||
var length = 15 + unknown.Length + caption.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x8B); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write(4 + packLength);
|
||||
writer.Write(length);
|
||||
writer.Write(packBuffer.AsSpan(0, packLength));
|
||||
|
||||
writer.Write(serial);
|
||||
writer.Write((short)gumpId);
|
||||
writer.Write((short)(unknown.Length + 1));
|
||||
writer.WriteAsciiNull(unknown);
|
||||
writer.Write((short)(caption.Length + 1));
|
||||
writer.WriteAsciiNull(caption);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
if (rentedBuffer != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(rentedBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries)
|
||||
{
|
||||
switches = 0;
|
||||
entries = 0;
|
||||
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var packed = ns.Unpack;
|
||||
|
||||
var layoutWriter = new SpanWriter(_layoutBuffer);
|
||||
|
||||
if (!gump.Draggable)
|
||||
{
|
||||
layoutWriter.Write(Gump.NoMove);
|
||||
}
|
||||
|
||||
if (!gump.Closable)
|
||||
{
|
||||
layoutWriter.Write(Gump.NoClose);
|
||||
}
|
||||
|
||||
if (!gump.Disposable)
|
||||
{
|
||||
layoutWriter.Write(Gump.NoDispose);
|
||||
}
|
||||
|
||||
if (!gump.Resizable)
|
||||
{
|
||||
layoutWriter.Write(Gump.NoResize);
|
||||
}
|
||||
|
||||
foreach (var entry in gump.Entries)
|
||||
{
|
||||
entry.AppendTo(ref layoutWriter, _stringsList, ref entries, ref switches);
|
||||
}
|
||||
|
||||
var stringsWriter = new SpanWriter(_stringsBuffer);
|
||||
|
||||
foreach (var str in _stringsList)
|
||||
{
|
||||
var s = str ?? "";
|
||||
stringsWriter.Write((ushort)s.Length);
|
||||
stringsWriter.WriteBigUni(s);
|
||||
}
|
||||
|
||||
int maxLength;
|
||||
if (packed)
|
||||
{
|
||||
var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten);
|
||||
var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten);
|
||||
maxLength = 40 + worstLayoutLength + worstStringsLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(maxLength);
|
||||
writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
|
||||
writer.Write(gump.Serial);
|
||||
writer.Write(gump.TypeID);
|
||||
writer.Write(gump.X);
|
||||
writer.Write(gump.Y);
|
||||
|
||||
if (packed)
|
||||
{
|
||||
layoutWriter.Write((byte)0); // Layout text terminator
|
||||
WritePacked(layoutWriter.Span, ref writer);
|
||||
|
||||
writer.Write(_stringsList.Count);
|
||||
WritePacked(stringsWriter.Span, ref writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((ushort)layoutWriter.BytesWritten);
|
||||
writer.Write(layoutWriter.Span);
|
||||
|
||||
writer.Write((ushort)_stringsList.Count);
|
||||
writer.Write(stringsWriter.Span);
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
|
||||
ns.Send(writer.Span);
|
||||
|
||||
layoutWriter.Dispose(); // Just in case
|
||||
stringsWriter.Dispose(); // Just in case
|
||||
|
||||
if (_stringsList.Count > 0)
|
||||
{
|
||||
_stringsList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendDisplaySignGump(this NetState ns, Serial serial, int gumpId, string unknown, string caption)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unknown ??= "";
|
||||
caption ??= "";
|
||||
|
||||
var length = 15 + unknown.Length + caption.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x8B); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
|
||||
writer.Write(serial);
|
||||
writer.Write((short)gumpId);
|
||||
writer.Write((short)(unknown.Length + 1));
|
||||
writer.WriteAsciiNull(unknown);
|
||||
writer.Write((short)(caption.Length + 1));
|
||||
writer.WriteAsciiNull(caption);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,84 +17,83 @@ using System;
|
|||
using System.Buffers;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingItemPackets
|
||||
{
|
||||
public static class OutgoingItemPackets
|
||||
public static int CreateWorldItem(Span<byte> buffer, Item item)
|
||||
{
|
||||
public static int CreateWorldItem(Span<byte> buffer, Item item)
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
// This assumes the packet was sliced properly
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
var itemID = item is BaseMulti ? item.ItemID | 0x4000 : item.ItemID & 0x3FFF;
|
||||
var amount = item.Amount;
|
||||
var hasAmount = amount != 0;
|
||||
var serial = hasAmount ? item.Serial.Value | 0x80000000 : item.Serial.Value & 0x7FFFFFFF;
|
||||
var loc = item.Location;
|
||||
var hue = item.Hue;
|
||||
var flags = item.GetPacketFlags();
|
||||
var direction = (int)item.Direction;
|
||||
var hasDirection = direction != 0;
|
||||
var hasHue = hue != 0;
|
||||
var hasFlags = flags != 0;
|
||||
var x = hasDirection ? loc.X | 0x8000 : loc.X & 0x7FFF;
|
||||
var y = (loc.Y & 0x3FFF) | (hasHue ? 0x8000 : 0) | (hasFlags ? 0x4000 : 0);
|
||||
var length = 14 + (hasAmount ? 2 : 0) +
|
||||
(hasDirection ? 1 : 0) +
|
||||
(hasHue ? 2 : 0) +
|
||||
(hasFlags ? 1 : 0);
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0x1A); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write(serial);
|
||||
writer.Write((ushort)itemID);
|
||||
|
||||
if (hasAmount)
|
||||
{
|
||||
writer.Write((ushort)amount);
|
||||
}
|
||||
|
||||
writer.Write((ushort)x);
|
||||
writer.Write((ushort)y);
|
||||
|
||||
if (hasDirection)
|
||||
{
|
||||
writer.Write((byte)direction);
|
||||
}
|
||||
|
||||
writer.Write((sbyte)loc.Z);
|
||||
|
||||
if (hasHue)
|
||||
{
|
||||
writer.Write((ushort)hue);
|
||||
}
|
||||
|
||||
if (hasFlags)
|
||||
{
|
||||
writer.Write((byte)flags);
|
||||
}
|
||||
|
||||
return writer.BytesWritten;
|
||||
// This assumes the packet was sliced properly
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
public static void SendWorldItem(this NetState ns, Item item)
|
||||
var itemID = item is BaseMulti ? item.ItemID | 0x4000 : item.ItemID & 0x3FFF;
|
||||
var amount = item.Amount;
|
||||
var hasAmount = amount != 0;
|
||||
var serial = hasAmount ? item.Serial.Value | 0x80000000 : item.Serial.Value & 0x7FFFFFFF;
|
||||
var loc = item.Location;
|
||||
var hue = item.Hue;
|
||||
var flags = item.GetPacketFlags();
|
||||
var direction = (int)item.Direction;
|
||||
var hasDirection = direction != 0;
|
||||
var hasHue = hue != 0;
|
||||
var hasFlags = flags != 0;
|
||||
var x = hasDirection ? loc.X | 0x8000 : loc.X & 0x7FFF;
|
||||
var y = (loc.Y & 0x3FFF) | (hasHue ? 0x8000 : 0) | (hasFlags ? 0x4000 : 0);
|
||||
var length = 14 + (hasAmount ? 2 : 0) +
|
||||
(hasDirection ? 1 : 0) +
|
||||
(hasHue ? 2 : 0) +
|
||||
(hasFlags ? 1 : 0);
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0x1A); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write(serial);
|
||||
writer.Write((ushort)itemID);
|
||||
|
||||
if (hasAmount)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket();
|
||||
|
||||
var length = ns.StygianAbyss ?
|
||||
OutgoingEntityPackets.CreateWorldEntity(buffer, item, ns.HighSeas) :
|
||||
CreateWorldItem(buffer, item);
|
||||
|
||||
ns.Send(buffer[..length]);
|
||||
writer.Write((ushort)amount);
|
||||
}
|
||||
|
||||
writer.Write((ushort)x);
|
||||
writer.Write((ushort)y);
|
||||
|
||||
if (hasDirection)
|
||||
{
|
||||
writer.Write((byte)direction);
|
||||
}
|
||||
|
||||
writer.Write((sbyte)loc.Z);
|
||||
|
||||
if (hasHue)
|
||||
{
|
||||
writer.Write((ushort)hue);
|
||||
}
|
||||
|
||||
if (hasFlags)
|
||||
{
|
||||
writer.Write((byte)flags);
|
||||
}
|
||||
|
||||
return writer.BytesWritten;
|
||||
}
|
||||
|
||||
public static void SendWorldItem(this NetState ns, Item item)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[OutgoingEntityPackets.MaxWorldEntityPacketLength].InitializePacket();
|
||||
|
||||
var length = ns.StygianAbyss ?
|
||||
OutgoingEntityPackets.CreateWorldEntity(buffer, item, ns.HighSeas) :
|
||||
CreateWorldItem(buffer, item);
|
||||
|
||||
ns.Send(buffer[..length]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,27 +16,26 @@
|
|||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingLightPackets
|
||||
{
|
||||
public static class OutgoingLightPackets
|
||||
public static void SendPersonalLightLevel(this NetState ns, Serial serial, int level)
|
||||
{
|
||||
public static void SendPersonalLightLevel(this NetState ns, Serial serial, int level)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[6]);
|
||||
writer.Write((byte)0x4E); // Packet ID
|
||||
writer.Write(serial);
|
||||
writer.Write((byte)level);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendGlobalLightLevel(this NetState ns, int level = 0) =>
|
||||
ns?.Send(stackalloc byte[] { 0x4F, (byte)level });
|
||||
var writer = new SpanWriter(stackalloc byte[6]);
|
||||
writer.Write((byte)0x4E); // Packet ID
|
||||
writer.Write(serial);
|
||||
writer.Write((byte)level);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendGlobalLightLevel(this NetState ns, int level = 0) =>
|
||||
ns?.Send(stackalloc byte[] { 0x4F, (byte)level });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,50 +16,49 @@
|
|||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingMapPackets
|
||||
{
|
||||
public static class OutgoingMapPackets
|
||||
private static byte[] _mapPatchesPacket = new byte[41];
|
||||
|
||||
public static void SendMapPatches(this NetState ns)
|
||||
{
|
||||
private static byte[] _mapPatchesPacket = new byte[41];
|
||||
|
||||
public static void SendMapPatches(this NetState ns)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mapPatchesPacket[0] == 0)
|
||||
{
|
||||
var writer = new SpanWriter(_mapPatchesPacket);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)41); // Length
|
||||
writer.Write((ushort)0x18); // Subpacket
|
||||
writer.Write(4);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
var map = Map.Maps[i];
|
||||
|
||||
writer.Write(map?.Tiles.Patch.StaticBlocks ?? 0);
|
||||
writer.Write(map?.Tiles.Patch.LandBlocks ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
ns.Send(_mapPatchesPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendInvalidMap(this NetState ns) => ns?.Send(stackalloc byte[] { 0xC6 });
|
||||
|
||||
public static void SendMapChange(this NetState ns, Map map)
|
||||
if (_mapPatchesPacket[0] == 0)
|
||||
{
|
||||
if (map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var writer = new SpanWriter(_mapPatchesPacket);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)41); // Length
|
||||
writer.Write((ushort)0x18); // Subpacket
|
||||
writer.Write(4);
|
||||
|
||||
ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x06, 0x00, 0x08, (byte)map.MapID });
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
var map = Map.Maps[i];
|
||||
|
||||
writer.Write(map?.Tiles.Patch.StaticBlocks ?? 0);
|
||||
writer.Write(map?.Tiles.Patch.LandBlocks ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
ns.Send(_mapPatchesPacket);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendInvalidMap(this NetState ns) => ns?.Send(stackalloc byte[] { 0xC6 });
|
||||
|
||||
public static void SendMapChange(this NetState ns, Map map)
|
||||
{
|
||||
if (map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x06, 0x00, 0x08, (byte)map.MapID });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,210 +5,209 @@ using Server.ContextMenus;
|
|||
using Server.Menus.ItemLists;
|
||||
using Server.Menus.Questions;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
[Flags]
|
||||
public enum CMEFlags
|
||||
{
|
||||
[Flags]
|
||||
public enum CMEFlags
|
||||
None = 0x00,
|
||||
Disabled = 0x01,
|
||||
Arrow = 0x02,
|
||||
Highlighted = 0x04,
|
||||
Colored = 0x20
|
||||
}
|
||||
|
||||
public static class OutgoingMenuPackets
|
||||
{
|
||||
public static void SendDisplayItemListMenu(this NetState ns, ItemListMenu menu)
|
||||
{
|
||||
None = 0x00,
|
||||
Disabled = 0x01,
|
||||
Arrow = 0x02,
|
||||
Highlighted = 0x04,
|
||||
Colored = 0x20
|
||||
if (ns == null || menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var question = menu.Question?.Trim();
|
||||
var questionLength = question?.Length ?? 0;
|
||||
|
||||
var entries = menu.Entries;
|
||||
int entriesLength = (byte)entries.Length;
|
||||
|
||||
var maxLength = 11 + questionLength;
|
||||
for (int i = 0; i < entriesLength; i++)
|
||||
{
|
||||
maxLength += 5 + entries[i].Name?.Length ?? 0; // could be trimmed
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||
writer.Write((byte)0x7C); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(menu.Serial);
|
||||
writer.Write((ushort)0);
|
||||
|
||||
writer.Write((byte)questionLength);
|
||||
|
||||
if (question != null)
|
||||
{
|
||||
writer.WriteAscii(question);
|
||||
}
|
||||
|
||||
writer.Write((byte)entriesLength);
|
||||
|
||||
for (var i = 0; i < entriesLength; ++i)
|
||||
{
|
||||
var e = entries[i];
|
||||
|
||||
writer.Write((ushort)e.ItemID);
|
||||
writer.Write((short)e.Hue);
|
||||
|
||||
var name = e.Name?.Trim();
|
||||
|
||||
if (name == null)
|
||||
{
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nameLength = name.Length;
|
||||
writer.Write((byte)nameLength);
|
||||
writer.WriteAscii(name);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static class OutgoingMenuPackets
|
||||
public static void SendDisplayQuestionMenu(this NetState ns, QuestionMenu menu)
|
||||
{
|
||||
public static void SendDisplayItemListMenu(this NetState ns, ItemListMenu menu)
|
||||
if (ns == null || menu == null)
|
||||
{
|
||||
if (ns == null || menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var question = menu.Question?.Trim();
|
||||
var questionLength = question?.Length ?? 0;
|
||||
|
||||
var entries = menu.Entries;
|
||||
int entriesLength = (byte)entries.Length;
|
||||
|
||||
var maxLength = 11 + questionLength;
|
||||
for (int i = 0; i < entriesLength; i++)
|
||||
{
|
||||
maxLength += 5 + entries[i].Name?.Length ?? 0; // could be trimmed
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||
writer.Write((byte)0x7C); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(menu.Serial);
|
||||
writer.Write((ushort)0);
|
||||
|
||||
writer.Write((byte)questionLength);
|
||||
|
||||
if (question != null)
|
||||
{
|
||||
writer.WriteAscii(question);
|
||||
}
|
||||
|
||||
writer.Write((byte)entriesLength);
|
||||
|
||||
for (var i = 0; i < entriesLength; ++i)
|
||||
{
|
||||
var e = entries[i];
|
||||
|
||||
writer.Write((ushort)e.ItemID);
|
||||
writer.Write((short)e.Hue);
|
||||
|
||||
var name = e.Name?.Trim();
|
||||
|
||||
if (name == null)
|
||||
{
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nameLength = name.Length;
|
||||
writer.Write((byte)nameLength);
|
||||
writer.WriteAscii(name);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendDisplayQuestionMenu(this NetState ns, QuestionMenu menu)
|
||||
var question = menu.Question?.Trim();
|
||||
var questionLength = question?.Length ?? 0;
|
||||
|
||||
var answers = menu.Answers;
|
||||
int answersLength = (byte)answers.Length;
|
||||
|
||||
var maxLength = 11 + questionLength;
|
||||
for (int i = 0; i < answersLength; i++)
|
||||
{
|
||||
if (ns == null || menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var question = menu.Question?.Trim();
|
||||
var questionLength = question?.Length ?? 0;
|
||||
|
||||
var answers = menu.Answers;
|
||||
int answersLength = (byte)answers.Length;
|
||||
|
||||
var maxLength = 11 + questionLength;
|
||||
for (int i = 0; i < answersLength; i++)
|
||||
{
|
||||
maxLength += 5 + answers[i]?.Length ?? 0; // could be trimmed
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||
writer.Write((byte)0x7C); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(menu.Serial);
|
||||
writer.Write((ushort)0);
|
||||
writer.Write((byte)questionLength);
|
||||
|
||||
if (question != null)
|
||||
{
|
||||
writer.WriteAscii(question);
|
||||
}
|
||||
|
||||
writer.Write((byte)answersLength);
|
||||
|
||||
for (var i = 0; i < answersLength; ++i)
|
||||
{
|
||||
writer.Write(0);
|
||||
|
||||
var answer = answers[i]?.Trim();
|
||||
|
||||
if (answer == null)
|
||||
{
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nameLength = answer.Length;
|
||||
writer.Write((byte)nameLength);
|
||||
writer.WriteAscii(answer);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
ns.Send(writer.Span);
|
||||
maxLength += 5 + answers[i]?.Length ?? 0; // could be trimmed
|
||||
}
|
||||
|
||||
public static void SendDisplayContextMenu(this NetState ns, ContextMenu menu)
|
||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||
writer.Write((byte)0x7C); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(menu.Serial);
|
||||
writer.Write((ushort)0);
|
||||
writer.Write((byte)questionLength);
|
||||
|
||||
if (question != null)
|
||||
{
|
||||
if (ns == null || menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newCommand = ns.NewHaven && menu.RequiresNewPacket;
|
||||
|
||||
var entries = menu.Entries;
|
||||
var entriesLength = (byte)entries.Length;
|
||||
var maxLength = 12 + entriesLength * 8;
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current); // Length
|
||||
writer.Write((short)0x14); // Subpacket
|
||||
writer.Write((short)(newCommand ? 0x02 : 0x01)); // Command
|
||||
|
||||
var target = menu.Target;
|
||||
writer.Write(target.Serial);
|
||||
writer.Write(entriesLength);
|
||||
|
||||
var p = target switch
|
||||
{
|
||||
Mobile _ => target.Location,
|
||||
Item item => item.GetWorldLocation(),
|
||||
_ => Point3D.Zero
|
||||
};
|
||||
|
||||
for (var i = 0; i < entriesLength; ++i)
|
||||
{
|
||||
var e = entries[i];
|
||||
|
||||
var range = e.Range;
|
||||
|
||||
if (range == -1)
|
||||
{
|
||||
range = Core.GlobalUpdateRange;
|
||||
}
|
||||
|
||||
var flags = e.Flags;
|
||||
if (!(e.Enabled && menu.From.InRange(p, range)))
|
||||
{
|
||||
flags |= CMEFlags.Disabled;
|
||||
}
|
||||
|
||||
if (newCommand)
|
||||
{
|
||||
writer.Write(e.Number);
|
||||
writer.Write((short)i);
|
||||
writer.Write((short)flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((short)i);
|
||||
writer.Write((ushort)(e.Number - 3000000));
|
||||
|
||||
var color = e.Color & 0xFFFF;
|
||||
|
||||
if (color != 0xFFFF)
|
||||
{
|
||||
flags |= CMEFlags.Colored;
|
||||
}
|
||||
|
||||
writer.Write((short)flags);
|
||||
|
||||
if ((flags & CMEFlags.Colored) != 0)
|
||||
{
|
||||
writer.Write((short)color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
ns.Send(writer.Span);
|
||||
writer.WriteAscii(question);
|
||||
}
|
||||
|
||||
writer.Write((byte)answersLength);
|
||||
|
||||
for (var i = 0; i < answersLength; ++i)
|
||||
{
|
||||
writer.Write(0);
|
||||
|
||||
var answer = answers[i]?.Trim();
|
||||
|
||||
if (answer == null)
|
||||
{
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nameLength = answer.Length;
|
||||
writer.Write((byte)nameLength);
|
||||
writer.WriteAscii(answer);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendDisplayContextMenu(this NetState ns, ContextMenu menu)
|
||||
{
|
||||
if (ns == null || menu == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newCommand = ns.NewHaven && menu.RequiresNewPacket;
|
||||
|
||||
var entries = menu.Entries;
|
||||
var entriesLength = (byte)entries.Length;
|
||||
var maxLength = 12 + entriesLength * 8;
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current); // Length
|
||||
writer.Write((short)0x14); // Subpacket
|
||||
writer.Write((short)(newCommand ? 0x02 : 0x01)); // Command
|
||||
|
||||
var target = menu.Target;
|
||||
writer.Write(target.Serial);
|
||||
writer.Write(entriesLength);
|
||||
|
||||
var p = target switch
|
||||
{
|
||||
Mobile _ => target.Location,
|
||||
Item item => item.GetWorldLocation(),
|
||||
_ => Point3D.Zero
|
||||
};
|
||||
|
||||
for (var i = 0; i < entriesLength; ++i)
|
||||
{
|
||||
var e = entries[i];
|
||||
|
||||
var range = e.Range;
|
||||
|
||||
if (range == -1)
|
||||
{
|
||||
range = Core.GlobalUpdateRange;
|
||||
}
|
||||
|
||||
var flags = e.Flags;
|
||||
if (!(e.Enabled && menu.From.InRange(p, range)))
|
||||
{
|
||||
flags |= CMEFlags.Disabled;
|
||||
}
|
||||
|
||||
if (newCommand)
|
||||
{
|
||||
writer.Write(e.Number);
|
||||
writer.Write((short)i);
|
||||
writer.Write((short)flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((short)i);
|
||||
writer.Write((ushort)(e.Number - 3000000));
|
||||
|
||||
var color = e.Color & 0xFFFF;
|
||||
|
||||
if (color != 0xFFFF)
|
||||
{
|
||||
flags |= CMEFlags.Colored;
|
||||
}
|
||||
|
||||
writer.Write((short)flags);
|
||||
|
||||
if ((flags & CMEFlags.Colored) != 0)
|
||||
{
|
||||
writer.Write((short)color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,264 +19,263 @@ using System.IO;
|
|||
using System.Runtime.CompilerServices;
|
||||
using Server.Prompts;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
[Flags]
|
||||
public enum AffixType : byte
|
||||
{
|
||||
[Flags]
|
||||
public enum AffixType : byte
|
||||
Append = 0x00,
|
||||
Prepend = 0x01,
|
||||
System = 0x02
|
||||
}
|
||||
|
||||
public static class OutgoingMessagePackets
|
||||
{
|
||||
public static void SendMessageLocalized(
|
||||
this NetState ns,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = ""
|
||||
)
|
||||
{
|
||||
Append = 0x00,
|
||||
Prepend = 0x01,
|
||||
System = 0x02
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[GetMaxMessageLocalizedLength(args)];
|
||||
var length = CreateMessageLocalized(
|
||||
buffer, serial, graphic, type, hue, font, number, name, args
|
||||
);
|
||||
|
||||
ns.Send(buffer[..length]);
|
||||
}
|
||||
|
||||
public static class OutgoingMessagePackets
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetMaxMessageLocalizedLength(string args) => 50 + (args?.Length ?? 0) * 2;
|
||||
|
||||
public static int CreateMessageLocalized(
|
||||
Span<byte> buffer,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = ""
|
||||
)
|
||||
{
|
||||
public static void SendMessageLocalized(
|
||||
this NetState ns,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = ""
|
||||
)
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[GetMaxMessageLocalizedLength(args)];
|
||||
var length = CreateMessageLocalized(
|
||||
buffer, serial, graphic, type, hue, font, number, name, args
|
||||
);
|
||||
|
||||
ns.Send(buffer[..length]);
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetMaxMessageLocalizedLength(string args) => 50 + (args?.Length ?? 0) * 2;
|
||||
name ??= "";
|
||||
args ??= "";
|
||||
|
||||
public static int CreateMessageLocalized(
|
||||
Span<byte> buffer,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, int number, string name = "", string args = ""
|
||||
)
|
||||
if (hue == 0)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return buffer.Length;
|
||||
}
|
||||
hue = 0x3B2;
|
||||
}
|
||||
|
||||
name ??= "";
|
||||
args ??= "";
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xC1);
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(serial);
|
||||
writer.Write((short)graphic);
|
||||
writer.Write((byte)type);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((short)font);
|
||||
writer.Write(number);
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.WriteLittleUniNull(args);
|
||||
|
||||
if (hue == 0)
|
||||
{
|
||||
hue = 0x3B2;
|
||||
}
|
||||
writer.WritePacketLength();
|
||||
return writer.Position;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xC1);
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(serial);
|
||||
writer.Write((short)graphic);
|
||||
writer.Write((byte)type);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((short)font);
|
||||
writer.Write(number);
|
||||
public static void SendMessageLocalizedAffix(
|
||||
this NetState ns,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, int number, string name,
|
||||
AffixType affixType, string affix = "", string args = ""
|
||||
)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix, args)].InitializePacket();
|
||||
var length = CreateMessageLocalizedAffix(
|
||||
buffer, serial, graphic, type, hue, font, number, name, affixType, affix, args
|
||||
);
|
||||
|
||||
ns.Send(buffer[..length]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetMaxMessageLocalizedAffixLength(string affix, string args) =>
|
||||
52 + (affix?.Length ?? 0) + (args?.Length ?? 0) * 2;
|
||||
|
||||
public static int CreateMessageLocalizedAffix(
|
||||
Span<byte> buffer,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, int number, string name,
|
||||
AffixType affixType, string affix = "", string args = ""
|
||||
)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
name ??= "";
|
||||
affix ??= "";
|
||||
args ??= "";
|
||||
|
||||
if (hue == 0)
|
||||
{
|
||||
hue = 0x3B2;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xCC);
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(serial);
|
||||
writer.Write((short)graphic);
|
||||
writer.Write((byte)type);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((short)font);
|
||||
writer.Write(number);
|
||||
writer.Write((byte)affixType);
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.WriteAsciiNull(affix);
|
||||
writer.WriteBigUniNull(args);
|
||||
|
||||
writer.WritePacketLength();
|
||||
return writer.Position;
|
||||
}
|
||||
|
||||
public static void SendMessage(
|
||||
this NetState ns,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, string name, string text
|
||||
)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[GetMaxMessageLength(text)].InitializePacket();
|
||||
var length = CreateMessage(
|
||||
buffer,
|
||||
serial,
|
||||
graphic,
|
||||
type,
|
||||
hue,
|
||||
font,
|
||||
ascii,
|
||||
lang,
|
||||
name,
|
||||
text
|
||||
);
|
||||
|
||||
ns.Send(buffer[..length]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetMaxMessageLength(string text) => 50 + (text?.Length ?? 0) * 2;
|
||||
|
||||
public static int CreateMessage(
|
||||
Span<byte> buffer,
|
||||
Serial serial,
|
||||
int graphic,
|
||||
MessageType type,
|
||||
int hue,
|
||||
int font,
|
||||
bool ascii,
|
||||
string lang,
|
||||
string name,
|
||||
string text
|
||||
)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
name ??= "";
|
||||
text ??= "";
|
||||
lang ??= "ENU";
|
||||
|
||||
if (hue == 0)
|
||||
{
|
||||
hue = 0x3B2;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)(ascii ? 0x1C : 0xAE)); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(serial);
|
||||
writer.Write((short)graphic);
|
||||
writer.Write((byte)type);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((short)font);
|
||||
if (ascii)
|
||||
{
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.WriteLittleUniNull(args);
|
||||
|
||||
writer.WritePacketLength();
|
||||
return writer.Position;
|
||||
writer.WriteAsciiNull(text);
|
||||
}
|
||||
|
||||
public static void SendMessageLocalizedAffix(
|
||||
this NetState ns,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, int number, string name,
|
||||
AffixType affixType, string affix = "", string args = ""
|
||||
)
|
||||
else
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[GetMaxMessageLocalizedAffixLength(affix, args)].InitializePacket();
|
||||
var length = CreateMessageLocalizedAffix(
|
||||
buffer, serial, graphic, type, hue, font, number, name, affixType, affix, args
|
||||
);
|
||||
|
||||
ns.Send(buffer[..length]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetMaxMessageLocalizedAffixLength(string affix, string args) =>
|
||||
52 + (affix?.Length ?? 0) + (args?.Length ?? 0) * 2;
|
||||
|
||||
public static int CreateMessageLocalizedAffix(
|
||||
Span<byte> buffer,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, int number, string name,
|
||||
AffixType affixType, string affix = "", string args = ""
|
||||
)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
name ??= "";
|
||||
affix ??= "";
|
||||
args ??= "";
|
||||
|
||||
if (hue == 0)
|
||||
{
|
||||
hue = 0x3B2;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0xCC);
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(serial);
|
||||
writer.Write((short)graphic);
|
||||
writer.Write((byte)type);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((short)font);
|
||||
writer.Write(number);
|
||||
writer.Write((byte)affixType);
|
||||
writer.WriteAscii(lang, 4);
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.WriteAsciiNull(affix);
|
||||
writer.WriteBigUniNull(args);
|
||||
|
||||
writer.WritePacketLength();
|
||||
return writer.Position;
|
||||
}
|
||||
|
||||
public static void SendMessage(
|
||||
this NetState ns,
|
||||
Serial serial, int graphic, MessageType type, int hue, int font, bool ascii, string lang, string name, string text
|
||||
)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[GetMaxMessageLength(text)].InitializePacket();
|
||||
var length = CreateMessage(
|
||||
buffer,
|
||||
serial,
|
||||
graphic,
|
||||
type,
|
||||
hue,
|
||||
font,
|
||||
ascii,
|
||||
lang,
|
||||
name,
|
||||
text
|
||||
);
|
||||
|
||||
ns.Send(buffer[..length]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetMaxMessageLength(string text) => 50 + (text?.Length ?? 0) * 2;
|
||||
|
||||
public static int CreateMessage(
|
||||
Span<byte> buffer,
|
||||
Serial serial,
|
||||
int graphic,
|
||||
MessageType type,
|
||||
int hue,
|
||||
int font,
|
||||
bool ascii,
|
||||
string lang,
|
||||
string name,
|
||||
string text
|
||||
)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return buffer.Length;
|
||||
}
|
||||
|
||||
name ??= "";
|
||||
text ??= "";
|
||||
lang ??= "ENU";
|
||||
|
||||
if (hue == 0)
|
||||
{
|
||||
hue = 0x3B2;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)(ascii ? 0x1C : 0xAE)); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
writer.Write(serial);
|
||||
writer.Write((short)graphic);
|
||||
writer.Write((byte)type);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((short)font);
|
||||
if (ascii)
|
||||
{
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.WriteAsciiNull(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteAscii(lang, 4);
|
||||
writer.WriteAscii(name, 30);
|
||||
writer.WriteBigUniNull(text);
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
return writer.Position;
|
||||
}
|
||||
|
||||
public static void SendFollowMessage(this NetState ns, Serial s1, Serial s2)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[9]);
|
||||
writer.Write((byte)0x15); // Packet ID
|
||||
writer.Write(s1);
|
||||
writer.Write(s2);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendPrompt(this NetState ns, Prompt prompt)
|
||||
{
|
||||
if (ns == null || prompt == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[21]);
|
||||
writer.Write((byte)0xC2); // Packet ID
|
||||
writer.Write((ushort)21);
|
||||
writer.Write(prompt.Serial);
|
||||
writer.Write(prompt.Serial);
|
||||
writer.Clear(10);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendHelpResponse(this NetState ns, Serial s, string text)
|
||||
{
|
||||
text = text?.Trim() ?? "";
|
||||
|
||||
if (ns == null || text.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var length = 9 + text.Length * 2;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xB7);
|
||||
writer.Write((ushort)length);
|
||||
writer.Write(s);
|
||||
writer.WriteBigUniNull(text);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
return writer.Position;
|
||||
}
|
||||
|
||||
public static void SendFollowMessage(this NetState ns, Serial s1, Serial s2)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[9]);
|
||||
writer.Write((byte)0x15); // Packet ID
|
||||
writer.Write(s1);
|
||||
writer.Write(s2);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendPrompt(this NetState ns, Prompt prompt)
|
||||
{
|
||||
if (ns == null || prompt == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[21]);
|
||||
writer.Write((byte)0xC2); // Packet ID
|
||||
writer.Write((ushort)21);
|
||||
writer.Write(prompt.Serial);
|
||||
writer.Write(prompt.Serial);
|
||||
writer.Clear(10);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendHelpResponse(this NetState ns, Serial s, string text)
|
||||
{
|
||||
text = text?.Trim() ?? "";
|
||||
|
||||
if (ns == null || text.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var length = 9 + text.Length * 2;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xB7);
|
||||
writer.Write((ushort)length);
|
||||
writer.Write(s);
|
||||
writer.WriteBigUniNull(text);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -16,102 +16,101 @@
|
|||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public enum SpeedControlSetting
|
||||
{
|
||||
public enum SpeedControlSetting
|
||||
Disable,
|
||||
Mount,
|
||||
Walk
|
||||
}
|
||||
|
||||
public static class OutgoingMovementPackets
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendSpeedControl(this NetState ns, SpeedControlSetting speedControl) =>
|
||||
ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x6, 0x00, 0x26, (byte)speedControl });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendMovePlayer(this NetState ns, Direction d) => ns?.Send(stackalloc byte[] { 0x97, (byte)d });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendMovementAck(this NetState ns, int seq, Mobile m) =>
|
||||
ns.SendMovementAck(seq, Notoriety.Compute(m, m));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendMovementAck(this NetState ns, int seq, int noto) =>
|
||||
ns?.Send(stackalloc byte[] { 0x22, (byte)seq, (byte)noto });
|
||||
|
||||
public static void SendMovementRej(this NetState ns, int seq, Mobile m)
|
||||
{
|
||||
Disable,
|
||||
Mount,
|
||||
Walk
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[8]);
|
||||
writer.Write((byte)0x21); // Packet ID
|
||||
writer.Write((byte)seq);
|
||||
writer.Write((short)m.X);
|
||||
writer.Write((short)m.Y);
|
||||
writer.Write((byte)m.Direction);
|
||||
writer.Write((sbyte)m.Z);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static class OutgoingMovementPackets
|
||||
public static void SendInitialFastwalkStack(this NetState ns, uint[] keys)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendSpeedControl(this NetState ns, SpeedControlSetting speedControl) =>
|
||||
ns?.Send(stackalloc byte[] { 0xBF, 0x00, 0x6, 0x00, 0x26, (byte)speedControl });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendMovePlayer(this NetState ns, Direction d) => ns?.Send(stackalloc byte[] { 0x97, (byte)d });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendMovementAck(this NetState ns, int seq, Mobile m) =>
|
||||
ns.SendMovementAck(seq, Notoriety.Compute(m, m));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendMovementAck(this NetState ns, int seq, int noto) =>
|
||||
ns?.Send(stackalloc byte[] { 0x22, (byte)seq, (byte)noto });
|
||||
|
||||
public static void SendMovementRej(this NetState ns, int seq, Mobile m)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[8]);
|
||||
writer.Write((byte)0x21); // Packet ID
|
||||
writer.Write((byte)seq);
|
||||
writer.Write((short)m.X);
|
||||
writer.Write((short)m.Y);
|
||||
writer.Write((byte)m.Direction);
|
||||
writer.Write((sbyte)m.Z);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendInitialFastwalkStack(this NetState ns, uint[] keys)
|
||||
var writer = new SpanWriter(stackalloc byte[29]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)29);
|
||||
writer.Write((ushort)0x1); // Subpacket
|
||||
writer.Write(keys[0]);
|
||||
writer.Write(keys[1]);
|
||||
writer.Write(keys[2]);
|
||||
writer.Write(keys[3]);
|
||||
writer.Write(keys[4]);
|
||||
writer.Write(keys[5]);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendFastwalkStackKey(this NetState ns, uint key = 0)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[29]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)29);
|
||||
writer.Write((ushort)0x1); // Subpacket
|
||||
writer.Write(keys[0]);
|
||||
writer.Write(keys[1]);
|
||||
writer.Write(keys[2]);
|
||||
writer.Write(keys[3]);
|
||||
writer.Write(keys[4]);
|
||||
writer.Write(keys[5]);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendFastwalkStackKey(this NetState ns, uint key = 0)
|
||||
var writer = new SpanWriter(stackalloc byte[9]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)9);
|
||||
writer.Write((ushort)0x2); // Subpacket
|
||||
writer.Write(key);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendTimeSyncResponse(this NetState ns)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[9]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)9);
|
||||
writer.Write((ushort)0x2); // Subpacket
|
||||
writer.Write(key);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendTimeSyncResponse(this NetState ns)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var writer = new SpanWriter(stackalloc byte[25]);
|
||||
writer.Write((byte)0xF2); // Packet ID
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[25]);
|
||||
writer.Write((byte)0xF2); // Packet ID
|
||||
writer.Write(Core.TickCount); // ??
|
||||
writer.Write(Core.TickCount); // ??
|
||||
writer.Write(Core.TickCount); // ??
|
||||
|
||||
writer.Write(Core.TickCount); // ??
|
||||
writer.Write(Core.TickCount); // ??
|
||||
writer.Write(Core.TickCount); // ??
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
Projects/Server/Network/Packets/OutgoingPackets.cs
Normal file
9
Projects/Server/Network/Packets/OutgoingPackets.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingPackets
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CannotSendPackets(this NetState ns) => ns?.Connection == null || ns.BlockAllPackets;
|
||||
}
|
||||
|
|
@ -17,338 +17,337 @@ using System;
|
|||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public enum LRReason : byte
|
||||
{
|
||||
public enum LRReason : byte
|
||||
CannotLift,
|
||||
OutOfRange,
|
||||
OutOfSight,
|
||||
TryToSteal,
|
||||
AreHolding,
|
||||
Inspecific
|
||||
}
|
||||
|
||||
public static class OutgoingPlayerPackets
|
||||
{
|
||||
public const int DragEffectPacketLength = 26;
|
||||
|
||||
public static void SendStatLockInfo(this NetState ns, Mobile m)
|
||||
{
|
||||
CannotLift,
|
||||
OutOfRange,
|
||||
OutOfSight,
|
||||
TryToSteal,
|
||||
AreHolding,
|
||||
Inspecific
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[12]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)12);
|
||||
writer.Write((short)0x19);
|
||||
writer.Write((byte)2);
|
||||
writer.Write(m.Serial);
|
||||
writer.Write((byte)0);
|
||||
|
||||
var lockBits = ((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock;
|
||||
|
||||
writer.Write((byte)lockBits);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static class OutgoingPlayerPackets
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendChangeUpdateRange(this NetState ns, byte range) =>
|
||||
ns?.Send(stackalloc byte[] { 0xC8, range });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendDeathStatus(this NetState ns, bool dead) =>
|
||||
ns?.Send(stackalloc byte[] { 0x2C, dead ? (byte)0 : (byte)2 });
|
||||
|
||||
public static void SendDisplayProfile(this NetState ns, Serial m, string header, string body, string footer)
|
||||
{
|
||||
public const int DragEffectPacketLength = 26;
|
||||
|
||||
public static void SendStatLockInfo(this NetState ns, Mobile m)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[12]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)12);
|
||||
writer.Write((short)0x19);
|
||||
writer.Write((byte)2);
|
||||
writer.Write(m.Serial);
|
||||
writer.Write((byte)0);
|
||||
|
||||
var lockBits = ((int)m.StrLock << 4) | ((int)m.DexLock << 2) | (int)m.IntLock;
|
||||
|
||||
writer.Write((byte)lockBits);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendChangeUpdateRange(this NetState ns, byte range) =>
|
||||
ns?.Send(stackalloc byte[] { 0xC8, range });
|
||||
header ??= "";
|
||||
body ??= "";
|
||||
footer ??= "";
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendDeathStatus(this NetState ns, bool dead) =>
|
||||
ns?.Send(stackalloc byte[] { 0x2C, dead ? (byte)0 : (byte)2 });
|
||||
var length = 12 + header.Length + footer.Length * 2 + body.Length * 2;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xB8); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write(m);
|
||||
writer.WriteAsciiNull(header);
|
||||
writer.WriteBigUniNull(footer);
|
||||
writer.WriteBigUniNull(body);
|
||||
|
||||
public static void SendDisplayProfile(this NetState ns, Serial m, string header, string body, string footer)
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendLiftReject(this NetState ns, LRReason reason) =>
|
||||
ns?.Send(stackalloc byte[] { 0x27, (byte)reason });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendLogoutAck(this NetState ns) => ns?.Send(stackalloc byte[] { 0xD1, 0x01 });
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendWeather(this NetState ns, byte type, byte density, byte temp) =>
|
||||
ns?.Send(stackalloc byte[] { 0x65, type, density, temp });
|
||||
|
||||
public static void SendServerChange(this NetState ns, Point3D p, Map map)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
header ??= "";
|
||||
body ??= "";
|
||||
footer ??= "";
|
||||
|
||||
var length = 12 + header.Length + footer.Length * 2 + body.Length * 2;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xB8); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write(m);
|
||||
writer.WriteAsciiNull(header);
|
||||
writer.WriteBigUniNull(footer);
|
||||
writer.WriteBigUniNull(body);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendLiftReject(this NetState ns, LRReason reason) =>
|
||||
ns?.Send(stackalloc byte[] { 0x27, (byte)reason });
|
||||
var writer = new SpanWriter(stackalloc byte[16]);
|
||||
writer.Write((byte)0x76); // Packet ID
|
||||
writer.Write((short)p.X);
|
||||
writer.Write((short)p.Y);
|
||||
writer.Write((short)p.Z);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((short)0);
|
||||
writer.Write((short)0);
|
||||
writer.Write((short)map.Width);
|
||||
writer.Write((short)map.Height);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendLogoutAck(this NetState ns) => ns?.Send(stackalloc byte[] { 0xD1, 0x01 });
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendWeather(this NetState ns, byte type, byte density, byte temp) =>
|
||||
ns?.Send(stackalloc byte[] { 0x65, type, density, temp });
|
||||
|
||||
public static void SendServerChange(this NetState ns, Point3D p, Map map)
|
||||
public static void SendSkillsUpdate(this NetState ns, Skills skills)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[16]);
|
||||
writer.Write((byte)0x76); // Packet ID
|
||||
writer.Write((short)p.X);
|
||||
writer.Write((short)p.Y);
|
||||
writer.Write((short)p.Z);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((short)0);
|
||||
writer.Write((short)0);
|
||||
writer.Write((short)map.Width);
|
||||
writer.Write((short)map.Height);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendSkillsUpdate(this NetState ns, Skills skills)
|
||||
var length = 6 + 9 * skills.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x3A); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((byte)0x02); // type: absolute, capped
|
||||
|
||||
for (var i = 0; i < skills.Length; ++i)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var s = skills[i];
|
||||
|
||||
var length = 6 + 9 * skills.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x3A); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((byte)0x02); // type: absolute, capped
|
||||
|
||||
for (var i = 0; i < skills.Length; ++i)
|
||||
{
|
||||
var s = skills[i];
|
||||
|
||||
var v = s.NonRacialValue;
|
||||
var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF);
|
||||
|
||||
writer.Write((ushort)(s.Info.SkillID + 1));
|
||||
writer.Write((ushort)uv);
|
||||
writer.Write((ushort)s.BaseFixedPoint);
|
||||
writer.Write((byte)s.Lock);
|
||||
writer.Write((ushort)s.CapFixedPoint);
|
||||
}
|
||||
|
||||
writer.Write((short)0); // terminate
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendSequence(this NetState ns, byte sequence) => ns?.Send(stackalloc byte[] { 0x7B, sequence });
|
||||
|
||||
public static void SendSkillChange(this NetState ns, Skill skill)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[13]);
|
||||
writer.Write((byte)0x3A); // Packet ID
|
||||
writer.Write((ushort)13);
|
||||
|
||||
var v = skill.NonRacialValue;
|
||||
var v = s.NonRacialValue;
|
||||
var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF);
|
||||
|
||||
writer.Write((byte)0xDF); // type: delta, capped
|
||||
writer.Write((ushort)skill.Info.SkillID);
|
||||
writer.Write((ushort)(s.Info.SkillID + 1));
|
||||
writer.Write((ushort)uv);
|
||||
writer.Write((ushort)skill.BaseFixedPoint);
|
||||
writer.Write((byte)skill.Lock);
|
||||
writer.Write((ushort)skill.CapFixedPoint);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
writer.Write((ushort)s.BaseFixedPoint);
|
||||
writer.Write((byte)s.Lock);
|
||||
writer.Write((ushort)s.CapFixedPoint);
|
||||
}
|
||||
|
||||
public static void SendLaunchBrowser(this NetState ns, string uri)
|
||||
writer.Write((short)0); // terminate
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendSequence(this NetState ns, byte sequence) => ns?.Send(stackalloc byte[] { 0x7B, sequence });
|
||||
|
||||
public static void SendSkillChange(this NetState ns, Skill skill)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uri ??= "";
|
||||
|
||||
var length = 4 + uri.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xA5); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.WriteAsciiNull(uri);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void CreateDragEffect(
|
||||
Span<byte> buffer,
|
||||
Serial srcSerial, Point3D srcLocation,
|
||||
Serial trgSerial, Point3D trgLocation,
|
||||
int itemID, int hue, int amount
|
||||
)
|
||||
var writer = new SpanWriter(stackalloc byte[13]);
|
||||
writer.Write((byte)0x3A); // Packet ID
|
||||
writer.Write((ushort)13);
|
||||
|
||||
var v = skill.NonRacialValue;
|
||||
var uv = Math.Clamp((int)(v * 10), 0, 0xFFFF);
|
||||
|
||||
writer.Write((byte)0xDF); // type: delta, capped
|
||||
writer.Write((ushort)skill.Info.SkillID);
|
||||
writer.Write((ushort)uv);
|
||||
writer.Write((ushort)skill.BaseFixedPoint);
|
||||
writer.Write((byte)skill.Lock);
|
||||
writer.Write((ushort)skill.CapFixedPoint);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendLaunchBrowser(this NetState ns, string uri)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0x23); // Packet ID
|
||||
writer.Write((short)itemID);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((short)amount);
|
||||
writer.Write(srcSerial);
|
||||
writer.Write((short)srcLocation.X);
|
||||
writer.Write((short)srcLocation.Y);
|
||||
writer.Write((sbyte)srcLocation.Z);
|
||||
writer.Write(trgSerial);
|
||||
writer.Write((short)trgLocation.X);
|
||||
writer.Write((short)trgLocation.Y);
|
||||
writer.Write((sbyte)trgLocation.Z);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendDragEffect(
|
||||
this NetState ns,
|
||||
Serial srcSerial, Point3D srcLocation,
|
||||
Serial trgSerial, Point3D trgLocation,
|
||||
int itemID, int hue, int amount
|
||||
)
|
||||
uri ??= "";
|
||||
|
||||
var length = 4 + uri.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xA5); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.WriteAsciiNull(uri);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void CreateDragEffect(
|
||||
Span<byte> buffer,
|
||||
Serial srcSerial, Point3D srcLocation,
|
||||
Serial trgSerial, Point3D trgLocation,
|
||||
int itemID, int hue, int amount
|
||||
)
|
||||
{
|
||||
if (buffer[0] != 0)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[DragEffectPacketLength].InitializePacket();
|
||||
CreateDragEffect(buffer, srcSerial, srcLocation, trgSerial, trgLocation, itemID, hue, amount);
|
||||
ns.Send(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void SendSeasonChange(this NetState ns, byte season, bool playSound) =>
|
||||
ns?.Send(stackalloc byte[]{ 0xBC, season, *(byte*)&playSound });
|
||||
var writer = new SpanWriter(buffer);
|
||||
writer.Write((byte)0x23); // Packet ID
|
||||
writer.Write((short)itemID);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((short)hue);
|
||||
writer.Write((short)amount);
|
||||
writer.Write(srcSerial);
|
||||
writer.Write((short)srcLocation.X);
|
||||
writer.Write((short)srcLocation.Y);
|
||||
writer.Write((sbyte)srcLocation.Z);
|
||||
writer.Write(trgSerial);
|
||||
writer.Write((short)trgLocation.X);
|
||||
writer.Write((short)trgLocation.Y);
|
||||
writer.Write((sbyte)trgLocation.Z);
|
||||
}
|
||||
|
||||
public static void SendDisplayPaperdoll(this NetState ns, Serial m, string title, bool warmode, bool canLift)
|
||||
public static void SendDragEffect(
|
||||
this NetState ns,
|
||||
Serial srcSerial, Point3D srcLocation,
|
||||
Serial trgSerial, Point3D trgLocation,
|
||||
int itemID, int hue, int amount
|
||||
)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte flags = 0x00;
|
||||
|
||||
if (warmode)
|
||||
{
|
||||
flags |= 0x01;
|
||||
}
|
||||
|
||||
if (canLift)
|
||||
{
|
||||
flags |= 0x02;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[66]);
|
||||
writer.Write((byte)0x88); // Packet ID
|
||||
writer.Write(m);
|
||||
writer.WriteAscii(title, 60);
|
||||
writer.Write(flags);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendPlayMusic(this NetState ns, MusicName music)
|
||||
Span<byte> buffer = stackalloc byte[DragEffectPacketLength].InitializePacket();
|
||||
CreateDragEffect(buffer, srcSerial, srcLocation, trgSerial, trgLocation, itemID, hue, amount);
|
||||
ns.Send(buffer);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void SendSeasonChange(this NetState ns, byte season, bool playSound) =>
|
||||
ns?.Send(stackalloc byte[]{ 0xBC, season, *(byte*)&playSound });
|
||||
|
||||
public static void SendDisplayPaperdoll(this NetState ns, Serial m, string title, bool warmode, bool canLift)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[3]);
|
||||
writer.Write((byte)0x6D); // Packet ID
|
||||
writer.Write((short)music);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendStopMusic(this NetState ns) => ns?.Send(stackalloc byte[] { 0x6D, 0x1F, 0xFF });
|
||||
byte flags = 0x00;
|
||||
|
||||
public static void SendScrollMessage(this NetState ns, int type, int tip, string text)
|
||||
if (warmode)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
text ??= "";
|
||||
|
||||
var length = 10 + text.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xA6); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((byte)type);
|
||||
writer.Write(tip);
|
||||
writer.Write((ushort)text.Length);
|
||||
writer.WriteAscii(text);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
flags |= 0x01;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendCurrentTime(this NetState ns) => ns.SendCurrentTime(Core.Now);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendCurrentTime(this NetState ns, DateTime date) =>
|
||||
ns?.Send(stackalloc byte[] { 0x5B, (byte)date.Hour, (byte)date.Minute, (byte)date.Second });
|
||||
|
||||
public static void SendPathfindMessage(this NetState ns, Point3D p)
|
||||
if (canLift)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[7]);
|
||||
writer.Write((byte)0x38); // Packet ID
|
||||
writer.Write((short)p.X);
|
||||
writer.Write((short)p.Y);
|
||||
writer.Write((short)p.Z);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
flags |= 0x02;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendPingAck(this NetState ns, byte ping) => ns?.Send(stackalloc byte[] { 0x73, ping });
|
||||
var writer = new SpanWriter(stackalloc byte[66]);
|
||||
writer.Write((byte)0x88); // Packet ID
|
||||
writer.Write(m);
|
||||
writer.WriteAscii(title, 60);
|
||||
writer.Write(flags);
|
||||
|
||||
public static void SendDisplayHuePicker(this NetState ns, Serial huePickerSerial, int huePickerItemID)
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendPlayMusic(this NetState ns, MusicName music)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[9]);
|
||||
writer.Write((byte)0x95); // Packet ID
|
||||
writer.Write(huePickerSerial);
|
||||
writer.Write((short)0);
|
||||
writer.Write((short)huePickerItemID);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[3]);
|
||||
writer.Write((byte)0x6D); // Packet ID
|
||||
writer.Write((short)music);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendStopMusic(this NetState ns) => ns?.Send(stackalloc byte[] { 0x6D, 0x1F, 0xFF });
|
||||
|
||||
public static void SendScrollMessage(this NetState ns, int type, int tip, string text)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
text ??= "";
|
||||
|
||||
var length = 10 + text.Length;
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0xA6); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((byte)type);
|
||||
writer.Write(tip);
|
||||
writer.Write((ushort)text.Length);
|
||||
writer.WriteAscii(text);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendCurrentTime(this NetState ns) => ns.SendCurrentTime(Core.Now);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendCurrentTime(this NetState ns, DateTime date) =>
|
||||
ns?.Send(stackalloc byte[] { 0x5B, (byte)date.Hour, (byte)date.Minute, (byte)date.Second });
|
||||
|
||||
public static void SendPathfindMessage(this NetState ns, Point3D p)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[7]);
|
||||
writer.Write((byte)0x38); // Packet ID
|
||||
writer.Write((short)p.X);
|
||||
writer.Write((short)p.Y);
|
||||
writer.Write((short)p.Z);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SendPingAck(this NetState ns, byte ping) => ns?.Send(stackalloc byte[] { 0x73, ping });
|
||||
|
||||
public static void SendDisplayHuePicker(this NetState ns, Serial huePickerSerial, int huePickerItemID)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[9]);
|
||||
writer.Write((byte)0x95); // Packet ID
|
||||
writer.Write(huePickerSerial);
|
||||
writer.Write((short)0);
|
||||
writer.Write((short)huePickerItemID);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,106 +16,105 @@
|
|||
using System.Buffers;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public enum TradeFlag : byte
|
||||
{
|
||||
public enum TradeFlag : byte
|
||||
Display,
|
||||
Close,
|
||||
Update,
|
||||
UpdateGold,
|
||||
UpdateLedger
|
||||
}
|
||||
|
||||
public static class OutgoingSecureTradePackets
|
||||
{
|
||||
public static void SendDisplaySecureTrade(
|
||||
this NetState ns, Mobile them, Container first, Container second, string name
|
||||
)
|
||||
{
|
||||
Display,
|
||||
Close,
|
||||
Update,
|
||||
UpdateGold,
|
||||
UpdateLedger
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[47]);
|
||||
writer.Write((byte)0x6F); // Packet ID
|
||||
writer.Write((ushort)47); // Length
|
||||
writer.Write((byte)TradeFlag.Display);
|
||||
writer.Write(them.Serial);
|
||||
writer.Write(first.Serial);
|
||||
writer.Write(second.Serial);
|
||||
writer.Write(true);
|
||||
|
||||
writer.WriteAscii(name ?? "", 30);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static class OutgoingSecureTradePackets
|
||||
public static void SendCloseSecureTrade(this NetState ns, Container cont)
|
||||
{
|
||||
public static void SendDisplaySecureTrade(
|
||||
this NetState ns, Mobile them, Container first, Container second, string name
|
||||
)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[47]);
|
||||
writer.Write((byte)0x6F); // Packet ID
|
||||
writer.Write((ushort)47); // Length
|
||||
writer.Write((byte)TradeFlag.Display);
|
||||
writer.Write(them.Serial);
|
||||
writer.Write(first.Serial);
|
||||
writer.Write(second.Serial);
|
||||
writer.Write(true);
|
||||
|
||||
writer.WriteAscii(name ?? "", 30);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendCloseSecureTrade(this NetState ns, Container cont)
|
||||
var writer = new SpanWriter(stackalloc byte[17]);
|
||||
writer.Write((byte)0x6F); // Packet ID
|
||||
writer.Write((ushort)17); // Length
|
||||
writer.Write((byte)TradeFlag.Close);
|
||||
writer.Write(cont.Serial);
|
||||
writer.Write(0);
|
||||
writer.Write(0);
|
||||
writer.Write(false);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendUpdateSecureTrade(this NetState ns, Container cont, bool first, bool second) =>
|
||||
ns.SendUpdateSecureTrade(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0);
|
||||
|
||||
public static void SendUpdateSecureTrade(this NetState ns, Container cont, TradeFlag flag, int first, int second)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[17]);
|
||||
writer.Write((byte)0x6F); // Packet ID
|
||||
writer.Write((ushort)17); // Length
|
||||
writer.Write((byte)TradeFlag.Close);
|
||||
writer.Write(cont.Serial);
|
||||
writer.Write(0);
|
||||
writer.Write(0);
|
||||
writer.Write(false);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendUpdateSecureTrade(this NetState ns, Container cont, bool first, bool second) =>
|
||||
ns.SendUpdateSecureTrade(cont, TradeFlag.Update, first ? 1 : 0, second ? 1 : 0);
|
||||
var writer = new SpanWriter(stackalloc byte[17]);
|
||||
writer.Write((byte)0x6F); // Packet ID
|
||||
writer.Write((ushort)17); // Length
|
||||
writer.Write((byte)flag);
|
||||
writer.Write(cont.Serial);
|
||||
writer.Write(first);
|
||||
writer.Write(second);
|
||||
writer.Write(false);
|
||||
|
||||
public static void SendUpdateSecureTrade(this NetState ns, Container cont, TradeFlag flag, int first, int second)
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendSecureTradeEquip(this NetState ns, Item item, Mobile m)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[17]);
|
||||
writer.Write((byte)0x6F); // Packet ID
|
||||
writer.Write((ushort)17); // Length
|
||||
writer.Write((byte)flag);
|
||||
writer.Write(cont.Serial);
|
||||
writer.Write(first);
|
||||
writer.Write(second);
|
||||
writer.Write(false);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendSecureTradeEquip(this NetState ns, Item item, Mobile m)
|
||||
var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]);
|
||||
writer.Write((byte)0x25); // Packet ID
|
||||
writer.Write(item.Serial);
|
||||
writer.Write((short)item.ItemID);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((short)item.Amount);
|
||||
writer.Write((short)item.X);
|
||||
writer.Write((short)item.Y);
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.ContainerGridLines ? 21 : 20]);
|
||||
writer.Write((byte)0x25); // Packet ID
|
||||
writer.Write(item.Serial);
|
||||
writer.Write((short)item.ItemID);
|
||||
writer.Write((byte)0);
|
||||
writer.Write((short)item.Amount);
|
||||
writer.Write((short)item.X);
|
||||
writer.Write((short)item.Y);
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
writer.Write(m.Serial);
|
||||
writer.Write((short)item.Hue);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
writer.Write(m.Serial);
|
||||
writer.Write((short)item.Hue);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,56 +16,55 @@
|
|||
using System.Buffers;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingTargetPackets
|
||||
{
|
||||
public static class OutgoingTargetPackets
|
||||
public static void SendMultiTargetReq(this NetState ns, MultiTarget t)
|
||||
{
|
||||
public static void SendMultiTargetReq(this NetState ns, MultiTarget t)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 30 : 26]);
|
||||
writer.Write((byte)0x99); // Packet ID
|
||||
writer.Write(t.AllowGround);
|
||||
writer.Write(t.TargetID);
|
||||
writer.Write((byte)t.Flags);
|
||||
writer.Clear(11);
|
||||
writer.Write((short)t.MultiID);
|
||||
writer.Write((short)t.Offset.X);
|
||||
writer.Write((short)t.Offset.Y);
|
||||
writer.Write((short)t.Offset.Z);
|
||||
if (ns.HighSeas)
|
||||
{
|
||||
writer.Write(0);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendCancelTarget(this NetState ns) =>
|
||||
ns?.Send(stackalloc byte[]
|
||||
{
|
||||
0x6C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
});
|
||||
|
||||
public static void SendTargetReq(this NetState ns, Target t)
|
||||
var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 30 : 26]);
|
||||
writer.Write((byte)0x99); // Packet ID
|
||||
writer.Write(t.AllowGround);
|
||||
writer.Write(t.TargetID);
|
||||
writer.Write((byte)t.Flags);
|
||||
writer.Clear(11);
|
||||
writer.Write((short)t.MultiID);
|
||||
writer.Write((short)t.Offset.X);
|
||||
writer.Write((short)t.Offset.Y);
|
||||
writer.Write((short)t.Offset.Z);
|
||||
if (ns.HighSeas)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[19]);
|
||||
writer.Write((byte)0x6C); // Packet ID
|
||||
writer.Write(t.AllowGround);
|
||||
writer.Write(t.TargetID);
|
||||
writer.Write((byte)t.Flags);
|
||||
writer.Clear(12);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
writer.Write(0);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendCancelTarget(this NetState ns) =>
|
||||
ns?.Send(stackalloc byte[]
|
||||
{
|
||||
0x6C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
|
||||
});
|
||||
|
||||
public static void SendTargetReq(this NetState ns, Target t)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[19]);
|
||||
writer.Write((byte)0x6C); // Packet ID
|
||||
writer.Write(t.AllowGround);
|
||||
writer.Write(t.TargetID);
|
||||
writer.Write((byte)t.Flags);
|
||||
writer.Clear(12);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,111 +17,110 @@ using System.Buffers;
|
|||
using System.Collections.Generic;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingVendorBuyPackets
|
||||
{
|
||||
public static class OutgoingVendorBuyPackets
|
||||
public static void SendVendorBuyContent(this NetState ns, List<BuyItemState> list)
|
||||
{
|
||||
public static void SendVendorBuyContent(this NetState ns, List<BuyItemState> list)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var length = 5 + list.Count * (ns.ContainerGridLines ? 20 : 19);
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x3C); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((short)list.Count);
|
||||
|
||||
for (var i = list.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var bis = list[i];
|
||||
|
||||
writer.Write(bis.MySerial);
|
||||
writer.Write((ushort)bis.ItemID);
|
||||
writer.Write((byte)0); // itemID offset
|
||||
writer.Write((ushort)bis.Amount);
|
||||
writer.Write((short)(i + 1)); // x
|
||||
writer.Write((short)1); // y
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((byte)0); // Grid Location?
|
||||
}
|
||||
writer.Write(bis.ContainerSerial);
|
||||
writer.Write((ushort)bis.Hue);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendDisplayBuyList(this NetState ns, Serial vendor)
|
||||
var length = 5 + list.Count * (ns.ContainerGridLines ? 20 : 19);
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x3C); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((short)list.Count);
|
||||
|
||||
for (var i = list.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var bis = list[i];
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]);
|
||||
writer.Write((byte)0x24); // Packet ID
|
||||
writer.Write(vendor);
|
||||
writer.Write((short)0x30); // Vendor Buy Window
|
||||
if (ns.HighSeas)
|
||||
writer.Write(bis.MySerial);
|
||||
writer.Write((ushort)bis.ItemID);
|
||||
writer.Write((byte)0); // itemID offset
|
||||
writer.Write((ushort)bis.Amount);
|
||||
writer.Write((short)(i + 1)); // x
|
||||
writer.Write((short)1); // y
|
||||
if (ns.ContainerGridLines)
|
||||
{
|
||||
writer.Write((short)0x0);
|
||||
writer.Write((byte)0); // Grid Location?
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
writer.Write(bis.ContainerSerial);
|
||||
writer.Write((ushort)bis.Hue);
|
||||
}
|
||||
|
||||
public static void SendVendorBuyList(this NetState ns, Mobile vendor, List<BuyItemState> list)
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendDisplayBuyList(this NetState ns, Serial vendor)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var length = 8;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
length += 6 + list[i].Description?.Length ?? 0;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x74); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((vendor.FindItemOnLayer(Layer.ShopBuy) as Container)?.Serial ?? Serial.MinusOne);
|
||||
writer.Write((byte)list.Count);
|
||||
|
||||
for (var i = 0; i < list.Count; ++i)
|
||||
{
|
||||
var bis = list[i];
|
||||
|
||||
writer.Write(bis.Price);
|
||||
|
||||
var desc = bis.Description ?? "";
|
||||
|
||||
writer.Write((byte)(desc.Length + 1));
|
||||
writer.WriteAsciiNull(desc);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendEndVendorBuy(this NetState ns, Serial vendor)
|
||||
var writer = new SpanWriter(stackalloc byte[ns.HighSeas ? 9 : 7]);
|
||||
writer.Write((byte)0x24); // Packet ID
|
||||
writer.Write(vendor);
|
||||
writer.Write((short)0x30); // Vendor Buy Window
|
||||
if (ns.HighSeas)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[8]);
|
||||
writer.Write((byte)0x3B); // Packet ID
|
||||
writer.Write((ushort)8);
|
||||
writer.Write(vendor);
|
||||
writer.Write((byte)0); // Buy count
|
||||
|
||||
ns.Send(writer.Span);
|
||||
writer.Write((short)0x0);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendVendorBuyList(this NetState ns, Mobile vendor, List<BuyItemState> list)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var length = 8;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
length += 6 + list[i].Description?.Length ?? 0;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[length]);
|
||||
writer.Write((byte)0x74); // Packet ID
|
||||
writer.Write((ushort)length);
|
||||
writer.Write((vendor.FindItemOnLayer(Layer.ShopBuy) as Container)?.Serial ?? Serial.MinusOne);
|
||||
writer.Write((byte)list.Count);
|
||||
|
||||
for (var i = 0; i < list.Count; ++i)
|
||||
{
|
||||
var bis = list[i];
|
||||
|
||||
writer.Write(bis.Price);
|
||||
|
||||
var desc = bis.Description ?? "";
|
||||
|
||||
writer.Write((byte)(desc.Length + 1));
|
||||
writer.WriteAsciiNull(desc);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendEndVendorBuy(this NetState ns, Serial vendor)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[8]);
|
||||
writer.Write((byte)0x3B); // Packet ID
|
||||
writer.Write((ushort)8);
|
||||
writer.Write(vendor);
|
||||
writer.Write((byte)0); // Buy count
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,66 +18,65 @@ using System.Buffers;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public static class OutgoingVendorSellPackets
|
||||
{
|
||||
public static class OutgoingVendorSellPackets
|
||||
public static void SendVendorSellList(this NetState ns, Serial vendor, List<SellItemState> list)
|
||||
{
|
||||
public static void SendVendorSellList(this NetState ns, Serial vendor, List<SellItemState> list)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var maxLength = 9;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
var sis = list[i];
|
||||
var item = sis.Item;
|
||||
maxLength += 14 + Math.Max(item.Name?.Length ?? 0, sis.Name?.Length ?? 0);
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||
writer.Write((byte)0x9E); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
|
||||
writer.Write(vendor);
|
||||
writer.Write((ushort)list.Count);
|
||||
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
var sis = list[i];
|
||||
var item = sis.Item;
|
||||
writer.Write(item.Serial);
|
||||
writer.Write((ushort)item.ItemID);
|
||||
writer.Write((ushort)item.Hue);
|
||||
writer.Write((ushort)item.Amount);
|
||||
writer.Write((ushort)sis.Price);
|
||||
|
||||
var name = (item.Name?.Trim()).DefaultIfNullOrEmpty(sis.Name ?? "");
|
||||
|
||||
writer.Write((ushort)name.Length);
|
||||
writer.WriteAscii(name);
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
ns.Send(writer.Span);
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SendEndVendorSell(this NetState ns, Serial vendor)
|
||||
var maxLength = 9;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[8]);
|
||||
writer.Write((byte)0x3B); // Packet ID
|
||||
writer.Write((ushort)8);
|
||||
writer.Write(vendor);
|
||||
writer.Write((byte)0);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
var sis = list[i];
|
||||
var item = sis.Item;
|
||||
maxLength += 14 + Math.Max(item.Name?.Length ?? 0, sis.Name?.Length ?? 0);
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[maxLength]);
|
||||
writer.Write((byte)0x9E); // Packet ID
|
||||
writer.Seek(2, SeekOrigin.Current);
|
||||
|
||||
writer.Write(vendor);
|
||||
writer.Write((ushort)list.Count);
|
||||
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
var sis = list[i];
|
||||
var item = sis.Item;
|
||||
writer.Write(item.Serial);
|
||||
writer.Write((ushort)item.ItemID);
|
||||
writer.Write((ushort)item.Hue);
|
||||
writer.Write((ushort)item.Amount);
|
||||
writer.Write((ushort)sis.Price);
|
||||
|
||||
var name = (item.Name?.Trim()).DefaultIfNullOrEmpty(sis.Name ?? "");
|
||||
|
||||
writer.Write((ushort)name.Length);
|
||||
writer.WriteAscii(name);
|
||||
}
|
||||
|
||||
writer.WritePacketLength();
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
||||
public static void SendEndVendorSell(this NetState ns, Serial vendor)
|
||||
{
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[8]);
|
||||
writer.Write((byte)0x3B); // Packet ID
|
||||
writer.Write((ushort)8);
|
||||
writer.Write(vendor);
|
||||
writer.Write((byte)0);
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,98 +18,97 @@ using System.Buffers;
|
|||
using System.Buffers.Binary;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server.Network
|
||||
namespace Server.Network;
|
||||
|
||||
public ref struct PacketContainerBuilder
|
||||
{
|
||||
public ref struct PacketContainerBuilder
|
||||
public const int MinPacketLength = 5;
|
||||
|
||||
private bool _finished;
|
||||
private int _count;
|
||||
|
||||
private byte[] _arrayToReturnToPool;
|
||||
private Span<byte> _bytes;
|
||||
|
||||
public PacketContainerBuilder(Span<byte> initialBuffer)
|
||||
{
|
||||
public const int MinPacketLength = 5;
|
||||
_arrayToReturnToPool = null;
|
||||
_finished = false;
|
||||
_count = 0;
|
||||
|
||||
private bool _finished;
|
||||
private int _count;
|
||||
_bytes = initialBuffer;
|
||||
_bytes[0] = 0xF7; // Packet ID
|
||||
Length = MinPacketLength; // Length + Count
|
||||
}
|
||||
|
||||
private byte[] _arrayToReturnToPool;
|
||||
private Span<byte> _bytes;
|
||||
public int Length { get; set; }
|
||||
|
||||
public PacketContainerBuilder(Span<byte> initialBuffer)
|
||||
public int Capacity => _bytes.Length;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public ReadOnlySpan<byte> Finalize()
|
||||
{
|
||||
if (!_finished)
|
||||
{
|
||||
_arrayToReturnToPool = null;
|
||||
_finished = false;
|
||||
_count = 0;
|
||||
|
||||
_bytes = initialBuffer;
|
||||
_bytes[0] = 0xF7; // Packet ID
|
||||
Length = MinPacketLength; // Length + Count
|
||||
BinaryPrimitives.WriteUInt16BigEndian(_bytes[1..3], (ushort)Length);
|
||||
BinaryPrimitives.WriteUInt16BigEndian(_bytes[3..5], (ushort)_count);
|
||||
}
|
||||
|
||||
public int Length { get; set; }
|
||||
return _bytes[..Length];
|
||||
}
|
||||
|
||||
public int Capacity => _bytes.Length;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public ReadOnlySpan<byte> Finalize()
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public Span<byte> GetSpan(int bytesNeeded)
|
||||
{
|
||||
if (_finished)
|
||||
{
|
||||
if (!_finished)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt16BigEndian(_bytes[1..3], (ushort)Length);
|
||||
BinaryPrimitives.WriteUInt16BigEndian(_bytes[3..5], (ushort)_count);
|
||||
}
|
||||
|
||||
return _bytes[..Length];
|
||||
throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public Span<byte> GetSpan(int bytesNeeded)
|
||||
if (Length > _bytes.Length - bytesNeeded)
|
||||
{
|
||||
if (_finished)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize");
|
||||
}
|
||||
|
||||
if (Length > _bytes.Length - bytesNeeded)
|
||||
{
|
||||
Grow(bytesNeeded);
|
||||
}
|
||||
|
||||
return _bytes[Length..];
|
||||
Grow(bytesNeeded);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Advance(int bytesWritten)
|
||||
{
|
||||
if (_finished)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize");
|
||||
}
|
||||
return _bytes[Length..];
|
||||
}
|
||||
|
||||
_count++;
|
||||
Length += bytesWritten;
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public void Advance(int bytesWritten)
|
||||
{
|
||||
if (_finished)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to use PacketContainerBuilder after finalize");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void Grow(int additionalCapacityBeyondPos)
|
||||
_count++;
|
||||
Length += bytesWritten;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void Grow(int additionalCapacityBeyondPos)
|
||||
{
|
||||
var newLength = Math.Max(Length + additionalCapacityBeyondPos, _bytes.Length * 2);
|
||||
byte[] poolArray = ArrayPool<byte>.Shared.Rent(newLength);
|
||||
|
||||
_bytes[..Length].CopyTo(poolArray);
|
||||
|
||||
byte[] toReturn = _arrayToReturnToPool;
|
||||
_bytes = _arrayToReturnToPool = poolArray;
|
||||
if (toReturn != null)
|
||||
{
|
||||
var newLength = Math.Max(Length + additionalCapacityBeyondPos, _bytes.Length * 2);
|
||||
byte[] poolArray = ArrayPool<byte>.Shared.Rent(newLength);
|
||||
|
||||
_bytes[..Length].CopyTo(poolArray);
|
||||
|
||||
byte[] toReturn = _arrayToReturnToPool;
|
||||
_bytes = _arrayToReturnToPool = poolArray;
|
||||
if (toReturn != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(toReturn);
|
||||
}
|
||||
ArrayPool<byte>.Shared.Return(toReturn);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
{
|
||||
byte[] toReturn = _arrayToReturnToPool;
|
||||
this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
|
||||
if (toReturn != null)
|
||||
{
|
||||
byte[] toReturn = _arrayToReturnToPool;
|
||||
this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
|
||||
if (toReturn != null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(toReturn);
|
||||
}
|
||||
ArrayPool<byte>.Shared.Return(toReturn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ namespace Server.Engines.Chat
|
|||
|
||||
public static void SendChatMessage(this NetState ns, string lang, int number, string param1, string param2)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -464,12 +464,7 @@ namespace Server.Engines.Harvest
|
|||
|
||||
var ns = from.NetState;
|
||||
|
||||
if (ns == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (number == 1043297 || ns.HighSeas)
|
||||
if (number == 1043297 || ns?.HighSeas == true)
|
||||
{
|
||||
from.SendLocalizedMessage(number, name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace Server.Engines.Help
|
|||
|
||||
public static void SendDisplayHelpTopic(this NetState ns, int topicID, bool display = true)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Server.Engines.PartySystem
|
|||
|
||||
public static void SendPartyMemberList(this NetState ns, Party p)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ namespace Server.Engines.PartySystem
|
|||
|
||||
public static void SendPartyRemoveMember(this NetState ns, Serial m, Party p = null)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ namespace Server.Engines.PartySystem
|
|||
|
||||
public static void SendPartyTextMessage(this NetState ns, Serial m, string text, bool toAll)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ namespace Server.Engines.PartySystem
|
|||
|
||||
public static void SendPartyInvitation(this NetState ns, Serial leader)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ namespace Server.Engines.VeteranRewards
|
|||
|
||||
public static void SendStatueAnimation(this NetState ns, Serial serial, int status, int anim, int frame)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ namespace Server.Items
|
|||
|
||||
public static void SendBookContent(this NetState ns, BaseBook book)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ namespace Server.Items
|
|||
|
||||
public static void SendBookCover(this NetState ns, Mobile from, BaseBook book)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendBBDisplayBoard(this NetState ns, BaseBulletinBoard board)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -203,7 +203,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendBBMessage(this NetState ns, BaseBulletinBoard board, BulletinMessage msg, bool content = false)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ namespace Server.Engines.Mahjong
|
|||
|
||||
public static void SendMahjongJoinGame(this NetState ns, Serial game)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -303,7 +303,7 @@ namespace Server.Engines.Mahjong
|
|||
|
||||
public static void SendMahjongPlayersInfo(this NetState ns, MahjongGame game, Mobile to)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -361,7 +361,7 @@ namespace Server.Engines.Mahjong
|
|||
|
||||
public static void SendMahjongTileInfo(this NetState ns, MahjongTile tile, Mobile to)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -407,7 +407,7 @@ namespace Server.Engines.Mahjong
|
|||
|
||||
public static void SendMahjongTilesInfo(this NetState ns, MahjongGame game, Mobile to)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -460,7 +460,7 @@ namespace Server.Engines.Mahjong
|
|||
|
||||
public static void SendMahjongGeneralInfo(this NetState ns, MahjongGame game)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -503,7 +503,7 @@ namespace Server.Engines.Mahjong
|
|||
|
||||
public static void SendMahjongRelieve(this NetState ns, Serial game)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendMapDetails(this NetState ns, MapItem map)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendMapCommand(this NetState ns, MapItem map, int command, int x = 0, int y = 0, bool editable = false)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace Server.Network
|
|||
{
|
||||
public static void SendCorpseEquip(this NetState ns, Mobile beholder, Corpse beheld)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendCorpseContent(this NetState ns, Mobile beholder, Corpse beheld)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -671,7 +671,7 @@ namespace Server.Items
|
|||
// The client must know about the spellbook or it will crash!
|
||||
var ns = to.NetState;
|
||||
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Server.Items
|
|||
|
||||
public static void SendToggleSpecialAbility(this NetState ns, int abilityId, bool active)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ namespace Server
|
|||
long ticks
|
||||
)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -207,7 +207,7 @@ namespace Server
|
|||
|
||||
public static void SendRemoveBuffPacket(NetState ns, Serial mob, BuffIcon iconID)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,19 +15,21 @@ namespace Server
|
|||
return true;
|
||||
}
|
||||
|
||||
var lowerName = name.ToLowerInvariant().Replace(" ", "");
|
||||
var lowerName = name?.ToLowerInvariant().Replace(" ", "");
|
||||
|
||||
foreach (var so in SkillInfo.Table)
|
||||
if (!string.IsNullOrEmpty(lowerName))
|
||||
{
|
||||
if (lowerName == so.ProfessionSkillName.ToLowerInvariant())
|
||||
foreach (var so in SkillInfo.Table)
|
||||
{
|
||||
skillName = (SkillName)so.SkillID;
|
||||
return true;
|
||||
if (lowerName == so.ProfessionSkillName.ToLowerInvariant())
|
||||
{
|
||||
skillName = (SkillName)so.SkillID;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
static ProfessionInfo()
|
||||
|
|
|
|||
|
|
@ -969,7 +969,7 @@ namespace Server.Mobiles
|
|||
|
||||
var ns = from.NetState;
|
||||
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace Server.Multis
|
|||
|
||||
public static void SendBeginHouseCustomization(this NetState ns, Serial house)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ namespace Server.Multis
|
|||
|
||||
public static void SendEndHouseCustomization(this NetState ns, Serial house)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ namespace Server.Multis
|
|||
|
||||
public static void SendDesignStateGeneral(this NetState ns, Serial house, int revision)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendServerPollInfo(this NetState ns)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendGuildMemberLocations(this NetState ns, Mobile from, Guild guild, bool sendLocations)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendPartyMemberLocations(this NetState ns, Mobile from, Party party)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace Server.Network
|
|||
this NetState ns, uint age, int clients, int items, int mobiles, long mem
|
||||
)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -82,7 +82,7 @@ namespace Server.Network
|
|||
this NetState ns, string name, int age, int clients, int items, int mobiles, int mem
|
||||
)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Server.Network
|
|||
|
||||
public static void SendArrow(this NetState ns, byte command, int x, int y, Serial s)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns.CannotSendPackets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue