From 534de0c41400c9b711ee413e3f7c174a9d9c211d Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 16 Oct 2024 23:04:10 -0700 Subject: [PATCH 01/93] Add my stream parser to utils, simple probably could use some work. --- utils/stream_parser/.gitignore | 388 +++++++++++ .../common/Daybreak/Connection.cs | 609 ++++++++++++++++++ .../common/Daybreak/GamePacket.cs | 114 ++++ .../common/Daybreak/IConnection.cs | 40 ++ .../stream_parser/common/Daybreak/IParser.cs | 16 + utils/stream_parser/common/Daybreak/Opcode.cs | 39 ++ utils/stream_parser/common/Daybreak/Parser.cs | 109 ++++ utils/stream_parser/common/Util/Crc32.cs | 103 +++ utils/stream_parser/common/common.csproj | 14 + .../stream_parser/ConsoleHostedService.cs | 317 +++++++++ .../ConsoleHostedServiceOptions.cs | 27 + utils/stream_parser/stream_parser/Program.cs | 25 + .../Properties/launchSettings.json | 9 + .../stream_parser/stream_parser.csproj | 24 + 14 files changed, 1834 insertions(+) create mode 100644 utils/stream_parser/.gitignore create mode 100644 utils/stream_parser/common/Daybreak/Connection.cs create mode 100644 utils/stream_parser/common/Daybreak/GamePacket.cs create mode 100644 utils/stream_parser/common/Daybreak/IConnection.cs create mode 100644 utils/stream_parser/common/Daybreak/IParser.cs create mode 100644 utils/stream_parser/common/Daybreak/Opcode.cs create mode 100644 utils/stream_parser/common/Daybreak/Parser.cs create mode 100644 utils/stream_parser/common/Util/Crc32.cs create mode 100644 utils/stream_parser/common/common.csproj create mode 100644 utils/stream_parser/stream_parser/ConsoleHostedService.cs create mode 100644 utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs create mode 100644 utils/stream_parser/stream_parser/Program.cs create mode 100644 utils/stream_parser/stream_parser/Properties/launchSettings.json create mode 100644 utils/stream_parser/stream_parser/stream_parser.csproj diff --git a/utils/stream_parser/.gitignore b/utils/stream_parser/.gitignore new file mode 100644 index 000000000..34c8dee45 --- /dev/null +++ b/utils/stream_parser/.gitignore @@ -0,0 +1,388 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Nuget personal access tokens and Credentials +nuget.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +.idea/ +*.sln.iml diff --git a/utils/stream_parser/common/Daybreak/Connection.cs b/utils/stream_parser/common/Daybreak/Connection.cs new file mode 100644 index 000000000..470e2b17a --- /dev/null +++ b/utils/stream_parser/common/Daybreak/Connection.cs @@ -0,0 +1,609 @@ +using Ionic.Zlib; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace StreamParser.Common.Daybreak +{ + public class Connection : IConnection + { + private class EncodeType + { + public const int None = 0; + public const int Compression = 1; + public const int XOR = 4; + } + + private enum SequenceOrder + { + Past, + Current, + Future + } + + private readonly IParser _owner; + private readonly IPAddress _srcAddr; + private readonly int _srcPort; + private readonly IPAddress _dstAddr; + private readonly int _dstPort; + private readonly Util.Crc32 _crc_generator = new Util.Crc32(); + private readonly Guid _id = Guid.NewGuid(); + private uint _connect_code = 0; + private int _encode_key = 0; + private int _crc_bytes = 0; + private int[] _encode_pass = new int[2] { 0, 0 }; + private ConnectionStream[] _client_streams = new ConnectionStream[4] { + new ConnectionStream(), + new ConnectionStream(), + new ConnectionStream(), + new ConnectionStream() + }; + private ConnectionStream[] _server_streams = new ConnectionStream[4] { + new ConnectionStream(), + new ConnectionStream(), + new ConnectionStream(), + new ConnectionStream() + }; + + public IConnection.OnPacketRecvHandler OnPacketRecv { get; set; } + + public IPAddress ClientAddress => _srcAddr; + public int ClientPort => _srcPort; + public IPAddress ServerAddress => _dstAddr; + public int ServerPort => _dstPort; + public Guid Id => _id; + + public ConnectionType ConnectionType { + get + { + //World servers used to be coded to always be 9000 but live started using dynamic ports + //I've seen from 9000 to 9008 on live + if (_dstPort >= 9000 && _dstPort <= 9010) + { + return ConnectionType.World; + } + else if (_encode_pass[0] == EncodeType.None && _encode_pass[1] == EncodeType.None) + { + return ConnectionType.Login; + } + else if (_encode_pass[0] == EncodeType.XOR && _encode_pass[1] == EncodeType.None) + { + return ConnectionType.Chat; + } + else if (_encode_pass[0] == EncodeType.Compression && _encode_pass[1] == EncodeType.None) + { + return ConnectionType.Zone; + } + + return ConnectionType.Unknown; + } + } + + public Connection(IParser owner, IPAddress srcAddr, int srcPort, IPAddress dstAddr, int dstPort) + { + _owner = owner; + _srcAddr = srcAddr; + _srcPort = srcPort; + _dstAddr = dstAddr; + _dstPort = dstPort; + } + + public void ProcessPacket(IPAddress srcAddr, int srcPort, DateTime packetTime, ReadOnlySpan data) + { + if (data.Length < 1) + { + return; + } + + var opcode = data[1]; + if (data[0] == 0 && (opcode == Opcode.KeepAlive || opcode == Opcode.OutboundPing)) + { + return; + } + + if (PacketCanBeDecoded(data)) + { + if (!ValidateCRC(data)) + { + return; + } + + if (_encode_pass[0] == EncodeType.None && _encode_pass[1] == EncodeType.None) + { + ProcessDecodedPacket(srcAddr, srcPort, packetTime, data.Slice(0, data.Length - _crc_bytes)); + } + else + { + //unfortunately we can't avoid a copy here + var temp = data.Slice(0, data.Length - _crc_bytes).ToArray(); + for (int i = 1; i >= 0; --i) + { + switch(_encode_pass[i]) + { + case EncodeType.Compression: + if(temp[0] == 0) + { + temp = Decompress(temp, 2, temp.Length - 2); + } + else + { + temp = Decompress(temp, 1, temp.Length - 1); + } + break; + case EncodeType.XOR: + if (temp[0] == 0) + { + temp = Decode(temp, 2, temp.Length - 2); + } + else + { + temp = Decode(temp, 1, temp.Length - 1); + } + break; + } + } + + ProcessDecodedPacket(srcAddr, srcPort, packetTime, temp); + } + } + else + { + ProcessDecodedPacket(srcAddr, srcPort, packetTime, data); + } + } + + private void ProcessDecodedPacket(IPAddress srcAddr, int srcPort, DateTime packetTime, ReadOnlySpan data) + { + if (data.Length < 1) + { + return; + } + + if (data[0] == 0) + { + if (data.Length < 2) + { + return; + } + + var opcode = data[1]; + switch (opcode) + { + case Opcode.SessionResponse: + if (_connect_code == 0) + { + _connect_code = BitConverter.ToUInt32(data.Slice(2, 4)); + _encode_key = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data.Slice(6, 4))); + _crc_bytes = data[10]; + _encode_pass[0] = data[11]; + _encode_pass[1] = data[12]; + _owner.OnNewConnection?.Invoke(this, packetTime); + } + break; + case Opcode.SessionDisconnect: + if(_connect_code != 0) + { + _connect_code = 0; + _encode_key = 0; + _crc_bytes = 0; + _encode_pass[0] = 0; + _encode_pass[1] = 0; + _owner.OnLostConnection?.Invoke(this, packetTime); + } + break; + case Opcode.Combined: + { + int current = 2; + int end = data.Length; + while (current < end) + { + byte subpacket_length = data[current]; + current += 1; + + if (end < current + subpacket_length) + { + return; + } + + var subpacket = data.Slice(current, subpacket_length); + ProcessDecodedPacket(srcAddr, srcPort, packetTime, subpacket); + current += subpacket_length; + } + } + break; + case Opcode.AppCombined: + { + int current = 2; + int end = data.Length; + while (current < end) + { + int subpacket_length = 0; + if (data[current] == 0xff) + { + if (end < current + 3) + { + return; + } + + if (data[current + 1] == 0xff && data[current + 2] == 0xff) + { + if (end < current + 7) + { + return; + } + + subpacket_length = + ((data[current + 3]) << 24) | + ((data[current + 4]) << 16) | + ((data[current + 5]) << 8) | + (data[current + 6]); + + current += 7; + } + else + { + subpacket_length = + ((data[current + 1]) << 8) | + (data[current + 2]); + + current += 3; + } + } + else + { + subpacket_length = data[current]; + current += 1; + } + + var subpacket = data.Slice(current, subpacket_length); + ProcessDecodedPacket(srcAddr, srcPort, packetTime, subpacket); + } + } + break; + case Opcode.Packet: + case Opcode.Packet2: + case Opcode.Packet3: + case Opcode.Packet4: + { + var stream_id = opcode - Opcode.Packet; + var stream = FindStream(srcAddr, srcPort, stream_id); + var sequence = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data.Slice(2, 2))); + var order = CompareSequence(stream.Sequence, sequence); + if (order == SequenceOrder.Future) + { + if (!stream.PacketQueue.ContainsKey(sequence)) + { + stream.PacketQueue.Add(sequence, new ConnectionStream.QueuedPacket + { + Data = data.ToArray(), + PacketTime = packetTime + }); + } + } + else if (order == SequenceOrder.Current) + { + if (stream.PacketQueue.ContainsKey(sequence)) + { + stream.PacketQueue.Remove(sequence); + } + + stream.Sequence++; + ProcessDecodedPacket(srcAddr, srcPort, packetTime, data.Slice(4)); + ProcessQueue(srcAddr, srcPort, stream_id); + } + } + break; + case Opcode.Fragment: + case Opcode.Fragment2: + case Opcode.Fragment3: + case Opcode.Fragment4: + { + var stream_id = opcode - Opcode.Fragment; + var stream = FindStream(srcAddr, srcPort, stream_id); + var sequence = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data.Slice(2, 2))); + var order = CompareSequence(stream.Sequence, sequence); + if (order == SequenceOrder.Future) + { + if (!stream.PacketQueue.ContainsKey(sequence)) + { + stream.PacketQueue.Add(sequence, new ConnectionStream.QueuedPacket + { + Data = data.ToArray(), + PacketTime = packetTime + }); + } + } + else if (order == SequenceOrder.Current) + { + if (stream.PacketQueue.ContainsKey(sequence)) + { + stream.PacketQueue.Remove(sequence); + } + + stream.Sequence++; + + if (stream.TotalFragmentedBytes == 0) + { + stream.TotalFragmentedBytes = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data.Slice(4, 4))); + stream.CurrentFragmentedBytes = (uint)(data.Length - 8); + + if(stream.FragmentBuffer == null || stream.FragmentBuffer.Length < (stream.TotalFragmentedBytes + 512)) + { + stream.FragmentBuffer = new byte[stream.TotalFragmentedBytes + 512]; + } + + var target = stream.FragmentBuffer.AsSpan(); + data.Slice(8).CopyTo(target); + } else + { + var target = stream.FragmentBuffer.AsSpan((int)stream.CurrentFragmentedBytes); + data.Slice(4).CopyTo(target); + + stream.CurrentFragmentedBytes += (uint)(data.Length - 4); + + if (stream.CurrentFragmentedBytes >= stream.TotalFragmentedBytes) + { + ProcessDecodedPacket(srcAddr, srcPort, packetTime, + stream.FragmentBuffer.AsSpan(0, (int)stream.TotalFragmentedBytes)); + stream.CurrentFragmentedBytes = 0; + stream.TotalFragmentedBytes = 0; + } + } + + ProcessQueue(srcAddr, srcPort, stream_id); + } + + } + break; + case Opcode.Padding: + OnPacketRecv?.Invoke(this, GetDirection(srcAddr, srcPort), packetTime, data.Slice(1)); + break; + default: + break; + } + } else + { + OnPacketRecv?.Invoke(this, GetDirection(srcAddr, srcPort), packetTime, data); + } + } + + private void ProcessQueue(IPAddress srcAddr, int srcPort, int stream_id) + { + var stream = FindStream(srcAddr, srcPort, stream_id); + var sequence = stream.Sequence; + + //try to get the current sequence in the queue, if it exists then process it + ConnectionStream.QueuedPacket value; + if(stream.PacketQueue.TryGetValue(sequence, out value)) + { + ProcessDecodedPacket(srcAddr, srcPort, value.PacketTime, value.Data); + } + } + + public bool Match(IPAddress srcAddr, int srcPort, IPAddress dstAddr, int dstPort) + { + var p1 = _srcAddr.Equals(srcAddr) && _srcPort == srcPort && _dstAddr.Equals(dstAddr) && _dstPort == dstPort; + var p2 = _srcAddr.Equals(dstAddr) && _srcPort == dstPort && _dstAddr.Equals(srcAddr) && _dstPort == srcPort; + return p1 || p2; + } + + private SequenceOrder CompareSequence(ushort expected, ushort actual) + { + int diff = (int)actual - (int)expected; + + if (diff == 0) + { + return SequenceOrder.Current; + } + + if (diff > 0) + { + if (diff > 10000) + { + return SequenceOrder.Past; + } + + return SequenceOrder.Future; + } + + if (diff < -10000) + { + return SequenceOrder.Future; + } + + return SequenceOrder.Past; + } + + private bool PacketCanBeDecoded(ReadOnlySpan p) + { + if (p.Length < 2) + { + return false; + } + + if (p[0] != 0) + { + return true; + } + + var opcode = p[1]; + + if (opcode == Opcode.SessionRequest || opcode == Opcode.SessionResponse || opcode == Opcode.OutOfSession) + { + return false; + } + + return true; + } + + private bool ValidateCRC(ReadOnlySpan p) + { + if (_crc_bytes == 0) + { + return true; + } + + int actual = 0; + int calculated = _crc_generator.Calculate(p.Slice(0, p.Length - _crc_bytes), _encode_key); + switch (_crc_bytes) + { + case 2: + actual = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(p.Slice(p.Length - 2, 2))) & 0xFFFF; + calculated = calculated & 0xFFFF; + break; + case 4: + actual = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(p.Slice(p.Length - 4, 4))); + break; + default: + return false; + } + + return actual == calculated; + } + + private byte[] Decompress(byte[] p, int offset, int length) + { + if (length < 2) + { + return p; + } + + Span header = p.AsSpan(0, offset); + byte flag = p[offset]; + Span payload = p.AsSpan(offset + 1, length - 1); + + if (flag == 0x5a) + { + var pl = payload.ToArray(); + var inflated = Inflate(payload.ToArray()); + byte[] ret = new byte[offset + inflated.Length]; + Array.Copy(p, 0, ret, 0, offset); + Array.Copy(inflated, 0, ret, offset, inflated.Length); + return ret; + } + else if (flag == 0xa5) + { + byte[] ret = new byte[offset + length - 1]; + Array.Copy(p, 0, ret, 0, offset); + Array.Copy(p, offset + 1, ret, offset, length - 1); + return ret; + } + else + { + return p; + } + } + + private byte[] Inflate(byte[] p) + { + try + { + using (var out_stream = new MemoryStream()) + { + using (var in_stream = new MemoryStream(p)) + { + var buffer = new byte[512]; + using (var zs = new ZlibStream(in_stream, CompressionMode.Decompress)) + { + int r = 0; + do + { + r = zs.Read(buffer, 0, 512); + out_stream.Write(buffer, 0, r); + } while (r == 512); + } + } + + var ret = out_stream.ToArray(); + return ret; + } + } + catch (Exception) + { + return null; + } + } + + private byte[] Decode(byte[] p, int offset, int length) + { + int key = _encode_key; + Span buffer = p.AsSpan(offset, length); + int i = 0; + for (i = 0; i + 4 <= length; i += 4) + { + int pt = BitConverter.ToInt32(buffer.Slice(i)) ^ key; + key = BitConverter.ToInt32(buffer.Slice(i)); + + if(BitConverter.TryWriteBytes(buffer.Slice(i), pt) == false) + { + throw new Exception("Error writing bytes back in decode."); + } + } + + byte kc = (byte)(key & 0xFF); + for (; i < length; i++) + { + buffer[i] = (byte)(buffer[i] ^ kc); + } + + return p; + } + + private Direction GetDirection(IPAddress srcAddr, int srcPort) + { + if(srcAddr.Equals(_srcAddr) && srcPort == _srcPort) + { + return Direction.ClientToServer; + } else + { + return Direction.ServerToClient; + } + } + + private ConnectionStream FindStream(IPAddress srcAddr, int srcPort, int index) + { + if (index < 0 || index > 3) + { + return null; + } + + var dir = GetDirection(srcAddr, srcPort); + + if(dir == Direction.ClientToServer) + { + return _client_streams[index]; + } else + { + return _server_streams[index]; + } + } + + private class ConnectionStream + { + public class QueuedPacket + { + public byte[] Data { get; set; } + public DateTime PacketTime { get; set; } + } + + public ConnectionStream() + { + Sequence = 0; + CurrentFragmentedBytes = 0; + TotalFragmentedBytes = 0; + FragmentBuffer = null; + PacketQueue = new Dictionary(); + } + + public ushort Sequence { get; set; } + public uint CurrentFragmentedBytes { get; set; } + public uint TotalFragmentedBytes { get; set; } + public byte[] FragmentBuffer { get; set; } + public Dictionary PacketQueue { get; set; } + } + } +} diff --git a/utils/stream_parser/common/Daybreak/GamePacket.cs b/utils/stream_parser/common/Daybreak/GamePacket.cs new file mode 100644 index 000000000..af73c831b --- /dev/null +++ b/utils/stream_parser/common/Daybreak/GamePacket.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StreamParser.Common.Daybreak +{ + public ref struct GamePacket + { + private readonly ReadOnlySpan _data; + + public GamePacket(ReadOnlySpan data) + { + _data = data; + } + + public readonly override string ToString() + { + return ToString(16); + } + + public readonly string ToString(int columns) + { + int rows = _data.Length / columns; + if (_data.Length % columns != 0) + { + rows += 1; + } + + int expected = (10 + columns * 4) * rows; + var sb = new StringBuilder(expected); + + for(var i = 0; i < rows; ++i) + { + sb.AppendFormat("{0} |", (i * columns).ToString("X5")); + + for(var j = 0; j < columns; ++j) + { + var index = (i * 16) + j; + if (index >= _data.Length) + { + sb.Append(" "); + } else + { + var c = _data[index]; + sb.AppendFormat("{0,3}", c.ToString("X2")); + } + } + + sb.Append(" | "); + + for (var j = 0; j < columns; ++j) + { + var index = (i * 16) + j; + if (index >= _data.Length) + { + sb.Append(" "); + } + else + { + var c = _data[index]; + var ch = (char)c; + if (char.IsLetterOrDigit(ch) || char.IsPunctuation(ch) || char.IsSymbol(ch) || (ch == ' ')) + { + sb.Append(ch); + } + else + { + sb.Append("."); + } + } + } + + sb.AppendLine(); + } + + return sb.ToString(); + } + + public readonly string ToModelString(int max_taken, bool hex) + { + int expected = Math.Min(_data.Length, max_taken) * (hex ? 2 : 1); + if(expected <= 0) + { + return string.Empty; + } + + var sb = new StringBuilder(expected); + + for(var i = 0; i < Math.Min(_data.Length, max_taken); ++i) + { + var c = _data[i]; + if(hex) + { + sb.Append(c.ToString("X2")); + } else + { + var ch = (char)c; + if (char.IsLetterOrDigit(ch) || char.IsPunctuation(ch) || char.IsSymbol(ch) || (ch == ' ')) + { + sb.Append(ch); + } + else + { + sb.Append("."); + } + } + } + + return sb.ToString(); + } + } +} diff --git a/utils/stream_parser/common/Daybreak/IConnection.cs b/utils/stream_parser/common/Daybreak/IConnection.cs new file mode 100644 index 000000000..3a422a2df --- /dev/null +++ b/utils/stream_parser/common/Daybreak/IConnection.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace StreamParser.Common.Daybreak +{ + public enum Direction + { + ClientToServer, + ServerToClient + } + + public enum ConnectionType + { + Unknown, + Login, + World, + Chat, + Zone + } + + public interface IConnection + { + delegate void OnPacketRecvHandler(Connection connection, Direction direction, DateTime packetTime, ReadOnlySpan data); + OnPacketRecvHandler OnPacketRecv { get; set; } + + void ProcessPacket(IPAddress srcAddr, int srcPort, DateTime packetTime, ReadOnlySpan data); + bool Match(IPAddress srcAddr, int srcPort, IPAddress dstAddr, int dstPort); + + ConnectionType ConnectionType { get; } + IPAddress ClientAddress { get; } + int ClientPort { get; } + IPAddress ServerAddress { get; } + int ServerPort { get; } + Guid Id { get; } + } +} diff --git a/utils/stream_parser/common/Daybreak/IParser.cs b/utils/stream_parser/common/Daybreak/IParser.cs new file mode 100644 index 000000000..28d85e05a --- /dev/null +++ b/utils/stream_parser/common/Daybreak/IParser.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StreamParser.Common.Daybreak +{ + public interface IParser + { + delegate void ConnectionHandler(IConnection connection, DateTime connectionTime); + ConnectionHandler OnNewConnection { get; set; } + ConnectionHandler OnLostConnection { get; set; } + void Parse(string filename); + } +} diff --git a/utils/stream_parser/common/Daybreak/Opcode.cs b/utils/stream_parser/common/Daybreak/Opcode.cs new file mode 100644 index 000000000..3239851f6 --- /dev/null +++ b/utils/stream_parser/common/Daybreak/Opcode.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StreamParser.Common.Daybreak +{ + public class Opcode + { + public const byte Padding = 0; + public const byte SessionRequest = 1; + public const byte SessionResponse = 2; + public const byte Combined = 3; + public const byte SessionDisconnect = 5; + public const byte KeepAlive = 6; + public const byte SessionStatRequest = 7; + public const byte SessionStatResponse = 8; + public const byte Packet = 9; + public const byte Packet2 = 10; + public const byte Packet3 = 11; + public const byte Packet4 = 12; + public const byte Fragment = 13; + public const byte Fragment2 = 14; + public const byte Fragment3 = 15; + public const byte Fragment4 = 16; + public const byte OutOfOrderAck = 17; + public const byte OutOfOrderAck2 = 18; + public const byte OutOfOrderAck3 = 19; + public const byte OutOfOrderAck4 = 20; + public const byte Ack = 21; + public const byte Ack2 = 22; + public const byte Ack3 = 23; + public const byte Ack4 = 22; + public const byte AppCombined = 23; + public const byte OutboundPing = 28; + public const byte OutOfSession = 29; + } +} diff --git a/utils/stream_parser/common/Daybreak/Parser.cs b/utils/stream_parser/common/Daybreak/Parser.cs new file mode 100644 index 000000000..8c95dd6f9 --- /dev/null +++ b/utils/stream_parser/common/Daybreak/Parser.cs @@ -0,0 +1,109 @@ +using Microsoft.Extensions.Logging; +using SharpPcap; +using SharpPcap.LibPcap; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace StreamParser.Common.Daybreak +{ + public class Parser : IParser + { + /** + * Dependencies + */ + private readonly ILogger _logger; + private readonly List _connections = new List(); + + public IParser.ConnectionHandler OnNewConnection { get; set; } + public IParser.ConnectionHandler OnLostConnection { get; set; } + + public Parser(ILogger logger) + { + _logger = logger; + } + + public void Parse(string filename) + { + ICaptureDevice device = null; + try + { + device = new CaptureFileReaderDevice(filename); + device.Open(); + + device.OnPacketArrival += new PacketArrivalEventHandler(OnPacketCapture); + + device.Capture(); + device.Close(); + } catch(Exception ex) + { + _logger.LogError(ex, "Error reading device capture."); + } + } + + private void OnPacketCapture(object sender, PacketCapture capture) + { + var raw = capture.GetPacket(); + if (raw.LinkLayerType == PacketDotNet.LinkLayers.Ethernet) + { + var packet = PacketDotNet.Packet.ParsePacket(raw.LinkLayerType, raw.Data); + var ipPacket = packet.Extract(); + var udpPacket = packet.Extract(); + + if (ipPacket != null && udpPacket != null) + { + try + { + ProcessPacket(ipPacket.SourceAddress, + udpPacket.SourcePort, + ipPacket.DestinationAddress, + udpPacket.DestinationPort, + raw.Timeval.Date, + udpPacket.PayloadData); + } catch(Exception ex) + { + _logger.LogError(ex, "Error processing packet"); + } + } + } + } + + private void ProcessPacket(IPAddress srcAddr, int srcPort, IPAddress dstAddr, int dstPort, DateTime packetTime, ReadOnlySpan data) + { + if(data.Length < 2) + { + _logger.LogTrace("Tossing packet, {0} was less than minimum packet size", data.Length); + return; + } + + var c = FindConnection(srcAddr, srcPort, dstAddr, dstPort); + + if(c != null) + { + c.ProcessPacket(srcAddr, srcPort, packetTime, data); + } + else if (data[0] == 0 && data[1] == Opcode.SessionRequest) + { + c = new Connection(this, srcAddr, srcPort, dstAddr, dstPort); + _connections.Add(c); + c.ProcessPacket(srcAddr, srcPort, packetTime, data); + } + } + + private IConnection FindConnection(IPAddress srcAddr, int srcPort, IPAddress dstAddr, int dstPort) + { + foreach (var c in _connections) + { + if (c.Match(srcAddr, srcPort, dstAddr, dstPort)) + { + return c; + } + } + + return null; + } + } +} diff --git a/utils/stream_parser/common/Util/Crc32.cs b/utils/stream_parser/common/Util/Crc32.cs new file mode 100644 index 000000000..ae0b863bb --- /dev/null +++ b/utils/stream_parser/common/Util/Crc32.cs @@ -0,0 +1,103 @@ +using System; + +namespace StreamParser.Common.Util +{ + public class Crc32 + { + private uint[] _encodeTable = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + public int Calculate(ReadOnlySpan data) + { + uint crc = 0xffffffff; + + for (int i = 0; i < data.Length; ++i) + { + crc = ((crc >> 8) & 0x00FFFFFF) ^ _encodeTable[(crc ^ data[i]) & 0x000000FF]; + } + + return (int)~crc; + } + + public int Calculate(ReadOnlySpan data, int key) + { + uint crc = 0xffffffff; + for (int i = 0; i < 4; ++i) + { + crc = ((crc >> 8) & 0x00FFFFFF) ^ _encodeTable[(crc ^ ((key >> (i * 8)) & 0xff)) & 0x000000FF]; + } + + for (int i = 0; i < data.Length; ++i) + { + crc = ((crc >> 8) & 0x00FFFFFF) ^ _encodeTable[(crc ^ data[i]) & 0x000000FF]; + } + + return (int)~crc; + } + } +} diff --git a/utils/stream_parser/common/common.csproj b/utils/stream_parser/common/common.csproj new file mode 100644 index 000000000..5d289096f --- /dev/null +++ b/utils/stream_parser/common/common.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + StreamParser.Common + + + + + + + + + diff --git a/utils/stream_parser/stream_parser/ConsoleHostedService.cs b/utils/stream_parser/stream_parser/ConsoleHostedService.cs new file mode 100644 index 000000000..69e071072 --- /dev/null +++ b/utils/stream_parser/stream_parser/ConsoleHostedService.cs @@ -0,0 +1,317 @@ +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using StreamParser.Common.Daybreak; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using CommandLine; +using System.Globalization; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace StreamParser +{ + public class ConsoleHostedService : IHostedService + { + private class ParsedPacket { + public byte[] Data { get; set; } + public Direction Direction { get; set; } + public DateTime Time { get; set; } + } + + private class ParsedConnection + { + public IPAddress ClientAddress { get; set; } + public int ClientPort { get; set; } + public IPAddress ServerAddress { get; set; } + public int ServerPort { get; set; } + public ConnectionType ConnectionType { get; set; } + public List Packets { get; set; } + public DateTime ConnectedTime { get; set; } + public DateTime? DisconnectedTime { get; set; } + } + + private readonly ILogger _logger; + private readonly IHostApplicationLifetime _applicationLifetime; + private readonly IParser _parser; + private readonly Dictionary _connections = new Dictionary(); + + public ConsoleHostedService(ILogger logger, + IHostApplicationLifetime applicationLifetime, + IParser parser) + { + _logger = logger; + _applicationLifetime = applicationLifetime; + _parser = parser; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _applicationLifetime.ApplicationStarted.Register(() => + { + var args = Environment.GetCommandLineArgs(); + + CommandLine.Parser.Default.ParseArguments(args) + .WithParsed(o => + { + _parser.OnNewConnection += OnNewConnection; + _parser.OnLostConnection += OnLostConnection; + + foreach(var f in o.Input) + { + _logger.LogInformation("Parsing {0}...", f); + _parser.Parse(f); + } + + foreach(var c in _connections) + { + if(o.Dump) + { + DumpConnectionToTextFile(c.Value, o.Output, o.Decrypt); + } + + if(o.Csv) + { + DumpConnectionToCsvFile(c.Value, o.Output, o.Decrypt); + } + } + + _applicationLifetime.StopApplication(); + }) + .WithNotParsed(e => + { + bool stops_processing = false; + foreach(var err in e) + { + _logger.LogError("Error: {0}", err.Tag); + stops_processing = stops_processing || err.StopsProcessing; + } + + if(stops_processing) + { + _applicationLifetime.StopApplication(); + } + }); + }); + + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + private void OnNewConnection(IConnection connection, DateTime connectionTime) + { + _logger.LogTrace("New connection {0}:{1} <-> {2}:{3} of type {4}", + connection.ClientAddress, + connection.ClientPort, + connection.ServerAddress, + connection.ServerPort, + connection.ConnectionType); + connection.OnPacketRecv += OnPacketRecv; + + _connections.Add(connection.Id, new ParsedConnection + { + ClientAddress = connection.ClientAddress, + ClientPort = connection.ClientPort, + ServerAddress = connection.ServerAddress, + ServerPort = connection.ServerPort, + ConnectionType = connection.ConnectionType, + Packets = new List(), + ConnectedTime = connectionTime, + }); + } + + private void OnLostConnection(IConnection connection, DateTime connectionTime) + { + _logger.LogTrace("Lost connection {0}:{1} <-> {2}:{3}", + connection.ClientAddress, + connection.ClientPort, + connection.ServerAddress, + connection.ServerPort); + connection.OnPacketRecv -= OnPacketRecv; + + var parsedConnection = _connections.GetValueOrDefault(connection.Id); + parsedConnection.DisconnectedTime = connectionTime; + } + + private void OnPacketRecv(IConnection connection, Direction direction, DateTime packetTime, ReadOnlySpan data) + { + var parsedConnection = _connections.GetValueOrDefault(connection.Id); + parsedConnection.Packets.Add(new ParsedPacket + { + Data = data.ToArray(), + Direction = direction, + Time = packetTime + }); + } + + private void DumpConnectionToTextFile(ParsedConnection c, string output, bool decrypt) + { + try + { + var path = output + string.Format("{0}-{1}.txt", c.ConnectionType.ToString().ToLower(), c.ConnectedTime.ToString("yyyyMMddHHmmssfff")); + if (File.Exists(path)) + { + File.Delete(path); + } + + File.AppendAllText(path, string.Format("### type: {0}\n", c.ConnectionType)); + File.AppendAllText(path, string.Format("### started: {0}\n", c.ConnectedTime.ToString("s"))); + File.AppendAllText(path, string.Format("### ended: {0}\n", c.DisconnectedTime.HasValue ? c.DisconnectedTime.Value.ToString("s") : "unknown")); + File.AppendAllText(path, string.Format("### client: {0}:{1}\n", c.ClientAddress.ToString(), c.ClientPort)); + File.AppendAllText(path, string.Format("### server: {0}:{1}\n\n", c.ServerAddress.ToString(), c.ServerPort)); + + foreach(var p in c.Packets) + { + ReadOnlySpan data = p.Data; + string dir = p.Direction == Direction.ClientToServer ? "Client -> Server" : "Server -> Client"; + + switch (c.ConnectionType) + { + case ConnectionType.Login: + { + int opcode = BitConverter.ToUInt16(data.Slice(0, 2)); + { + File.AppendAllText(path, + string.Format("{0} [Opcode: 0x{1}, Size: {2}] ({3})\n", dir, opcode.ToString("X4"), data.Length - 2, p.Time.ToString("s"))); + + var gp = new GamePacket(data.Slice(2)); + File.AppendAllText(path, string.Format("{0}\n", gp.ToString())); + + if(decrypt && opcode == 2 || opcode == 24) + { + var encrypted_block = data.Slice(12, data.Length - 12); + var dec = EQDecrypt(encrypted_block); + + if(dec != null) + { + File.AppendAllText(path, string.Format("[Decrypted Data, Offset: {0}, Size: {1}]\n", 10, dec.Length)); + gp = new GamePacket(dec); + File.AppendAllText(path, string.Format("{0}\n", gp.ToString())); + } + } + } + } + break; + case ConnectionType.Chat: + { + int opcode = data[0]; + File.AppendAllText(path, + string.Format("{0} [Opcode: 0x{1}, Size: {2}] ({3})\n", dir, opcode.ToString("X2"), data.Length - 1, p.Time.ToString("s"))); + + var gp = new GamePacket(data.Slice(1)); + File.AppendAllText(path, string.Format("{0}\n", gp.ToString())); + } + break; + default: + { + int opcode = BitConverter.ToUInt16(data.Slice(0, 2)); + File.AppendAllText(path, + string.Format("{0} [Opcode: 0x{1}, Size: {2}] ({3})\n", dir, opcode.ToString("X4"), data.Length - 2, p.Time.ToString("s"))); + + var gp = new GamePacket(data.Slice(2)); + File.AppendAllText(path, string.Format("{0}\n", gp.ToString())); + } + break; + } + } + } + catch(Exception ex) + { + _logger.LogError(ex, "Error dumping connection {0} to txt file", c.ConnectedTime.ToString("s")); + } + } + + private class CsvRow + { + public int Index { get; set; } + public string Direction { get; set; } + public string Opcode { get; set; } + public int Size { get; set; } + public string Data { get; set; } + } + + private void DumpConnectionToCsvFile(ParsedConnection c, string output, bool decrypt) + { + try + { + var path = output + string.Format("{0}-{1}.csv", c.ConnectionType.ToString().ToLower(), c.ConnectedTime.ToString("yyyyMMddHHmmssfff")); + if (File.Exists(path)) + { + File.Delete(path); + } + + var rows = new List(); + var i = 0; + foreach (var p in c.Packets) + { + var row = new CsvRow(); + row.Index = i++; + ReadOnlySpan data = p.Data; + row.Direction = p.Direction == Direction.ClientToServer ? "0" : "1"; + + switch (c.ConnectionType) + { + case ConnectionType.Chat: + { + row.Opcode = data[0].ToString(); + var gp = new GamePacket(data.Slice(1)); + row.Size = data.Length - 1; + row.Data = gp.ToModelString(512, false); + } + break; + default: + { + + row.Opcode = BitConverter.ToUInt16(data.Slice(0, 2)).ToString(); + var gp = new GamePacket(data.Slice(2)); + row.Size = data.Length - 2; + row.Data = gp.ToModelString(512, false); + } + break; + } + + rows.Add(row); + } + + using (var writer = new StreamWriter(path)) + using (var csv = new CsvHelper.CsvWriter(writer, CultureInfo.InvariantCulture)) + { + csv.WriteRecords(rows); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error dumping connection {0} to csv file", c.ConnectedTime.ToString("s")); + } + } + + private byte[] EQDecrypt(ReadOnlySpan data) + { + try + { + var desEngine = new DesEngine(); + var cbcBlockCipher = new CbcBlockCipher(desEngine); + var bufferedBlockCipher = new BufferedBlockCipher(cbcBlockCipher); + bufferedBlockCipher.Init(false, new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8])); + var cipherData = new byte[bufferedBlockCipher.GetOutputSize(data.Length)]; + var outputLength = bufferedBlockCipher.ProcessBytes(data.ToArray(), 0, data.Length, cipherData, 0); + bufferedBlockCipher.DoFinal(cipherData, outputLength); + return cipherData; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error decrypting EQ Datablock"); + return null; + } + } + } +} diff --git a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs new file mode 100644 index 000000000..ae7e5da00 --- /dev/null +++ b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CommandLine; + +namespace StreamParser +{ + public class ConsoleHostedServiceOptions + { + [Option("input", Required = true, HelpText = "Input pcap files to be processed.")] + public IEnumerable Input { get; set; } + + [Option("output", Default = "output/", HelpText = "Directory to put output files")] + public string Output { get; set; } + + [Option("text", Default = true, HelpText = "Dump connections to text files.")] + public bool Dump { get; set; } + + [Option("csv", Default = false, HelpText = "Dump connections to csv files appropriate for building ML models.")] + public bool Csv { get; set; } + + [Option("decrypt", Default = false, HelpText = "Decrypt the \"Encrypted\" packets.")] + public bool Decrypt { get; set; } + } +} diff --git a/utils/stream_parser/stream_parser/Program.cs b/utils/stream_parser/stream_parser/Program.cs new file mode 100644 index 000000000..44fee4d80 --- /dev/null +++ b/utils/stream_parser/stream_parser/Program.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using StreamParser.Common.Daybreak; +using System; + +namespace StreamParser +{ + class Program + { + static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureServices((hostContext, services) => + { + services.AddScoped(); + services.AddHostedService(); + }); + } + } +} diff --git a/utils/stream_parser/stream_parser/Properties/launchSettings.json b/utils/stream_parser/stream_parser/Properties/launchSettings.json new file mode 100644 index 000000000..59fe04b2f --- /dev/null +++ b/utils/stream_parser/stream_parser/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "stream_parser": { + "commandName": "Project", + "commandLineArgs": "--input input/test.pcap --csv --text --decrypt", + "workingDirectory": "E:\\Projects\\stream_parser\\stream_parser\\bin\\Debug\\net5.0" + } + } +} \ No newline at end of file diff --git a/utils/stream_parser/stream_parser/stream_parser.csproj b/utils/stream_parser/stream_parser/stream_parser.csproj new file mode 100644 index 000000000..20cd75e79 --- /dev/null +++ b/utils/stream_parser/stream_parser/stream_parser.csproj @@ -0,0 +1,24 @@ + + + + Exe + net6.0 + StreamParser + + + + false + + + + + + + + + + + + + + From 124b9c7abe9af2a231339d3c4ede14c081529c29 Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 16 Oct 2024 23:07:30 -0700 Subject: [PATCH 02/93] Update packages --- utils/stream_parser/common/common.csproj | 4 ++-- utils/stream_parser/stream_parser/stream_parser.csproj | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/utils/stream_parser/common/common.csproj b/utils/stream_parser/common/common.csproj index 5d289096f..60854da5e 100644 --- a/utils/stream_parser/common/common.csproj +++ b/utils/stream_parser/common/common.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/utils/stream_parser/stream_parser/stream_parser.csproj b/utils/stream_parser/stream_parser/stream_parser.csproj index 20cd75e79..66d3df69b 100644 --- a/utils/stream_parser/stream_parser/stream_parser.csproj +++ b/utils/stream_parser/stream_parser/stream_parser.csproj @@ -11,10 +11,10 @@ - - - - + + + + From dc1509e768b686b04d3c99621f854c984dd00a7f Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 16 Oct 2024 23:08:33 -0700 Subject: [PATCH 03/93] update from 6LTS to 8LTS because 6 goes out of support in november. --- utils/stream_parser/stream_parser/stream_parser.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/stream_parser/stream_parser/stream_parser.csproj b/utils/stream_parser/stream_parser/stream_parser.csproj index 66d3df69b..cd24cb5eb 100644 --- a/utils/stream_parser/stream_parser/stream_parser.csproj +++ b/utils/stream_parser/stream_parser/stream_parser.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 StreamParser From d3cd037fa7e2be111618236a0f015fda6d8874c5 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 17 Oct 2024 20:12:33 -0700 Subject: [PATCH 04/93] Fix for broken AppCombined; both logic error and bad enum doh --- utils/stream_parser/common/Daybreak/Connection.cs | 1 + utils/stream_parser/common/Daybreak/Opcode.cs | 2 +- .../stream_parser/Properties/launchSettings.json | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/stream_parser/common/Daybreak/Connection.cs b/utils/stream_parser/common/Daybreak/Connection.cs index 470e2b17a..454d16efe 100644 --- a/utils/stream_parser/common/Daybreak/Connection.cs +++ b/utils/stream_parser/common/Daybreak/Connection.cs @@ -261,6 +261,7 @@ namespace StreamParser.Common.Daybreak var subpacket = data.Slice(current, subpacket_length); ProcessDecodedPacket(srcAddr, srcPort, packetTime, subpacket); + current += subpacket_length; } } break; diff --git a/utils/stream_parser/common/Daybreak/Opcode.cs b/utils/stream_parser/common/Daybreak/Opcode.cs index 3239851f6..e69dd138e 100644 --- a/utils/stream_parser/common/Daybreak/Opcode.cs +++ b/utils/stream_parser/common/Daybreak/Opcode.cs @@ -32,7 +32,7 @@ namespace StreamParser.Common.Daybreak public const byte Ack2 = 22; public const byte Ack3 = 23; public const byte Ack4 = 22; - public const byte AppCombined = 23; + public const byte AppCombined = 25; public const byte OutboundPing = 28; public const byte OutOfSession = 29; } diff --git a/utils/stream_parser/stream_parser/Properties/launchSettings.json b/utils/stream_parser/stream_parser/Properties/launchSettings.json index 59fe04b2f..493c4e30c 100644 --- a/utils/stream_parser/stream_parser/Properties/launchSettings.json +++ b/utils/stream_parser/stream_parser/Properties/launchSettings.json @@ -2,8 +2,8 @@ "profiles": { "stream_parser": { "commandName": "Project", - "commandLineArgs": "--input input/test.pcap --csv --text --decrypt", - "workingDirectory": "E:\\Projects\\stream_parser\\stream_parser\\bin\\Debug\\net5.0" + "commandLineArgs": "--input input/cap_login_to_zone_10_16_2024.pcap --output output_test/ --text --decrypt", + "workingDirectory": "E:\\Projects\\stream_parser\\stream_parser\\bin\\Debug\\net6.0\\" } } } \ No newline at end of file From 999ccdcb19c75107e9617ebe962cfdac126adbfc Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 17 Oct 2024 22:53:33 -0700 Subject: [PATCH 05/93] Add experimental decompression support. --- .../stream_parser/ConsoleHostedService.cs | 89 +++++++++++++++++-- .../ConsoleHostedServiceOptions.cs | 9 +- .../Properties/launchSettings.json | 4 +- 3 files changed, 89 insertions(+), 13 deletions(-) diff --git a/utils/stream_parser/stream_parser/ConsoleHostedService.cs b/utils/stream_parser/stream_parser/ConsoleHostedService.cs index 69e071072..7779c1688 100644 --- a/utils/stream_parser/stream_parser/ConsoleHostedService.cs +++ b/utils/stream_parser/stream_parser/ConsoleHostedService.cs @@ -13,6 +13,7 @@ using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Ionic.Zlib; namespace StreamParser { @@ -72,7 +73,7 @@ namespace StreamParser { if(o.Dump) { - DumpConnectionToTextFile(c.Value, o.Output, o.Decrypt); + DumpConnectionToTextFile(c.Value, o.Output, o.Decrypt, o.DecompressOpcodes); } if(o.Csv) @@ -153,7 +154,7 @@ namespace StreamParser }); } - private void DumpConnectionToTextFile(ParsedConnection c, string output, bool decrypt) + private void DumpConnectionToTextFile(ParsedConnection c, string output, bool decrypt, IEnumerable decompressOpcodes) { try { @@ -213,12 +214,58 @@ namespace StreamParser break; default: { + bool reported_decompressed = false; int opcode = BitConverter.ToUInt16(data.Slice(0, 2)); - File.AppendAllText(path, - string.Format("{0} [Opcode: 0x{1}, Size: {2}] ({3})\n", dir, opcode.ToString("X4"), data.Length - 2, p.Time.ToString("s"))); + foreach (var decompressOpcode in decompressOpcodes) + { + if (opcode == decompressOpcode && data.Length > 12) + { + if (data[10] == 0x78 && data[11] == 0xDA) + { + var totalLen = BitConverter.ToInt32(data.Slice(6, 4)); + if (totalLen > 0) + { + var decompressed = Inflate(data.Slice(10)); + if(decompressed != null) + { + var decompressed_gp = new GamePacket(decompressed); + File.AppendAllText(path, + string.Format("{0} [Opcode: 0x{1}, Size (decompressed): {2}] ({3})\n", dir, opcode.ToString("X4"), totalLen, p.Time.ToString("s"))); - var gp = new GamePacket(data.Slice(2)); - File.AppendAllText(path, string.Format("{0}\n", gp.ToString())); + File.AppendAllText(path, string.Format("{0}\n", decompressed_gp.ToString())); + reported_decompressed = true; + break; + } + } + } + else if (data[6] == 0x78 && data[7] == 0xDA) + { + var totalLen = BitConverter.ToInt32(data.Slice(2, 4)); + if (totalLen > 0) + { + var decompressed = Inflate(data.Slice(6)); + if (decompressed != null) + { + File.AppendAllText(path, + string.Format("{0} [Opcode: 0x{1}, Size (decompressed): {2}] ({3})\n", dir, opcode.ToString("X4"), totalLen, p.Time.ToString("s"))); + + var decompressed_gp = new GamePacket(decompressed); + File.AppendAllText(path, string.Format("{0}\n", decompressed_gp.ToString())); + reported_decompressed = true; + break; + } + } + } + } + } + + if (!reported_decompressed) + { + File.AppendAllText(path, + string.Format("{0} [Opcode: 0x{1}, Size: {2}] ({3})\n", dir, opcode.ToString("X4"), data.Length - 2, p.Time.ToString("s"))); + var gp = new GamePacket(data.Slice(2)); + File.AppendAllText(path, string.Format("{0}\n", gp.ToString())); + } } break; } @@ -230,6 +277,36 @@ namespace StreamParser } } + private byte[] Inflate(ReadOnlySpan data) + { + try + { + using (var out_stream = new MemoryStream()) + using (var in_stream = new MemoryStream(data.ToArray())) + { + const int bufferLen = 4096; + var buffer = new byte[bufferLen]; + using (var zs = new ZlibStream(in_stream, CompressionMode.Decompress)) + { + int r = 0; + do + { + r = zs.Read(buffer, 0, bufferLen); + out_stream.Write(buffer, 0, r); + } while (r == bufferLen); + } + + var ret = out_stream.ToArray(); + return ret; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error inflating data"); + return null; + } + } + private class CsvRow { public int Index { get; set; } diff --git a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs index ae7e5da00..de560175d 100644 --- a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs +++ b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; using CommandLine; namespace StreamParser @@ -23,5 +19,8 @@ namespace StreamParser [Option("decrypt", Default = false, HelpText = "Decrypt the \"Encrypted\" packets.")] public bool Decrypt { get; set; } + + [Option("decompress", Default = false, HelpText = "Which opcodes to attempt to decompress")] + public IEnumerable DecompressOpcodes { get; set; } } } diff --git a/utils/stream_parser/stream_parser/Properties/launchSettings.json b/utils/stream_parser/stream_parser/Properties/launchSettings.json index 493c4e30c..894e77546 100644 --- a/utils/stream_parser/stream_parser/Properties/launchSettings.json +++ b/utils/stream_parser/stream_parser/Properties/launchSettings.json @@ -2,8 +2,8 @@ "profiles": { "stream_parser": { "commandName": "Project", - "commandLineArgs": "--input input/cap_login_to_zone_10_16_2024.pcap --output output_test/ --text --decrypt", + "commandLineArgs": "--input input/cap_login_to_zone_10_16_2024.pcap --output output_test/ --text --decrypt --decompress 16742 168 30346", "workingDirectory": "E:\\Projects\\stream_parser\\stream_parser\\bin\\Debug\\net6.0\\" } } -} \ No newline at end of file +} From d1c7c00f1958862bf2d76a70829a232185e72bd4 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 18 Oct 2024 15:49:49 -0700 Subject: [PATCH 06/93] Binary dumping; not yet implemented --- .../stream_parser/ConsoleHostedService.cs | 71 +------------------ .../ConsoleHostedServiceOptions.cs | 6 +- .../Properties/launchSettings.json | 2 +- 3 files changed, 5 insertions(+), 74 deletions(-) diff --git a/utils/stream_parser/stream_parser/ConsoleHostedService.cs b/utils/stream_parser/stream_parser/ConsoleHostedService.cs index 7779c1688..c635e360a 100644 --- a/utils/stream_parser/stream_parser/ConsoleHostedService.cs +++ b/utils/stream_parser/stream_parser/ConsoleHostedService.cs @@ -71,15 +71,10 @@ namespace StreamParser foreach(var c in _connections) { - if(o.Dump) + if(o.Text) { DumpConnectionToTextFile(c.Value, o.Output, o.Decrypt, o.DecompressOpcodes); } - - if(o.Csv) - { - DumpConnectionToCsvFile(c.Value, o.Output, o.Decrypt); - } } _applicationLifetime.StopApplication(); @@ -307,70 +302,6 @@ namespace StreamParser } } - private class CsvRow - { - public int Index { get; set; } - public string Direction { get; set; } - public string Opcode { get; set; } - public int Size { get; set; } - public string Data { get; set; } - } - - private void DumpConnectionToCsvFile(ParsedConnection c, string output, bool decrypt) - { - try - { - var path = output + string.Format("{0}-{1}.csv", c.ConnectionType.ToString().ToLower(), c.ConnectedTime.ToString("yyyyMMddHHmmssfff")); - if (File.Exists(path)) - { - File.Delete(path); - } - - var rows = new List(); - var i = 0; - foreach (var p in c.Packets) - { - var row = new CsvRow(); - row.Index = i++; - ReadOnlySpan data = p.Data; - row.Direction = p.Direction == Direction.ClientToServer ? "0" : "1"; - - switch (c.ConnectionType) - { - case ConnectionType.Chat: - { - row.Opcode = data[0].ToString(); - var gp = new GamePacket(data.Slice(1)); - row.Size = data.Length - 1; - row.Data = gp.ToModelString(512, false); - } - break; - default: - { - - row.Opcode = BitConverter.ToUInt16(data.Slice(0, 2)).ToString(); - var gp = new GamePacket(data.Slice(2)); - row.Size = data.Length - 2; - row.Data = gp.ToModelString(512, false); - } - break; - } - - rows.Add(row); - } - - using (var writer = new StreamWriter(path)) - using (var csv = new CsvHelper.CsvWriter(writer, CultureInfo.InvariantCulture)) - { - csv.WriteRecords(rows); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error dumping connection {0} to csv file", c.ConnectedTime.ToString("s")); - } - } - private byte[] EQDecrypt(ReadOnlySpan data) { try diff --git a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs index de560175d..d744e549d 100644 --- a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs +++ b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs @@ -12,10 +12,10 @@ namespace StreamParser public string Output { get; set; } [Option("text", Default = true, HelpText = "Dump connections to text files.")] - public bool Dump { get; set; } + public bool Text { get; set; } - [Option("csv", Default = false, HelpText = "Dump connections to csv files appropriate for building ML models.")] - public bool Csv { get; set; } + [Option("binary", Default = false, HelpText = "Dump connections to binary files.")] + public bool Binary { get; set; } [Option("decrypt", Default = false, HelpText = "Decrypt the \"Encrypted\" packets.")] public bool Decrypt { get; set; } diff --git a/utils/stream_parser/stream_parser/Properties/launchSettings.json b/utils/stream_parser/stream_parser/Properties/launchSettings.json index 894e77546..18f9da4e2 100644 --- a/utils/stream_parser/stream_parser/Properties/launchSettings.json +++ b/utils/stream_parser/stream_parser/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "stream_parser": { "commandName": "Project", - "commandLineArgs": "--input input/cap_login_to_zone_10_16_2024.pcap --output output_test/ --text --decrypt --decompress 16742 168 30346", + "commandLineArgs": "--input input/cap_login_to_zone_10_16_2024.pcap --output output_test/ --binary --decrypt --decompress 16742 168 30346", "workingDirectory": "E:\\Projects\\stream_parser\\stream_parser\\bin\\Debug\\net6.0\\" } } From a7c1c85f716c70487d31190380744f0ca9628241 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 18 Oct 2024 17:18:37 -0700 Subject: [PATCH 07/93] Initial --- common/CMakeLists.txt | 14 +++- common/emu_versions.h | 3 +- common/patches/larion.cpp | 129 +++++++++++++++++++++++++++++++ common/patches/larion.h | 55 +++++++++++++ common/patches/larion_limits.cpp | 18 +++++ common/patches/larion_limits.h | 31 ++++++++ common/patches/larion_ops.h | 6 ++ common/patches/larion_structs.h | 33 ++++++++ common/patches/patches.cpp | 4 +- 9 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 common/patches/larion.cpp create mode 100644 common/patches/larion.h create mode 100644 common/patches/larion_limits.cpp create mode 100644 common/patches/larion_limits.h create mode 100644 common/patches/larion_ops.h create mode 100644 common/patches/larion_structs.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 3b06c0e1f..dcc778bf2 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -111,6 +111,8 @@ SET(common_sources net/websocket_server.cpp net/websocket_server_connection.cpp patches/patches.cpp + patches/larion.cpp + patches/larion_limits.cpp patches/sod.cpp patches/sod_limits.cpp patches/sof.cpp @@ -653,7 +655,11 @@ SET(common_headers net/websocket_server.h net/websocket_server_connection.h patches/patches.h - patches/sod.h + patches/larion.h + patches/larion_limits.h + patches/larion_ops.h + patches/larion_structs.h + patches/sod.h patches/sod_limits.h patches/sod_ops.h patches/sod_structs.h @@ -739,6 +745,10 @@ SOURCE_GROUP(Net FILES SOURCE_GROUP(Patches FILES patches/patches.h + patches/larion.h + patches/larion_limits.h + patches/larion_ops.h + patches/larion_structs.h patches/sod.h patches/sod_limits.h patches/sod_ops.h @@ -767,6 +777,8 @@ SOURCE_GROUP(Patches FILES patches/uf_ops.h patches/uf_structs.h patches/patches.cpp + patches/larion.cpp + patches/larion_limits.cpp patches/sod.cpp patches/sod_limits.cpp patches/sof.cpp diff --git a/common/emu_versions.h b/common/emu_versions.h index 75f6808c3..16315cf3f 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -36,7 +36,8 @@ namespace EQ SoD, // Build: 'Dec 19 2008 15:22:49' UF, // Build: 'Jun 8 2010 16:44:32' RoF, // Build: 'Dec 10 2012 17:35:44' - RoF2 // Build: 'May 10 2013 23:30:08' + RoF2, // Build: 'May 10 2013 23:30:08' + Larion }; enum ClientVersionBitmask : uint32 { diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp new file mode 100644 index 000000000..460d0d1b5 --- /dev/null +++ b/common/patches/larion.cpp @@ -0,0 +1,129 @@ +/* EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "../global_define.h" +#include "../eqemu_config.h" +#include "../eqemu_logsys.h" +#include "larion.h" +#include "../opcodemgr.h" + +#include "../eq_stream_ident.h" +#include "../crc32.h" + +#include "../eq_packet_structs.h" +#include "../misc_functions.h" +#include "../strings.h" +#include "../inventory_profile.h" +#include "larion_structs.h" +#include "../rulesys.h" +#include "../path_manager.h" +#include "../classes.h" +#include "../races.h" +#include "../raid.h" + +#include +#include +#include +#include +#include + +namespace Larion +{ + static const char* name = "Larion"; + static OpcodeManager* opcodes = nullptr; + static Strategy struct_strategy; + + void Register(EQStreamIdentifier& into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + + std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name); + + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + LogNetcode("[OPCODES] Error loading opcodes file [{}]. Not registering patch [{}]", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStreamInterface::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + LogNetcode("[StreamIdentify] Registered patch [{}]", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + std::string opfile = fmt::format("{}/patch_{}.conf", path.GetPatchPath(), name); + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + LogNetcode("[OPCODES] Error reloading opcodes file [{}] for patch [{}]", opfile.c_str(), name); + return; + } + LogNetcode("[OPCODES] Reloaded opcodes for patch [{}]", name); + } + } + + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "larion_ops.h" + } + + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } + + const EQ::versions::ClientVersion Strategy::ClientVersion() const + { + return EQ::versions::ClientVersion::Larion; + } + +#include "ss_define.h" + + // ENCODE methods + // DECODE methods +} /*Larion*/ diff --git a/common/patches/larion.h b/common/patches/larion.h new file mode 100644 index 000000000..7d7b83e32 --- /dev/null +++ b/common/patches/larion.h @@ -0,0 +1,55 @@ +/* EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef COMMON_LARION_H +#define COMMON_LARION_H + +#include "../struct_strategy.h" + +class EQStreamIdentifier; + +namespace Larion +{ + + //these are the only public member of this namespace. + extern void Register(EQStreamIdentifier& into); + extern void Reload(); + + + + //you should not directly access anything below.. + //I just dont feel like making a seperate header for it. + + class Strategy : public StructStrategy { + public: + Strategy(); + + protected: + + virtual std::string Describe() const; + virtual const EQ::versions::ClientVersion ClientVersion() const; + + //magic macro to declare our opcode processors +#include "ss_declare.h" +#include "larion_ops.h" + }; + +}; /*Larion*/ + +#endif /*COMMON_LARION_H*/ diff --git a/common/patches/larion_limits.cpp b/common/patches/larion_limits.cpp new file mode 100644 index 000000000..273e74a5c --- /dev/null +++ b/common/patches/larion_limits.cpp @@ -0,0 +1,18 @@ +/* EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/common/patches/larion_limits.h b/common/patches/larion_limits.h new file mode 100644 index 000000000..2ac784c91 --- /dev/null +++ b/common/patches/larion_limits.h @@ -0,0 +1,31 @@ +/* EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef COMMON_LARION_LIMITS_H +#define COMMON_LARION_LIMITS_H + + +namespace Larion +{ + + // put constants here and #include appropriately + +}; /* Larion */ + +#endif /*COMMON_LARION_LIMITS_H*/ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h new file mode 100644 index 000000000..4ef87be2b --- /dev/null +++ b/common/patches/larion_ops.h @@ -0,0 +1,6 @@ + +//list of packets we need to encode on the way out: +//list of packets we need to decode on the way in: + +#undef E +#undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h new file mode 100644 index 000000000..cd81f7915 --- /dev/null +++ b/common/patches/larion_structs.h @@ -0,0 +1,33 @@ +#ifndef LARION_STRUCTS_H_ +#define LARION_STRUCTS_H_ + +namespace Larion { + namespace structs { + struct LoginInfo_Struct { + /*000*/ char login_info[64]; + /*064*/ uint8 unknown064[124]; + /*188*/ uint8 zoning; // 01 if zoning, 00 if not + /*189*/ uint8 unknown189[275]; + /*488*/ + }; + + struct ClientZoneEntry_Struct { + /*00*/ uint32 unknown00; // ***Placeholder + /*04*/ char char_name[64]; // Player firstname [32] + /*68*/ uint32 unknown68; + /*72*/ uint32 unknown72; + }; + }; //end namespace structs +}; //end namespace larion + +#endif /*LARION_STRUCTS_H_*/ + + + + + + + + + + diff --git a/common/patches/patches.cpp b/common/patches/patches.cpp index 775e4e3dc..3a1a0fb6e 100644 --- a/common/patches/patches.cpp +++ b/common/patches/patches.cpp @@ -26,7 +26,7 @@ #include "sod.h" #include "rof.h" #include "rof2.h" - +#include "larion.h" void RegisterAllPatches(EQStreamIdentifier &into) { @@ -36,6 +36,7 @@ void RegisterAllPatches(EQStreamIdentifier &into) UF::Register(into); RoF::Register(into); RoF2::Register(into); + Larion::Register(into); } void ReloadAllPatches() @@ -46,4 +47,5 @@ void ReloadAllPatches() UF::Reload(); RoF::Reload(); RoF2::Reload(); + Larion::Reload(); } From 2e087cde5b35273baf7a57b1ca9d99411cc25e03 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 18 Oct 2024 17:23:12 -0700 Subject: [PATCH 08/93] Force add stream_parser.sln --- utils/stream_parser/stream_parser.sln | 51 +++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 utils/stream_parser/stream_parser.sln diff --git a/utils/stream_parser/stream_parser.sln b/utils/stream_parser/stream_parser.sln new file mode 100644 index 000000000..67c8dcd55 --- /dev/null +++ b/utils/stream_parser/stream_parser.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.6.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "stream_parser", "stream_parser\stream_parser.csproj", "{A5662497-4771-4A00-92E7-E7790CEB20E9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "common", "common\common.csproj", "{FC625344-C003-4602-A571-D8811CF5B37A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Debug|x64.Build.0 = Debug|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Debug|x86.Build.0 = Debug|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Release|Any CPU.Build.0 = Release|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Release|x64.ActiveCfg = Release|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Release|x64.Build.0 = Release|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Release|x86.ActiveCfg = Release|Any CPU + {A5662497-4771-4A00-92E7-E7790CEB20E9}.Release|x86.Build.0 = Release|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Debug|x64.Build.0 = Debug|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Debug|x86.Build.0 = Debug|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Release|Any CPU.Build.0 = Release|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Release|x64.ActiveCfg = Release|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Release|x64.Build.0 = Release|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Release|x86.ActiveCfg = Release|Any CPU + {FC625344-C003-4602-A571-D8811CF5B37A}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {89CDF826-F878-4BF4-8583-B9E66FE8B61D} + EndGlobalSection +EndGlobal From 0c87af7d6b71233c46e81008239d977502f370f0 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 18 Oct 2024 17:32:55 -0700 Subject: [PATCH 09/93] Decompress wont default to false --- .../stream_parser/stream_parser/ConsoleHostedServiceOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs index d744e549d..749d20922 100644 --- a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs +++ b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs @@ -20,7 +20,7 @@ namespace StreamParser [Option("decrypt", Default = false, HelpText = "Decrypt the \"Encrypted\" packets.")] public bool Decrypt { get; set; } - [Option("decompress", Default = false, HelpText = "Which opcodes to attempt to decompress")] + [Option("decompress", Default = null, HelpText = "Which opcodes to attempt to decompress")] public IEnumerable DecompressOpcodes { get; set; } } } From b8ecb297ec3db25b84e7e297de29df62b567bd0b Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 19 Oct 2024 22:34:43 -0700 Subject: [PATCH 10/93] Beginning of world opcodes --- utils/patches/patch_Larion.conf | 730 ++++++++++++++++++++++++++++++++ 1 file changed, 730 insertions(+) create mode 100644 utils/patches/patch_Larion.conf diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Larion.conf new file mode 100644 index 000000000..34f8ef278 --- /dev/null +++ b/utils/patches/patch_Larion.conf @@ -0,0 +1,730 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x2fca +OP_ApproveWorld=0x0000 +OP_LogServer=0x6d4d +OP_SendCharInfo=0x832 +OP_ExpansionInfo=0x0000 +OP_GuildsList=0x0000 +OP_EnterWorld=0x6691 +OP_PostEnterWorld=0x2062 +OP_World_Client_CRC1=0x74c8 +OP_World_Client_CRC2=0x3984 +OP_World_Client_CRC3=0x6516 +OP_SendSpellChecksum=0x0000 +OP_SendSkillCapsChecksum=0x0000 + +# Character Select Related: +OP_SendMaxCharacters=0x13af +OP_SendMembership=0x2aca +OP_SendMembershipDetails=0x2608 +OP_CharacterCreateRequest=0x0000 +OP_CharacterCreate=0x0000 +OP_DeleteCharacter=0x0000 +OP_RandomNameGenerator=0x0000 +OP_ApproveName=0x0000 +OP_MOTD=0x0000 +OP_SetChatServer=0x2726 +OP_SetChatServer2=0x0000 +OP_ZoneServerInfo=0x2273 +OP_WorldComplete=0x195c +OP_WorldUnknown001=0x2049 +OP_FloatListThing=0x0000 + +# Reasons for Disconnect: +OP_ZoneUnavail=0x0000 +OP_WorldClientReady=0x0000 +OP_CharacterStillInZone=0x0000 +OP_WorldChecksumFailure=0x0000 +OP_WorldLoginFailed=0x0000 +OP_WorldLogout=0x0000 +OP_WorldLevelTooHigh=0x0000 +OP_CharInacessable=0x0000 +OP_UserCompInfo=0x0000 +OP_SendExeChecksum=0x0000 +OP_SendBaseDataChecksum=0x0000 + +# Zone in opcodes +OP_AckPacket=0x77c9 +OP_ZoneEntry=0x0000 +OP_ReqNewZone=0x0000 +OP_NewZone=0x0000 +OP_ZoneSpawns=0x0000 +OP_PlayerProfile=0x0000 +OP_TimeOfDay=0x0000 +OP_LevelUpdate=0x0000 +OP_Stamina=0x0000 +OP_RequestClientZoneChange=0x0000 +OP_ZoneChange=0x0000 +OP_LockoutTimerInfo=0x0000 +OP_ZoneServerReady=0x0000 +OP_ZoneInUnknown=0x0000 +OP_LogoutReply=0x0000 +OP_PreLogoutReply=0x0000 + +# Required to fully log in +OP_SpawnAppearance=0x0000 +OP_ChangeSize=0x0000 +OP_TributeUpdate=0x0000 +OP_TributeTimer=0x0000 +OP_SendTributes=0x0000 +OP_RequestGuildTributes=0x0000 +OP_TributeInfo=0x0000 +OP_Weather=0x0000 +OP_ReqClientSpawn=0x0000 +OP_SpawnDoor=0x0000 +OP_GroundSpawn=0x0000 +OP_SendZonepoints=0x0000 +OP_BlockedBuffs=0x0000 +OP_RemoveBlockedBuffs=0x0000 +OP_ClearBlockedBuffs=0x0000 +OP_WorldObjectsSent=0x0000 +OP_SendExpZonein=0x0000 +OP_SendAATable=0x0000 +OP_ClearAA=0x0000 +OP_ClearLeadershipAbilities=0x0000 +OP_RespondAA=0x0000 +OP_UpdateAA=0x0000 +OP_SendAAStats=0x0000 +OP_AAExpUpdate=0x0000 +OP_ExpUpdate=0x0000 +OP_HPUpdate=0x0000 +OP_ManaChange=0x0000 +OP_TGB=0x0000 +OP_SpecialMesg=0x0000 +OP_GuildMemberList=0x0000 +OP_GuildMOTD=0x0000 +OP_CharInventory=0x0000 +OP_WearChange=0x0000 +OP_ClientUpdate=0x0000 +OP_ClientReady=0x0000 +OP_SetServerFilter=0x0000 + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 +OP_GetGuildMOTDReply=0x0000 +OP_GuildMemberUpdate=0x0000 +OP_GuildInvite=0x0000 +OP_GuildRemove=0x0000 +OP_GuildPeace=0x0000 +OP_SetGuildMOTD=0x0000 +OP_GuildWar=0x0000 +OP_GuildLeader=0x0000 +OP_GuildDelete=0x0000 +OP_GuildInviteAccept=0x0000 +OP_GuildDemote=0x0000 +OP_GuildPromote=0x0000 +OP_GuildPublicNote=0x0000 +OP_GuildManageBanker=0x0000 +OP_GuildBank=0x0000 +OP_GuildBankItemList=0x0000 +OP_SetGuildRank=0x0000 +OP_GuildUpdate=0x0000 +OP_GuildStatus=0x0000 +OP_GuildCreate=0x0000 +OP_GuildOpenGuildWindow=0x0000 +OP_GuildMemberLevel=0x0000 +OP_GuildMemberRankAltBanker=0x0000 +OP_GuildMemberPublicNote=0x0000 +OP_GuildMemberAdd=0x0000 +OP_GuildMemberRename=0x0000 +OP_GuildMemberDelete=0x0000 +OP_GuildMemberDetails=0x0000 +OP_GuildRenameGuild=0x0000 +OP_LFGuild=0x0000 +OP_GuildDeleteGuild=0x0000 + +# GM/Guide Opcodes +OP_GMServers=0x0000 +OP_GMBecomeNPC=0x0000 +OP_GMZoneRequest=0x0000 +OP_GMZoneRequest2=0x0000 +OP_GMGoto=0x0000 +OP_GMSearchCorpse=0x0000 +OP_GMHideMe=0x0000 +OP_GMDelCorpse=0x0000 +OP_GMApproval=0x0000 +OP_GMToggle=0x0000 +OP_GMSummon=0x0000 +OP_GMEmoteZone=0x0000 +OP_GMEmoteWorld=0x0000 +OP_GMFind=0x0000 +OP_GMKick=0x0000 +OP_GMKill=0x0000 +OP_GMNameChange=0x0000 +OP_GMLastName=0x0000 + +# Misc Opcodes +OP_QueryUCSServerStatus=0x0000 +OP_InspectRequest=0x0000 +OP_InspectAnswer=0x0000 +OP_InspectMessageUpdate=0x0000 +OP_BeginCast=0x0000 +OP_ColoredText=0x0000 +OP_ConsentResponse=0x0000 +OP_MemorizeSpell=0x0000 +OP_LinkedReuse=0x0000 +OP_SwapSpell=0x0000 +OP_CastSpell=0x0000 +OP_Consider=0x0000 +OP_FormattedMessage=0x0000 +OP_SimpleMessage=0x0000 +OP_Buff=0x0000 +OP_Illusion=0x0000 +OP_MoneyOnCorpse=0x0000 +OP_RandomReply=0x0000 +OP_DenyResponse=0x0000 +OP_SkillUpdate=0x04c +OP_GMTrainSkillConfirm=0x0000 +OP_RandomReq=0x0000 +OP_Death=0x0000 +OP_GMTraining=0x0000 +OP_GMEndTraining=0x0000 +OP_GMTrainSkill=0x0000 +OP_Animation=0x0000 +OP_Begging=0x0000 +OP_Consent=0x0000 +OP_ConsentDeny=0x0000 +OP_AutoFire=0x0000 +OP_PetCommands=0x0000 +OP_PetCommandState=0x0000 +OP_PetHoTT=0x0000 +OP_DeleteSpell=0x0000 +OP_Surname=0x0000 +OP_ClearSurname=0x0000 +OP_FaceChange=0x0000 +OP_SetFace=0x0000 +OP_SenseHeading=0x0000 +OP_Action=0x0000 +OP_ConsiderCorpse=0x0000 +OP_HideCorpse=0x0000 +OP_CorpseDrag=0x0000 +OP_CorpseDrop=0x0000 +OP_Bug=0x0000 +OP_Feedback=0x0000 +OP_Report=0x0000 +OP_Damage=0x0000 +OP_ChannelMessage=0x0000 +OP_Assist=0x0000 +OP_AssistGroup=0x0000 +OP_MoveCoin=0x0000 +OP_ZonePlayerToBind=0x0000 +OP_KeyRing=0x0000 +OP_WhoAllRequest=0x0000 +OP_WhoAllResponse=0x0000 +OP_FriendsWho=0x0000 +OP_ConfirmDelete=0x0000 +OP_Logout=0x0000 +OP_Rewind=0x0000 +OP_TargetCommand=0x0000 +OP_Hide=0x0000 +OP_Jump=0x0000 +OP_Camp=0x0000 +OP_Emote=0x0000 +OP_SetRunMode=0x0000 +OP_BankerChange=0x0000 +OP_TargetMouse=0x0000 +OP_MobHealth=0x0000 +OP_InitialMobHealth=0x0000 # Unused? +OP_TargetHoTT=0x0000 +OP_TargetBuffs=0x0000 +OP_XTargetResponse=0x0000 +OP_XTargetRequest=0x0000 +OP_XTargetAutoAddHaters=0x0000 +OP_XTargetOpen=0x0000 +OP_XTargetOpenResponse=0x0000 +OP_BuffCreate=0x0000 +OP_BuffRemoveRequest=0x0000 +OP_DeleteSpawn=0x0000 +OP_AutoAttack=0x0000 +OP_AutoAttack2=0x0000 +OP_Consume=0x0000 +OP_MoveItem=0x0000 +OP_MoveMultipleItems=0x0000 +OP_DeleteItem=0x0000 +OP_DeleteCharge=0x0000 +OP_ItemPacket=0x0000 +OP_ItemLinkResponse=0x0000 +OP_ItemLinkClick=0x0000 +OP_ItemPreview=0x0000 +OP_NewSpawn=0x0000 +OP_Track=0x0000 +OP_TrackTarget=0x0000 +OP_TrackUnknown=0x0000 +OP_ClickDoor=0x0000 +OP_MoveDoor=0x0000 +OP_RemoveAllDoors=0x0000 +OP_EnvDamage=0x0000 +OP_BoardBoat=0x0000 +OP_LeaveBoat=0x0000 +OP_ControlBoat=0x0000 +OP_Forage=0x0000 +OP_SafeFallSuccess=0x0000 +OP_RezzComplete=0x0000 +OP_RezzRequest=0x0000 +OP_RezzAnswer=0x0000 +OP_Shielding=0x0000 +OP_RequestDuel=0x0000 +OP_MobRename=0x0000 +OP_AugmentItem=0x0000 +OP_WeaponEquip1=0x0000 +OP_PlayerStateAdd=0x0000 +OP_PlayerStateRemove=0x0000 +OP_ApplyPoison=0x0000 +OP_Save=0x0000 +OP_TestBuff=0x0000 +OP_CustomTitles=0x0000 +OP_Split=0x0000 +OP_YellForHelp=0x0000 +OP_LoadSpellSet=0x0000 +OP_Bandolier=0x0000 +OP_PotionBelt=0x0000 +OP_DuelDecline=0x0000 +OP_DuelAccept=0x0000 +OP_SaveOnZoneReq=0x0000 +OP_ReadBook=0x0000 +OP_Dye=0x0000 +OP_InterruptCast=0x0000 +OP_AAAction=0x0000 +OP_LeadershipExpToggle=0x0000 +OP_LeadershipExpUpdate=0x0000 +OP_PurchaseLeadershipAA=0x0000 +OP_UpdateLeadershipAA=0x0000 +OP_MarkNPC=0x0000 +OP_ClearNPCMarks=0x0000 +OP_DelegateAbility=0x0000 +OP_SetGroupTarget=0x0000 +OP_Charm=0x0000 +OP_Stun=0x0000 +OP_SendFindableNPCs=0x0000 +OP_FindPersonRequest=0x0000 +OP_FindPersonReply=0x0000 +OP_Sound=0x0000 +OP_CashReward=0x0000 +OP_PetBuffWindow=0x0000 +OP_LevelAppearance=0x0000 +OP_Translocate=0x0000 +OP_Sacrifice=0x0000 +OP_PopupResponse=0x0000 +OP_OnLevelMessage=0x0000 +OP_AugmentInfo=0x0000 +OP_Petition=0x0000 +OP_SomeItemPacketMaybe=0x0000 +OP_PVPStats=0x0000 +OP_PVPLeaderBoardRequest=0x0000 +OP_PVPLeaderBoardReply=0x0000 +OP_PVPLeaderBoardDetailsRequest=0x0000 +OP_PVPLeaderBoardDetailsReply=0x0000 +OP_RestState=0x0000 +OP_RespawnWindow=0x0000 +OP_LDoNButton=0x0000 +OP_SetStartCity=0x0000 +OP_VoiceMacroIn=0x0000 +OP_VoiceMacroOut=0x0000 +OP_ItemViewUnknown=0x0000 +OP_VetRewardsAvaliable=0x0000 +OP_VetClaimRequest=0x0000 +OP_VetClaimReply=0x0000 +OP_DisciplineUpdate=0x0000 +OP_DisciplineTimer=0x0000 +OP_BecomeCorpse=0x0000 # Unused? +OP_Action2=0x0000 # Unused? +OP_MobUpdate=0x0000 +OP_NPCMoveUpdate=0x0000 +OP_CameraEffect=0x0000 +OP_SpellEffect=0x0000 +OP_AddNimbusEffect=0x0000 +OP_RemoveNimbusEffect=0x0000 +OP_AltCurrency=0x0000 +OP_AltCurrencyMerchantRequest=0x0000 +OP_AltCurrencyMerchantReply=0x0000 +OP_AltCurrencyPurchase=0x0000 +OP_AltCurrencySell=0x0000 +OP_AltCurrencySellSelection=0x0000 +OP_AltCurrencyReclaim=0x0000 +OP_CrystalCountUpdate=0x0000 +OP_CrystalCreate=0x0000 +OP_CrystalReclaim=0x0000 +OP_Untargetable=0x0000 +OP_IncreaseStats=0x0000 +OP_Weblink=0x0000 +OP_OpenContainer=0x0000 +OP_Marquee=0x0000 +OP_ItemRecastDelay=0x0000 +#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U +OP_ResetAA=0x0000 +OP_Fling=0x0000 +OP_CancelSneakHide=0x0000 +OP_AggroMeterLockTarget=0x0000 +OP_AggroMeterTargetInfo=0x0000 +OP_AggroMeterUpdate=0x0000 +OP_UnderWorld=0x0000 # clients sends up when they detect an underworld issue, might be useful for cheat detection +OP_KickPlayers=0x0000 +OP_BookButton=0x0000 + +# Expeditions +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzExpeditionInvite=0x0000 +OP_DzExpeditionInviteResponse=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzExpeditionLockoutTimers=0x0000 +OP_DzMemberList=0x0000 +OP_DzMemberListName=0x0000 +OP_DzMemberListStatus=0x0000 +OP_DzSetLeaderName=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 +OP_DzChooseZoneReply=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ? +OP_ManaUpdate=0x0000 +OP_EnduranceUpdate=0x0000 +OP_MobManaUpdate=0x0000 +OP_MobEnduranceUpdate=0x0000 + +# Mercenary Opcodes +OP_MercenaryDataUpdateRequest=0x0000 +OP_MercenaryDataUpdate=0x0000 +OP_MercenaryDataRequest=0x0000 +OP_MercenaryDataResponse=0x0000 +OP_MercenaryHire=0x0000 +OP_MercenaryDismiss=0x0000 +OP_MercenaryTimerRequest=0x0000 +OP_MercenaryTimer=0x0000 +OP_MercenaryUnknown1=0x0000 +OP_MercenaryCommand=0x0000 +OP_MercenarySuspendRequest=0x0000 +OP_MercenarySuspendResponse=0x0000 +OP_MercenaryUnsuspendResponse=0x0000 + +# Looting +OP_LootRequest=0x0000 +OP_EndLootRequest=0x0000 +OP_LootItem=0x0000 +OP_LootComplete=0x0000 + +# bazaar trader stuff: +OP_BazaarSearch=0x0000 +OP_TraderDelItem=0x0000 +OP_BecomeTrader=0x0000 +OP_TraderShop=0x0000 +OP_TraderBulkSend=0x0000 +OP_Trader=0x0000 +OP_Barter=0x0000 +OP_BuyerItems=0x0000 +OP_TraderBuy=0x0000 +OP_ShopItem=0x0000 +OP_BazaarInspect=0x0000 +OP_Bazaar=0x0000 +OP_TraderItemUpdate=0x0000 + +# pc/npc trading +OP_TradeRequest=0x0000 +OP_TradeAcceptClick=0x0000 +OP_TradeRequestAck=0x0000 +OP_TradeCoins=0x0000 +OP_FinishTrade=0x0000 +OP_CancelTrade=0x0000 +OP_TradeMoneyUpdate=0x0000 +OP_MoneyUpdate=0x0000 +OP_TradeBusy=0x0000 + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 +OP_FinishWindow2=0x0000 + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 +OP_ItemVerifyReply=0x0000 + +OP_ItemAdvancedLoreText=0x0000 + +# merchant stuff +OP_ShopPlayerSell=0x0000 +OP_ShopRequest=0x0000 +OP_ShopEnd=0x0000 +OP_ShopEndConfirm=0x0000 +OP_ShopPlayerBuy=0x0000 +OP_ShopDelItem=0x0000 +OP_ShopSendParcel=0x0000 +OP_ShopDeleteParcel=0x0000 +OP_ShopRetrieveParcel=0x0000 +OP_ShopParcelIcon=0x0000 + +# tradeskill stuff: +OP_ClickObject=0x0000 +OP_ClickObjectAction=0x0000 +OP_ClearObject=0x0000 +OP_RecipeDetails=0x0000 +OP_RecipesFavorite=0x0000 +OP_RecipesSearch=0x0000 +OP_RecipeReply=0x0000 +OP_RecipeAutoCombine=0x0000 +OP_TradeSkillCombine=0x0000 + +# Tribute Packets: +OP_OpenTributeMaster=0x0000 +OP_SelectTribute=0x0000 +OP_TributeItem=0x0000 +OP_TributeMoney=0x0000 +OP_TributeToggle=0x0000 +OP_TributePointUpdate=0x0000 +OP_TributeNPC=0x0000 +OP_GuildTributeInfo=0x0000 +OP_OpenTributeReply=0x0000 +OP_GuildTributeStatus=0x0000 +OP_GuildSaveActiveTributes=0x0000 +OP_GuildSendActiveTributes=0x0000 +OP_GuildTributeToggleReq=0x0000 +OP_GuildTributeToggleReply=0x0000 +OP_GuildTributeFavorAndTimer=0x0000 +OP_GuildTributeDonateItem=0x0000 +OP_GuildTributeDonatePlat=0x0000 +OP_GuildSelectTribute=0x0000 +OP_GuildModifyBenefits=0x0000 +OP_GuildOptInOut=0x0000 +OP_SendGuildTributes=0x0000 +OP_OpenGuildTributeMaster=0x0000 + +# Adventure packets: +OP_LeaveAdventure=0x0000 +OP_AdventureFinish=0x0000 +OP_AdventureInfoRequest=0x0000 +OP_AdventureInfo=0x0000 +OP_AdventureRequest=0x0000 +OP_AdventureDetails=0x0000 +OP_AdventureData=0x0000 +OP_AdventureUpdate=0x0000 +OP_AdventureMerchantRequest=0x0000 +OP_AdventureMerchantResponse=0x0000 +OP_AdventureMerchantPurchase=0x0000 +OP_AdventureMerchantSell=0x0000 +OP_AdventurePointsUpdate=0x0000 +OP_AdventureStatsRequest=0x0000 +OP_AdventureStatsReply=0x0000 +OP_AdventureLeaderboardRequest=0x0000 +OP_AdventureLeaderboardReply=0x0000 + +# Group Opcodes +OP_GroupDisband=0x0000 +OP_GroupInvite=0x0000 +OP_GroupFollow=0x0000 +OP_GroupUpdate=0x0000 +OP_GroupUpdateB=0x0000 +OP_GroupCancelInvite=0x0000 +OP_GroupAcknowledge=0x0000 +OP_GroupDelete=0x0000 +OP_CancelInvite=0x0000 +OP_GroupFollow2=0x0000 +OP_GroupInvite2=0x0000 +OP_GroupDisbandYou=0x0000 +OP_GroupDisbandOther=0x0000 +OP_GroupLeaderChange=0x0000 +OP_GroupRoles=0x0000 +OP_GroupMakeLeader=0x0000 +OP_DoGroupLeadershipAbility=0x0000 +OP_GroupLeadershipAAUpdate=0x0000 +OP_GroupMentor=0x0000 +OP_InspectBuffs=0x0000 + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 +OP_LFGGetMatchesRequest=0x0000 +OP_LFGGetMatchesResponse=0x0000 +OP_LFPGetMatchesRequest=0x0000 +OP_LFPGetMatchesResponse=0x0000 +OP_LFPCommand=0x0000 +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 + +# Raid Opcodes +OP_RaidInvite=0x0000 +OP_RaidUpdate=0x0000 +OP_RaidJoin=0x0000 +OP_RaidDelegateAbility=0x0000 +OP_MarkRaidNPC=0x0000 +OP_RaidClearNPCMarks=0x0000 + +# Button-push commands +OP_Taunt=0x0000 +OP_CombatAbility=0x0000 +OP_SenseTraps=0x0000 +OP_PickPocket=0x0000 +OP_DisarmTraps=0x0000 +OP_Disarm=0x0000 +OP_Sneak=0x0000 +OP_Fishing=0x0000 +OP_InstillDoubt=0x0000 +OP_FeignDeath=0x0000 +OP_Mend=0x0000 +OP_Bind_Wound=0x0000 +OP_LDoNOpen=0x0000 +#OP_LDoNDisarmTraps= #Same as OP_DisarmTraps in RoF +OP_LDoNPickLock=0x0000 +OP_LDoNInspect=0x0000 + +# Task packets +OP_TaskDescription=0x0000 +OP_TaskActivity=0x0000 +OP_CompletedTasks=0x0000 +OP_TaskActivityComplete=0x0000 +OP_AcceptNewTask=0x0000 +OP_CancelTask=0x0000 +OP_AvaliableTask=0x0000 +OP_TaskHistoryRequest=0x0000 +OP_TaskHistoryReply=0x0000 +OP_DeclineAllTasks=0x0000 +OP_TaskRequestTimer=0x0000 +OP_TaskSelectWindow=0x0000 + +# Shared Tasks +OP_SharedTaskMemberList=0x0000 # +OP_SharedTaskRemovePlayer=0x0000 # /taskremoveplayer +OP_SharedTaskAddPlayer=0x0000 # /taskaddplayer +OP_SharedTaskMakeLeader=0x0000 # /taskmakeleader +OP_SharedTaskInvite=0x0000 # Dialog window +OP_SharedTaskInviteResponse=0x0000 # Dialog window response +OP_SharedTaskAcceptNew=0x0000 # +OP_SharedTaskMemberChange=0x0000 # +OP_TaskTimers=0x0000 # /tasktimers +OP_SharedTaskQuit=0x0000 # /taskquit +OP_SharedTaskSelectWindow=0x0000 +OP_SharedTaskPlayerList=0x0000 # /taskplayerlist + +# Title opcodes +OP_NewTitlesAvailable=0x0000 +OP_RequestTitles=0x0000 +OP_SendTitleList=0x0000 +OP_SetTitle=0x0000 +OP_SetTitleReply=0x0000 + +# mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + +########### Below this point should not be needed ########### + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 +OP_LocInfo=0x0000 +OP_ReloadUI=0x0000 +OP_ItemName=0x0000 +OP_ItemLinkText=0x0000 +OP_MultiLineMsg=0x0000 +OP_MendHPUpdate=0x0000 +OP_TargetReject=0x0000 +OP_SafePoint=0x0000 +OP_ApproveZone=0x0000 +OP_ZoneComplete=0x0000 +OP_ClientError=0x0000 +OP_DumpName=0x0000 +OP_Heartbeat=0x0000 +OP_CrashDump=0x0000 +OP_LoginComplete=0x0000 + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 +OP_PlayMP3=0x0000 +OP_ReclaimCrystals=0x0000 +OP_DynamicWall=0x0000 +OP_OpenDiscordMerchant=0x0000 +OP_DiscordMerchantInventory=0x0000 +OP_GiveMoney=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 +OP_Some6ByteHPUpdate=0x0000 #seems to happen when you target group members +OP_QueryResponseThing=0x0000 + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 #search term for petition +OP_PetitionSearchResults=0x0000 #(list of?) matches from search +OP_PetitionSearchText=0x0000 #text results of search + +OP_PetitionUpdate=0x0000 +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +# Login opcodes +OP_SessionReady=0x0000 +OP_Login=0x0000 +OP_ServerListRequest=0x0000 +OP_PlayEverquestRequest=0x0000 +OP_PlayEverquestResponse=0x0000 +OP_ChatMessage=0x0000 +OP_LoginAccepted=0x0000 +OP_ServerListResponse=0x0000 +OP_Poll=0x0000 +OP_EnterChat=0x0000 +OP_PollResponse=0x0000 + +# raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +OP_RAWOutOfSession=0x0000 + +# we need to document the differences between these packets to make identifying them easier +OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs +OP_InitialHPUpdate=0x0000 + +#aura related +OP_UpdateAura=0x0000 +OP_RemoveTrap=0x0000 From 823bacf08a3869aebbab26aa3872edd046d4bb4b Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 20 Oct 2024 19:18:32 -0700 Subject: [PATCH 11/93] Starting to poke at op_logserver --- common/patches/larion.cpp | 5 +++++ common/patches/larion_ops.h | 2 ++ common/patches/larion_structs.h | 15 +-------------- utils/patches/patch_Larion.conf | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 460d0d1b5..2a84e67aa 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -125,5 +125,10 @@ namespace Larion #include "ss_define.h" // ENCODE methods + ENCODE(OP_LogServer) { + + } + // DECODE methods + } /*Larion*/ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 4ef87be2b..37b2db42c 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -2,5 +2,7 @@ //list of packets we need to encode on the way out: //list of packets we need to decode on the way in: +E(OP_LogServer) + #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index cd81f7915..20c5a9084 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -3,20 +3,7 @@ namespace Larion { namespace structs { - struct LoginInfo_Struct { - /*000*/ char login_info[64]; - /*064*/ uint8 unknown064[124]; - /*188*/ uint8 zoning; // 01 if zoning, 00 if not - /*189*/ uint8 unknown189[275]; - /*488*/ - }; - - struct ClientZoneEntry_Struct { - /*00*/ uint32 unknown00; // ***Placeholder - /*04*/ char char_name[64]; // Player firstname [32] - /*68*/ uint32 unknown68; - /*72*/ uint32 unknown72; - }; + }; //end namespace structs }; //end namespace larion diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Larion.conf index 34f8ef278..5fd29482f 100644 --- a/utils/patches/patch_Larion.conf +++ b/utils/patches/patch_Larion.conf @@ -18,7 +18,7 @@ OP_SendLoginInfo=0x2fca OP_ApproveWorld=0x0000 OP_LogServer=0x6d4d OP_SendCharInfo=0x832 -OP_ExpansionInfo=0x0000 +OP_ExpansionInfo=0x066d OP_GuildsList=0x0000 OP_EnterWorld=0x6691 OP_PostEnterWorld=0x2062 From f485dfd8d349073c2554eedcd2de6219c8c78c11 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 21 Oct 2024 19:31:45 -0700 Subject: [PATCH 12/93] WIP logserver struct --- common/patches/larion_structs.h | 216 +++++++++++++++++++++++++++++++- 1 file changed, 215 insertions(+), 1 deletion(-) diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 20c5a9084..3dc356429 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -3,7 +3,221 @@ namespace Larion { namespace structs { - + struct LoginInfo_Struct { + /*000*/ char login_info[64]; + /*064*/ uint8 unknown064[124]; + /*188*/ uint8 zoning; // 01 if zoning, 00 if not + /*189*/ uint8 unknown189[275]; + /*488*/ + }; + + struct LogServer_Struct { + //As far as I can tell the client does not read this field + /*000*/ uint32 unknown000; + //client sets this to 140e32edc, confirmed it deals with pvp via disassembly aka hasn't moved + /*004*/ uint8 enable_pvp; + /*005*/ uint8 unknown005; + /*006*/ uint8 unknown006; + /*007*/ uint8 unknown007; + //client sets this to 140e32ee0, this was set to 1 on special ruleset servers but 0 on non-special ruleset servers + /*008*/ uint8 special_ruleset; + //client sets this to 140e3bedd + //I've seen 0 on normal servers + //it deals with char select and # of char slots somehow + /*009*/ uint8 unknown009; + //client sets this to 140e32ee1, confirmed via disassembly to be enable_FV + /*00a*/ uint8 enable_FV; + //client sets this to 140e3bf0, seems to affect the made by label on items + //always has been 0 for me + /*00b*/ uint8 enable_made_by; + //client sets this to 140e3bf09 + //I see it being written to but not read in code + //I've only ever seen it passed as 1 + /*00c*/ uint8 unknown00c; + //client sets this to 140e3bf0a + //I've only ever seen it passed as 1 + //0 seems to disable something about languages? + /*00d*/ uint8 languages_enabled; + /*00e*/ uint8 unknown00e; + /*00f*/ uint8 unknown00f; + /*010*/ uint8 unknown010; + /*011*/ uint8 unknown011; + /*012*/ uint8 unknown012; + /*013*/ uint8 unknown013; + /*014*/ uint8 unknown014; + /*015*/ char worldshortname[32]; + /*035*/ uint8 unknown035[11]; + /*040*/ uint8 unknown040[16]; + /*050*/ uint8 unknown050[16]; + /*060*/ uint8 unknown060[16]; + /*070*/ uint8 unknown070[16]; + /*080*/ uint8 unknown080[16]; + /*090*/ uint8 unknown090[16]; + /*0a0*/ uint8 unknown0a0[16]; + /*0b0*/ uint8 unknown0b0[16]; + /*0c0*/ uint8 unknown0c0[16]; + /*0d0*/ uint8 unknown0d0[16]; + /*0e0*/ uint8 unknown0e0[16]; + /*0f0*/ uint8 unknown0f0[16]; + /*100*/ uint8 unknown100[16]; + /*110*/ uint8 unknown110[16]; + /*120*/ uint8 unknown120[16]; + /*130*/ uint8 unknown130[16]; + /*140*/ uint8 unknown140[16]; + /*140*/ uint8 unknown140[16]; + /*150*/ uint8 unknown150[16]; + /*160*/ uint8 unknown160[16]; + /*170*/ uint8 unknown170[16]; + /*180*/ uint8 unknown180[16]; + /*190*/ uint8 unknown190[16]; + /*1a0*/ uint8 unknown1a0[16]; + /*1b0*/ uint8 unknown1b0[16]; + /*1c0*/ uint8 unknown1c0[16]; + /*1d0*/ uint8 unknown1d0[16]; + /*1e0*/ uint8 unknown1e0[16]; + /*1f0*/ uint8 unknown1f0[16]; + /*200*/ uint8 unknown200[16]; + /*210*/ uint8 unknown210[16]; + /*220*/ uint8 unknown220[16]; + /*230*/ uint8 unknown230[16]; + /*240*/ uint8 unknown240[16]; + /*240*/ uint8 unknown240[16]; + /*250*/ uint8 unknown250[16]; + /*260*/ uint8 unknown260[16]; + /*270*/ uint8 unknown270[16]; + /*280*/ uint8 unknown280[16]; + /*290*/ uint8 unknown290[16]; + /*2a0*/ uint8 unknown2a0[16]; + /*2b0*/ uint8 unknown2b0[16]; + /*2c0*/ uint8 unknown2c0[16]; + /*2d0*/ uint8 unknown2d0[16]; + /*2e0*/ uint8 unknown2e0[16]; + /*2f0*/ uint8 unknown2f0[16]; + /*300*/ uint8 unknown300[16]; + /*310*/ uint8 unknown310[16]; + /*320*/ uint8 unknown320[16]; + /*330*/ uint8 unknown330[16]; + /*340*/ uint8 unknown340[16]; + /*340*/ uint8 unknown340[16]; + /*350*/ uint8 unknown350[16]; + /*360*/ uint8 unknown360[16]; + /*370*/ uint8 unknown370[16]; + /*380*/ uint8 unknown380[16]; + /*390*/ uint8 unknown390[16]; + /*3a0*/ uint8 unknown3a0[16]; + /*3b0*/ uint8 unknown3b0[16]; + /*3c0*/ uint8 unknown3c0[16]; + /*3d0*/ uint8 unknown3d0[16]; + /*3e0*/ uint8 unknown3e0[16]; + /*3f0*/ uint8 unknown3f0[16]; + /*400*/ uint8 unknown400[16]; + /*410*/ uint8 unknown410[16]; + /*420*/ uint8 unknown420[16]; + /*430*/ uint8 unknown430[16]; + /*440*/ uint8 unknown440[16]; + /*440*/ uint8 unknown440[16]; + /*450*/ uint8 unknown450[16]; + /*460*/ uint8 unknown460[16]; + /*470*/ uint8 unknown470[16]; + /*480*/ uint8 unknown480[16]; + /*490*/ uint8 unknown490[16]; + /*4a0*/ uint8 unknown4a0[16]; + /*4b0*/ uint8 unknown4b0[16]; + /*4c0*/ uint8 unknown4c0[16]; + /*4d0*/ uint8 unknown4d0[16]; + /*4e0*/ uint8 unknown4e0[16]; + /*4f0*/ uint8 unknown4f0[16]; + /*500*/ uint8 unknown500[16]; + /*510*/ uint8 unknown510[16]; + /*520*/ uint8 unknown520[16]; + /*530*/ uint8 unknown530[16]; + /*540*/ uint8 unknown540[16]; + /*540*/ uint8 unknown540[16]; + /*550*/ uint8 unknown550[16]; + /*560*/ uint8 unknown560[16]; + /*570*/ uint8 unknown570[16]; + /*580*/ uint8 unknown580[16]; + /*590*/ uint8 unknown590[16]; + /*5a0*/ uint8 unknown5a0[12]; + //Client reads this and sets it to 140e32ee2 + //I've only ever seen 0, it affects some calculation in the client + /*5ac*/ uint8 unknown5ac; + /*5ad*/ uint8 unknown5ad[3]; + /*5b0*/ uint8 unknown5b0[3]; + //Client reads this and sets it to 140e3bf18 + //seems to enable the voice system + /*5b4*/ uint8 enable_voice_system; + //Client reads this and sets it to 140e3bef0 + //Seems to enable the mail system + /*5b5*/ uint8 enable_mail; + //Client reads this and sets it to 140e3bef1 + //Not entirely sure what this does, it seems to be related to character create + //I've only ever seen it set to 0 + /*5b6*/ uint8 unknown5b6; + /*5b7*/ uint8 unknown5b7; + //Client reads this and sets it to 140e3bef2 + //Not entirely sure what this does, it seems to be related to character create + //Seen set to 1 + /*5b8*/ uint8 unknown5b8; + /*5b9*/ uint8 unknown5b9[8]; + //Client sets this to 140e3beec + //Seems to help enable beta buff command + /*5c0*/ uint8 beta_buff_enabled; + //Client sets this to 140e3beed + //Guessing this is for marking as a beta server + /*5c1*/ uint8 is_beta_server; + //Client sets this to 140e3beee + //Test sets this to one and all 3 of these need to be 1 to use /betabuff + /*5c2*/ uint8 is_test_server; + //Client sets this to 140e3beef + //Client writes but doesn't read this anywhere as far as I can tell + /*5c3*/ uint8 unknown5c3; + /*5c4*/ uint8 unknown5c4[12]; + /*5d0*/ uint8 unknown5d0[16]; + /*5e0*/ uint8 unknown5e0[16]; + /*5f0*/ uint8 unknown5f0[12]; + //Client sets this to 140e3d5f4 + //Not sure what this affects + //I've seen ff ff ff ff passed + /*5fc*/ int32 unknown5fc; + //Client sets this to 140e3d5f4 + //not sure what this does + //Ive seen 0 on normal servers but 1 on test + /*600*/ int32 unknown600; + /*604*/ uint8 unknown604[12]; + /*610*/ uint8 unknown610[16]; + /*620*/ uint8 unknown620[16]; + /*630*/ uint8 unknown630[16]; + /*640*/ uint8 unknown640[16]; + /*640*/ uint8 unknown640[16]; + /*650*/ uint8 unknown650[16]; + /*660*/ uint8 unknown660[16]; + /*670*/ uint8 unknown670[16]; + /*680*/ uint8 unknown680[16]; + /*690*/ uint8 unknown690[16]; + /*6a0*/ uint8 unknown6a0[16]; + /*6b0*/ uint8 unknown6b0[16]; + /*6c0*/ uint8 unknown6c0[16]; + /*6d0*/ uint8 unknown6d0[16]; + /*6e0*/ uint8 unknown6e0[16]; + /*6f0*/ uint8 unknown6f0[16]; + /*700*/ uint8 unknown700[16]; + /*710*/ uint8 unknown710[14]; + //Client reads this and sets it to 140e32ee2 + //based on strings near related disassembly im guessing this controls + //which kind of items are allowed (there are strings for heirloom, prestige, etc) + //every server ive logged in so far has had this to 0 but I haven't tried a TLP yet + /*71d*/ uint8 heirlooms_disabled; + /*71e*/ uint8 unknown71e; + /*720*/ uint8 unknown720[16]; + }; + + struct ClientZoneEntry_Struct { + /*00*/ uint32 unknown00; // ***Placeholder + /*04*/ char char_name[64]; // Player firstname [32] + /*68*/ uint32 unknown68; + /*72*/ uint32 unknown72; + }; }; //end namespace structs }; //end namespace larion From 6db7c624e92c76a478bac5f1a34246e44577673c Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 22 Oct 2024 17:52:14 -0700 Subject: [PATCH 13/93] Larion expansion data --- loginserver/client.cpp | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 62f7fb1f2..dc1701959 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -573,10 +573,13 @@ void Client::SendExpansionPacketData(PlayerLoginReply_Struct& plrs) { SerializeBuffer buf; //from eqlsstr_us.txt id of each expansion, excluding 'Everquest' - int ExpansionLookup[20] = { 3007, 3008, 3009, 3010, 3012, + int ExpansionLookup[30] = { 3007, 3008, 3009, 3010, 3012, 3014, 3031, 3033, 3036, 3040, 3045, 3046, 3047, 3514, 3516, - 3518, 3520, 3522, 3524 }; + 3518, 3520, 3522, 3524, 3526, + 3528, 3530, 3532, 3534, 3535, + 3536, 3537, 3538, 3539, 3540 + }; if (server.options.IsDisplayExpansions()) { @@ -584,7 +587,26 @@ void Client::SendExpansionPacketData(PlayerLoginReply_Struct& plrs) int32_t expansion = server.options.GetMaxExpansions(); int32_t owned_expansion = (expansion << 1) | 1; - if (m_client_version == cv_sod) { + if (m_client_version == cv_larion) { + buf.WriteInt32(0x00); + buf.WriteInt32(0x00); + buf.WriteInt16(0x00); + buf.WriteInt32(30); + + for (int i = 0; i < 30; i++) + { + buf.WriteUInt32(i + 1); + buf.WriteUInt8(1); + buf.WriteInt32(ExpansionLookup[i]); + buf.WriteInt32(6046); + buf.WriteUInt32(0xFFFFFFFF); + buf.WriteUInt32(0); + } + + auto out = std::make_unique(OP_LoginExpansionPacketData, buf); + m_connection->QueuePacket(out.get()); + } + else if (m_client_version == cv_sod) { // header info of packet. Requires OP_LoginExpansionPacketData=0x0031 to be in login_opcodes_sod.conf buf.WriteInt32(0x00); From acdd65b55c2fbc732a418346ee8ea601dca750ba Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 22 Oct 2024 20:08:31 -0700 Subject: [PATCH 14/93] Distracted by world server issues --- common/patches/larion.cpp | 9 + common/patches/larion_structs.h | 396 ++++++++++++++++---------------- loginserver/world_server.cpp | 73 ++++-- loginserver/world_server.h | 5 +- 4 files changed, 269 insertions(+), 214 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 2a84e67aa..5c073063c 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -126,7 +126,16 @@ namespace Larion // ENCODE methods ENCODE(OP_LogServer) { + EQApplicationPacket* in = *p; + *p = nullptr; + LogServer_Struct* emu = (LogServer_Struct*)in->pBuffer; + auto outapp = new EQApplicationPacket(OP_LogServer, 1840); + + + + dest->FastQueuePacket(&outapp); + safe_delete(in); } // DECODE methods diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 3dc356429..dceb7974c 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -12,204 +12,204 @@ namespace Larion { }; struct LogServer_Struct { - //As far as I can tell the client does not read this field - /*000*/ uint32 unknown000; - //client sets this to 140e32edc, confirmed it deals with pvp via disassembly aka hasn't moved - /*004*/ uint8 enable_pvp; - /*005*/ uint8 unknown005; - /*006*/ uint8 unknown006; - /*007*/ uint8 unknown007; - //client sets this to 140e32ee0, this was set to 1 on special ruleset servers but 0 on non-special ruleset servers - /*008*/ uint8 special_ruleset; - //client sets this to 140e3bedd - //I've seen 0 on normal servers - //it deals with char select and # of char slots somehow - /*009*/ uint8 unknown009; - //client sets this to 140e32ee1, confirmed via disassembly to be enable_FV - /*00a*/ uint8 enable_FV; - //client sets this to 140e3bf0, seems to affect the made by label on items - //always has been 0 for me - /*00b*/ uint8 enable_made_by; - //client sets this to 140e3bf09 - //I see it being written to but not read in code - //I've only ever seen it passed as 1 - /*00c*/ uint8 unknown00c; - //client sets this to 140e3bf0a - //I've only ever seen it passed as 1 - //0 seems to disable something about languages? - /*00d*/ uint8 languages_enabled; - /*00e*/ uint8 unknown00e; - /*00f*/ uint8 unknown00f; - /*010*/ uint8 unknown010; - /*011*/ uint8 unknown011; - /*012*/ uint8 unknown012; - /*013*/ uint8 unknown013; - /*014*/ uint8 unknown014; - /*015*/ char worldshortname[32]; - /*035*/ uint8 unknown035[11]; - /*040*/ uint8 unknown040[16]; - /*050*/ uint8 unknown050[16]; - /*060*/ uint8 unknown060[16]; - /*070*/ uint8 unknown070[16]; - /*080*/ uint8 unknown080[16]; - /*090*/ uint8 unknown090[16]; - /*0a0*/ uint8 unknown0a0[16]; - /*0b0*/ uint8 unknown0b0[16]; - /*0c0*/ uint8 unknown0c0[16]; - /*0d0*/ uint8 unknown0d0[16]; - /*0e0*/ uint8 unknown0e0[16]; - /*0f0*/ uint8 unknown0f0[16]; - /*100*/ uint8 unknown100[16]; - /*110*/ uint8 unknown110[16]; - /*120*/ uint8 unknown120[16]; - /*130*/ uint8 unknown130[16]; - /*140*/ uint8 unknown140[16]; - /*140*/ uint8 unknown140[16]; - /*150*/ uint8 unknown150[16]; - /*160*/ uint8 unknown160[16]; - /*170*/ uint8 unknown170[16]; - /*180*/ uint8 unknown180[16]; - /*190*/ uint8 unknown190[16]; - /*1a0*/ uint8 unknown1a0[16]; - /*1b0*/ uint8 unknown1b0[16]; - /*1c0*/ uint8 unknown1c0[16]; - /*1d0*/ uint8 unknown1d0[16]; - /*1e0*/ uint8 unknown1e0[16]; - /*1f0*/ uint8 unknown1f0[16]; - /*200*/ uint8 unknown200[16]; - /*210*/ uint8 unknown210[16]; - /*220*/ uint8 unknown220[16]; - /*230*/ uint8 unknown230[16]; - /*240*/ uint8 unknown240[16]; - /*240*/ uint8 unknown240[16]; - /*250*/ uint8 unknown250[16]; - /*260*/ uint8 unknown260[16]; - /*270*/ uint8 unknown270[16]; - /*280*/ uint8 unknown280[16]; - /*290*/ uint8 unknown290[16]; - /*2a0*/ uint8 unknown2a0[16]; - /*2b0*/ uint8 unknown2b0[16]; - /*2c0*/ uint8 unknown2c0[16]; - /*2d0*/ uint8 unknown2d0[16]; - /*2e0*/ uint8 unknown2e0[16]; - /*2f0*/ uint8 unknown2f0[16]; - /*300*/ uint8 unknown300[16]; - /*310*/ uint8 unknown310[16]; - /*320*/ uint8 unknown320[16]; - /*330*/ uint8 unknown330[16]; - /*340*/ uint8 unknown340[16]; - /*340*/ uint8 unknown340[16]; - /*350*/ uint8 unknown350[16]; - /*360*/ uint8 unknown360[16]; - /*370*/ uint8 unknown370[16]; - /*380*/ uint8 unknown380[16]; - /*390*/ uint8 unknown390[16]; - /*3a0*/ uint8 unknown3a0[16]; - /*3b0*/ uint8 unknown3b0[16]; - /*3c0*/ uint8 unknown3c0[16]; - /*3d0*/ uint8 unknown3d0[16]; - /*3e0*/ uint8 unknown3e0[16]; - /*3f0*/ uint8 unknown3f0[16]; - /*400*/ uint8 unknown400[16]; - /*410*/ uint8 unknown410[16]; - /*420*/ uint8 unknown420[16]; - /*430*/ uint8 unknown430[16]; - /*440*/ uint8 unknown440[16]; - /*440*/ uint8 unknown440[16]; - /*450*/ uint8 unknown450[16]; - /*460*/ uint8 unknown460[16]; - /*470*/ uint8 unknown470[16]; - /*480*/ uint8 unknown480[16]; - /*490*/ uint8 unknown490[16]; - /*4a0*/ uint8 unknown4a0[16]; - /*4b0*/ uint8 unknown4b0[16]; - /*4c0*/ uint8 unknown4c0[16]; - /*4d0*/ uint8 unknown4d0[16]; - /*4e0*/ uint8 unknown4e0[16]; - /*4f0*/ uint8 unknown4f0[16]; - /*500*/ uint8 unknown500[16]; - /*510*/ uint8 unknown510[16]; - /*520*/ uint8 unknown520[16]; - /*530*/ uint8 unknown530[16]; - /*540*/ uint8 unknown540[16]; - /*540*/ uint8 unknown540[16]; - /*550*/ uint8 unknown550[16]; - /*560*/ uint8 unknown560[16]; - /*570*/ uint8 unknown570[16]; - /*580*/ uint8 unknown580[16]; - /*590*/ uint8 unknown590[16]; - /*5a0*/ uint8 unknown5a0[12]; - //Client reads this and sets it to 140e32ee2 - //I've only ever seen 0, it affects some calculation in the client - /*5ac*/ uint8 unknown5ac; - /*5ad*/ uint8 unknown5ad[3]; - /*5b0*/ uint8 unknown5b0[3]; - //Client reads this and sets it to 140e3bf18 - //seems to enable the voice system - /*5b4*/ uint8 enable_voice_system; - //Client reads this and sets it to 140e3bef0 - //Seems to enable the mail system - /*5b5*/ uint8 enable_mail; - //Client reads this and sets it to 140e3bef1 - //Not entirely sure what this does, it seems to be related to character create - //I've only ever seen it set to 0 - /*5b6*/ uint8 unknown5b6; - /*5b7*/ uint8 unknown5b7; - //Client reads this and sets it to 140e3bef2 - //Not entirely sure what this does, it seems to be related to character create - //Seen set to 1 - /*5b8*/ uint8 unknown5b8; - /*5b9*/ uint8 unknown5b9[8]; - //Client sets this to 140e3beec - //Seems to help enable beta buff command - /*5c0*/ uint8 beta_buff_enabled; - //Client sets this to 140e3beed - //Guessing this is for marking as a beta server - /*5c1*/ uint8 is_beta_server; - //Client sets this to 140e3beee - //Test sets this to one and all 3 of these need to be 1 to use /betabuff - /*5c2*/ uint8 is_test_server; - //Client sets this to 140e3beef - //Client writes but doesn't read this anywhere as far as I can tell - /*5c3*/ uint8 unknown5c3; - /*5c4*/ uint8 unknown5c4[12]; - /*5d0*/ uint8 unknown5d0[16]; - /*5e0*/ uint8 unknown5e0[16]; - /*5f0*/ uint8 unknown5f0[12]; - //Client sets this to 140e3d5f4 - //Not sure what this affects - //I've seen ff ff ff ff passed - /*5fc*/ int32 unknown5fc; - //Client sets this to 140e3d5f4 - //not sure what this does - //Ive seen 0 on normal servers but 1 on test - /*600*/ int32 unknown600; - /*604*/ uint8 unknown604[12]; - /*610*/ uint8 unknown610[16]; - /*620*/ uint8 unknown620[16]; - /*630*/ uint8 unknown630[16]; - /*640*/ uint8 unknown640[16]; - /*640*/ uint8 unknown640[16]; - /*650*/ uint8 unknown650[16]; - /*660*/ uint8 unknown660[16]; - /*670*/ uint8 unknown670[16]; - /*680*/ uint8 unknown680[16]; - /*690*/ uint8 unknown690[16]; - /*6a0*/ uint8 unknown6a0[16]; - /*6b0*/ uint8 unknown6b0[16]; - /*6c0*/ uint8 unknown6c0[16]; - /*6d0*/ uint8 unknown6d0[16]; - /*6e0*/ uint8 unknown6e0[16]; - /*6f0*/ uint8 unknown6f0[16]; - /*700*/ uint8 unknown700[16]; - /*710*/ uint8 unknown710[14]; - //Client reads this and sets it to 140e32ee2 - //based on strings near related disassembly im guessing this controls - //which kind of items are allowed (there are strings for heirloom, prestige, etc) - //every server ive logged in so far has had this to 0 but I haven't tried a TLP yet - /*71d*/ uint8 heirlooms_disabled; - /*71e*/ uint8 unknown71e; - /*720*/ uint8 unknown720[16]; + ////As far as I can tell the client does not read this field + ///*000*/ uint32 unknown000; + ////client sets this to 140e32edc, confirmed it deals with pvp via disassembly aka hasn't moved + ///*004*/ uint8 enable_pvp; + ///*005*/ uint8 unknown005; + ///*006*/ uint8 unknown006; + ///*007*/ uint8 unknown007; + ////client sets this to 140e32ee0, this was set to 1 on special ruleset servers but 0 on non-special ruleset servers + ///*008*/ uint8 special_ruleset; + ////client sets this to 140e3bedd + ////I've seen 0 on normal servers + ////it deals with char select and # of char slots somehow + ///*009*/ uint8 unknown009; + ////client sets this to 140e32ee1, confirmed via disassembly to be enable_FV + ///*00a*/ uint8 enable_FV; + ////client sets this to 140e3bf0, seems to affect the made by label on items + ////always has been 0 for me + ///*00b*/ uint8 enable_made_by; + ////client sets this to 140e3bf09 + ////I see it being written to but not read in code + ////I've only ever seen it passed as 1 + ///*00c*/ uint8 unknown00c; + ////client sets this to 140e3bf0a + ////I've only ever seen it passed as 1 + ////0 seems to disable something about languages? + ///*00d*/ uint8 languages_enabled; + ///*00e*/ uint8 unknown00e; + ///*00f*/ uint8 unknown00f; + ///*010*/ uint8 unknown010; + ///*011*/ uint8 unknown011; + ///*012*/ uint8 unknown012; + ///*013*/ uint8 unknown013; + ///*014*/ uint8 unknown014; + ///*015*/ char worldshortname[32]; + ///*035*/ uint8 unknown035[11]; + ///*040*/ uint8 unknown040[16]; + ///*050*/ uint8 unknown050[16]; + ///*060*/ uint8 unknown060[16]; + ///*070*/ uint8 unknown070[16]; + ///*080*/ uint8 unknown080[16]; + ///*090*/ uint8 unknown090[16]; + ///*0a0*/ uint8 unknown0a0[16]; + ///*0b0*/ uint8 unknown0b0[16]; + ///*0c0*/ uint8 unknown0c0[16]; + ///*0d0*/ uint8 unknown0d0[16]; + ///*0e0*/ uint8 unknown0e0[16]; + ///*0f0*/ uint8 unknown0f0[16]; + ///*100*/ uint8 unknown100[16]; + ///*110*/ uint8 unknown110[16]; + ///*120*/ uint8 unknown120[16]; + ///*130*/ uint8 unknown130[16]; + ///*140*/ uint8 unknown140[16]; + ///*140*/ uint8 unknown140[16]; + ///*150*/ uint8 unknown150[16]; + ///*160*/ uint8 unknown160[16]; + ///*170*/ uint8 unknown170[16]; + ///*180*/ uint8 unknown180[16]; + ///*190*/ uint8 unknown190[16]; + ///*1a0*/ uint8 unknown1a0[16]; + ///*1b0*/ uint8 unknown1b0[16]; + ///*1c0*/ uint8 unknown1c0[16]; + ///*1d0*/ uint8 unknown1d0[16]; + ///*1e0*/ uint8 unknown1e0[16]; + ///*1f0*/ uint8 unknown1f0[16]; + ///*200*/ uint8 unknown200[16]; + ///*210*/ uint8 unknown210[16]; + ///*220*/ uint8 unknown220[16]; + ///*230*/ uint8 unknown230[16]; + ///*240*/ uint8 unknown240[16]; + ///*240*/ uint8 unknown240[16]; + ///*250*/ uint8 unknown250[16]; + ///*260*/ uint8 unknown260[16]; + ///*270*/ uint8 unknown270[16]; + ///*280*/ uint8 unknown280[16]; + ///*290*/ uint8 unknown290[16]; + ///*2a0*/ uint8 unknown2a0[16]; + ///*2b0*/ uint8 unknown2b0[16]; + ///*2c0*/ uint8 unknown2c0[16]; + ///*2d0*/ uint8 unknown2d0[16]; + ///*2e0*/ uint8 unknown2e0[16]; + ///*2f0*/ uint8 unknown2f0[16]; + ///*300*/ uint8 unknown300[16]; + ///*310*/ uint8 unknown310[16]; + ///*320*/ uint8 unknown320[16]; + ///*330*/ uint8 unknown330[16]; + ///*340*/ uint8 unknown340[16]; + ///*340*/ uint8 unknown340[16]; + ///*350*/ uint8 unknown350[16]; + ///*360*/ uint8 unknown360[16]; + ///*370*/ uint8 unknown370[16]; + ///*380*/ uint8 unknown380[16]; + ///*390*/ uint8 unknown390[16]; + ///*3a0*/ uint8 unknown3a0[16]; + ///*3b0*/ uint8 unknown3b0[16]; + ///*3c0*/ uint8 unknown3c0[16]; + ///*3d0*/ uint8 unknown3d0[16]; + ///*3e0*/ uint8 unknown3e0[16]; + ///*3f0*/ uint8 unknown3f0[16]; + ///*400*/ uint8 unknown400[16]; + ///*410*/ uint8 unknown410[16]; + ///*420*/ uint8 unknown420[16]; + ///*430*/ uint8 unknown430[16]; + ///*440*/ uint8 unknown440[16]; + ///*440*/ uint8 unknown440[16]; + ///*450*/ uint8 unknown450[16]; + ///*460*/ uint8 unknown460[16]; + ///*470*/ uint8 unknown470[16]; + ///*480*/ uint8 unknown480[16]; + ///*490*/ uint8 unknown490[16]; + ///*4a0*/ uint8 unknown4a0[16]; + ///*4b0*/ uint8 unknown4b0[16]; + ///*4c0*/ uint8 unknown4c0[16]; + ///*4d0*/ uint8 unknown4d0[16]; + ///*4e0*/ uint8 unknown4e0[16]; + ///*4f0*/ uint8 unknown4f0[16]; + ///*500*/ uint8 unknown500[16]; + ///*510*/ uint8 unknown510[16]; + ///*520*/ uint8 unknown520[16]; + ///*530*/ uint8 unknown530[16]; + ///*540*/ uint8 unknown540[16]; + ///*540*/ uint8 unknown540[16]; + ///*550*/ uint8 unknown550[16]; + ///*560*/ uint8 unknown560[16]; + ///*570*/ uint8 unknown570[16]; + ///*580*/ uint8 unknown580[16]; + ///*590*/ uint8 unknown590[16]; + ///*5a0*/ uint8 unknown5a0[12]; + ////Client reads this and sets it to 140e32ee2 + ////I've only ever seen 0, it affects some calculation in the client + ///*5ac*/ uint8 unknown5ac; + ///*5ad*/ uint8 unknown5ad[3]; + ///*5b0*/ uint8 unknown5b0[3]; + ////Client reads this and sets it to 140e3bf18 + ////seems to enable the voice system + ///*5b4*/ uint8 enable_voice_system; + ////Client reads this and sets it to 140e3bef0 + ////Seems to enable the mail system + ///*5b5*/ uint8 enable_mail; + ////Client reads this and sets it to 140e3bef1 + ////Not entirely sure what this does, it seems to be related to character create + ////I've only ever seen it set to 0 + ///*5b6*/ uint8 unknown5b6; + ///*5b7*/ uint8 unknown5b7; + ////Client reads this and sets it to 140e3bef2 + ////Not entirely sure what this does, it seems to be related to character create + ////Seen set to 1 + ///*5b8*/ uint8 unknown5b8; + ///*5b9*/ uint8 unknown5b9[8]; + ////Client sets this to 140e3beec + ////Seems to help enable beta buff command + ///*5c0*/ uint8 beta_buff_enabled; + ////Client sets this to 140e3beed + ////Guessing this is for marking as a beta server + ///*5c1*/ uint8 is_beta_server; + ////Client sets this to 140e3beee + ////Test sets this to one and all 3 of these need to be 1 to use /betabuff + ///*5c2*/ uint8 is_test_server; + ////Client sets this to 140e3beef + ////Client writes but doesn't read this anywhere as far as I can tell + ///*5c3*/ uint8 unknown5c3; + ///*5c4*/ uint8 unknown5c4[12]; + ///*5d0*/ uint8 unknown5d0[16]; + ///*5e0*/ uint8 unknown5e0[16]; + ///*5f0*/ uint8 unknown5f0[12]; + ////Client sets this to 140e3d5f4 + ////Not sure what this affects + ////I've seen ff ff ff ff passed + ///*5fc*/ int32 unknown5fc; + ////Client sets this to 140e3d5f4 + ////not sure what this does + ////Ive seen 0 on normal servers but 1 on test + ///*600*/ int32 unknown600; + ///*604*/ uint8 unknown604[12]; + ///*610*/ uint8 unknown610[16]; + ///*620*/ uint8 unknown620[16]; + ///*630*/ uint8 unknown630[16]; + ///*640*/ uint8 unknown640[16]; + ///*640*/ uint8 unknown640[16]; + ///*650*/ uint8 unknown650[16]; + ///*660*/ uint8 unknown660[16]; + ///*670*/ uint8 unknown670[16]; + ///*680*/ uint8 unknown680[16]; + ///*690*/ uint8 unknown690[16]; + ///*6a0*/ uint8 unknown6a0[16]; + ///*6b0*/ uint8 unknown6b0[16]; + ///*6c0*/ uint8 unknown6c0[16]; + ///*6d0*/ uint8 unknown6d0[16]; + ///*6e0*/ uint8 unknown6e0[16]; + ///*6f0*/ uint8 unknown6f0[16]; + ///*700*/ uint8 unknown700[16]; + ///*710*/ uint8 unknown710[14]; + ////Client reads this and sets it to 140e32ee2 + ////based on strings near related disassembly im guessing this controls + ////which kind of items are allowed (there are strings for heirloom, prestige, etc) + ////every server ive logged in so far has had this to 0 but I haven't tried a TLP yet + ///*71d*/ uint8 heirlooms_disabled; + ///*71e*/ uint8 unknown71e; + ///*720*/ uint8 unknown720[16]; }; struct ClientZoneEntry_Struct { diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index b43651fd7..ece3f16d5 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -977,8 +977,7 @@ bool WorldServer::ValidateWorldServerAdminLogin( return false; } -void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const -{ +void WorldServer::SerializeForClientServerListLegacy(class SerializeBuffer& out, bool use_local_ip) const { // see LoginClientServerData_Struct if (use_local_ip) { out.WriteString(GetLocalIP()); @@ -987,10 +986,6 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_lo out.WriteString(GetRemoteIP()); } - if (version == cv_larion) { - out.WriteUInt32(9000); - } - switch (GetServerListID()) { case LS::ServerType::Legends: out.WriteInt32(LS::ServerTypeFlags::Legends); @@ -1002,15 +997,8 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_lo out.WriteInt32(LS::ServerTypeFlags::Standard); break; } - if (version == cv_larion) { - auto server_id = GetServerId(); - //if this is 0, the client will not show the server in the list - out.WriteUInt32(1); - out.WriteUInt32(server_id); - } - else { - out.WriteUInt32(GetServerId()); - } + + out.WriteUInt32(GetServerId()); out.WriteString(GetServerLongName()); out.WriteString("us"); // country code @@ -1032,6 +1020,61 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_lo out.WriteUInt32(GetPlayersOnline()); } +void WorldServer::SerializeForClientServerListLarion(class SerializeBuffer& out, bool use_local_ip) const { + if (use_local_ip) { + out.WriteString(GetLocalIP()); + } + else { + out.WriteString(GetRemoteIP()); + } + + out.WriteUInt32(9000); + out.WriteUInt32(0); + + uint32_t flags = 32; //all servers i saw had this set + switch (GetServerListID()) { + case LS::ServerType::Legends: + flags += LS::ServerTypeFlags::Legends; + break; + case LS::ServerType::Preferred: + flags += LS::ServerTypeFlags::Preferred; + break; + default: + flags += LS::ServerTypeFlags::Standard; + break; + } + + out.WriteUInt32(flags); + out.WriteUInt32(GetServerId()); + out.WriteString(GetServerLongName()); + out.WriteString("EN"); + out.WriteString("US"); + + if (GetStatus() < 0) { + if (GetZonesBooted() == 0) { + out.WriteInt32(LS::ServerStatusFlags::Down); + } + else { + out.WriteInt32(LS::ServerStatusFlags::Locked); + } + } + else { + out.WriteInt32(LS::ServerStatusFlags::Up); + } + + out.WriteUInt32(GetPlayersOnline()); +} + +void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const +{ + if (version == cv_larion) { + SerializeForClientServerListLarion(out, use_local_ip); + } + else { + SerializeForClientServerListLegacy(out, use_local_ip); + } +} + /** * @param in_server_list_id * @return diff --git a/loginserver/world_server.h b/loginserver/world_server.h index df5324240..0b8da5851 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -149,7 +149,10 @@ public: bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration); bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration); - +private: + void SerializeForClientServerListLegacy(class SerializeBuffer& out, bool use_local_ip) const; + void SerializeForClientServerListLarion(class SerializeBuffer& out, bool use_local_ip) const; +public: void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const; private: From 702024fe04d9a58b01b4db554137a9dc4495f168 Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 23 Oct 2024 18:15:40 -0700 Subject: [PATCH 15/93] Encode OP_LogServer --- common/patches/larion.cpp | 80 ++++++++++++- common/patches/larion_structs.h | 201 -------------------------------- 2 files changed, 79 insertions(+), 202 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 5c073063c..d5530eb7e 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -131,9 +131,87 @@ namespace Larion LogServer_Struct* emu = (LogServer_Struct*)in->pBuffer; auto outapp = new EQApplicationPacket(OP_LogServer, 1840); + auto buffer = outapp->pBuffer; + //pvp + if (emu->enable_pvp) { + *(char*)&buffer[0x04] = 1; + } + + if (emu->enable_FV) { + //FV sets these both to 1 + //one appears to enable the no drop flag the other just marks the server as special? + *(char*)&buffer[0x08] = 1; + *(char*)&buffer[0x0a] = 1; + } + + //This has something to do with heirloom and prestige items but im not sure what it does + //Seems to sit at 0 + *(char*)&buffer[0x71d] = 0; + + //not sure what this does, something to do with server select + *(char*)&buffer[0x09] = 0; + + //this appears to have some effect on the tradeskill system; disabling made by tags perhaps? + *(char*)&buffer[0x0b] = 0; + + //not sure, setting it to the value ive seen + *(char*)&buffer[0x0c] = 1; + + //Something to do with languages + *(char*)&buffer[0x0d] = 1; + + //These seem to affect if server has betabuff enabled + *(char*)&buffer[0x5c0] = 0; + *(char*)&buffer[0x5c1] = 0; + //This is set on test so it's probably indicating this is a test server + *(char*)&buffer[0x5c2] = 0; + + //not sure, but it's grouped with the beta and test stuff + *(char*)&buffer[0x5c3] = 0; + + //world short name + strncpy((char*)&buffer[0x15], emu->worldshortname, 32); + + //not sure, affects some player calculation but didn't care to look more + *(char*)&buffer[0x5c2] = 0; + + //Looks right + if (emu->enablemail) { + *(char*)&buffer[0x5b5] = 1; + } + + //Looks right + if (emu->enablevoicemacros) { + *(char*)&buffer[0x5b4] = 1; + } + + //Not sure, sending what we've seen + *(char*)&buffer[0x5b6] = 0; + + //Not sure sending what we've seen + *(char*)&buffer[0x5b8] = 1; + + //Not sure sending what we've seen + *(int32_t*)&buffer[0x5fc] = -1; + + //Test sets this to 1, everyone else seems to set it to 0 + *(int32_t*)&buffer[0x600] = 0; + + //Disassembly puts it next to code dealing with commands, ive not seen anyone send anything but 0 + *(char*)&buffer[0x705] = 0; + + //Something about item restrictions, seems to always be set to 1 + *(char*)&buffer[0x710] = 0; + + //This and 0x724 are often multiplied together in guild favor calcs, live and test send 1.0f + *(float*)&buffer[0x720] = 1.0f; + *(float*)&buffer[0x724] = 1.0f; + + //This and 0x72c are often multiplied together in non-guild favor calcs, live and test send 1.0f + *(float*)&buffer[0x728] = 1.0f; + *(float*)&buffer[0x72c] = 1.0f; - dest->FastQueuePacket(&outapp); safe_delete(in); } diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index dceb7974c..cd81f7915 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -11,207 +11,6 @@ namespace Larion { /*488*/ }; - struct LogServer_Struct { - ////As far as I can tell the client does not read this field - ///*000*/ uint32 unknown000; - ////client sets this to 140e32edc, confirmed it deals with pvp via disassembly aka hasn't moved - ///*004*/ uint8 enable_pvp; - ///*005*/ uint8 unknown005; - ///*006*/ uint8 unknown006; - ///*007*/ uint8 unknown007; - ////client sets this to 140e32ee0, this was set to 1 on special ruleset servers but 0 on non-special ruleset servers - ///*008*/ uint8 special_ruleset; - ////client sets this to 140e3bedd - ////I've seen 0 on normal servers - ////it deals with char select and # of char slots somehow - ///*009*/ uint8 unknown009; - ////client sets this to 140e32ee1, confirmed via disassembly to be enable_FV - ///*00a*/ uint8 enable_FV; - ////client sets this to 140e3bf0, seems to affect the made by label on items - ////always has been 0 for me - ///*00b*/ uint8 enable_made_by; - ////client sets this to 140e3bf09 - ////I see it being written to but not read in code - ////I've only ever seen it passed as 1 - ///*00c*/ uint8 unknown00c; - ////client sets this to 140e3bf0a - ////I've only ever seen it passed as 1 - ////0 seems to disable something about languages? - ///*00d*/ uint8 languages_enabled; - ///*00e*/ uint8 unknown00e; - ///*00f*/ uint8 unknown00f; - ///*010*/ uint8 unknown010; - ///*011*/ uint8 unknown011; - ///*012*/ uint8 unknown012; - ///*013*/ uint8 unknown013; - ///*014*/ uint8 unknown014; - ///*015*/ char worldshortname[32]; - ///*035*/ uint8 unknown035[11]; - ///*040*/ uint8 unknown040[16]; - ///*050*/ uint8 unknown050[16]; - ///*060*/ uint8 unknown060[16]; - ///*070*/ uint8 unknown070[16]; - ///*080*/ uint8 unknown080[16]; - ///*090*/ uint8 unknown090[16]; - ///*0a0*/ uint8 unknown0a0[16]; - ///*0b0*/ uint8 unknown0b0[16]; - ///*0c0*/ uint8 unknown0c0[16]; - ///*0d0*/ uint8 unknown0d0[16]; - ///*0e0*/ uint8 unknown0e0[16]; - ///*0f0*/ uint8 unknown0f0[16]; - ///*100*/ uint8 unknown100[16]; - ///*110*/ uint8 unknown110[16]; - ///*120*/ uint8 unknown120[16]; - ///*130*/ uint8 unknown130[16]; - ///*140*/ uint8 unknown140[16]; - ///*140*/ uint8 unknown140[16]; - ///*150*/ uint8 unknown150[16]; - ///*160*/ uint8 unknown160[16]; - ///*170*/ uint8 unknown170[16]; - ///*180*/ uint8 unknown180[16]; - ///*190*/ uint8 unknown190[16]; - ///*1a0*/ uint8 unknown1a0[16]; - ///*1b0*/ uint8 unknown1b0[16]; - ///*1c0*/ uint8 unknown1c0[16]; - ///*1d0*/ uint8 unknown1d0[16]; - ///*1e0*/ uint8 unknown1e0[16]; - ///*1f0*/ uint8 unknown1f0[16]; - ///*200*/ uint8 unknown200[16]; - ///*210*/ uint8 unknown210[16]; - ///*220*/ uint8 unknown220[16]; - ///*230*/ uint8 unknown230[16]; - ///*240*/ uint8 unknown240[16]; - ///*240*/ uint8 unknown240[16]; - ///*250*/ uint8 unknown250[16]; - ///*260*/ uint8 unknown260[16]; - ///*270*/ uint8 unknown270[16]; - ///*280*/ uint8 unknown280[16]; - ///*290*/ uint8 unknown290[16]; - ///*2a0*/ uint8 unknown2a0[16]; - ///*2b0*/ uint8 unknown2b0[16]; - ///*2c0*/ uint8 unknown2c0[16]; - ///*2d0*/ uint8 unknown2d0[16]; - ///*2e0*/ uint8 unknown2e0[16]; - ///*2f0*/ uint8 unknown2f0[16]; - ///*300*/ uint8 unknown300[16]; - ///*310*/ uint8 unknown310[16]; - ///*320*/ uint8 unknown320[16]; - ///*330*/ uint8 unknown330[16]; - ///*340*/ uint8 unknown340[16]; - ///*340*/ uint8 unknown340[16]; - ///*350*/ uint8 unknown350[16]; - ///*360*/ uint8 unknown360[16]; - ///*370*/ uint8 unknown370[16]; - ///*380*/ uint8 unknown380[16]; - ///*390*/ uint8 unknown390[16]; - ///*3a0*/ uint8 unknown3a0[16]; - ///*3b0*/ uint8 unknown3b0[16]; - ///*3c0*/ uint8 unknown3c0[16]; - ///*3d0*/ uint8 unknown3d0[16]; - ///*3e0*/ uint8 unknown3e0[16]; - ///*3f0*/ uint8 unknown3f0[16]; - ///*400*/ uint8 unknown400[16]; - ///*410*/ uint8 unknown410[16]; - ///*420*/ uint8 unknown420[16]; - ///*430*/ uint8 unknown430[16]; - ///*440*/ uint8 unknown440[16]; - ///*440*/ uint8 unknown440[16]; - ///*450*/ uint8 unknown450[16]; - ///*460*/ uint8 unknown460[16]; - ///*470*/ uint8 unknown470[16]; - ///*480*/ uint8 unknown480[16]; - ///*490*/ uint8 unknown490[16]; - ///*4a0*/ uint8 unknown4a0[16]; - ///*4b0*/ uint8 unknown4b0[16]; - ///*4c0*/ uint8 unknown4c0[16]; - ///*4d0*/ uint8 unknown4d0[16]; - ///*4e0*/ uint8 unknown4e0[16]; - ///*4f0*/ uint8 unknown4f0[16]; - ///*500*/ uint8 unknown500[16]; - ///*510*/ uint8 unknown510[16]; - ///*520*/ uint8 unknown520[16]; - ///*530*/ uint8 unknown530[16]; - ///*540*/ uint8 unknown540[16]; - ///*540*/ uint8 unknown540[16]; - ///*550*/ uint8 unknown550[16]; - ///*560*/ uint8 unknown560[16]; - ///*570*/ uint8 unknown570[16]; - ///*580*/ uint8 unknown580[16]; - ///*590*/ uint8 unknown590[16]; - ///*5a0*/ uint8 unknown5a0[12]; - ////Client reads this and sets it to 140e32ee2 - ////I've only ever seen 0, it affects some calculation in the client - ///*5ac*/ uint8 unknown5ac; - ///*5ad*/ uint8 unknown5ad[3]; - ///*5b0*/ uint8 unknown5b0[3]; - ////Client reads this and sets it to 140e3bf18 - ////seems to enable the voice system - ///*5b4*/ uint8 enable_voice_system; - ////Client reads this and sets it to 140e3bef0 - ////Seems to enable the mail system - ///*5b5*/ uint8 enable_mail; - ////Client reads this and sets it to 140e3bef1 - ////Not entirely sure what this does, it seems to be related to character create - ////I've only ever seen it set to 0 - ///*5b6*/ uint8 unknown5b6; - ///*5b7*/ uint8 unknown5b7; - ////Client reads this and sets it to 140e3bef2 - ////Not entirely sure what this does, it seems to be related to character create - ////Seen set to 1 - ///*5b8*/ uint8 unknown5b8; - ///*5b9*/ uint8 unknown5b9[8]; - ////Client sets this to 140e3beec - ////Seems to help enable beta buff command - ///*5c0*/ uint8 beta_buff_enabled; - ////Client sets this to 140e3beed - ////Guessing this is for marking as a beta server - ///*5c1*/ uint8 is_beta_server; - ////Client sets this to 140e3beee - ////Test sets this to one and all 3 of these need to be 1 to use /betabuff - ///*5c2*/ uint8 is_test_server; - ////Client sets this to 140e3beef - ////Client writes but doesn't read this anywhere as far as I can tell - ///*5c3*/ uint8 unknown5c3; - ///*5c4*/ uint8 unknown5c4[12]; - ///*5d0*/ uint8 unknown5d0[16]; - ///*5e0*/ uint8 unknown5e0[16]; - ///*5f0*/ uint8 unknown5f0[12]; - ////Client sets this to 140e3d5f4 - ////Not sure what this affects - ////I've seen ff ff ff ff passed - ///*5fc*/ int32 unknown5fc; - ////Client sets this to 140e3d5f4 - ////not sure what this does - ////Ive seen 0 on normal servers but 1 on test - ///*600*/ int32 unknown600; - ///*604*/ uint8 unknown604[12]; - ///*610*/ uint8 unknown610[16]; - ///*620*/ uint8 unknown620[16]; - ///*630*/ uint8 unknown630[16]; - ///*640*/ uint8 unknown640[16]; - ///*640*/ uint8 unknown640[16]; - ///*650*/ uint8 unknown650[16]; - ///*660*/ uint8 unknown660[16]; - ///*670*/ uint8 unknown670[16]; - ///*680*/ uint8 unknown680[16]; - ///*690*/ uint8 unknown690[16]; - ///*6a0*/ uint8 unknown6a0[16]; - ///*6b0*/ uint8 unknown6b0[16]; - ///*6c0*/ uint8 unknown6c0[16]; - ///*6d0*/ uint8 unknown6d0[16]; - ///*6e0*/ uint8 unknown6e0[16]; - ///*6f0*/ uint8 unknown6f0[16]; - ///*700*/ uint8 unknown700[16]; - ///*710*/ uint8 unknown710[14]; - ////Client reads this and sets it to 140e32ee2 - ////based on strings near related disassembly im guessing this controls - ////which kind of items are allowed (there are strings for heirloom, prestige, etc) - ////every server ive logged in so far has had this to 0 but I haven't tried a TLP yet - ///*71d*/ uint8 heirlooms_disabled; - ///*71e*/ uint8 unknown71e; - ///*720*/ uint8 unknown720[16]; - }; - struct ClientZoneEntry_Struct { /*00*/ uint32 unknown00; // ***Placeholder /*04*/ char char_name[64]; // Player firstname [32] From 0a9a941b6bd4fba2d8b863e146817b302f6045dc Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 23 Oct 2024 21:53:24 -0700 Subject: [PATCH 16/93] WIP on membership --- common/patches/larion.cpp | 49 +++++++++++++++++++++++++++++++++ common/patches/larion_ops.h | 2 ++ common/patches/larion_structs.h | 38 +++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index d5530eb7e..6997b7a7f 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -216,6 +216,55 @@ namespace Larion safe_delete(in); } + ENCODE(OP_SendMembership) { + ENCODE_LENGTH_EXACT(Membership_Struct); + SETUP_DIRECT_ENCODE(Membership_Struct, structs::Membership_Struct); + + eq->membership = emu->membership; + eq->races = emu->races; + eq->classes = emu->classes; + eq->entrysize = 33; + eq->entries[0] = -1; + eq->entries[1] = -1; + eq->entries[2] = -1; + eq->entries[3] = -1; + eq->entries[4] = -1; + eq->entries[5] = -1; + eq->entries[6] = 1; + eq->entries[7] = 1; + eq->entries[8] = 1; + eq->entries[9] = -1; + eq->entries[10] = 1; + eq->entries[11] = -1; + eq->entries[12] = -1; + eq->entries[13] = 1; + eq->entries[14] = 1; + eq->entries[15] = 1; + eq->entries[16] = 1; + eq->entries[17] = 1; + eq->entries[18] = 1; + eq->entries[19] = -1; + eq->entries[20] = -1; + eq->entries[21] = 0; + eq->entries[22] = 0; + eq->entries[23] = 0; + eq->entries[24] = 0; + eq->entries[25] = 0; + eq->entries[26] = 0; + eq->entries[27] = 0; + eq->entries[28] = 0; + eq->entries[29] = 0; + eq->entries[30] = 0; + eq->entries[31] = 0; + eq->entries[32] = 0; + + FINISH_ENCODE(); + } + + ENCODE(OP_SendMembershipDetails) { + + } + // DECODE methods } /*Larion*/ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 37b2db42c..0daf37f65 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -3,6 +3,8 @@ //list of packets we need to decode on the way in: E(OP_LogServer) +E(OP_SendMembership) +E(OP_SendMembershipDetails) #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index cd81f7915..e05d96240 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -17,6 +17,44 @@ namespace Larion { /*68*/ uint32 unknown68; /*72*/ uint32 unknown72; }; + + struct Membership_Struct + { + /*000*/ uint8 membership; //0 not gold, 2 gold + /*001*/ uint32 races; // Seen ff ff 01 00 + /*005*/ uint32 classes; // Seen ff ff 01 00 + /*009*/ uint32 entrysize; // Seen 33 + /*013*/ int32 entries[33]; // Most -1, 1, and 0 for Gold Status + /*145*/ + }; + + struct Membership_Entry_Struct + { + /*000*/ uint32 purchase_id; // Seen 1, then increments 90287 to 90300 + /*004*/ uint32 bitwise_entry; // Seen 16 to 65536 - Skips 4096 + /*008*/ + }; + + struct Membership_Setting_Struct + { + /*000*/ uint8 setting_index; // 0, 1, 2 or 3 + /*001*/ uint32 setting_id; // 0 to 23 + /*005*/ int32 setting_value; // All can be 0, 1, or -1 + /*009*/ + }; + + struct Membership_Details_Struct + { + /*000*/ uint32 membership_setting_count; // Seen 96 + /*004*/ Membership_Setting_Struct settings[96]; // 864 Bytes + /*364*/ uint32 race_entry_count; // Seen 17 + /*368*/ Membership_Entry_Struct membership_races[17]; // 136 Bytes + /*3f0*/ uint32 class_entry_count; // Seen 15 + /*3f4*/ Membership_Entry_Struct membership_classes[17]; // 136 Bytes + /*47c*/ uint32 exit_url_length; // Length of the exit_url string (0 for none) + /*480*/ //char exit_url[42]; // Upgrade to Silver or Gold Membership URL + }; + }; //end namespace structs }; //end namespace larion From dbb0288a13efd14582835fb2c7a0e6317ce163fc Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 24 Oct 2024 20:35:53 -0700 Subject: [PATCH 17/93] Filled out a lot of the version stuff for a new client version (oh my lawd this could be easier!) --- common/emu_versions.cpp | 103 +++++ common/emu_versions.h | 49 ++- common/eq_limits.cpp | 62 +++ common/eq_limits.h | 2 +- common/patches/larion.cpp | 640 ++++++++++++++++++++++++++++--- common/patches/larion_limits.cpp | 266 +++++++++++++ common/patches/larion_limits.h | 302 ++++++++++++++- common/patches/larion_ops.h | 1 + common/patches/larion_structs.h | 24 +- 9 files changed, 1379 insertions(+), 70 deletions(-) diff --git a/common/emu_versions.cpp b/common/emu_versions.cpp index ea8ddba73..ebde39ddf 100644 --- a/common/emu_versions.cpp +++ b/common/emu_versions.cpp @@ -56,6 +56,8 @@ const char* EQ::versions::ClientVersionName(ClientVersion client_version) return "RoF"; case ClientVersion::RoF2: return "RoF2"; + case ClientVersion::Larion: + return "Larion"; default: return "Invalid Version"; }; @@ -76,6 +78,8 @@ uint32 EQ::versions::ConvertClientVersionToClientVersionBit(ClientVersion client return bitRoF; case ClientVersion::RoF2: return bitRoF2; + case ClientVersion::Larion: + return bitLarion; default: return bitUnknown; } @@ -96,6 +100,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertClientVersionBitToClientVersion return ClientVersion::RoF; case ((uint32)1 << (static_cast(ClientVersion::RoF2) - 1)) : return ClientVersion::RoF2; + case ((uint32)1 << (static_cast(ClientVersion::Larion) - 1)): + return ClientVersion::Larion; default: return ClientVersion::Unknown; } @@ -184,6 +190,8 @@ const char* EQ::versions::MobVersionName(MobVersion mob_version) return "RoF"; case MobVersion::RoF2: return "RoF2"; + case MobVersion::Larion: + return "Larion"; case MobVersion::NPC: return "NPC"; case MobVersion::NPCMerchant: @@ -212,6 +220,8 @@ const char* EQ::versions::MobVersionName(MobVersion mob_version) return "Offline RoF"; case MobVersion::OfflineRoF2: return "Offline RoF2"; + case MobVersion::OfflineLarion: + return "Offline Larion"; default: return "Invalid Version"; }; @@ -235,6 +245,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertMobVersionToClientVersion(MobVe return ClientVersion::RoF; case MobVersion::RoF2: return ClientVersion::RoF2; + case MobVersion::Larion: + return ClientVersion::Larion; default: return ClientVersion::Unknown; } @@ -258,6 +270,8 @@ EQ::versions::MobVersion EQ::versions::ConvertClientVersionToMobVersion(ClientVe return MobVersion::RoF; case ClientVersion::RoF2: return MobVersion::RoF2; + case ClientVersion::Larion: + return MobVersion::Larion; default: return MobVersion::Unknown; } @@ -278,6 +292,8 @@ EQ::versions::MobVersion EQ::versions::ConvertPCMobVersionToOfflinePCMobVersion( return MobVersion::OfflineRoF; case MobVersion::RoF2: return MobVersion::OfflineRoF2; + case MobVersion::Larion: + return MobVersion::OfflineLarion; default: return MobVersion::Unknown; } @@ -298,6 +314,8 @@ EQ::versions::MobVersion EQ::versions::ConvertOfflinePCMobVersionToPCMobVersion( return MobVersion::RoF; case MobVersion::OfflineRoF2: return MobVersion::RoF2; + case MobVersion::OfflineLarion: + return MobVersion::Larion; default: return MobVersion::Unknown; } @@ -318,6 +336,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertOfflinePCMobVersionToClientVers return ClientVersion::RoF; case MobVersion::OfflineRoF2: return ClientVersion::RoF2; + case MobVersion::OfflineLarion: + return ClientVersion::Larion; default: return ClientVersion::Unknown; } @@ -338,6 +358,8 @@ EQ::versions::MobVersion EQ::versions::ConvertClientVersionToOfflinePCMobVersion return MobVersion::OfflineRoF; case ClientVersion::RoF2: return MobVersion::OfflineRoF2; + case ClientVersion::Larion: + return MobVersion::OfflineLarion; default: return MobVersion::Unknown; } @@ -388,6 +410,27 @@ const char* EQ::expansions::ExpansionName(Expansion expansion) return "Rain of Fear"; case Expansion::CotF: return "Call of the Forsaken"; + case Expansion::TDS: + return "The Darkened Sea"; + case Expansion::TBM: + return "The Broken Mirror"; + case Expansion::EoK: + return "Empires of Kunark"; + case Expansion::RoS: + return "Ring of Scale"; + case Expansion::TBL: + return "The Burning Lands"; + case Expansion::ToV: + return "Torment of Velious"; + case Expansion::CoV: + return "Claws of Veeshan"; + case Expansion::ToL: + return "Terror of Luclin"; + case Expansion::NoS: + return "Night of Shadows"; + case Expansion::LS: + return "Laurion's Song"; + default: return "Invalid Expansion"; } @@ -441,6 +484,26 @@ uint32 EQ::expansions::ConvertExpansionToExpansionBit(Expansion expansion) return bitRoF; case Expansion::CotF: return bitCotF; + case Expansion::TDS: + return bitTDS; + case Expansion::TBM: + return bitTBM; + case Expansion::EoK: + return bitEoK; + case Expansion::RoS: + return bitRoS; + case Expansion::TBL: + return bitTBL; + case Expansion::ToV: + return bitToV; + case Expansion::CoV: + return bitCoV; + case Expansion::ToL: + return bitToL; + case Expansion::NoS: + return bitNoS; + case Expansion::LS: + return bitLS; default: return bitEverQuest; } @@ -489,6 +552,26 @@ EQ::expansions::Expansion EQ::expansions::ConvertExpansionBitToExpansion(uint32 return Expansion::RoF; case bitCotF: return Expansion::CotF; + case bitTDS: + return Expansion::TDS; + case bitTBM: + return Expansion::TBM; + case bitEoK: + return Expansion::EoK; + case bitRoS: + return Expansion::RoS; + case bitTBL: + return Expansion::TBL; + case bitToV: + return Expansion::ToV; + case bitCoV: + return Expansion::CoV; + case bitToL: + return Expansion::ToL; + case bitNoS: + return Expansion::NoS; + case bitLS: + return Expansion::LS; default: return Expansion::EverQuest; } @@ -537,6 +620,26 @@ uint32 EQ::expansions::ConvertExpansionToExpansionsMask(Expansion expansion) return maskRoF; case Expansion::CotF: return maskCotF; + case Expansion::TDS: + return maskTDS; + case Expansion::TBM: + return maskTBM; + case Expansion::EoK: + return maskEoK; + case Expansion::RoS: + return maskRoS; + case Expansion::TBL: + return maskTBL; + case Expansion::ToV: + return maskToV; + case Expansion::CoV: + return maskCoV; + case Expansion::ToL: + return maskToL; + case Expansion::NoS: + return maskNoS; + case Expansion::LS: + return maskLS; default: return maskEverQuest; } diff --git a/common/emu_versions.h b/common/emu_versions.h index 16315cf3f..4c36ce39c 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -49,6 +49,7 @@ namespace EQ bitUF = 0x00000010, bitRoF = 0x00000020, bitRoF2 = 0x00000040, + bitLarion = 0x00000080, maskUnknown = 0x00000000, maskTitaniumAndEarlier = 0x00000003, maskSoFAndEarlier = 0x00000007, @@ -60,6 +61,7 @@ namespace EQ maskUFAndLater = 0xFFFFFFF0, maskRoFAndLater = 0xFFFFFFE0, maskRoF2AndLater = 0xFFFFFFC0, + maskLarionAndLater = 0xFFFFFF80, maskAllClients = 0xFFFFFFFF }; @@ -81,6 +83,7 @@ namespace EQ UF, RoF, RoF2, + Larion, NPC, NPCMerchant, Merc, @@ -94,13 +97,14 @@ namespace EQ OfflineSoD, OfflineUF, OfflineRoF, - OfflineRoF2 + OfflineRoF2, + OfflineLarion }; - const MobVersion LastMobVersion = MobVersion::OfflineRoF2; - const MobVersion LastPCMobVersion = MobVersion::RoF2; + const MobVersion LastMobVersion = MobVersion::OfflineLarion; + const MobVersion LastPCMobVersion = MobVersion::Larion; const MobVersion LastNonPCMobVersion = MobVersion::BotPet; - const MobVersion LastOfflinePCMobVersion = MobVersion::OfflineRoF2; + const MobVersion LastOfflinePCMobVersion = MobVersion::OfflineLarion; const size_t MobVersionCount = (static_cast(LastMobVersion) + 1); bool IsValidMobVersion(MobVersion mob_version); @@ -132,7 +136,8 @@ namespace EQ ucsSoDCombined = 'D', ucsUFCombined = 'E', ucsRoFCombined = 'F', - ucsRoF2Combined = 'G' + ucsRoF2Combined = 'G', + ucsLaurionCombined = 'G' }; } /*versions*/ @@ -159,7 +164,17 @@ namespace EQ HoT, VoA, RoF, - CotF + CotF, + TDS, + TBM, + EoK, + RoS, + TBL, + ToV, + CoV, + ToL, + NoS, + LS }; enum ExpansionBitmask : uint32 { @@ -184,6 +199,16 @@ namespace EQ bitVoA = 0x00020000, bitRoF = 0x00040000, bitCotF = 0x00080000, + bitTDS = 0x00100000, + bitTBM = 0x00200000, + bitEoK = 0x00400000, + bitRoS = 0x00800000, + bitTBL = 0x01000000, + bitToV = 0x02000000, + bitCoV = 0x04000000, + bitToL = 0x08000000, + bitNoS = 0x10000000, + bitLS = 0x20000000, maskEverQuest = 0x00000000, maskRoK = 0x00000001, maskSoV = 0x00000003, @@ -204,7 +229,17 @@ namespace EQ maskHoT = 0x0001FFFF, maskVoA = 0x0003FFFF, maskRoF = 0x0007FFFF, - maskCotF = 0x000FFFFF + maskCotF = 0x000FFFFF, + maskTDS = 0x001FFFFF, + maskTBM = 0x003FFFFF, + maskEoK = 0x007FFFFF, + maskRoS = 0x00FFFFFF, + maskTBL = 0x01FFFFFF, + maskToV = 0x03FFFFFF, + maskCoV = 0x07FFFFFF, + maskToL = 0x0FFFFFFF, + maskNoS = 0x1FFFFFFF, + maskLS = 0x3FFFFFFF, }; const char* ExpansionName(Expansion expansion); diff --git a/common/eq_limits.cpp b/common/eq_limits.cpp index 051857681..14486b4f0 100644 --- a/common/eq_limits.cpp +++ b/common/eq_limits.cpp @@ -370,6 +370,33 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers RoF2::inventory::ConcatenateInvTypeLimbo, RoF2::inventory::AllowOverLevelEquipment ), + /*[MobVersion::LS] =*/ + EQ::inventory::LookupEntry( + EQ::inventory::LookupEntry::InventoryTypeSize_Struct( + EQ::invtype::POSSESSIONS_SIZE, RoF2::invtype::BANK_SIZE, RoF2::invtype::SHARED_BANK_SIZE, + RoF2::invtype::TRADE_SIZE, RoF2::invtype::WORLD_SIZE, RoF2::invtype::LIMBO_SIZE, + RoF2::invtype::TRIBUTE_SIZE, RoF2::invtype::TROPHY_TRIBUTE_SIZE, RoF2::invtype::GUILD_TRIBUTE_SIZE, + RoF2::invtype::MERCHANT_SIZE, RoF2::invtype::DELETED_SIZE, RoF2::invtype::CORPSE_SIZE, + RoF2::invtype::BAZAAR_SIZE, RoF2::invtype::INSPECT_SIZE, RoF2::invtype::REAL_ESTATE_SIZE, + RoF2::invtype::VIEW_MOD_PC_SIZE, RoF2::invtype::VIEW_MOD_BANK_SIZE, RoF2::invtype::VIEW_MOD_SHARED_BANK_SIZE, + RoF2::invtype::VIEW_MOD_LIMBO_SIZE, RoF2::invtype::ALT_STORAGE_SIZE, RoF2::invtype::ARCHIVED_SIZE, + RoF2::invtype::MAIL_SIZE, RoF2::invtype::GUILD_TROPHY_TRIBUTE_SIZE, RoF2::invtype::KRONO_SIZE, + RoF2::invtype::OTHER_SIZE + ), + + RoF2::invslot::EQUIPMENT_BITMASK, + RoF2::invslot::GENERAL_BITMASK, + RoF2::invslot::CURSOR_BITMASK, + RoF2::invslot::POSSESSIONS_BITMASK, + RoF2::invslot::CORPSE_BITMASK, + RoF2::invbag::SLOT_COUNT, + RoF2::invaug::SOCKET_COUNT, + + RoF2::inventory::AllowEmptyBagInBag, + RoF2::inventory::AllowClickCastFromBag, + RoF2::inventory::ConcatenateInvTypeLimbo, + RoF2::inventory::AllowOverLevelEquipment + ), /*[MobVersion::NPC] =*/ EQ::inventory::LookupEntry( EQ::inventory::LookupEntry::InventoryTypeSize_Struct( @@ -743,6 +770,33 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers RoF2::invbag::SLOT_COUNT, RoF2::invaug::SOCKET_COUNT, + false, + false, + false, + false + ), + /*[MobVersion::OfflineLS] =*/ + EQ::inventory::LookupEntry( + EQ::inventory::LookupEntry::InventoryTypeSize_Struct( + RoF2::INULL, RoF2::INULL, RoF2::INULL, + RoF2::invtype::TRADE_SIZE, RoF2::INULL, RoF2::INULL, + RoF2::INULL, RoF2::INULL, RoF2::INULL, + RoF2::invtype::MERCHANT_SIZE, RoF2::INULL, RoF2::INULL, + RoF2::invtype::BAZAAR_SIZE, RoF2::invtype::INSPECT_SIZE, RoF2::INULL, + RoF2::invtype::VIEW_MOD_PC_SIZE, RoF2::invtype::VIEW_MOD_BANK_SIZE, RoF2::invtype::VIEW_MOD_SHARED_BANK_SIZE, + RoF2::invtype::VIEW_MOD_LIMBO_SIZE, RoF2::INULL, RoF2::INULL, + RoF2::INULL, RoF2::INULL, RoF2::INULL, + RoF2::INULL + ), + + RoF2::INULL, + RoF2::INULL, + RoF2::INULL, + RoF2::INULL, + RoF2::INULL, + RoF2::invbag::SLOT_COUNT, + RoF2::invaug::SOCKET_COUNT, + false, false, false, @@ -994,6 +1048,10 @@ static const EQ::behavior::LookupEntry behavior_static_lookup_entries[EQ::versio EQ::behavior::LookupEntry( RoF2::behavior::CoinHasWeight ), + /*[MobVersion::LS] =*/ + EQ::behavior::LookupEntry( + RoF2::behavior::CoinHasWeight + ), /*[MobVersion::NPC] =*/ EQ::behavior::LookupEntry( EQ::behavior::CoinHasWeight @@ -1047,6 +1105,10 @@ static const EQ::behavior::LookupEntry behavior_static_lookup_entries[EQ::versio RoF::behavior::CoinHasWeight ), /*[MobVersion::OfflineRoF2] =*/ + EQ::behavior::LookupEntry( + RoF2::behavior::CoinHasWeight + ), + /*[MobVersion::OfflineLS] =*/ EQ::behavior::LookupEntry( RoF2::behavior::CoinHasWeight ) diff --git a/common/eq_limits.h b/common/eq_limits.h index 40b095b15..e1e20aec6 100644 --- a/common/eq_limits.h +++ b/common/eq_limits.h @@ -29,7 +29,7 @@ #include "../common/patches/uf_limits.h" #include "../common/patches/rof_limits.h" #include "../common/patches/rof2_limits.h" - +#include "../common/patches/larion_limits.h" namespace EQ { diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 6997b7a7f..9b49c961b 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -126,94 +126,89 @@ namespace Larion // ENCODE methods ENCODE(OP_LogServer) { - EQApplicationPacket* in = *p; - *p = nullptr; - LogServer_Struct* emu = (LogServer_Struct*)in->pBuffer; - - auto outapp = new EQApplicationPacket(OP_LogServer, 1840); - auto buffer = outapp->pBuffer; + SETUP_VAR_ENCODE(LogServer_Struct); + ALLOC_LEN_ENCODE(1840); //pvp if (emu->enable_pvp) { - *(char*)&buffer[0x04] = 1; + *(char*)&__packet->pBuffer[0x04] = 1; } if (emu->enable_FV) { //FV sets these both to 1 //one appears to enable the no drop flag the other just marks the server as special? - *(char*)&buffer[0x08] = 1; - *(char*)&buffer[0x0a] = 1; + *(char*)&__packet->pBuffer[0x08] = 1; + *(char*)&__packet->pBuffer[0x0a] = 1; } //This has something to do with heirloom and prestige items but im not sure what it does //Seems to sit at 0 - *(char*)&buffer[0x71d] = 0; + *(char*)&__packet->pBuffer[0x71d] = 0; //not sure what this does, something to do with server select - *(char*)&buffer[0x09] = 0; + *(char*)&__packet->pBuffer[0x09] = 0; //this appears to have some effect on the tradeskill system; disabling made by tags perhaps? - *(char*)&buffer[0x0b] = 0; + *(char*)&__packet->pBuffer[0x0b] = 0; //not sure, setting it to the value ive seen - *(char*)&buffer[0x0c] = 1; + *(char*)&__packet->pBuffer[0x0c] = 1; //Something to do with languages - *(char*)&buffer[0x0d] = 1; + *(char*)&__packet->pBuffer[0x0d] = 1; //These seem to affect if server has betabuff enabled - *(char*)&buffer[0x5c0] = 0; - *(char*)&buffer[0x5c1] = 0; + *(char*)&__packet->pBuffer[0x5c0] = 0; + *(char*)&__packet->pBuffer[0x5c1] = 0; //This is set on test so it's probably indicating this is a test server - *(char*)&buffer[0x5c2] = 0; + *(char*)&__packet->pBuffer[0x5c2] = 0; //not sure, but it's grouped with the beta and test stuff - *(char*)&buffer[0x5c3] = 0; + *(char*)&__packet->pBuffer[0x5c3] = 0; //world short name - strncpy((char*)&buffer[0x15], emu->worldshortname, 32); + strncpy((char*)&__packet->pBuffer[0x15], emu->worldshortname, 32); //not sure, affects some player calculation but didn't care to look more - *(char*)&buffer[0x5c2] = 0; + *(char*)&__packet->pBuffer[0x5c2] = 0; //Looks right if (emu->enablemail) { - *(char*)&buffer[0x5b5] = 1; + *(char*)&__packet->pBuffer[0x5b5] = 1; } //Looks right if (emu->enablevoicemacros) { - *(char*)&buffer[0x5b4] = 1; + *(char*)&__packet->pBuffer[0x5b4] = 1; } //Not sure, sending what we've seen - *(char*)&buffer[0x5b6] = 0; + *(char*)&__packet->pBuffer[0x5b6] = 0; //Not sure sending what we've seen - *(char*)&buffer[0x5b8] = 1; + *(char*)&__packet->pBuffer[0x5b8] = 1; //Not sure sending what we've seen - *(int32_t*)&buffer[0x5fc] = -1; + *(int32_t*)&__packet->pBuffer[0x5fc] = -1; //Test sets this to 1, everyone else seems to set it to 0 - *(int32_t*)&buffer[0x600] = 0; + *(int32_t*)&__packet->pBuffer[0x600] = 0; //Disassembly puts it next to code dealing with commands, ive not seen anyone send anything but 0 - *(char*)&buffer[0x705] = 0; + *(char*)&__packet->pBuffer[0x705] = 0; //Something about item restrictions, seems to always be set to 1 - *(char*)&buffer[0x710] = 0; + *(char*)&__packet->pBuffer[0x710] = 0; //This and 0x724 are often multiplied together in guild favor calcs, live and test send 1.0f - *(float*)&buffer[0x720] = 1.0f; - *(float*)&buffer[0x724] = 1.0f; + *(float*)&__packet->pBuffer[0x720] = 1.0f; + *(float*)&__packet->pBuffer[0x724] = 1.0f; //This and 0x72c are often multiplied together in non-guild favor calcs, live and test send 1.0f - *(float*)&buffer[0x728] = 1.0f; - *(float*)&buffer[0x72c] = 1.0f; + *(float*)&__packet->pBuffer[0x728] = 1.0f; + *(float*)&__packet->pBuffer[0x72c] = 1.0f; - dest->FastQueuePacket(&outapp); - safe_delete(in); + FINISH_ENCODE(); } ENCODE(OP_SendMembership) { @@ -224,31 +219,31 @@ namespace Larion eq->races = emu->races; eq->classes = emu->classes; eq->entrysize = 33; - eq->entries[0] = -1; - eq->entries[1] = -1; - eq->entries[2] = -1; - eq->entries[3] = -1; - eq->entries[4] = -1; - eq->entries[5] = -1; - eq->entries[6] = 1; - eq->entries[7] = 1; - eq->entries[8] = 1; - eq->entries[9] = -1; - eq->entries[10] = 1; - eq->entries[11] = -1; - eq->entries[12] = -1; - eq->entries[13] = 1; - eq->entries[14] = 1; - eq->entries[15] = 1; - eq->entries[16] = 1; - eq->entries[17] = 1; - eq->entries[18] = 1; - eq->entries[19] = -1; - eq->entries[20] = -1; + eq->entries[0] = -1; // Max AA Restriction + eq->entries[1] = -1; // Max Level Restriction + eq->entries[2] = -1; // Max Char Slots per Account (not used by client?) + eq->entries[3] = -1; // 1 for Silver + eq->entries[4] = -1; // Main Inventory Size + eq->entries[5] = -1; // Max Platinum per level + eq->entries[6] = 1; // Send Mail + eq->entries[7] = 1; // Use Parcels? + eq->entries[8] = 1; // Voice Chat + eq->entries[9] = -1; // Merc Tiers + eq->entries[10] = 1; // Create Guilds + eq->entries[11] = -1; // Shared Bank Slots + eq->entries[12] = -1; // Max Journal Quests + eq->entries[13] = 1; // Housing Enabled + eq->entries[14] = 1; // Prestiege + eq->entries[15] = 1; // Broker System + eq->entries[16] = 1; // Chat + eq->entries[17] = 1; // Progression Server Access + eq->entries[18] = 1; // Customer Support + eq->entries[19] = -1; // Popup reminders? + eq->entries[20] = -1; // Exit Popup? eq->entries[21] = 0; eq->entries[22] = 0; - eq->entries[23] = 0; - eq->entries[24] = 0; + eq->entries[23] = 0; // This is the highest we actually see in detail entries + eq->entries[24] = 0; eq->entries[25] = 0; eq->entries[26] = 0; eq->entries[27] = 0; @@ -262,7 +257,538 @@ namespace Larion } ENCODE(OP_SendMembershipDetails) { + SETUP_VAR_ENCODE(Membership_Details_Struct); + SerializeBuffer buffer; + // Generated via script; we should put this in a better format later + // count + buffer.WriteUInt32(96); + + // settings + buffer.WriteUInt8(0); + buffer.WriteUInt32(0); + buffer.WriteInt32(250); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(0); + buffer.WriteInt32(1000); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(1); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(1); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(2); + buffer.WriteInt32(2); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(0); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(0); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(2); + buffer.WriteInt32(4); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(3); + buffer.WriteInt32(1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(1); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(1); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(3); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(4); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(2); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(2); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(4); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(5); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(3); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(3); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(5); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(6); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(4); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(4); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(6); + buffer.WriteInt32(0); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(7); + buffer.WriteInt32(1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(5); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(5); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(7); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(8); + buffer.WriteInt32(1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(6); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(6); + buffer.WriteInt32(1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(8); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(9); + buffer.WriteInt32(5); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(7); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(7); + buffer.WriteInt32(1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(9); + buffer.WriteInt32(5); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(10); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(8); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(8); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(11); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(10); + buffer.WriteInt32(1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(9); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(9); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(12); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(11); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(10); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(10); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(13); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(12); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(11); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(11); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(14); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(13); + buffer.WriteInt32(1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(12); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(12); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(15); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(14); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(13); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(13); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(16); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(15); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(14); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(14); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(17); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(16); + buffer.WriteInt32(1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(15); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(15); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(18); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(17); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(16); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(16); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(19); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(18); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(17); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(17); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(20); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(19); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(18); + buffer.WriteInt32(1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(18); + buffer.WriteInt32(1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(21); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(20); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(19); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(19); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(22); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(21); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(20); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(20); + buffer.WriteInt32(-1); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(21); + buffer.WriteInt32(0); + + buffer.WriteUInt8(0); + buffer.WriteUInt32(23); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(22); + buffer.WriteInt32(0); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(21); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(22); + buffer.WriteInt32(0); + + buffer.WriteUInt8(1); + buffer.WriteUInt32(23); + buffer.WriteInt32(0); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(22); + buffer.WriteInt32(0); + + buffer.WriteUInt8(2); + buffer.WriteUInt32(23); + buffer.WriteInt32(0); + + buffer.WriteUInt8(3); + buffer.WriteUInt32(23); + buffer.WriteInt32(0); + + // race count + buffer.WriteUInt32(17); + + // races + buffer.WriteUInt32(1); + buffer.WriteUInt32(131071); + + buffer.WriteUInt32(333); + buffer.WriteUInt32(131071); + + buffer.WriteUInt32(90287); + buffer.WriteUInt32(131071); + + buffer.WriteUInt32(90289); + buffer.WriteUInt32(16); + + buffer.WriteUInt32(90290); + buffer.WriteUInt32(32); + + buffer.WriteUInt32(90291); + buffer.WriteUInt32(64); + + buffer.WriteUInt32(90292); + buffer.WriteUInt32(128); + + buffer.WriteUInt32(90293); + buffer.WriteUInt32(256); + + buffer.WriteUInt32(90294); + buffer.WriteUInt32(512); + + buffer.WriteUInt32(90295); + buffer.WriteUInt32(1024); + + buffer.WriteUInt32(90296); + buffer.WriteUInt32(2048); + + buffer.WriteUInt32(90297); + buffer.WriteUInt32(8192); + + buffer.WriteUInt32(90298); + buffer.WriteUInt32(16384); + + buffer.WriteUInt32(90299); + buffer.WriteUInt32(32768); + + buffer.WriteUInt32(90300); + buffer.WriteUInt32(65536); + + buffer.WriteUInt32(2012271); + buffer.WriteUInt32(131071); + + buffer.WriteUInt32(2012277); + buffer.WriteUInt32(131071); + + // classes count + buffer.WriteUInt32(17); + + // classes + buffer.WriteUInt32(1); + buffer.WriteUInt32(131071); + + buffer.WriteUInt32(333); + buffer.WriteUInt32(131071); + + buffer.WriteUInt32(90287); + buffer.WriteUInt32(131071); + + buffer.WriteUInt32(90301); + buffer.WriteUInt32(8); + + buffer.WriteUInt32(90302); + buffer.WriteUInt32(16); + + buffer.WriteUInt32(90303); + buffer.WriteUInt32(32); + + buffer.WriteUInt32(90304); + buffer.WriteUInt32(64); + + buffer.WriteUInt32(90305); + buffer.WriteUInt32(128); + + buffer.WriteUInt32(90306); + buffer.WriteUInt32(256); + + buffer.WriteUInt32(90307); + buffer.WriteUInt32(1024); + + buffer.WriteUInt32(90308); + buffer.WriteUInt32(2048); + + buffer.WriteUInt32(90309); + buffer.WriteUInt32(8192); + + buffer.WriteUInt32(90310); + buffer.WriteUInt32(16384); + + buffer.WriteUInt32(90311); + buffer.WriteUInt32(32768); + + buffer.WriteUInt32(90312); + buffer.WriteUInt32(65536); + + buffer.WriteUInt32(2012271); + buffer.WriteUInt32(131071); + + buffer.WriteUInt32(2012277); + buffer.WriteUInt32(131071); + + // exit string length + buffer.WriteUInt32(0); + + __packet->size = buffer.size(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, buffer.buffer(), __packet->size); + FINISH_ENCODE(); + } + + ENCODE(OP_SendMaxCharacters) { + ENCODE_LENGTH_EXACT(MaxCharacters_Struct); + SETUP_DIRECT_ENCODE(MaxCharacters_Struct, structs::MaxCharacters_Struct); + + //OUT(max_chars); + eq->max_chars = 8; //needs to be fixed + eq->marketplace_chars = 0; + eq->unknown008 = -1; + eq->unknown00c = 196608; + eq->unknown010 = 0; + eq->unknown014 = 0; + eq->unknown018 = 0; + eq->unknown01c = 0; + eq->unknown020 = -1; + eq->unknown024 = 0; + eq->unknown028 = 0; + eq->unknown02c = 0; + eq->unknown030 = 0; + eq->unknown034 = 0; + + FINISH_ENCODE(); } // DECODE methods diff --git a/common/patches/larion_limits.cpp b/common/patches/larion_limits.cpp index 273e74a5c..49ea7a396 100644 --- a/common/patches/larion_limits.cpp +++ b/common/patches/larion_limits.cpp @@ -16,3 +16,269 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "larion_limits.h" + +#include "../strings.h" + + +int16 Larion::invtype::GetInvTypeSize(int16 inv_type) +{ + switch (inv_type) { + case invtype::typePossessions: + return invtype::POSSESSIONS_SIZE; + case invtype::typeBank: + return invtype::BANK_SIZE; + case invtype::typeSharedBank: + return invtype::SHARED_BANK_SIZE; + case invtype::typeTrade: + return invtype::TRADE_SIZE; + case invtype::typeWorld: + return invtype::WORLD_SIZE; + case invtype::typeLimbo: + return invtype::LIMBO_SIZE; + case invtype::typeTribute: + return invtype::TRIBUTE_SIZE; + case invtype::typeTrophyTribute: + return invtype::TROPHY_TRIBUTE_SIZE; + case invtype::typeGuildTribute: + return invtype::GUILD_TRIBUTE_SIZE; + case invtype::typeMerchant: + return invtype::MERCHANT_SIZE; + case invtype::typeDeleted: + return invtype::DELETED_SIZE; + case invtype::typeCorpse: + return invtype::CORPSE_SIZE; + case invtype::typeBazaar: + return invtype::BAZAAR_SIZE; + case invtype::typeInspect: + return invtype::INSPECT_SIZE; + case invtype::typeRealEstate: + return invtype::REAL_ESTATE_SIZE; + case invtype::typeViewMODPC: + return invtype::VIEW_MOD_PC_SIZE; + case invtype::typeViewMODBank: + return invtype::VIEW_MOD_BANK_SIZE; + case invtype::typeViewMODSharedBank: + return invtype::VIEW_MOD_SHARED_BANK_SIZE; + case invtype::typeViewMODLimbo: + return invtype::VIEW_MOD_LIMBO_SIZE; + case invtype::typeAltStorage: + return invtype::ALT_STORAGE_SIZE; + case invtype::typeArchived: + return invtype::ARCHIVED_SIZE; + case invtype::typeMail: + return invtype::MAIL_SIZE; + case invtype::typeGuildTrophyTribute: + return invtype::GUILD_TROPHY_TRIBUTE_SIZE; + case invtype::typeKrono: + return invtype::KRONO_SIZE; + case invtype::typeOther: + return invtype::OTHER_SIZE; + default: + return INULL; + } +} + +const char* Larion::invtype::GetInvTypeName(int16 inv_type) +{ + switch (inv_type) { + case invtype::TYPE_INVALID: + return "Invalid Type"; + case invtype::typePossessions: + return "Possessions"; + case invtype::typeBank: + return "Bank"; + case invtype::typeSharedBank: + return "Shared Bank"; + case invtype::typeTrade: + return "Trade"; + case invtype::typeWorld: + return "World"; + case invtype::typeLimbo: + return "Limbo"; + case invtype::typeTribute: + return "Tribute"; + case invtype::typeTrophyTribute: + return "Trophy Tribute"; + case invtype::typeGuildTribute: + return "Guild Tribute"; + case invtype::typeMerchant: + return "Merchant"; + case invtype::typeDeleted: + return "Deleted"; + case invtype::typeCorpse: + return "Corpse"; + case invtype::typeBazaar: + return "Bazaar"; + case invtype::typeInspect: + return "Inspect"; + case invtype::typeRealEstate: + return "Real Estate"; + case invtype::typeViewMODPC: + return "View MOD PC"; + case invtype::typeViewMODBank: + return "View MOD Bank"; + case invtype::typeViewMODSharedBank: + return "View MOD Shared Bank"; + case invtype::typeViewMODLimbo: + return "View MOD Limbo"; + case invtype::typeAltStorage: + return "Alt Storage"; + case invtype::typeArchived: + return "Archived"; + case invtype::typeMail: + return "Mail"; + case invtype::typeGuildTrophyTribute: + return "Guild Trophy Tribute"; + case invtype::typeKrono: + return "Krono"; + case invtype::typeOther: + return "Other"; + default: + return "Unknown Type"; + } +} + +bool Larion::invtype::IsInvTypePersistent(int16 inv_type) +{ + switch (inv_type) { + case invtype::typePossessions: + case invtype::typeBank: + case invtype::typeSharedBank: + case invtype::typeTrade: + case invtype::typeWorld: + case invtype::typeLimbo: + case invtype::typeTribute: + case invtype::typeTrophyTribute: + case invtype::typeGuildTribute: + return true; + default: + return false; + } +} + +const char* Larion::invslot::GetInvPossessionsSlotName(int16 inv_slot) +{ + switch (inv_slot) { + case invslot::SLOT_INVALID: + return "Invalid Slot"; + case invslot::slotCharm: + return "Charm"; + case invslot::slotEar1: + return "Ear 1"; + case invslot::slotHead: + return "Head"; + case invslot::slotFace: + return "Face"; + case invslot::slotEar2: + return "Ear 2"; + case invslot::slotNeck: + return "Neck"; + case invslot::slotShoulders: + return "Shoulders"; + case invslot::slotArms: + return "Arms"; + case invslot::slotBack: + return "Back"; + case invslot::slotWrist1: + return "Wrist 1"; + case invslot::slotWrist2: + return "Wrist 2"; + case invslot::slotRange: + return "Range"; + case invslot::slotHands: + return "Hands"; + case invslot::slotPrimary: + return "Primary"; + case invslot::slotSecondary: + return "Secondary"; + case invslot::slotFinger1: + return "Finger 1"; + case invslot::slotFinger2: + return "Finger 2"; + case invslot::slotChest: + return "Chest"; + case invslot::slotLegs: + return "Legs"; + case invslot::slotFeet: + return "Feet"; + case invslot::slotWaist: + return "Waist"; + case invslot::slotPowerSource: + return "Power Source"; + case invslot::slotAmmo: + return "Ammo"; + case invslot::slotGeneral1: + return "General 1"; + case invslot::slotGeneral2: + return "General 2"; + case invslot::slotGeneral3: + return "General 3"; + case invslot::slotGeneral4: + return "General 4"; + case invslot::slotGeneral5: + return "General 5"; + case invslot::slotGeneral6: + return "General 6"; + case invslot::slotGeneral7: + return "General 7"; + case invslot::slotGeneral8: + return "General 8"; + case invslot::slotGeneral9: + return "General 9"; + case invslot::slotGeneral10: + return "General 10"; + case invslot::slotCursor: + return "Cursor"; + default: + return "Unknown Slot"; + } +} + +const char* Larion::invslot::GetInvSlotName(int16 inv_type, int16 inv_slot) +{ + if (inv_type == invtype::typePossessions) + return invslot::GetInvPossessionsSlotName(inv_slot); + + int16 type_size = invtype::GetInvTypeSize(inv_type); + + if (!type_size || inv_slot == invslot::SLOT_INVALID) + return "Invalid Slot"; + + if ((inv_slot + 1) >= type_size) + return "Unknown Slot"; + + static std::string ret_str; + ret_str = StringFormat("Slot %i", (inv_slot + 1)); + + return ret_str.c_str(); +} + +const char* Larion::invbag::GetInvBagIndexName(int16 bag_index) +{ + if (bag_index == invbag::SLOT_INVALID) + return "Invalid Bag"; + + if (bag_index >= invbag::SLOT_COUNT) + return "Unknown Bag"; + + static std::string ret_str; + ret_str = StringFormat("Bag %i", (bag_index + 1)); + + return ret_str.c_str(); +} + +const char* Larion::invaug::GetInvAugIndexName(int16 aug_index) +{ + if (aug_index == invaug::SOCKET_INVALID) + return "Invalid Augment"; + + if (aug_index >= invaug::SOCKET_COUNT) + return "Unknown Augment"; + + static std::string ret_str; + ret_str = StringFormat("Augment %i", (aug_index + 1)); + + return ret_str.c_str(); +} diff --git a/common/patches/larion_limits.h b/common/patches/larion_limits.h index 2ac784c91..6d3a1ddb0 100644 --- a/common/patches/larion_limits.h +++ b/common/patches/larion_limits.h @@ -20,11 +20,309 @@ #ifndef COMMON_LARION_LIMITS_H #define COMMON_LARION_LIMITS_H +#include "../types.h" +#include "../emu_versions.h" +#include "../skills.h" namespace Larion { - - // put constants here and #include appropriately + const int16 IINVALID = -1; + const int16 INULL = 0; + + namespace inventory { + inline EQ::versions::ClientVersion GetInventoryRef() { return EQ::versions::ClientVersion::Larion; } + + const bool ConcatenateInvTypeLimbo = false; + + const bool AllowOverLevelEquipment = true; + + const bool AllowEmptyBagInBag = true; + const bool AllowClickCastFromBag = true; + + } /*inventory*/ + + namespace invtype { + inline EQ::versions::ClientVersion GetInvTypeRef() { return EQ::versions::ClientVersion::Larion; } + + namespace enum_ { + enum InventoryTypes : int16 { + typePossessions = INULL, + typeBank, + typeSharedBank, + typeTrade, + typeWorld, + typeLimbo, + typeTribute, + typeTrophyTribute, + typeGuildTribute, + typeMerchant, + typeDeleted, + typeCorpse, + typeBazaar, + typeInspect, + typeRealEstate, + typeViewMODPC, + typeViewMODBank, + typeViewMODSharedBank, + typeViewMODLimbo, + typeAltStorage, + typeArchived, + typeMail, + typeGuildTrophyTribute, + typeKrono, + typeOther + }; + + } // namespace enum_ + using namespace enum_; + + const int16 POSSESSIONS_SIZE = 34; + const int16 BANK_SIZE = 24; + const int16 SHARED_BANK_SIZE = 2; + const int16 TRADE_SIZE = 8; + const int16 WORLD_SIZE = 10; + const int16 LIMBO_SIZE = 36; + const int16 TRIBUTE_SIZE = 5; + const int16 TROPHY_TRIBUTE_SIZE = 0;//unknown + const int16 GUILD_TRIBUTE_SIZE = 2;//unverified + const int16 MERCHANT_SIZE = 200; + const int16 DELETED_SIZE = 0;//unknown - "Recovery Tab" + const int16 CORPSE_SIZE = POSSESSIONS_SIZE; + const int16 BAZAAR_SIZE = 200; + const int16 INSPECT_SIZE = 23; + const int16 REAL_ESTATE_SIZE = 0;//unknown + const int16 VIEW_MOD_PC_SIZE = POSSESSIONS_SIZE; + const int16 VIEW_MOD_BANK_SIZE = BANK_SIZE; + const int16 VIEW_MOD_SHARED_BANK_SIZE = SHARED_BANK_SIZE; + const int16 VIEW_MOD_LIMBO_SIZE = LIMBO_SIZE; + const int16 ALT_STORAGE_SIZE = 0;//unknown - "Shroud Bank" + const int16 ARCHIVED_SIZE = 0;//unknown + const int16 MAIL_SIZE = 0;//unknown + const int16 GUILD_TROPHY_TRIBUTE_SIZE = 0;//unknown + const int16 KRONO_SIZE = 0;//unknown + const int16 OTHER_SIZE = 0;//unknown + + const int16 TRADE_NPC_SIZE = 4; // defined by implication + + const int16 TYPE_INVALID = IINVALID; + const int16 TYPE_BEGIN = typePossessions; + const int16 TYPE_END = typeOther; + const int16 TYPE_COUNT = (TYPE_END - TYPE_BEGIN) + 1; + + int16 GetInvTypeSize(int16 inv_type); + const char* GetInvTypeName(int16 inv_type); + + bool IsInvTypePersistent(int16 inv_type); + + } /*invtype*/ + + namespace invslot { + inline EQ::versions::ClientVersion GetInvSlotRef() { return EQ::versions::ClientVersion::Larion; } + + namespace enum_ { + enum InventorySlots : int16 { + slotCharm = INULL, + slotEar1, + slotHead, + slotFace, + slotEar2, + slotNeck, + slotShoulders, + slotArms, + slotBack, + slotWrist1, + slotWrist2, + slotRange, + slotHands, + slotPrimary, + slotSecondary, + slotFinger1, + slotFinger2, + slotChest, + slotLegs, + slotFeet, + slotWaist, + slotPowerSource, + slotAmmo, + slotGeneral1, + slotGeneral2, + slotGeneral3, + slotGeneral4, + slotGeneral5, + slotGeneral6, + slotGeneral7, + slotGeneral8, + slotGeneral9, + slotGeneral10, + slotCursor + }; + + constexpr int16 format_as(InventorySlots slot) { return static_cast(slot); } + } // namespace enum_ + using namespace enum_; + + const int16 SLOT_INVALID = IINVALID; + const int16 SLOT_BEGIN = INULL; + + const int16 POSSESSIONS_BEGIN = slotCharm; + const int16 POSSESSIONS_END = slotCursor; + const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1; + + const int16 EQUIPMENT_BEGIN = slotCharm; + const int16 EQUIPMENT_END = slotAmmo; + const int16 EQUIPMENT_COUNT = (EQUIPMENT_END - EQUIPMENT_BEGIN) + 1; + + const int16 GENERAL_BEGIN = slotGeneral1; + const int16 GENERAL_END = slotGeneral10; + const int16 GENERAL_COUNT = (GENERAL_END - GENERAL_BEGIN) + 1; + + const int16 BONUS_BEGIN = invslot::slotCharm; + const int16 BONUS_STAT_END = invslot::slotPowerSource; + const int16 BONUS_SKILL_END = invslot::slotAmmo; + + const int16 CORPSE_BEGIN = invslot::slotGeneral1; + const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor; + + const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF; + const uint64 GENERAL_BITMASK = 0x00000001FF800000; + const uint64 CURSOR_BITMASK = 0x0000000200000000; + const uint64 POSSESSIONS_BITMASK = (EQUIPMENT_BITMASK | GENERAL_BITMASK | CURSOR_BITMASK); // based on 34-slot count (RoF+) + const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 34)); // based on 34-slot count (RoF+) + + + const char* GetInvPossessionsSlotName(int16 inv_slot); + const char* GetInvSlotName(int16 inv_type, int16 inv_slot); + + } /*invslot*/ + + namespace invbag { + inline EQ::versions::ClientVersion GetInvBagRef() { return EQ::versions::ClientVersion::Larion; } + + const int16 SLOT_INVALID = IINVALID; + const int16 SLOT_BEGIN = INULL; + const int16 SLOT_END = 9; //254; + const int16 SLOT_COUNT = 10; //255; // server Size will be 255..unsure what actual client is (test) + + const char* GetInvBagIndexName(int16 bag_index); + + } /*invbag*/ + + namespace invaug { + inline EQ::versions::ClientVersion GetInvAugRef() { return EQ::versions::ClientVersion::Larion; } + + const int16 SOCKET_INVALID = IINVALID; + const int16 SOCKET_BEGIN = INULL; + const int16 SOCKET_END = 5; + const int16 SOCKET_COUNT = 6; + + const char* GetInvAugIndexName(int16 aug_index); + + } /*invaug*/ + + namespace item { + inline EQ::versions::ClientVersion GetItemRef() { return EQ::versions::ClientVersion::Larion; } + + //enum Unknown : int { // looks like item class..but, RoF has it too - nothing in UF- + // Unknown1 = 0, + // Unknown2 = 1, + // Unknown3 = 2, + // Unknown4 = 5 // krono? + //}; + + enum ItemPacketType : int { + ItemPacketMerchant = 100, + ItemPacketTradeView = 101, + ItemPacketLoot = 102, + ItemPacketTrade = 103, + ItemPacketCharInventory = 105, + ItemPacketLimbo = 106, + ItemPacketWorldContainer = 107, + ItemPacketTributeItem = 108, + ItemPacketGuildTribute = 109, + ItemPacket10 = 110, + ItemPacket11 = 111, + ItemPacket12 = 112, + ItemPacketRecovery = 113, + ItemPacket14 = 115 // Parcel? adds to merchant window too + }; + + } /*item*/ + + namespace profile { + inline EQ::versions::ClientVersion GetProfileRef() { return EQ::versions::ClientVersion::Larion; } + + const int16 BANDOLIERS_SIZE = 20; // number of bandolier instances + const int16 BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance + + const int16 POTION_BELT_SIZE = 5; + + const int16 SKILL_ARRAY_SIZE = 100; + + } /*profile*/ + + namespace constants { + inline EQ::versions::ClientVersion GetConstantsRef() { return EQ::versions::ClientVersion::Larion; } + + const EQ::expansions::Expansion EXPANSION = EQ::expansions::Expansion::RoF; + const uint32 EXPANSION_BIT = EQ::expansions::bitRoF; + const uint32 EXPANSIONS_MASK = EQ::expansions::maskRoF; + + const size_t CHARACTER_CREATION_LIMIT = 12; + + const size_t SAY_LINK_BODY_SIZE = 56; + const uint32 MAX_GUILD_ID = 50000; + + } /*constants*/ + + namespace behavior { + inline EQ::versions::ClientVersion GetBehaviorRef() { return EQ::versions::ClientVersion::Larion; } + + const bool CoinHasWeight = false; + + } /*behavior*/ + + namespace skills { + inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::Larion; } + + const size_t LastUsableSkill = EQ::skills::Skill2HPiercing; + + } /*skills*/ + + namespace spells { + inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::Larion; } + + enum class CastingSlot : uint32 { + Gem1 = 0, + Gem2 = 1, + Gem3 = 2, + Gem4 = 3, + Gem5 = 4, + Gem6 = 5, + Gem7 = 6, + Gem8 = 7, + Gem9 = 8, + Gem10 = 9, + Gem11 = 10, + Gem12 = 11, + MaxGems = 16, // fallacy..only 12 slot are useable... + Item = 12, + Discipline = 13, + AltAbility = 0xFF + }; + + const int SPELL_ID_MAX = 45000; + const int SPELLBOOK_SIZE = 720; + const int SPELL_GEM_COUNT = static_cast(CastingSlot::MaxGems); + + const int LONG_BUFFS = 42; + const int SHORT_BUFFS = 20; + const int DISC_BUFFS = 1; + const int TOTAL_BUFFS = LONG_BUFFS + SHORT_BUFFS + DISC_BUFFS; + const int NPC_BUFFS = 97; + const int PET_BUFFS = NPC_BUFFS; + const int MERC_BUFFS = LONG_BUFFS; + + } /*spells*/ }; /* Larion */ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 0daf37f65..fac805f07 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -5,6 +5,7 @@ E(OP_LogServer) E(OP_SendMembership) E(OP_SendMembershipDetails) +E(OP_SendMaxCharacters) #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index e05d96240..d9640219e 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -37,9 +37,9 @@ namespace Larion { struct Membership_Setting_Struct { - /*000*/ uint8 setting_index; // 0, 1, 2 or 3 - /*001*/ uint32 setting_id; // 0 to 23 - /*005*/ int32 setting_value; // All can be 0, 1, or -1 + /*000*/ uint8 setting_index; // 0, 1, 2 or 3: f2p, silver, gold, platinum? + /*001*/ uint32 setting_id; // 0 to 23 actually seen but the OP_Membership packet has up to 32 + /*005*/ int32 setting_value; /*009*/ }; @@ -55,6 +55,24 @@ namespace Larion { /*480*/ //char exit_url[42]; // Upgrade to Silver or Gold Membership URL }; + struct MaxCharacters_Struct { + /*000*/ uint32 max_chars; + /*004*/ uint32 marketplace_chars; + /*008*/ int32 unknown008; //some of these probably deal with heroic characters or something + /*00c*/ int32 unknown00c; + /*010*/ int32 unknown010; + /*014*/ int32 unknown014; + /*018*/ int32 unknown018; + /*01c*/ int32 unknown01c; + /*020*/ int32 unknown020; + /*024*/ int32 unknown024; + /*028*/ int32 unknown028; + /*02c*/ int32 unknown02c; + /*030*/ int32 unknown030; + /*034*/ int32 unknown034; + /*038*/ + }; + }; //end namespace structs }; //end namespace larion From 4093b505d3c461c58fbf6ae37534ee302a6db1d2 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 24 Oct 2024 21:11:01 -0700 Subject: [PATCH 18/93] wip char select --- common/patches/larion.cpp | 61 +++++++++++++++++++++++++++++++++++++ common/patches/larion_ops.h | 1 + 2 files changed, 62 insertions(+) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 9b49c961b..a36514f13 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -791,6 +791,67 @@ namespace Larion FINISH_ENCODE(); } + ENCODE(OP_SendCharInfo) { + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + SerializeBuffer buffer; + + buffer.WriteUInt32(emu->CharCount); + + for (int i = 0; i < emu->CharCount; ++i) { + auto *char_info = &emu->Entries[i]; + buffer.WriteString(char_info->Name); + buffer.WriteUInt32(char_info->Class); + buffer.WriteUInt32(char_info->Race); + buffer.WriteUInt8(1); //not sure seen 1 + buffer.WriteUInt32(char_info->ShroudRace); + buffer.WriteUInt32(char_info->ShroudClass); + buffer.WriteUInt16(char_info->Zone); + buffer.WriteUInt16(char_info->Instance); + buffer.WriteUInt8(char_info->Gender); + buffer.WriteUInt8(char_info->Face); + + for (int j = 0; j < 9; ++j) { + buffer.WriteUInt32(char_info->Equip[j].Material); + buffer.WriteUInt32(char_info->Equip[j].Unknown1); + buffer.WriteUInt32(char_info->Equip[j].EliteModel); + buffer.WriteUInt32(char_info->Equip[j].HerosForgeModel); + buffer.WriteUInt32(char_info->Equip[j].Unknown2); + buffer.WriteUInt32(char_info->Equip[j].Color); + } + + buffer.WriteUInt8(255); //seen 255 + buffer.WriteUInt8(0); //seen 0 + buffer.WriteUInt32(char_info->DrakkinTattoo); + buffer.WriteUInt32(char_info->DrakkinDetails); + buffer.WriteUInt32(char_info->Deity); + buffer.WriteUInt32(char_info->PrimaryIDFile); + buffer.WriteUInt32(char_info->SecondaryIDFile); + buffer.WriteUInt8(char_info->HairColor); + buffer.WriteUInt8(char_info->BeardColor); + buffer.WriteUInt8(char_info->EyeColor1); + buffer.WriteUInt8(char_info->EyeColor2); + buffer.WriteUInt8(char_info->HairStyle); + buffer.WriteUInt8(char_info->Beard); + buffer.WriteUInt8(char_info->GoHome); + buffer.WriteUInt8(char_info->Tutorial); + buffer.WriteUInt32(char_info->DrakkinHeritage); + buffer.WriteUInt8(0); //seen 0 + buffer.WriteUInt8(0); //seen 0 + buffer.WriteUInt32(char_info->LastLogin); + buffer.WriteUInt32(0); //last login might just be 64bit now + buffer.WriteUInt32(2590000 + i); //unique character id? + buffer.WriteUInt32(104); + } + + buffer.WriteUInt8(0); + + __packet->size = buffer.size(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, buffer.buffer(), __packet->size); + FINISH_ENCODE(); + } + // DECODE methods } /*Larion*/ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index fac805f07..d10bebd2e 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -6,6 +6,7 @@ E(OP_LogServer) E(OP_SendMembership) E(OP_SendMembershipDetails) E(OP_SendMaxCharacters) +E(OP_SendCharInfo) #undef E #undef D From c1651b7dca8fd4c5013972ae77723736d22a53c8 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 25 Oct 2024 22:47:27 -0700 Subject: [PATCH 19/93] Fix char select --- common/emu_versions.h | 2 +- common/eq_limits.cpp | 21 + common/patches/larion.cpp | 753 +++++------------- common/patches/larion_limits.h | 6 +- common/patches/larion_ops.h | 1 + common/patches/larion_structs.h | 108 ++- .../stream_parser/ConsoleHostedService.cs | 70 +- .../ConsoleHostedServiceOptions.cs | 3 + 8 files changed, 361 insertions(+), 603 deletions(-) diff --git a/common/emu_versions.h b/common/emu_versions.h index 4c36ce39c..5a2027770 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -65,7 +65,7 @@ namespace EQ maskAllClients = 0xFFFFFFFF }; - const ClientVersion LastClientVersion = ClientVersion::RoF2; + const ClientVersion LastClientVersion = ClientVersion::Larion; const size_t ClientVersionCount = (static_cast(LastClientVersion) + 1); bool IsValidClientVersion(ClientVersion client_version); diff --git a/common/eq_limits.cpp b/common/eq_limits.cpp index 14486b4f0..7c0f72038 100644 --- a/common/eq_limits.cpp +++ b/common/eq_limits.cpp @@ -104,6 +104,14 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers RoF2::constants::EXPANSIONS_MASK, RoF2::constants::CHARACTER_CREATION_LIMIT, RoF2::constants::SAY_LINK_BODY_SIZE + ), + /*[ClientVersion::Larion] =*/ + EQ::constants::LookupEntry( + Larion::constants::EXPANSION, + Larion::constants::EXPANSION_BIT, + Larion::constants::EXPANSIONS_MASK, + Larion::constants::CHARACTER_CREATION_LIMIT, + Larion::constants::SAY_LINK_BODY_SIZE ) }; @@ -1264,6 +1272,19 @@ static const EQ::spells::LookupEntry spells_static_lookup_entries[EQ::versions:: RoF2::spells::NPC_BUFFS, RoF2::spells::PET_BUFFS, RoF2::spells::MERC_BUFFS + ), + /*[ClientVersion::Larion] =*/ + EQ::spells::LookupEntry( + Larion::spells::SPELL_ID_MAX, + Larion::spells::SPELLBOOK_SIZE, + UF::spells::SPELL_GEM_COUNT, // client translators are setup to allow the max value a client supports..however, the top 4 indices are not valid in this case + Larion::spells::LONG_BUFFS, + Larion::spells::SHORT_BUFFS, + Larion::spells::DISC_BUFFS, + Larion::spells::TOTAL_BUFFS, + Larion::spells::NPC_BUFFS, + Larion::spells::PET_BUFFS, + Larion::spells::MERC_BUFFS ) }; diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index a36514f13..e668934e9 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -257,514 +257,98 @@ namespace Larion } ENCODE(OP_SendMembershipDetails) { - SETUP_VAR_ENCODE(Membership_Details_Struct); + ENCODE_LENGTH_EXACT(Membership_Details_Struct); + SETUP_DIRECT_ENCODE(Membership_Details_Struct, structs::Membership_Details_Struct); + + int32 settings[96][3] = { + { 0, 0, 250 }, { 1, 0, 1000 }, { 0, 1, -1 }, { 1, 1, -1 }, + { 0, 2, 2 }, { 2, 0, -1 }, { 3, 0, -1 }, { 1, 2, 4 }, + { 0, 3, 1 }, { 2, 1, -1 }, { 3, 1, -1 }, { 1, 3, 1 }, + { 0, 4, -1 }, { 2, 2, -1 }, { 3, 2, -1 }, { 1, 4, -1 }, + { 0, 5, -1 }, { 2, 3, -1 }, { 3, 3, -1 }, { 1, 5, -1 }, + { 0, 6, 0 }, { 2, 4, -1 }, { 3, 4, -1 }, { 1, 6, 0 }, + { 0, 7, 1 }, { 2, 5, -1 }, { 3, 5, -1 }, { 1, 7, 1 }, + { 0, 8, 1 }, { 2, 6, 1 }, { 3, 6, 1 }, { 1, 8, 1 }, + { 0, 9, 5 }, { 2, 7, 1 }, { 3, 7, 1 }, { 1, 9, 5 }, + { 0, 10, 0 }, { 2, 8, 1 }, { 3, 8, 1 }, { 0, 11, -1 }, + { 1, 10, 1 }, { 2, 9, -1 }, { 3, 9, -1 }, { 0, 12, -1 }, + { 1, 11, -1 }, { 2, 10, 1 }, { 3, 10, 1 }, { 0, 13, 0 }, + { 1, 12, -1 }, { 2, 11, -1 }, { 3, 11, -1 }, { 0, 14, 0 }, + { 1, 13, 1 }, { 2, 12, -1 }, { 3, 12, -1 }, { 0, 15, 0 }, + { 1, 14, 0 }, { 2, 13, 1 }, { 3, 13, 1 }, { 0, 16, 0 }, + { 1, 15, 0 }, { 2, 14, 1 }, { 3, 14, 1 }, { 0, 17, 0 }, + { 1, 16, 1 }, { 2, 15, 1 }, { 3, 15, 1 }, { 0, 18, 0 }, + { 1, 17, 0 }, { 2, 16, 1 }, { 3, 16, 1 }, { 0, 19, 0 }, + { 1, 18, 0 }, { 2, 17, 1 }, { 3, 17, 1 }, { 0, 20, 0 }, + { 1, 19, 0 }, { 2, 18, 1 }, { 3, 18, 1 }, { 0, 21, 0 }, + { 1, 20, 0 }, { 2, 19, -1 }, { 3, 19, -1 }, { 0, 22, 0 }, + { 1, 21, 0 }, { 2, 20, -1 }, { 3, 20, -1 }, { 2, 21, 0 }, + { 0, 23, 0 }, { 1, 22, 0 }, { 3, 21, 0 }, { 2, 22, 0 }, + { 1, 23, 0 }, { 3, 22, 0 }, { 2, 23, 0 }, { 3, 23, 0 } + }; + + uint32 races[17][2] = { + { 1, 131071 }, + { 333, 131071 }, + { 90287, 131071 }, + { 90289, 16 }, + { 90290, 32 }, + { 90291, 64 }, + { 90292, 128 }, + { 90293, 256 }, + { 90294, 512 }, + { 90295, 1024 }, + { 90296, 2048 }, + { 90297, 8192 }, + { 90298, 16384 }, + { 90299, 32768 }, + { 90300, 65536 }, + { 2012271, 131071 }, + { 2012277, 131071 } + }; + + uint32 classes[17][2] = { + { 1, 131071 }, + { 333, 131071 }, + { 90287, 131071 }, + { 90301, 8 }, + { 90302, 16 }, + { 90303, 32 }, + { 90304, 64 }, + { 90305, 128 }, + { 90306, 256 }, + { 90307, 1024 }, + { 90308, 2048 }, + { 90309, 8192 }, + { 90310, 16384 }, + { 90311, 32768 }, + { 90312, 65536 }, + { 2012271, 131071 }, + { 2012277, 131071 } + }; + + eq->membership_setting_count = 96; + + for (int i = 0; i < 96; ++i) { + eq->settings[i].setting_index = (int8)settings[i][0]; + eq->settings[i].setting_id = settings[i][1]; + eq->settings[i].setting_value = settings[i][2]; + } + + eq->class_entry_count = 17; + for (int i = 0; i < 17; ++i) { + eq->membership_classes[i].purchase_id = classes[i][0]; + eq->membership_classes[i].bitwise_entry = classes[i][1]; + } + + eq->race_entry_count = 17; + for (int i = 0; i < 17; ++i) { + eq->membership_races[i].purchase_id = races[i][0]; + eq->membership_races[i].bitwise_entry = races[i][1]; + } + + eq->exit_url_length = 0; - SerializeBuffer buffer; - // Generated via script; we should put this in a better format later - // count - buffer.WriteUInt32(96); - - // settings - buffer.WriteUInt8(0); - buffer.WriteUInt32(0); - buffer.WriteInt32(250); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(0); - buffer.WriteInt32(1000); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(1); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(1); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(2); - buffer.WriteInt32(2); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(0); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(0); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(2); - buffer.WriteInt32(4); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(3); - buffer.WriteInt32(1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(1); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(1); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(3); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(4); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(2); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(2); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(4); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(5); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(3); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(3); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(5); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(6); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(4); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(4); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(6); - buffer.WriteInt32(0); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(7); - buffer.WriteInt32(1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(5); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(5); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(7); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(8); - buffer.WriteInt32(1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(6); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(6); - buffer.WriteInt32(1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(8); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(9); - buffer.WriteInt32(5); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(7); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(7); - buffer.WriteInt32(1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(9); - buffer.WriteInt32(5); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(10); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(8); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(8); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(11); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(10); - buffer.WriteInt32(1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(9); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(9); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(12); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(11); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(10); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(10); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(13); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(12); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(11); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(11); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(14); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(13); - buffer.WriteInt32(1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(12); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(12); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(15); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(14); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(13); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(13); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(16); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(15); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(14); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(14); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(17); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(16); - buffer.WriteInt32(1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(15); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(15); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(18); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(17); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(16); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(16); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(19); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(18); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(17); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(17); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(20); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(19); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(18); - buffer.WriteInt32(1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(18); - buffer.WriteInt32(1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(21); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(20); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(19); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(19); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(22); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(21); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(20); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(20); - buffer.WriteInt32(-1); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(21); - buffer.WriteInt32(0); - - buffer.WriteUInt8(0); - buffer.WriteUInt32(23); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(22); - buffer.WriteInt32(0); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(21); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(22); - buffer.WriteInt32(0); - - buffer.WriteUInt8(1); - buffer.WriteUInt32(23); - buffer.WriteInt32(0); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(22); - buffer.WriteInt32(0); - - buffer.WriteUInt8(2); - buffer.WriteUInt32(23); - buffer.WriteInt32(0); - - buffer.WriteUInt8(3); - buffer.WriteUInt32(23); - buffer.WriteInt32(0); - - // race count - buffer.WriteUInt32(17); - - // races - buffer.WriteUInt32(1); - buffer.WriteUInt32(131071); - - buffer.WriteUInt32(333); - buffer.WriteUInt32(131071); - - buffer.WriteUInt32(90287); - buffer.WriteUInt32(131071); - - buffer.WriteUInt32(90289); - buffer.WriteUInt32(16); - - buffer.WriteUInt32(90290); - buffer.WriteUInt32(32); - - buffer.WriteUInt32(90291); - buffer.WriteUInt32(64); - - buffer.WriteUInt32(90292); - buffer.WriteUInt32(128); - - buffer.WriteUInt32(90293); - buffer.WriteUInt32(256); - - buffer.WriteUInt32(90294); - buffer.WriteUInt32(512); - - buffer.WriteUInt32(90295); - buffer.WriteUInt32(1024); - - buffer.WriteUInt32(90296); - buffer.WriteUInt32(2048); - - buffer.WriteUInt32(90297); - buffer.WriteUInt32(8192); - - buffer.WriteUInt32(90298); - buffer.WriteUInt32(16384); - - buffer.WriteUInt32(90299); - buffer.WriteUInt32(32768); - - buffer.WriteUInt32(90300); - buffer.WriteUInt32(65536); - - buffer.WriteUInt32(2012271); - buffer.WriteUInt32(131071); - - buffer.WriteUInt32(2012277); - buffer.WriteUInt32(131071); - - // classes count - buffer.WriteUInt32(17); - - // classes - buffer.WriteUInt32(1); - buffer.WriteUInt32(131071); - - buffer.WriteUInt32(333); - buffer.WriteUInt32(131071); - - buffer.WriteUInt32(90287); - buffer.WriteUInt32(131071); - - buffer.WriteUInt32(90301); - buffer.WriteUInt32(8); - - buffer.WriteUInt32(90302); - buffer.WriteUInt32(16); - - buffer.WriteUInt32(90303); - buffer.WriteUInt32(32); - - buffer.WriteUInt32(90304); - buffer.WriteUInt32(64); - - buffer.WriteUInt32(90305); - buffer.WriteUInt32(128); - - buffer.WriteUInt32(90306); - buffer.WriteUInt32(256); - - buffer.WriteUInt32(90307); - buffer.WriteUInt32(1024); - - buffer.WriteUInt32(90308); - buffer.WriteUInt32(2048); - - buffer.WriteUInt32(90309); - buffer.WriteUInt32(8192); - - buffer.WriteUInt32(90310); - buffer.WriteUInt32(16384); - - buffer.WriteUInt32(90311); - buffer.WriteUInt32(32768); - - buffer.WriteUInt32(90312); - buffer.WriteUInt32(65536); - - buffer.WriteUInt32(2012271); - buffer.WriteUInt32(131071); - - buffer.WriteUInt32(2012277); - buffer.WriteUInt32(131071); - - // exit string length - buffer.WriteUInt32(0); - - __packet->size = buffer.size(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, buffer.buffer(), __packet->size); FINISH_ENCODE(); } @@ -792,63 +376,116 @@ namespace Larion } ENCODE(OP_SendCharInfo) { + ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct); - SerializeBuffer buffer; + // Zero-character count shunt + if (emu->CharCount == 0) { + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); + eq->CharCount = emu->CharCount; - buffer.WriteUInt32(emu->CharCount); - - for (int i = 0; i < emu->CharCount; ++i) { - auto *char_info = &emu->Entries[i]; - buffer.WriteString(char_info->Name); - buffer.WriteUInt32(char_info->Class); - buffer.WriteUInt32(char_info->Race); - buffer.WriteUInt8(1); //not sure seen 1 - buffer.WriteUInt32(char_info->ShroudRace); - buffer.WriteUInt32(char_info->ShroudClass); - buffer.WriteUInt16(char_info->Zone); - buffer.WriteUInt16(char_info->Instance); - buffer.WriteUInt8(char_info->Gender); - buffer.WriteUInt8(char_info->Face); - - for (int j = 0; j < 9; ++j) { - buffer.WriteUInt32(char_info->Equip[j].Material); - buffer.WriteUInt32(char_info->Equip[j].Unknown1); - buffer.WriteUInt32(char_info->Equip[j].EliteModel); - buffer.WriteUInt32(char_info->Equip[j].HerosForgeModel); - buffer.WriteUInt32(char_info->Equip[j].Unknown2); - buffer.WriteUInt32(char_info->Equip[j].Color); - } - - buffer.WriteUInt8(255); //seen 255 - buffer.WriteUInt8(0); //seen 0 - buffer.WriteUInt32(char_info->DrakkinTattoo); - buffer.WriteUInt32(char_info->DrakkinDetails); - buffer.WriteUInt32(char_info->Deity); - buffer.WriteUInt32(char_info->PrimaryIDFile); - buffer.WriteUInt32(char_info->SecondaryIDFile); - buffer.WriteUInt8(char_info->HairColor); - buffer.WriteUInt8(char_info->BeardColor); - buffer.WriteUInt8(char_info->EyeColor1); - buffer.WriteUInt8(char_info->EyeColor2); - buffer.WriteUInt8(char_info->HairStyle); - buffer.WriteUInt8(char_info->Beard); - buffer.WriteUInt8(char_info->GoHome); - buffer.WriteUInt8(char_info->Tutorial); - buffer.WriteUInt32(char_info->DrakkinHeritage); - buffer.WriteUInt8(0); //seen 0 - buffer.WriteUInt8(0); //seen 0 - buffer.WriteUInt32(char_info->LastLogin); - buffer.WriteUInt32(0); //last login might just be 64bit now - buffer.WriteUInt32(2590000 + i); //unique character id? - buffer.WriteUInt32(104); + FINISH_ENCODE(); + return; } - buffer.WriteUInt8(0); + unsigned char* emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + CharacterSelectEntry_Struct* emu_cse = (CharacterSelectEntry_Struct*)nullptr; + + size_t names_length = 0; + size_t character_count = 0; + for (; character_count < emu->CharCount; ++character_count) { + emu_cse = (CharacterSelectEntry_Struct*)emu_ptr; + names_length += strlen(emu_cse->Name); + emu_ptr += sizeof(CharacterSelectEntry_Struct); + } + + size_t total_length = sizeof(structs::CharacterSelect_Struct) + + character_count * sizeof(structs::CharacterSelectEntry_Struct) + + names_length; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + structs::CharacterSelectEntry_Struct* eq_cse = (structs::CharacterSelectEntry_Struct*)nullptr; + + eq->CharCount = character_count; + + emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + + unsigned char* eq_ptr = __packet->pBuffer; + eq_ptr += sizeof(structs::CharacterSelect_Struct); + + for (int counter = 0; counter < character_count; ++counter) { + emu_cse = (CharacterSelectEntry_Struct*)emu_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct*)eq_ptr; // base address + + strcpy(eq_cse->Name, emu_cse->Name); + eq_ptr += strlen(emu_cse->Name); + + eq_cse = (structs::CharacterSelectEntry_Struct*)eq_ptr; + eq_cse->Name[0] = '\0'; + + eq_cse->Class = emu_cse->Class; + eq_cse->Race = emu_cse->Race; + eq_cse->Level = emu_cse->Level; + eq_cse->ShroudClass = emu_cse->ShroudClass; + eq_cse->ShroudRace = emu_cse->ShroudRace; + eq_cse->Zone = emu_cse->Zone; + eq_cse->Instance = emu_cse->Instance; + eq_cse->Gender = emu_cse->Gender; + eq_cse->Face = emu_cse->Face; + + for (int equip_index = 0; equip_index < EQ::textures::materialCount; equip_index++) { + eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material; + eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1; + eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteModel; + eq_cse->Equip[equip_index].HeroForgeModel = emu_cse->Equip[equip_index].HerosForgeModel; + eq_cse->Equip[equip_index].Material2 = emu_cse->Equip[equip_index].Unknown2; + eq_cse->Equip[equip_index].Color = emu_cse->Equip[equip_index].Color; + } + + eq_cse->Unknown1 = 255; + eq_cse->Unknown2 = 0; + eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo; + eq_cse->DrakkinDetails = emu_cse->DrakkinDetails; + eq_cse->Deity = emu_cse->Deity; + eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile; + eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile; + eq_cse->HairColor = emu_cse->HairColor; + eq_cse->BeardColor = emu_cse->BeardColor; + eq_cse->EyeColor1 = emu_cse->EyeColor1; + eq_cse->EyeColor2 = emu_cse->EyeColor2; + eq_cse->HairStyle = emu_cse->HairStyle; + eq_cse->Beard = emu_cse->Beard; + eq_cse->GoHome = emu_cse->GoHome; + eq_cse->Tutorial = emu_cse->Tutorial; + eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage; + eq_cse->Enabled = emu_cse->Enabled; + eq_cse->LastLogin = emu_cse->LastLogin; + eq_cse->Unknown3 = 0; + eq_cse->Unknown4 = 0; + eq_cse->Unknown5 = 0; + eq_cse->Unknown6 = 0; + eq_cse->Unknown7 = 0; + eq_cse->CharacterId = 0; + eq_cse->Unknown8 = 1; + + emu_ptr += sizeof(CharacterSelectEntry_Struct); + eq_ptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + DumpPacket(__packet); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); - __packet->size = buffer.size(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, buffer.buffer(), __packet->size); FINISH_ENCODE(); } diff --git a/common/patches/larion_limits.h b/common/patches/larion_limits.h index 6d3a1ddb0..69d134a60 100644 --- a/common/patches/larion_limits.h +++ b/common/patches/larion_limits.h @@ -263,9 +263,9 @@ namespace Larion namespace constants { inline EQ::versions::ClientVersion GetConstantsRef() { return EQ::versions::ClientVersion::Larion; } - const EQ::expansions::Expansion EXPANSION = EQ::expansions::Expansion::RoF; - const uint32 EXPANSION_BIT = EQ::expansions::bitRoF; - const uint32 EXPANSIONS_MASK = EQ::expansions::maskRoF; + const EQ::expansions::Expansion EXPANSION = EQ::expansions::Expansion::LS; + const uint32 EXPANSION_BIT = EQ::expansions::bitLS; + const uint32 EXPANSIONS_MASK = EQ::expansions::maskLS; const size_t CHARACTER_CREATION_LIMIT = 12; diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index d10bebd2e..f795231a3 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -7,6 +7,7 @@ E(OP_SendMembership) E(OP_SendMembershipDetails) E(OP_SendMaxCharacters) E(OP_SendCharInfo) +E(OP_ExpansionInfo) #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index d9640219e..4aa9cc6ef 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -3,6 +3,9 @@ namespace Larion { namespace structs { + +#pragma pack(1) + struct LoginInfo_Struct { /*000*/ char login_info[64]; /*064*/ uint8 unknown064[124]; @@ -37,8 +40,8 @@ namespace Larion { struct Membership_Setting_Struct { - /*000*/ uint8 setting_index; // 0, 1, 2 or 3: f2p, silver, gold, platinum? - /*001*/ uint32 setting_id; // 0 to 23 actually seen but the OP_Membership packet has up to 32 + /*000*/ int8 setting_index; // 0, 1, 2 or 3: f2p, silver, gold, platinum? + /*001*/ int32 setting_id; // 0 to 23 actually seen but the OP_Membership packet has up to 32 /*005*/ int32 setting_value; /*009*/ }; @@ -73,17 +76,98 @@ namespace Larion { /*038*/ }; + struct ExpansionInfo_Struct { + /*000*/ char Unknown000[64]; + /*064*/ uint32 Expansions; + }; + + /* + * Visible equiptment. + * Size: 20 Octets + */ + struct Texture_Struct + { + uint32 Material; + uint32 Unknown1; + uint32 EliteMaterial; + uint32 HeroForgeModel; + uint32 Material2; // Same as material? + }; + + /* + ** Color_Struct + ** Size: 4 bytes + ** Used for convenience + ** Merth: Gave struct a name so gcc 2.96 would compile + ** + */ + struct Tint_Struct + { + union { + struct { + uint8 Blue; + uint8 Green; + uint8 Red; + uint8 UseTint; // if there's a tint this is FF + }; + uint32 Color; + }; + }; + + struct CharSelectEquip : Texture_Struct, Tint_Struct {}; + + struct CharacterSelectEntry_Struct + { + char Name[1]; + uint32 Class; + uint32 Race; + uint8 Level; + uint32 ShroudClass; + uint32 ShroudRace; + uint16 Zone; + uint16 Instance; + uint8 Gender; + uint8 Face; + CharSelectEquip Equip[9]; + uint8 Unknown1; //Seen 256 + uint8 Unknown2; //Seen 0 + uint32 DrakkinTattoo; + uint32 DrakkinDetails; + uint32 Deity; + uint32 PrimaryIDFile; + uint32 SecondaryIDFile; + uint8 HairColor; + uint8 BeardColor; + uint8 EyeColor1; + uint8 EyeColor2; + uint8 HairStyle; + uint8 Beard; + uint8 Enabled; + uint8 Tutorial; + uint32 DrakkinHeritage; + uint8 Unknown3; + uint8 GoHome; + uint32 LastLogin; + uint8 Unknown4; // Seen 0 + uint8 Unknown5; // Seen 0 + uint8 Unknown6; // Seen 0 + uint8 Unknown7; // Seen 0 + uint32 CharacterId; //A Guess, Character I made a little bit after has a number a few hundred after the first + uint32 Unknown8; // Seen 1 + }; + + /* + ** Character Selection Struct + ** + */ + struct CharacterSelect_Struct + { + /*000*/ uint32 CharCount; //number of chars in this packet + }; + +#pragma pack() + }; //end namespace structs }; //end namespace larion #endif /*LARION_STRUCTS_H_*/ - - - - - - - - - - diff --git a/utils/stream_parser/stream_parser/ConsoleHostedService.cs b/utils/stream_parser/stream_parser/ConsoleHostedService.cs index c635e360a..d0fdb281a 100644 --- a/utils/stream_parser/stream_parser/ConsoleHostedService.cs +++ b/utils/stream_parser/stream_parser/ConsoleHostedService.cs @@ -57,42 +57,54 @@ namespace StreamParser { var args = Environment.GetCommandLineArgs(); - CommandLine.Parser.Default.ParseArguments(args) - .WithParsed(o => - { - _parser.OnNewConnection += OnNewConnection; - _parser.OnLostConnection += OnLostConnection; - - foreach(var f in o.Input) + try + { + CommandLine.Parser.Default.ParseArguments(args) + .WithParsed(o => { - _logger.LogInformation("Parsing {0}...", f); - _parser.Parse(f); - } + _parser.OnNewConnection += OnNewConnection; + _parser.OnLostConnection += OnLostConnection; - foreach(var c in _connections) - { - if(o.Text) + foreach (var f in o.Input) { - DumpConnectionToTextFile(c.Value, o.Output, o.Decrypt, o.DecompressOpcodes); + _logger.LogInformation("Parsing {0}...", f); + _parser.Parse(f); } - } - _applicationLifetime.StopApplication(); - }) - .WithNotParsed(e => - { - bool stops_processing = false; - foreach(var err in e) - { - _logger.LogError("Error: {0}", err.Tag); - stops_processing = stops_processing || err.StopsProcessing; - } + foreach (var c in _connections) + { + if (c.Value.ConnectionType == ConnectionType.Unknown && !o.DumpUnknownStreams) + { + continue; + } + + if (o.Text) + { + DumpConnectionToTextFile(c.Value, o.Output, o.Decrypt, o.DecompressOpcodes); + } + } - if(stops_processing) - { _applicationLifetime.StopApplication(); - } - }); + }) + .WithNotParsed(e => + { + bool stops_processing = false; + foreach (var err in e) + { + stops_processing = stops_processing || err.StopsProcessing || err is MissingRequiredOptionError; + } + + if (stops_processing) + { + _applicationLifetime.StopApplication(); + } + }); + } + catch(Exception ex) + { + _logger.LogError(ex, "Error parsing command line arguments"); + _applicationLifetime.StopApplication(); + } }); return Task.CompletedTask; diff --git a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs index 749d20922..132a6a4c4 100644 --- a/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs +++ b/utils/stream_parser/stream_parser/ConsoleHostedServiceOptions.cs @@ -22,5 +22,8 @@ namespace StreamParser [Option("decompress", Default = null, HelpText = "Which opcodes to attempt to decompress")] public IEnumerable DecompressOpcodes { get; set; } + + [Option("enable-unknown-streams", Default = false, HelpText = "Enable dumping of unknown streams; they will be skipped otherwise.")] + public bool DumpUnknownStreams { get; set; } } } From b7c93e12de557cb11aa30b41fefd35962a828be0 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 26 Oct 2024 10:03:57 -0700 Subject: [PATCH 20/93] Add some sanity checking for stream_parser so it's less likely to pickup other protocols as an everquest protocol. --- .../common/Daybreak/Connection.cs | 45 ++++++++++++------- utils/stream_parser/common/Daybreak/Parser.cs | 6 +++ 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/utils/stream_parser/common/Daybreak/Connection.cs b/utils/stream_parser/common/Daybreak/Connection.cs index 454d16efe..98fa93b5a 100644 --- a/utils/stream_parser/common/Daybreak/Connection.cs +++ b/utils/stream_parser/common/Daybreak/Connection.cs @@ -176,6 +176,11 @@ namespace StreamParser.Common.Daybreak case Opcode.SessionResponse: if (_connect_code == 0) { + if(data.Length != 21) + { + return; + } + _connect_code = BitConverter.ToUInt32(data.Slice(2, 4)); _encode_key = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data.Slice(6, 4))); _crc_bytes = data[10]; @@ -443,27 +448,33 @@ namespace StreamParser.Common.Daybreak private bool ValidateCRC(ReadOnlySpan p) { - if (_crc_bytes == 0) + try { - return true; - } + if (_crc_bytes == 0) + { + return true; + } - int actual = 0; - int calculated = _crc_generator.Calculate(p.Slice(0, p.Length - _crc_bytes), _encode_key); - switch (_crc_bytes) + int actual = 0; + int calculated = _crc_generator.Calculate(p.Slice(0, p.Length - _crc_bytes), _encode_key); + switch (_crc_bytes) + { + case 2: + actual = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(p.Slice(p.Length - 2, 2))) & 0xFFFF; + calculated = calculated & 0xFFFF; + break; + case 4: + actual = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(p.Slice(p.Length - 4, 4))); + break; + default: + return false; + } + + return actual == calculated; + } catch(Exception ex) { - case 2: - actual = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(p.Slice(p.Length - 2, 2))) & 0xFFFF; - calculated = calculated & 0xFFFF; - break; - case 4: - actual = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(p.Slice(p.Length - 4, 4))); - break; - default: - return false; + return false; } - - return actual == calculated; } private byte[] Decompress(byte[] p, int offset, int length) diff --git a/utils/stream_parser/common/Daybreak/Parser.cs b/utils/stream_parser/common/Daybreak/Parser.cs index 8c95dd6f9..be5d24e96 100644 --- a/utils/stream_parser/common/Daybreak/Parser.cs +++ b/utils/stream_parser/common/Daybreak/Parser.cs @@ -87,6 +87,12 @@ namespace StreamParser.Common.Daybreak } else if (data[0] == 0 && data[1] == Opcode.SessionRequest) { + if(data.Length != 24) + { + _logger.LogTrace("Tossing packet, {0} was not the right size for a SessionRequest", data.Length); + return; + } + c = new Connection(this, srcAddr, srcPort, dstAddr, dstPort); _connections.Add(c); c.ProcessPacket(srcAddr, srcPort, packetTime, data); From 799593c394c6816b6a2304f9a224f13178b77d4b Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 5 Nov 2024 21:38:10 -0800 Subject: [PATCH 21/93] Working on zone packets. --- common/patches/larion.cpp | 85 +++++++++++++++++++++++++++++++++ common/patches/larion_ops.h | 7 ++- common/patches/larion_structs.h | 39 +++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index e668934e9..2638e67cf 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -489,6 +489,91 @@ namespace Larion FINISH_ENCODE(); } + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZoneSpawns) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char* __emu_buffer = in->pBuffer; + Spawn_Struct* emu = (Spawn_Struct*)__emu_buffer; + + int entrycount = in->size / sizeof(Spawn_Struct); + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + delete in; + return; + } + + for (int i = 0; i < entrycount; i++, emu++) { + SerializeBuffer buffer; + + auto SpawnSize = emu->size; + if (!((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) || + (emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin)) + ) + { + if (emu->size == 0) + { + emu->size = 6; + SpawnSize = 6; + } + } + + if (SpawnSize == 0) + { + SpawnSize = 3; + } + + buffer.WriteString(emu->name); + buffer.WriteUInt32(emu->spawnId); + buffer.WriteUInt8(emu->level); + if (emu->DestructibleObject) //bounding radius: we should consider supporting this officially in the future + { + buffer.WriteFloat(10.0f); + } + else + { + + buffer.WriteFloat(SpawnSize - 0.7f); + } + + buffer.WriteUInt32(emu->spawnId); //player "id" we should consider supporting this in the future + buffer.WriteUInt32(103); //not sure + buffer.WriteUInt8(emu->NPC); //npc/player flag + + structs::Spawn_Struct_Bitfields flags; + flags.gender = emu->gender; + flags.gender = emu->gender; + flags.ispet = emu->is_pet; + flags.afk = emu->afk; + flags.anon = emu->anon; + flags.gm = emu->gm; + flags.sneak = 0; + flags.lfg = emu->lfg; + flags.invis = emu->invis; + flags.linkdead = 0; + flags.showhelm = emu->showhelm; + flags.trader = emu->trader ? 1 : 0; + flags.targetable = 1; + flags.targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0; + flags.showname = emu->show_name; + + } + } + // DECODE methods + + DECODE(OP_ZoneEntry) + { + DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct); + SETUP_DIRECT_DECODE(ClientZoneEntry_Struct, structs::ClientZoneEntry_Struct); + + memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); + + FINISH_DIRECT_DECODE(); + } } /*Larion*/ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index f795231a3..9546e5742 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -1,13 +1,16 @@ //list of packets we need to encode on the way out: -//list of packets we need to decode on the way in: - E(OP_LogServer) E(OP_SendMembership) E(OP_SendMembershipDetails) E(OP_SendMaxCharacters) E(OP_SendCharInfo) E(OP_ExpansionInfo) +E(OP_ZoneEntry) +E(OP_ZoneSpawns) + +//list of packets we need to decode on the way in: +D(OP_ZoneEntry) #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 4aa9cc6ef..c52f9e16f 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -19,6 +19,11 @@ namespace Larion { /*04*/ char char_name[64]; // Player firstname [32] /*68*/ uint32 unknown68; /*72*/ uint32 unknown72; + /*76*/ uint32 unknown76; + /*80*/ uint32 unknown80; + /*84*/ uint32 unknown84; + /*88*/ uint32 unknown88; + /*92*/ }; struct Membership_Struct @@ -165,6 +170,40 @@ namespace Larion { /*000*/ uint32 CharCount; //number of chars in this packet }; + struct Spawn_Struct_Bitfields + { + // byte 1 + /*00*/ unsigned gender : 2; // Gender (0=male, 1=female, 2=monster) + /*02*/ unsigned ispet : 1; // Guessed based on observing live spawns + /*03*/ unsigned afk : 1; // 0=no, 1=afk + /*04*/ unsigned anon : 2; // 0=normal, 1=anon, 2=roleplay + /*06*/ unsigned gm : 1; + /*07*/ unsigned sneak : 1; + // byte 2 + /*08*/ unsigned lfg : 1; + /*09*/ unsigned unk9 : 1; + /*10*/ unsigned invis : 12; // there are 3000 different (non-GM) invis levels + /*22*/ unsigned linkdead : 1; // 1 Toggles LD on or off after name. Correct for RoF2 + /*23*/ unsigned showhelm : 1; + // byte 4 + /*24*/ unsigned betabuffed : 1; // Prefixes name with ! + /*25*/ unsigned trader : 1; + /*26*/ unsigned animationonpop : 1; + /*27*/ unsigned targetable : 1; + /*28*/ unsigned targetable_with_hotkey : 1; + /*29*/ unsigned showname : 1; + /*30*/ unsigned idleanimationsoff : 1; // what we called statue? + /*31*/ unsigned untargetable : 1; // bClickThrough + // byte 5 + /*32*/ unsigned buyer : 1; + /*33*/ unsigned offline : 1; + /*34*/ unsigned interactiveobject : 1; + /*35*/ unsigned missile : 1; + /*36*/ unsigned title : 1; + /*37*/ unsigned suffix : 1; + /*38*/ unsigned unk38 : 1; + /*39*/ unsigned unk39 : 1; + }; #pragma pack() }; //end namespace structs From d29937cba662afe3b68fc99e5d57bd9e5f8b3b54 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 7 Nov 2024 23:03:38 -0800 Subject: [PATCH 22/93] WIP player profile --- common/patches/larion.cpp | 288 +++++++++++++++++++++++++++++++- common/patches/larion_limits.h | 5 +- common/patches/larion_ops.h | 3 + common/patches/larion_structs.h | 21 ++- 4 files changed, 312 insertions(+), 5 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 2638e67cf..d419f083a 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -489,7 +489,291 @@ namespace Larion FINISH_ENCODE(); } - ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + ENCODE(OP_SpawnAppearance) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + unsigned char* emu_buffer = in->pBuffer; + + SpawnAppearance_Struct* sas = (SpawnAppearance_Struct*)emu_buffer; + + if (sas->type != AppearanceType::Size) + { + //larion struct is different than rof2's but the idea is the same + auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); + structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; + + eq->spawn_id = sas->spawn_id; + eq->type = sas->type; + eq->parameter = sas->parameter; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + return; + } + + auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); + + ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer; + + css->EntityID = sas->spawn_id; + css->Size = (float)sas->parameter; + css->Unknown08 = 0; + css->Unknown12 = 1.0f; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_PlayerProfile) { + EQApplicationPacket* in = *p; + *p = nullptr; + + unsigned char* __emu_buffer = in->pBuffer; + PlayerProfile_Struct* emu = (PlayerProfile_Struct*)__emu_buffer; + + SerializeBuffer buffer; + buffer.WriteUInt32(0); //crc + buffer.WriteUInt32(0); //size; + buffer.WriteUInt32(0); //profile_type; + buffer.WriteUInt32(0); //profile_id; + buffer.WriteUInt32(0); //shroud_template_id; + buffer.WriteUInt8(emu->gender); + buffer.WriteUInt32(emu->race); + buffer.WriteUInt32(emu->class_); + buffer.WriteUInt8(emu->level); + buffer.WriteUInt8(emu->level2); + + buffer.WriteUInt32(5); //bind_count + for (int r = 0; r < 5; r++) //binds + { + buffer.WriteUInt32(emu->binds[r].zone_id); + buffer.WriteFloat(emu->binds[r].x); + buffer.WriteFloat(emu->binds[r].y); + buffer.WriteFloat(emu->binds[r].z); + buffer.WriteFloat(emu->binds[r].heading); + } + + buffer.WriteUInt32(emu->deity); + buffer.WriteUInt32(emu->intoxication); + + buffer.WriteUInt32(10); //properties count + for (int r = 0; r < 10; r++) //properties + { + buffer.WriteUInt32(0); + } + + buffer.WriteUInt32(22); // Equipment count + + for (int r = EQ::textures::textureBegin; r < EQ::textures::materialCount; r++) + { + buffer.WriteUInt32(emu->item_material.Slot[r].Material); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + } + + // Write zeroes for the next 13 equipment slots + + for (int r = 0; r < 13; r++) + { + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + } + + buffer.WriteUInt32(EQ::textures::materialCount); // Base Equipment count + + //live seems to send the materials you had when you created the character here + //not entirely sure for what purpose though + + for (int r = EQ::textures::textureBegin; r < EQ::textures::materialCount; r++) + { + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + } + + buffer.WriteUInt32(EQ::textures::materialCount); // Tint Count + + for (int r = 0; r < 7; r++) + { + buffer.WriteUInt32(emu->item_tint.Slot[r].Color); + } + + // Write extra two tint values + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + + buffer.WriteUInt32(EQ::textures::materialCount); // Equip Tint Count + for (int r = 0; r < 7; r++) + { + buffer.WriteUInt32(emu->item_tint.Slot[r].Color); + } + + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + + buffer.WriteUInt8(emu->haircolor); + buffer.WriteUInt8(emu->beardcolor); + buffer.WriteUInt32(0); //npc tint index + buffer.WriteUInt8(emu->eyecolor1); + buffer.WriteUInt8(emu->eyecolor2); + buffer.WriteUInt8(emu->hairstyle); + buffer.WriteUInt8(emu->beard); + buffer.WriteUInt8(emu->face); + buffer.WriteUInt8(0); //old_face + buffer.WriteUInt32(emu->drakkin_heritage); + buffer.WriteUInt32(emu->drakkin_tattoo); + buffer.WriteUInt32(emu->drakkin_details); + buffer.WriteInt8(-1); //texture type + buffer.WriteInt8(0); //material + buffer.WriteInt8(0); //variation + buffer.WriteFloat(5.0f); //height + buffer.WriteFloat(3.0f); //width + buffer.WriteFloat(2.5f); //length + buffer.WriteFloat(5.5f); //view height + buffer.WriteInt32(0); //primary_actor + buffer.WriteInt32(0); //secondary_actor + + buffer.WriteUInt32(emu->points); // Unspent skill points + buffer.WriteUInt32(emu->mana); + buffer.WriteUInt32(emu->cur_hp); + buffer.WriteUInt32(emu->STR); + buffer.WriteUInt32(emu->STA); + buffer.WriteUInt32(emu->CHA); + buffer.WriteUInt32(emu->DEX); + buffer.WriteUInt32(emu->INT); + buffer.WriteUInt32(emu->AGI); + buffer.WriteUInt32(emu->WIS); + buffer.WriteUInt32(0); //heroic_sta + buffer.WriteUInt32(0); //heroic_str + buffer.WriteUInt32(0); //heroic_cha + buffer.WriteUInt32(0); //heroic_dex + buffer.WriteUInt32(0); //heroic_int + buffer.WriteUInt32(0); //heroic_agi + buffer.WriteUInt32(0); //heroic_wis + + buffer.WriteUInt32(structs::MAX_PP_AA_ARRAY); // AA Count + for (uint32 r = 0; r < MAX_PP_AA_ARRAY; r++) + { + buffer.WriteUInt32(emu->aa_array[r].AA); + buffer.WriteUInt32(emu->aa_array[r].value); + buffer.WriteUInt32(emu->aa_array[r].charges); + } + + //honestly we should look to up the base size and just limit titanium later + for (uint32 r = 0; r < structs::MAX_PP_AA_ARRAY - MAX_PP_AA_ARRAY; r++) + { + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + } + + buffer.WriteUInt32(structs::MAX_PP_SKILL); + + for (uint32 r = 0; r < structs::MAX_PP_SKILL; r++) + { + buffer.WriteUInt32(emu->skills[r]); + } + + buffer.WriteUInt32(structs::MAX_PP_INNATE_SKILL); // Innate Skills count + + for (uint32 r = 0; r < structs::MAX_PP_INNATE_SKILL; r++) + { + buffer.WriteUInt32(emu->InnateSkills[r]); // Innate Skills (regen, slam, etc) + } + + buffer.WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count + + for (uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) + { + buffer.WriteUInt32(emu->disciplines.values[r]); + } + + for (uint32 r = 0; r < structs::MAX_PP_DISCIPLINES - MAX_PP_DISCIPLINES; r++) + { + buffer.WriteUInt32(0); + } + + buffer.WriteUInt32(structs::MAX_PP_DISCIPLINE_TIMERS); // Disc timers + + for (uint32 r = 0; r < structs::MAX_PP_DISCIPLINE_TIMERS; r++) + { + buffer.WriteUInt32(0); //todo: support sending actual timestamps + } + + buffer.WriteUInt32(0); // Unk Ability Count + + buffer.WriteUInt32(structs::MAX_RECAST_TYPES); // linked_spell_timer_count + + for (uint32 r = 0; r < MAX_RECAST_TYPES; r++) + { + buffer.WriteUInt32(emu->recastTimers[r]); + } + + for (uint32 r = 0; r < structs::MAX_RECAST_TYPES - MAX_RECAST_TYPES; r++) + { + buffer.WriteUInt32(0); + } + + buffer.WriteUInt32(structs::MAX_ITEM_RECAST_TYPES); // item recast timers + + for (uint32 r = 0; r < structs::MAX_ITEM_RECAST_TYPES; r++) + { + buffer.WriteUInt32(0); + } + + buffer.WriteUInt32(spells::SPELLBOOK_SIZE); + + for (uint32 r = 0; r < EQ::spells::SPELLBOOK_SIZE; r++) + { + if (emu->spell_book[r] <= spells::SPELL_ID_MAX) + buffer.WriteUInt32(emu->spell_book[r]); + else + buffer.WriteInt32(-1); + } + + for (uint32 r = 0; r < spells::SPELLBOOK_SIZE - EQ::spells::SPELLBOOK_SIZE; r++) + { + buffer.WriteInt32(-1); + } + + buffer.WriteUInt32(spells::SPELL_GEM_COUNT); // Memorised spell slots + + for (uint32 r = 0; r < EQ::spells::SPELL_GEM_COUNT; r++) // write first 12, we need to actually look into this for larion im sure it's changed + { + buffer.WriteUInt32(emu->mem_spells[r]); + } + + for (uint32 r = 0; r < spells::SPELL_GEM_COUNT - EQ::spells::SPELL_GEM_COUNT; r++) + { + buffer.WriteInt32(-1); + } + + buffer.WriteUInt32(spells::SPELL_GEM_RECAST_TIMER); + + for (uint32 r = 0; r < EQ::spells::SPELL_GEM_COUNT; r++) + { + buffer.WriteUInt32(emu->spellSlotRefresh[r]); // spell gem refresh + } + //also refresh + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + + buffer.WriteUInt8(0); + + buffer.WriteUInt32(structs::BUFF_COUNT); + } + + /*ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } ENCODE(OP_ZoneSpawns) { @@ -562,7 +846,7 @@ namespace Larion flags.showname = emu->show_name; } - } + }*/ // DECODE methods diff --git a/common/patches/larion_limits.h b/common/patches/larion_limits.h index 69d134a60..74d0e9213 100644 --- a/common/patches/larion_limits.h +++ b/common/patches/larion_limits.h @@ -304,15 +304,16 @@ namespace Larion Gem10 = 9, Gem11 = 10, Gem12 = 11, - MaxGems = 16, // fallacy..only 12 slot are useable... + MaxGems = 18, // fallacy..only 12 slot are useable... Item = 12, Discipline = 13, AltAbility = 0xFF }; const int SPELL_ID_MAX = 45000; - const int SPELLBOOK_SIZE = 720; + const int SPELLBOOK_SIZE = 1120; const int SPELL_GEM_COUNT = static_cast(CastingSlot::MaxGems); + const int SPELL_GEM_RECAST_TIMER = 15; const int LONG_BUFFS = 42; const int SHORT_BUFFS = 20; diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 9546e5742..6be56c322 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -6,6 +6,9 @@ E(OP_SendMembershipDetails) E(OP_SendMaxCharacters) E(OP_SendCharInfo) E(OP_ExpansionInfo) +E(OP_SpawnAppearance) +//E(OP_SendAATable) +E(OP_PlayerProfile) E(OP_ZoneEntry) E(OP_ZoneSpawns) diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index c52f9e16f..7bc7a096a 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -3,7 +3,15 @@ namespace Larion { namespace structs { - + // constants + static const uint32 MAX_PP_AA_ARRAY = 300; + static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; + static const uint32 MAX_PP_INNATE_SKILL = 25; + static const uint32 MAX_PP_DISCIPLINES = 300; + static const uint32 MAX_PP_DISCIPLINE_TIMERS = 25; + static const uint32 MAX_RECAST_TYPES = 25; + static const uint32 MAX_ITEM_RECAST_TYPES = 100; + static const uint32 BUFF_COUNT = 62; #pragma pack(1) struct LoginInfo_Struct { @@ -170,6 +178,17 @@ namespace Larion { /*000*/ uint32 CharCount; //number of chars in this packet }; + struct SpawnAppearance_Struct + { + /*0000*/ uint32 spawn_id; // ID of the spawn + /*0004*/ uint32 type; // Values associated with the type + /*0008*/ uint32 parameter; // Type of data sent + /*0012*/ uint32 unknown012; + /*0016*/ uint32 unknown016; + /*0020*/ uint32 unknown020; + /*0024*/ + }; + struct Spawn_Struct_Bitfields { // byte 1 From ce728c4d0d21ecb829c5c9b1e18197795f0eefb3 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 10 Nov 2024 10:07:32 -0800 Subject: [PATCH 23/93] This is close but no cigar yet. --- common/base_packet.h | 1 + common/crc32.cpp | 18 + common/crc32.h | 1 + common/patches/larion.cpp | 1134 +++++++++++++++++++++++++------ common/patches/larion_limits.h | 2 +- common/patches/larion_ops.h | 4 +- common/patches/larion_structs.h | 4 +- common/patches/rof2.cpp | 2 +- 8 files changed, 966 insertions(+), 200 deletions(-) diff --git a/common/base_packet.h b/common/base_packet.h index 3b92e3e90..b5e3917d0 100644 --- a/common/base_packet.h +++ b/common/base_packet.h @@ -59,6 +59,7 @@ public: void WriteUInt8(uint8 value) { *(uint8 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8); } void WriteUInt32(uint32 value) { *(uint32 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint32); } void WriteUInt64(uint64 value) { *(uint64 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint64); } + void WriteSInt16(int32 value) { *(int16*)(pBuffer + _wpos) = value; _wpos += sizeof(int16); } void WriteUInt16(uint32 value) { *(uint16 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint16); } void WriteSInt32(int32 value) { *(int32 *)(pBuffer + _wpos) = value; _wpos += sizeof(int32); } void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); } diff --git a/common/crc32.cpp b/common/crc32.cpp index 0e9746a08..a2c7abe69 100644 --- a/common/crc32.cpp +++ b/common/crc32.cpp @@ -101,6 +101,24 @@ void CRC32::SetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at) memcpy(in_data, (char*)&check, 4); } +unsigned long CRC32::GetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at) +{ + unsigned long data; + unsigned long check = 0xffffffff; + + for (uint32 i = start_at; i < in_length; i++) + { + data = in_data[i]; + data = data ^ (check); + data = data & 0x000000ff; + check = check >> 8; + data = CRC32Table[data]; + check = check ^ data; + } + + return check; +} + uint32 CRC32::Update(const uint8* buf, uint32 bufsize, uint32 crc32var) { for(uint32 i=0; i < bufsize; i++) Calc(buf[i], crc32var); diff --git a/common/crc32.h b/common/crc32.h index 3a391a564..5ce463718 100644 --- a/common/crc32.h +++ b/common/crc32.h @@ -8,6 +8,7 @@ public: static uint32 Generate(const uint8* buf, uint32 bufsize); static uint32 GenerateNoFlip(const uint8* buf, uint32 bufsize); // Same as Generate(), but without the ~ static void SetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at=4); + static unsigned long GetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at = 4); // Multiple buffer CRC32 static uint32 Update(const uint8* buf, uint32 bufsize, uint32 crc32 = 0xFFFFFFFF); diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index d419f083a..577265c07 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -533,244 +533,988 @@ namespace Larion unsigned char* __emu_buffer = in->pBuffer; PlayerProfile_Struct* emu = (PlayerProfile_Struct*)__emu_buffer; - SerializeBuffer buffer; - buffer.WriteUInt32(0); //crc - buffer.WriteUInt32(0); //size; - buffer.WriteUInt32(0); //profile_type; - buffer.WriteUInt32(0); //profile_id; - buffer.WriteUInt32(0); //shroud_template_id; - buffer.WriteUInt8(emu->gender); - buffer.WriteUInt32(emu->race); - buffer.WriteUInt32(emu->class_); - buffer.WriteUInt8(emu->level); - buffer.WriteUInt8(emu->level2); + SerializeBuffer out; - buffer.WriteUInt32(5); //bind_count - for (int r = 0; r < 5; r++) //binds + /* + u32 crc; + u32 length; + */ + out.WriteUInt32(0); + out.WriteUInt32(0); + + /* + u32 profile_type; + u32 profile_id; + u32 shroud_template_id; + */ + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + + /* + u8 gender; + u32 race; + u32 class; + u8 level; + u8 level1; + */ + out.WriteUInt8(emu->gender); + out.WriteUInt32(emu->race); + out.WriteUInt32(emu->class_); + out.WriteUInt8(emu->level); + out.WriteUInt8(emu->level); + + //u32 bind_count; + out.WriteUInt32(5); + + for (int r = 0; r < 5; r++) { - buffer.WriteUInt32(emu->binds[r].zone_id); - buffer.WriteFloat(emu->binds[r].x); - buffer.WriteFloat(emu->binds[r].y); - buffer.WriteFloat(emu->binds[r].z); - buffer.WriteFloat(emu->binds[r].heading); + /* + u32 zoneid; + float x; + float y; + float z; + float heading; + */ + out.WriteUInt32(emu->binds[r].zone_id); + out.WriteFloat(emu->binds[r].x); + out.WriteFloat(emu->binds[r].y); + out.WriteFloat(emu->binds[r].z); + out.WriteFloat(emu->binds[r].heading); } - buffer.WriteUInt32(emu->deity); - buffer.WriteUInt32(emu->intoxication); + /* + u32 deity; + u32 intoxication; + */ + out.WriteUInt32(emu->deity); + out.WriteUInt32(emu->intoxication); - buffer.WriteUInt32(10); //properties count - for (int r = 0; r < 10; r++) //properties - { - buffer.WriteUInt32(0); + //u32 property_count; + out.WriteUInt32(10); // properties count + + //u32 properties[property_count]; + for (int i = 0; i < 10; i++) { + out.WriteUInt32(0); } - buffer.WriteUInt32(22); // Equipment count - - for (int r = EQ::textures::textureBegin; r < EQ::textures::materialCount; r++) - { - buffer.WriteUInt32(emu->item_material.Slot[r].Material); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); + //u32 armor_prop_count; + out.WriteUInt32(22); //armor count + for (int i = 0; i < 22; ++i) { + /* + s32 type; + s32 variation; + s32 material; + s32 newArmorId; + s32 newArmorType; + */ + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); } - // Write zeroes for the next 13 equipment slots - - for (int r = 0; r < 13; r++) - { - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); + //u32 base_armor_prop_count; + out.WriteUInt32(9); //base armor count + for (int i = 0; i < 9; ++i) { + /* + s32 type; + s32 variation; + s32 material; + s32 newArmorId; + s32 newArmorType; + */ + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); } - buffer.WriteUInt32(EQ::textures::materialCount); // Base Equipment count + //u32 body_tint_count; + out.WriteUInt32(9); //body_tint_count + //u32 body_tints[body_tint_count]; + for (int i = 0; i < 9; ++i) { + out.WriteUInt32(0); + } + + //u32 equip_tint_count; + out.WriteUInt32(9); //equip_tint_count + //u32 equip_tints[equip_tint_count]; + for (int i = 0; i < 9; ++i) { + out.WriteUInt32(0); + } + + /* + u8 hair_color; + u8 facial_hair_color; + u32 npc_tint_index; + u8 eye_color1; + u8 eye_color2; + u8 hair_style; + u8 facial_hair; + u8 face; + u8 old_face; + u32 heritage; + u32 tattoo; + u32 details; + */ + out.WriteUInt8(emu->haircolor); + out.WriteUInt8(emu->beardcolor); + out.WriteUInt32(0); //npc tint index + out.WriteUInt8(emu->eyecolor1); + out.WriteUInt8(emu->eyecolor2); + out.WriteUInt8(emu->hairstyle); + out.WriteUInt8(emu->beard); + out.WriteUInt8(emu->face); + out.WriteUInt8(0); //old face + out.WriteUInt32(emu->drakkin_heritage); + out.WriteUInt32(emu->drakkin_tattoo); + out.WriteUInt32(emu->drakkin_details); + + /* + u8 texture_type; + u8 material; + u8 variation; + */ + out.WriteUInt8(0); + out.WriteUInt8(0); + out.WriteUInt8(0); + + /* + float height; + float width; + float length; + float view_height; + */ + out.WriteFloat(5.0f); + out.WriteFloat(3.0f); + out.WriteFloat(2.5f); + out.WriteFloat(5.5f); + + /* + u32 primary; + u32 secondary; + */ + out.WriteUInt32(0); + out.WriteUInt32(0); + + /* + u32 practices; + u32 base_mana; + u32 base_hp; + u32 base_str; + u32 base_sta; + u32 base_cha; + u32 base_dex; + u32 base_int; + u32 base_agi; + u32 base_wis; + u32 base_heroic_str; + u32 base_heroic_sta; + u32 base_heroic_cha; + u32 base_heroic_dex; + u32 base_heroic_int; + u32 base_heroic_agi; + u32 base_heroic_wis; + */ + out.WriteUInt32(emu->points); + out.WriteUInt32(emu->mana); + out.WriteUInt32(emu->cur_hp); + out.WriteUInt32(emu->STR); + out.WriteUInt32(emu->STA); + out.WriteUInt32(emu->CHA); + out.WriteUInt32(emu->DEX); + out.WriteUInt32(emu->INT); + out.WriteUInt32(emu->AGI); + out.WriteUInt32(emu->WIS); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + + //u32 aa_count; + out.WriteUInt32(300); + for (int i = 0; i < 240; ++i) { + /* + s32 index; + s32 points_spent; + s32 charges_spent; + u8 unknown1; + */ + out.WriteUInt32(emu->aa_array[i].AA); + out.WriteUInt32(emu->aa_array[i].value); + out.WriteUInt32(emu->aa_array[i].charges); + out.WriteUInt8(0); + } + + for (int i = 0; i < 60; ++i) { + /* + s32 index; + s32 points_spent; + s32 charges_spent; + u8 unknown1; //not sure about this one + */ + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt8(0); + } + + /*u32 skill_count;*/ + out.WriteUInt32(100); + //s32 skills[skill_count]; + for (int i = 0; i < 100; ++i) { + out.WriteUInt32(emu->skills[i]); + } + + //u32 innate_skill_count; + out.WriteUInt32(25); + //s32 innate_skills[innate_skill_count]; + for (int i = 0; i < 25; ++i) { + out.WriteUInt32(emu->InnateSkills[i]); + } + + /* + u32 combat_ability_count; + */ + out.WriteUInt32(300); + //s32 combat_abilities[combat_ability_count]; + for (int i = 0; i < 100; ++i) { + out.WriteUInt32(emu->disciplines.values[i]); + } + + for (int i = 0; i < 200; ++i) { + out.WriteUInt32(0); + } + + //u32 combat_ability_timer_count; + out.WriteUInt32(25); + //s32 combat_ability_timers[combat_ability_timer_count]; + for (int i = 0; i < 20; ++i) { + out.WriteUInt32(emu->disciplines.values[i]); + } + + for (int i = 0; i < 5; ++i) { + out.WriteUInt32(0); + } + + //u32 unk_ability_count; + out.WriteUInt32(0); + + //u32 linked_spell_timer_count; + out.WriteUInt32(25); + //s32 linked_spell_timers[linked_spell_timer_count]; + for (int i = 0; i < 25; ++i) { + out.WriteUInt32(0); + } + + //u32 item_recast_timer_count; + out.WriteUInt32(100); + //s32 item_recast_timers[item_recast_timer_count]; + for (int i = 0; i < 100; ++i) { + out.WriteUInt32(0); + } + + //u32 spell_book_slot_count; + out.WriteUInt32(1120); + + //s32 spell_book_slots[spell_book_slot_count]; + for (int i = 0; i < 720; ++i) { + out.WriteUInt32(emu->spell_book[i]); + } + + for (int i = 0; i < 400; ++i) { + out.WriteUInt32(0xFFFFFFFF); + } + + //u32 spell_gem_count; + out.WriteUInt32(18); + //s32 spell_gems[spell_gem_count]; + for (int i = 0; i < 12; ++i) { + out.WriteUInt32(emu->mem_spells[i]); + } + + for (int i = 0; i < 6; ++i) { + out.WriteUInt32(0xFFFFFFFF); + } + + /* + u32 spell_recast_timer_count; + */ + out.WriteUInt32(15); + + //s32 spell_recast_timers[spell_recast_timer_count]; + for (int i = 0; i < 12; ++i) { + out.WriteUInt32(emu->spellSlotRefresh[i]); + } + + for (int i = 0; i < 3; ++i) { + out.WriteUInt32(0); + } + + //u8 max_allowed_spell_slots; + out.WriteUInt8(0); + + //u32 buff_count; + out.WriteUInt32(42); + + //PackedEQAffect buffs[buff_count]; + for (int i = 0; i < 42; ++i) { + /* + float modifier; + u32 caster; + u32 flags; + u32 duration; + u32 max_duration; + u8 level; + s32 spell_id; + s32 hitcount; + u8 unknown1; + u32 unknown2; + u32 unknown3; + */ + out.WriteFloat(1.0f); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt8(0); + out.WriteUInt32(0xFFFFFFFF); + out.WriteUInt32(0); + out.WriteUInt8(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + //SlotData slots[6]; + for (int j = 0; j < 6; ++j) { + /* + s32 slot_id; + s64 value; + */ + out.WriteInt32(-1); + out.WriteUInt64(0); + } + } + + //Coin coin; + /* + u32 platinum; + u32 gold; + u32 silver; + u32 copper; + */ + out.WriteUInt32(emu->platinum); + out.WriteUInt32(emu->gold); + out.WriteUInt32(emu->silver); + out.WriteUInt32(emu->copper); + + //Coin cursor_coin; + /* + u32 platinum; + u32 gold; + u32 silver; + u32 copper; + */ + out.WriteUInt32(emu->platinum_cursor); + out.WriteUInt32(emu->gold_cursor); + out.WriteUInt32(emu->silver_cursor); + out.WriteUInt32(emu->copper_cursor); + + /* + u32 disc_timer; + u32 mend_timer; + u32 forage_timer; + u32 thirst; + u32 hunger; + */ + + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(emu->thirst_level); + out.WriteUInt32(emu->hunger_level); + + //u32 aa_spent; + out.WriteUInt32(emu->aapoints_spent); + + //u32 aa_window_count; + out.WriteUInt32(6); + //u32 aa_window_stats[aa_window_count]; + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + + //u32 aa_points_unspent; + out.WriteUInt32(emu->aapoints); + + /* + u8 sneak; + u8 hide; + */ + out.WriteUInt8(0); + out.WriteUInt8(0); + + //u32 bandolier_count; + out.WriteUInt32(20); + + //BandolierSet bandolier_sets[bandolier_count]; + for (int i = 0; i < 20; ++i) { + //char name[]; + out.WriteString(emu->bandoliers[i].Name); + + //BandolierItemInfo items[4]; + for (int j = 0; j < 4; ++j) { + //char name[]; + out.WriteString(emu->bandoliers[i].Items[j].Name); + //s32 item_id; + out.WriteUInt32(emu->bandoliers[i].Items[j].ID); + //s32 icon; + out.WriteUInt32(emu->bandoliers[i].Items[j].Icon); + } + } + + //u32 invslot_bitmask; + out.WriteUInt32(0xFFFFFFFF); + + /* + u32 basedata_hp; + u32 basedata_mana; + u32 basedata_endur; + u32 basedata_mr; + u32 basedata_fr; + u32 basedata_cr; + u32 basedata_pr; + u32 basedata_dr; + u32 basedata_corrupt; + u32 basedata_phr; + */ + + out.WriteUInt32(5); + out.WriteUInt32(5); + out.WriteUInt32(5); + out.WriteUInt32(25); + out.WriteUInt32(25); + out.WriteUInt32(25); + out.WriteUInt32(15); + out.WriteUInt32(15); + out.WriteUInt32(15); + out.WriteUInt32(15); + + /* + float basedata_walkspeed; + float basedata_runspeed; + */ + + out.WriteFloat(0.46f); + out.WriteFloat(0.7f); + + /* + u32 basedata_hpregen; + u32 basedata_manaregen; + u32 basedata_mountmanaregen; + u32 basedata_endurregen; + u32 basedata_ac; + u32 basedata_atk; + u32 basedata_dmg; + u32 basedata_delay; + u32 endurance; + u32 heroic_type; + */ + + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(emu->endurance); + out.WriteUInt32(0); + + //ItemIndex keyring_item_index[5]; + for (int i = 0; i < 5; ++i) { + /* + s16 slot1; + s16 slot2; + s16 slot3; + */ + + out.WriteInt16(-1); + out.WriteInt16(-1); + out.WriteInt16(-1); + } + + /* + u64 exp; + u32 aa_exp; + */ + out.WriteUInt64(emu->exp); + out.WriteUInt32(emu->expAA); + + /* + u32 character_id; + u32 character_id2; + */ + out.WriteUInt32(emu->char_id); + out.WriteUInt32(100); + + /*u32 name_length;*/ + /*char name[name_length];*/ + out.WriteLengthString(64, emu->name); + + //u32 last_name_length; + //char last_name[last_name_length]; + out.WriteLengthString(32, emu->last_name); + + /* + u32 creation_time; + u32 account_creation_time; + u32 last_played_time; + u32 played_minutes; + u32 entitled_days; + u32 expansion_flags; + */ + + out.WriteUInt32(emu->birthday); + out.WriteUInt32(emu->birthday); + out.WriteUInt32(emu->lastlogin); + out.WriteUInt32(emu->timePlayedMin); + out.WriteUInt32(6000); + out.WriteUInt32(0x3FFFFFFF); + + //u32 language_count; + out.WriteUInt32(32); + for (int i = 0; i < 28; i++) + { + //u8 languages[language_count]; + out.WriteUInt8(emu->languages[i]); + } + + for (int i = 0; i < 4; i++) + { + out.WriteUInt8(0); + } + + /* + u32 current_zone; + float current_x; + float current_y; + float current_z; + float current_heading; + */ + out.WriteUInt16(emu->zone_id); + out.WriteUInt16(emu->zoneInstance); + out.WriteFloat(emu->x); + out.WriteFloat(emu->y); + out.WriteFloat(emu->z); + out.WriteFloat(emu->heading); + + /* + u8 animation; + u8 pvp; + u8 anon; + u8 gm; + */ + out.WriteUInt8(100); + out.WriteUInt8(emu->pvp); + out.WriteUInt8(emu->anon); + out.WriteUInt8(emu->gm); + + /* + u64 guild_id; + u8 guild_show_sprite; + u8 status; + */ + + out.WriteUInt64(0); + out.WriteUInt8(1); + out.WriteUInt8(5); + + //Coin coin; + out.WriteUInt32(emu->platinum); + out.WriteUInt32(emu->gold); + out.WriteUInt32(emu->silver); + out.WriteUInt32(emu->copper); + + //Coin bank; + out.WriteUInt32(emu->platinum_bank); + out.WriteUInt32(emu->gold_bank); + out.WriteUInt32(emu->silver_bank); + out.WriteUInt32(emu->copper_bank); + + //u32 bank_shared_plat; + out.WriteUInt32(emu->platinum_shared); + + //u32 claim_count; + out.WriteUInt32(0); + //Claim claims[claim_count]; - //live seems to send the materials you had when you created the character here - //not entirely sure for what purpose though + //Tribute tribute; + /* + u32 BenefitTimer; + s32 unknown1; + s32 current_favor; + s32 unknown2; + s32 all_time_favor; + s32 unknown3; //some of these are probably the bools on the pcclient; + u16 unknown4; + */ + out.WriteUInt32(600000); + out.WriteInt32(-1); + out.WriteUInt32(emu->tribute_points); + out.WriteUInt32(0); + out.WriteUInt32(emu->career_tribute_points); + out.WriteUInt32(0); + out.WriteUInt16(0); - for (int r = EQ::textures::textureBegin; r < EQ::textures::materialCount; r++) - { - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); + //u32 tribute_benefit_count + out.WriteUInt32(5); + + //TributeBenefit tribute_benefits[tribute_benefit_count]; + for (int i = 0; i < 5; ++i) { + /* + s32 benefit_id; + s32 benefit_tier; + */ + + out.WriteUInt32(emu->tributes[i].tribute); + out.WriteUInt32(emu->tributes[i].tier); } - buffer.WriteUInt32(EQ::textures::materialCount); // Tint Count + //u32 trophy_tribute_benefit_count; + out.WriteUInt32(10); - for (int r = 0; r < 7; r++) - { - buffer.WriteUInt32(emu->item_tint.Slot[r].Color); + //TributeBenefit trophy_tribute_benefit[trophy_tribute_benefit_count]; + for (int i = 0; i < 10; ++i) { + /* + s32 benefit_id; + s32 benefit_tier; + */ + + out.WriteUInt32(0xFFFFFFFF); + out.WriteUInt32(0); } - // Write extra two tint values - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - - buffer.WriteUInt32(EQ::textures::materialCount); // Equip Tint Count - for (int r = 0; r < 7; r++) + //u8 tasks[137]; //on live and on xac's capture from 12/28/23 these both are the same size; + for (int i = 0; i < 137; i++) { - buffer.WriteUInt32(emu->item_tint.Slot[r].Color); + out.WriteUInt8(0); } - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); + /* + u32 good_points_available; + u32 good_points_earned; + u32 bad_points_available; + u32 bad_points_earned; + */ - buffer.WriteUInt8(emu->haircolor); - buffer.WriteUInt8(emu->beardcolor); - buffer.WriteUInt32(0); //npc tint index - buffer.WriteUInt8(emu->eyecolor1); - buffer.WriteUInt8(emu->eyecolor2); - buffer.WriteUInt8(emu->hairstyle); - buffer.WriteUInt8(emu->beard); - buffer.WriteUInt8(emu->face); - buffer.WriteUInt8(0); //old_face - buffer.WriteUInt32(emu->drakkin_heritage); - buffer.WriteUInt32(emu->drakkin_tattoo); - buffer.WriteUInt32(emu->drakkin_details); - buffer.WriteInt8(-1); //texture type - buffer.WriteInt8(0); //material - buffer.WriteInt8(0); //variation - buffer.WriteFloat(5.0f); //height - buffer.WriteFloat(3.0f); //width - buffer.WriteFloat(2.5f); //length - buffer.WriteFloat(5.5f); //view height - buffer.WriteInt32(0); //primary_actor - buffer.WriteInt32(0); //secondary_actor + out.WriteUInt32(emu->currentRadCrystals); + out.WriteUInt32(emu->careerRadCrystals); + out.WriteUInt32(emu->currentEbonCrystals); + out.WriteUInt32(emu->careerEbonCrystals); - buffer.WriteUInt32(emu->points); // Unspent skill points - buffer.WriteUInt32(emu->mana); - buffer.WriteUInt32(emu->cur_hp); - buffer.WriteUInt32(emu->STR); - buffer.WriteUInt32(emu->STA); - buffer.WriteUInt32(emu->CHA); - buffer.WriteUInt32(emu->DEX); - buffer.WriteUInt32(emu->INT); - buffer.WriteUInt32(emu->AGI); - buffer.WriteUInt32(emu->WIS); - buffer.WriteUInt32(0); //heroic_sta - buffer.WriteUInt32(0); //heroic_str - buffer.WriteUInt32(0); //heroic_cha - buffer.WriteUInt32(0); //heroic_dex - buffer.WriteUInt32(0); //heroic_int - buffer.WriteUInt32(0); //heroic_agi - buffer.WriteUInt32(0); //heroic_wis + /* + u32 momentum_balance; + u32 loyalty_reward_balance; + u32 parcel_status; + */ - buffer.WriteUInt32(structs::MAX_PP_AA_ARRAY); // AA Count - for (uint32 r = 0; r < MAX_PP_AA_ARRAY; r++) - { - buffer.WriteUInt32(emu->aa_array[r].AA); - buffer.WriteUInt32(emu->aa_array[r].value); - buffer.WriteUInt32(emu->aa_array[r].charges); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + + /* + u32 vehicle_name_length; + char vehicle_name[vehicle_name_length]; + */ + + out.WriteUInt32(64); + for (int i = 0; i < 64; ++i) { + out.WriteUInt8(0); } - //honestly we should look to up the base size and just limit titanium later - for (uint32 r = 0; r < structs::MAX_PP_AA_ARRAY - MAX_PP_AA_ARRAY; r++) - { - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); + /* + u8 super_pkill; + u8 unclone; + u8 dead; + */ + + out.WriteUInt8(0); + out.WriteUInt8(0); + out.WriteUInt8(0); + + /* + u32 ld_timer; + u32 spell_interrupt_count; + u8 autosplit; + u8 tells_off; + */ + + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt8(emu->autosplit); + out.WriteUInt8(0); + + /* + u8 gm_invis; + u32 kill_me; + u8 cheater_ld_flag; + u8 norent; + u8 corpse; + u8 client_gm_flag_set; + u32 mentor_pct; + */ + + out.WriteUInt8(0); + out.WriteUInt32(0); + out.WriteUInt8(0); + out.WriteUInt8(0); + out.WriteUInt8(0); + out.WriteUInt8(0); + out.WriteUInt32(0); + + //RaidData raid; + /* + u32 main_assist1; + u32 main_assist2; + u32 main_assist3; + char main_assist_name1[]; + char main_assist_name2[]; + char main_assist_name3[]; + u32 main_marker1; + u32 main_marker2; + u32 main_marker3; + u32 master_looter; + */ + + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt8(0); + out.WriteUInt8(0); + out.WriteUInt8(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(0); + + //u32 unique_player_id; + out.WriteUInt32(emu->char_id); + + //LdonData ldon_data; + /* + u32 count; + u32 ldon_categories[count]; + u32 ldon_points_available; + */ + + out.WriteUInt32(6); + out.WriteUInt32(0); + out.WriteUInt32(emu->ldon_points_guk); + out.WriteUInt32(emu->ldon_points_mir); + out.WriteUInt32(emu->ldon_points_mmc); + out.WriteUInt32(emu->ldon_points_ruj); + out.WriteUInt32(emu->ldon_points_tak); + out.WriteUInt32(emu->ldon_points_available); + + //u32 air_supply; + out.WriteUInt32(emu->air_remaining); + + //PvPData pvp_data; + /* + u32 kills; + u32 deaths; + u32 current_points; + u32 career_points; + u32 best_kill_streak; + u32 worst_death_streak; + u32 current_kill_streak; + */ + + out.WriteUInt32(emu->PVPKills); + out.WriteUInt32(emu->PVPDeaths); + out.WriteUInt32(emu->PVPCurrentPoints); + out.WriteUInt32(emu->PVPCareerPoints); + out.WriteUInt32(emu->PVPBestKillStreak); + out.WriteUInt32(emu->PVPWorstDeathStreak); + out.WriteUInt32(emu->PVPCurrentKillStreak); + + //PvPKill last_kill; + /* + char name[]; + u32 level; + u32 unknown1; //not sure + u32 unknown2; //not sure + u32 race; + u32 class; + u32 zone; + u32 time; + u32 points; + */ + out.WriteString(emu->PVPLastKill.Name); + out.WriteUInt32(emu->PVPLastKill.Level); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(emu->PVPLastKill.Race); + out.WriteUInt32(emu->PVPLastKill.Class); + out.WriteUInt32(emu->PVPLastKill.Zone); + out.WriteUInt32(emu->PVPLastKill.Time); + out.WriteUInt32(emu->PVPLastKill.Points); + + //PvPDeath last_death; + /* + char name[]; + u32 level; + u32 race; + u32 class; + u32 zone; + u32 time; + u32 points; + */ + + out.WriteString(emu->PVPLastDeath.Name); + out.WriteUInt32(emu->PVPLastDeath.Level); + out.WriteUInt32(emu->PVPLastDeath.Race); + out.WriteUInt32(emu->PVPLastDeath.Class); + out.WriteUInt32(emu->PVPLastDeath.Zone); + out.WriteUInt32(emu->PVPLastDeath.Time); + out.WriteUInt32(emu->PVPLastDeath.Points); + + /* + u32 kills_in_past_24_hours; + */ + + out.WriteUInt32(emu->PVPNumberOfKillsInLast24Hours); + + //u32 kill_list_count; + out.WriteUInt32(50); + + //PvPKill kill_list[kill_list_count]; + for (int i = 0; i < 50; ++i) { + /* + char name[]; + u32 level; + u32 unknown1; //not sure + u32 unknown2; //not sure + u32 race; + u32 class; + u32 zone; + u32 time; + u32 points; + */ + out.WriteString(emu->PVPRecentKills[i].Name); + out.WriteUInt32(emu->PVPRecentKills[i].Level); + out.WriteUInt32(0); + out.WriteUInt32(0); + out.WriteUInt32(emu->PVPRecentKills[i].Race); + out.WriteUInt32(emu->PVPRecentKills[i].Class); + out.WriteUInt32(emu->PVPRecentKills[i].Zone); + out.WriteUInt32(emu->PVPRecentKills[i].Time); + out.WriteUInt32(emu->PVPRecentKills[i].Points); } - buffer.WriteUInt32(structs::MAX_PP_SKILL); + /* + u32 pvp_infamy_level; + u32 pvp_vitality; + */ + out.WriteUInt32(0); + out.WriteUInt32(0); - for (uint32 r = 0; r < structs::MAX_PP_SKILL; r++) - { - buffer.WriteUInt32(emu->skills[r]); - } + /* + u32 cursor_krono; + u32 krono; + */ + out.WriteUInt32(0); + out.WriteUInt32(0); - buffer.WriteUInt32(structs::MAX_PP_INNATE_SKILL); // Innate Skills count + /* + u8 autoconsent_group; + u8 autoconsent_raid; + u8 autoconsent_guild; + u8 autoconsent_fellowship; + */ - for (uint32 r = 0; r < structs::MAX_PP_INNATE_SKILL; r++) - { - buffer.WriteUInt32(emu->InnateSkills[r]); // Innate Skills (regen, slam, etc) - } + out.WriteUInt8(emu->groupAutoconsent); + out.WriteUInt8(emu->raidAutoconsent); + out.WriteUInt8(emu->guildAutoconsent); + out.WriteUInt8(1); - buffer.WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count + /* + u8 private_for_eq_players; + u32 main_level; + u8 show_helm; + u32 downtime; + */ + out.WriteUInt8(0); + out.WriteUInt32(emu->level); + out.WriteUInt8(emu->showhelm); + out.WriteUInt32(emu->RestTimer); - for (uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) - { - buffer.WriteUInt32(emu->disciplines.values[r]); - } + //AltCurrency alt_currency; + /* + u32 alt_currency_str_length; + u32 unknown1; + char alt_currency_string[alt_currency_str_length]; + */ + out.WriteUInt32(1); + out.WriteUInt32(0); + out.WriteUInt8(0x31); - for (uint32 r = 0; r < structs::MAX_PP_DISCIPLINES - MAX_PP_DISCIPLINES; r++) - { - buffer.WriteUInt32(0); - } + //u32 completed_event_subcomponent_count; + out.WriteUInt32(0); + //AchivementSubComponentData completed_event_subcomponents[completed_event_subcomponent_count]; - buffer.WriteUInt32(structs::MAX_PP_DISCIPLINE_TIMERS); // Disc timers + //u32 inprogress_event_subcomponent_count; + out.WriteUInt32(0); + //AchivementSubComponentData inprogress_event_subcomponents[inprogress_event_subcomponent_count]; - for (uint32 r = 0; r < structs::MAX_PP_DISCIPLINE_TIMERS; r++) - { - buffer.WriteUInt32(0); //todo: support sending actual timestamps - } + /* + u64 merc_aa_exp; + u32 merc_aa_points; + u32 merc_aa_spent; + */ + out.WriteUInt64(0); + out.WriteUInt32(0); + out.WriteUInt32(0); - buffer.WriteUInt32(0); // Unk Ability Count + //u32 starting_city_zone_id; + //we don't actually support this yet + out.WriteUInt32(1); - buffer.WriteUInt32(structs::MAX_RECAST_TYPES); // linked_spell_timer_count + /* + u8 use_advanced_looting; + u8 is_master_loot_candidate; + */ - for (uint32 r = 0; r < MAX_RECAST_TYPES; r++) - { - buffer.WriteUInt32(emu->recastTimers[r]); - } + out.WriteUInt8(1); + out.WriteUInt8(1); - for (uint32 r = 0; r < structs::MAX_RECAST_TYPES - MAX_RECAST_TYPES; r++) - { - buffer.WriteUInt32(0); - } + //alchemy_bonus_list_count + out.WriteUInt32(0); + //AlchemyBonusSkillData alchemy_bonus_list[alchemy_bonus_list_count]; - buffer.WriteUInt32(structs::MAX_ITEM_RECAST_TYPES); // item recast timers + //u32 persona_count; + out.WriteUInt32(0); + //PersonaEquipmentSet persona_equipment_set[persona_count]; - for (uint32 r = 0; r < structs::MAX_ITEM_RECAST_TYPES; r++) - { - buffer.WriteUInt32(0); - } + //u8 term; + out.WriteUInt8(0); - buffer.WriteUInt32(spells::SPELLBOOK_SIZE); - - for (uint32 r = 0; r < EQ::spells::SPELLBOOK_SIZE; r++) - { - if (emu->spell_book[r] <= spells::SPELL_ID_MAX) - buffer.WriteUInt32(emu->spell_book[r]); - else - buffer.WriteInt32(-1); - } - - for (uint32 r = 0; r < spells::SPELLBOOK_SIZE - EQ::spells::SPELLBOOK_SIZE; r++) - { - buffer.WriteInt32(-1); - } - - buffer.WriteUInt32(spells::SPELL_GEM_COUNT); // Memorised spell slots - - for (uint32 r = 0; r < EQ::spells::SPELL_GEM_COUNT; r++) // write first 12, we need to actually look into this for larion im sure it's changed - { - buffer.WriteUInt32(emu->mem_spells[r]); - } - - for (uint32 r = 0; r < spells::SPELL_GEM_COUNT - EQ::spells::SPELL_GEM_COUNT; r++) - { - buffer.WriteInt32(-1); - } - - buffer.WriteUInt32(spells::SPELL_GEM_RECAST_TIMER); - - for (uint32 r = 0; r < EQ::spells::SPELL_GEM_COUNT; r++) - { - buffer.WriteUInt32(emu->spellSlotRefresh[r]); // spell gem refresh - } - //also refresh - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - buffer.WriteUInt32(0); - - buffer.WriteUInt8(0); - - buffer.WriteUInt32(structs::BUFF_COUNT); + auto outapp = new EQApplicationPacket(OP_PlayerProfile, out.length()); + outapp->WriteData(out.buffer(), out.length()); + outapp->SetWritePosition(4); + outapp->WriteUInt32(outapp->size - 9); + CRC32::SetEQChecksum(outapp->pBuffer, outapp->size - 1, 8); + dest->FastQueuePacket(&outapp, ack_req); + delete in; } /*ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } diff --git a/common/patches/larion_limits.h b/common/patches/larion_limits.h index 74d0e9213..b6598c20f 100644 --- a/common/patches/larion_limits.h +++ b/common/patches/larion_limits.h @@ -310,7 +310,7 @@ namespace Larion AltAbility = 0xFF }; - const int SPELL_ID_MAX = 45000; + const int SPELL_ID_MAX = 71999; const int SPELLBOOK_SIZE = 1120; const int SPELL_GEM_COUNT = static_cast(CastingSlot::MaxGems); const int SPELL_GEM_RECAST_TIMER = 15; diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 6be56c322..44fd9c3ab 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -9,8 +9,8 @@ E(OP_ExpansionInfo) E(OP_SpawnAppearance) //E(OP_SendAATable) E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_ZoneSpawns) +//E(OP_ZoneEntry) +//E(OP_ZoneSpawns) //list of packets we need to decode on the way in: D(OP_ZoneEntry) diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 7bc7a096a..209e6dea3 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -8,10 +8,12 @@ namespace Larion { static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; static const uint32 MAX_PP_INNATE_SKILL = 25; static const uint32 MAX_PP_DISCIPLINES = 300; - static const uint32 MAX_PP_DISCIPLINE_TIMERS = 25; + static const uint32 MAX_PP_COMBAT_ABILITY_TIMERS = 25; + static const uint32 MAX_PP_UNKNOWN_ABILITIES = 25; static const uint32 MAX_RECAST_TYPES = 25; static const uint32 MAX_ITEM_RECAST_TYPES = 100; static const uint32 BUFF_COUNT = 62; + static const uint32 MAX_PP_LANGUAGE = 32; #pragma pack(1) struct LoginInfo_Struct { diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 9024f93b9..29c52f9ef 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -2680,7 +2680,7 @@ namespace RoF2 { float instrument_mod = 0.0f; uint8 effect_type = emu->buffs[r].effect_type; - uint32 player_id = emu->buffs[r].player_id;; + uint32 player_id = emu->buffs[r].player_id; if (emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) { From fa91559a856113c677024cc9de6b37acc6146b01 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 14 Nov 2024 22:13:36 -0800 Subject: [PATCH 24/93] Starting work on character guids --- common/eq_packet_structs.h | 7 ++++ common/patches/larion.cpp | 67 +++++++++++++++++++++++-------------- common/patches/larion_ops.h | 4 +-- common/ruletypes.h | 1 + 4 files changed, 51 insertions(+), 28 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index e9b80aaf0..b626e94a0 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -6435,6 +6435,13 @@ struct BuylineItemDetails_Struct { uint32 item_quantity; }; +struct EqGuid +{ + uint32_t Id; + uint16_t WorldId; + uint16_t Reserved; +}; + // Restore structure packing to default #pragma pack() diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 577265c07..5803fc11f 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -542,6 +542,7 @@ namespace Larion out.WriteUInt32(0); out.WriteUInt32(0); + //PcProfile begin /* u32 profile_type; u32 profile_id; @@ -1059,6 +1060,7 @@ namespace Larion out.WriteUInt64(emu->exp); out.WriteUInt32(emu->expAA); + //PcClient begin /* u32 character_id; u32 character_id2; @@ -1086,7 +1088,7 @@ namespace Larion out.WriteUInt32(emu->birthday); out.WriteUInt32(emu->birthday); out.WriteUInt32(emu->lastlogin); - out.WriteUInt32(emu->timePlayedMin); + out.WriteUInt32(5000); out.WriteUInt32(6000); out.WriteUInt32(0x3FFFFFFF); @@ -1135,7 +1137,7 @@ namespace Larion */ out.WriteUInt64(0); - out.WriteUInt8(1); + out.WriteUInt8(0); out.WriteUInt8(5); //Coin coin; @@ -1202,11 +1204,22 @@ namespace Larion out.WriteUInt32(0xFFFFFFFF); out.WriteUInt32(0); } + const uint8_t task_data[137] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, + }; //u8 tasks[137]; //on live and on xac's capture from 12/28/23 these both are the same size; for (int i = 0; i < 137; i++) { - out.WriteUInt8(0); + out.WriteUInt8(task_data[i]); } /* @@ -1453,7 +1466,7 @@ namespace Larion u8 show_helm; u32 downtime; */ - out.WriteUInt8(0); + out.WriteUInt8(1); out.WriteUInt32(emu->level); out.WriteUInt8(emu->showhelm); out.WriteUInt32(emu->RestTimer); @@ -1487,7 +1500,7 @@ namespace Larion //u32 starting_city_zone_id; //we don't actually support this yet - out.WriteUInt32(1); + out.WriteUInt32(394); /* u8 use_advanced_looting; @@ -1517,7 +1530,7 @@ namespace Larion delete in; } - /*ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } ENCODE(OP_ZoneSpawns) { @@ -1568,29 +1581,31 @@ namespace Larion buffer.WriteFloat(SpawnSize - 0.7f); } - buffer.WriteUInt32(emu->spawnId); //player "id" we should consider supporting this in the future - buffer.WriteUInt32(103); //not sure - buffer.WriteUInt8(emu->NPC); //npc/player flag + //buffer.WriteUInt32(0); //should be character id + //buffer.WriteUInt16(RuleI(World, Id)); //world id + //buffer.WriteUInt16(0); //reserved - structs::Spawn_Struct_Bitfields flags; - flags.gender = emu->gender; - flags.gender = emu->gender; - flags.ispet = emu->is_pet; - flags.afk = emu->afk; - flags.anon = emu->anon; - flags.gm = emu->gm; - flags.sneak = 0; - flags.lfg = emu->lfg; - flags.invis = emu->invis; - flags.linkdead = 0; - flags.showhelm = emu->showhelm; - flags.trader = emu->trader ? 1 : 0; - flags.targetable = 1; - flags.targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0; - flags.showname = emu->show_name; + //buffer.WriteUInt8(emu->NPC); //npc/player flag + + //structs::Spawn_Struct_Bitfields flags; + //flags.gender = emu->gender; + //flags.gender = emu->gender; + //flags.ispet = emu->is_pet; + //flags.afk = emu->afk; + //flags.anon = emu->anon; + //flags.gm = emu->gm; + //flags.sneak = 0; + //flags.lfg = emu->lfg; + //flags.invis = emu->invis; + //flags.linkdead = 0; + //flags.showhelm = emu->showhelm; + //flags.trader = emu->trader ? 1 : 0; + //flags.targetable = 1; + //flags.targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0; + //flags.showname = emu->show_name; } - }*/ + } // DECODE methods diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 44fd9c3ab..6be56c322 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -9,8 +9,8 @@ E(OP_ExpansionInfo) E(OP_SpawnAppearance) //E(OP_SendAATable) E(OP_PlayerProfile) -//E(OP_ZoneEntry) -//E(OP_ZoneSpawns) +E(OP_ZoneEntry) +E(OP_ZoneSpawns) //list of packets we need to decode on the way in: D(OP_ZoneEntry) diff --git a/common/ruletypes.h b/common/ruletypes.h index a42832527..77a1ee7f7 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -339,6 +339,7 @@ RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to ha RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C") RULE_BOOL(World, EnableAutoLogin, false, "Enables or disables auto login of characters, allowing people to log characters in directly from loginserver to ingame") RULE_BOOL(World, EnablePVPRegions, true, "Enables or disables PVP Regions automatically setting your PVP flag") +RULE_INT(World, Id, 100, "Used by later clients to create GUIDs, expected to be Unique to the world but ultimately not that important") RULE_CATEGORY_END() RULE_CATEGORY(Zone) From 200b7fa6046737fa5f319f348928037603193877 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 14 Nov 2024 22:32:26 -0800 Subject: [PATCH 25/93] Add prelim support for CharacterGuid to spawn struct. --- common/eq_packet_structs.h | 15 ++++++++------- zone/mob.cpp | 8 ++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 8c091ebea..93170c0a7 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -47,6 +47,13 @@ static const uint32 ADVANCED_LORE_LENGTH = 8192; */ #pragma pack(1) +struct EqGuid +{ + uint32_t Id; + uint16_t WorldId; + uint16_t Reserved; +}; + struct LoginInfo_Struct { /*000*/ char login_info[64]; /*064*/ uint8 unknown064[124]; @@ -324,6 +331,7 @@ union bool guild_show; bool trader; bool buyer; + EqGuid CharacterGuid; }; struct PlayerState_Struct { @@ -6436,13 +6444,6 @@ struct BuylineItemDetails_Struct { uint32 item_quantity; }; -struct EqGuid -{ - uint32_t Id; - uint16_t WorldId; - uint16_t Reserved; -}; - // Restore structure packing to default #pragma pack() diff --git a/zone/mob.cpp b/zone/mob.cpp index a06dbc743..ef544c671 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1275,6 +1275,14 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) strcpy(ns->spawn.name, name); if(IsClient()) { strn0cpy(ns->spawn.lastName, lastname, sizeof(ns->spawn.lastName)); + ns->spawn.CharacterGuid.Id = CastToClient()->CharacterID(); + ns->spawn.CharacterGuid.WorldId = RuleI(World, Id); + ns->spawn.CharacterGuid.Reserved = 0; + } + else { + ns->spawn.CharacterGuid.Id = 0; + ns->spawn.CharacterGuid.WorldId = 0; + ns->spawn.CharacterGuid.Reserved = 0; } ns->spawn.heading = FloatToEQ12(m_Position.w); From 2f46da5d9971a0fbd1aaa2d26c2f2284d61dc82f Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 15 Nov 2024 22:52:52 -0800 Subject: [PATCH 26/93] WIP Spawn struct, need to go confirm struct for cphysics --- common/patches/larion.cpp | 324 +++++++++++++++++++++++++++++--- common/patches/larion_structs.h | 67 ++++--- 2 files changed, 333 insertions(+), 58 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 5803fc11f..03faaecca 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -1063,10 +1063,12 @@ namespace Larion //PcClient begin /* u32 character_id; - u32 character_id2; + u16 world_id; + u16 reserved; */ out.WriteUInt32(emu->char_id); - out.WriteUInt32(100); + out.WriteUInt16(RuleI(World, Id)); + out.WriteUInt16(0); /*u32 name_length;*/ /*char name[name_length];*/ @@ -1567,44 +1569,312 @@ namespace Larion { SpawnSize = 3; } - + + /* + char Name[]; + u32 SpawnId; + u8 Level; + float MeleeRadius; + */ buffer.WriteString(emu->name); buffer.WriteUInt32(emu->spawnId); buffer.WriteUInt8(emu->level); if (emu->DestructibleObject) //bounding radius: we should consider supporting this officially in the future { - buffer.WriteFloat(10.0f); + buffer.WriteFloat(10.0f); } - else + else { - + buffer.WriteFloat(SpawnSize - 0.7f); } - - //buffer.WriteUInt32(0); //should be character id - //buffer.WriteUInt16(RuleI(World, Id)); //world id - //buffer.WriteUInt16(0); //reserved - //buffer.WriteUInt8(emu->NPC); //npc/player flag + /* + EqGuid HashKey; + */ + buffer.WriteUInt32(emu->CharacterGuid.Id); + buffer.WriteUInt16(emu->CharacterGuid.WorldId); + buffer.WriteUInt16(0); + + /* + u8 Type; + ActorFlags Flags; + */ + buffer.WriteUInt8(emu->NPC); + + structs::Spawn_Struct_Bitfields flags; + flags.gender = emu->gender; + flags.ispet = emu->is_pet; + flags.afk = emu->afk; + flags.anon = emu->anon; + flags.gm = emu->gm; + flags.sneak = 0; + flags.lfg = emu->lfg; + flags.invis = emu->invis; //we need to implement this + flags.linkdead = 0; //on live I often see this as 1 for npcs, maybe consider adding this in the future + flags.showhelm = emu->showhelm; + flags.trader = emu->trader ? 1 : 0; + flags.targetable = 1; + flags.targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0; + flags.showname = emu->show_name; + flags.buyer = emu->buyer ? 1 : 0; + flags.title = strlen(emu->title) > 0 ? 1 : 0; + flags.suffix = strlen(emu->suffix) > 0 ? 1 : 0; + + if (emu->DestructibleObject || emu->class_ == Class::LDoNTreasure) { + flags.interactiveobject = 1; + } + + //write flags + buffer.WriteUInt8(flags.data[0]); + buffer.WriteUInt8(flags.data[1]); + buffer.WriteUInt8(flags.data[2]); + buffer.WriteUInt8(flags.data[3]); + buffer.WriteUInt8(flags.data[4]); + + /* + float EmitterScalingRadius; + s32 DefaultEmitterID; + */ + + //we don't support this atm; wouldn't be hard to add I don't think + //RoF also supports this so might be worth implementing. + buffer.WriteFloat(1.0f); + buffer.WriteInt32(-1); + + if (emu->DestructibleObject || emu->class_ == Class::LDoNTreasure) + { + /* + char InteractiveObjectModelName[]; + char InteractiveObjectName[]; + char InteractiveObjectOtherName[]; + */ + + buffer.WriteString(emu->DestructibleModel); + buffer.WriteString(emu->DestructibleName2); + buffer.WriteString(emu->DestructibleString); + + /* + s32 CurrIOState; + s32 ObjectAnimationID; + */ + buffer.WriteUInt32(emu->DestructibleAppearance); + buffer.WriteUInt32(emu->DestructibleUnk1); + + /* + s32 SoundId[10]; + */ + buffer.WriteUInt32(emu->DestructibleID1); + buffer.WriteUInt32(emu->DestructibleID2); + buffer.WriteUInt32(emu->DestructibleID3); + buffer.WriteUInt32(emu->DestructibleID4); + buffer.WriteUInt32(emu->DestructibleUnk2); + buffer.WriteUInt32(emu->DestructibleUnk3); + buffer.WriteUInt32(emu->DestructibleUnk4); + buffer.WriteUInt32(emu->DestructibleUnk5); + buffer.WriteUInt32(emu->DestructibleUnk6); + buffer.WriteUInt32(emu->DestructibleUnk7); + /* + s8 Collidable; + s8 ObjectType; + */ + buffer.WriteUInt8(emu->DestructibleUnk8); + buffer.WriteUInt8(emu->DestructibleUnk9); + } + + /* + u8 PropertyCount; + u32 Properties[PropertyCount]; + */ + //We don't actually support multiple body types yet, but we should consider it in the future + + if (!emu->DestructibleObject) + { + buffer.WriteUInt8(1); + buffer.WriteUInt32(emu->bodytype); + } + else + { + buffer.WriteUInt8(0); + } + + /* + u8 HPCurrentPct; + */ + buffer.WriteUInt8(emu->curHp); + + /* + s8 HairColor; + s8 FacialHairColor; + s8 EyeColor1; + s8 EyeColor2; + s8 HairStyle; + s8 FacialHair; + s32 Heritage; + s32 Tattoo; + s32 Details; + */ + + buffer.WriteUInt8(emu->haircolor); + buffer.WriteUInt8(emu->beardcolor); + buffer.WriteUInt8(emu->eyecolor1); + buffer.WriteUInt8(emu->eyecolor2); + buffer.WriteUInt8(emu->hairstyle); + buffer.WriteUInt8(emu->beard); + buffer.WriteUInt32(emu->drakkin_heritage); + buffer.WriteUInt32(emu->drakkin_tattoo); + buffer.WriteUInt32(emu->drakkin_details); + + /* + s8 TextureType; + s8 Material; + s8 Variation; + s8 HeadType; + */ + buffer.WriteUInt8(emu->equip_chest2); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(emu->helm); + + /* + float Height; + s8 FaceStyle; + float MyWalkSpeed; + float RunSpeed; + s32 Race; + */ + + buffer.WriteFloat(emu->size); + buffer.WriteInt8(emu->face); + buffer.WriteFloat(emu->walkspeed); + buffer.WriteFloat(emu->runspeed); + buffer.WriteInt32(emu->race); + + /* + u8 HoldingAnimation; + u32 Deity; + EqGuid GuildID; + u32 Class; + */ + + buffer.WriteUInt8(0); + buffer.WriteUInt32(emu->deity); + if (emu->NPC) { + buffer.WriteInt32(-1); + buffer.WriteUInt32(0); + } + else { //guilds will probably need a ton of work + buffer.WriteUInt32(emu->guildID); + buffer.WriteUInt32(0); + } + buffer.WriteUInt32(emu->class_); + + /* + u8 PvP; + u8 StandState; + u8 Light; + u8 GravityBehavior; + */ + + buffer.WriteUInt8(0); + buffer.WriteUInt8(emu->StandState); + buffer.WriteUInt8(emu->light); + buffer.WriteUInt8(emu->flymode); + + /* + char LastName[]; + */ + buffer.WriteString(emu->lastName); + + /* + u8 bGuildShowAnim; + u8 bTempPet; + u32 MasterID; + u8 FindBits; + */ + + buffer.WriteUInt8(emu->guild_show); + buffer.WriteUInt8(0); + buffer.WriteUInt32(emu->petOwnerId); + buffer.WriteUInt8(0); + + /* + u32 PlayerState; + u32 NpcTintIndex; + u32 PrimaryTintIndex; + u32 SecondaryTintIndex; + */ + + buffer.WriteUInt32(emu->PlayerState); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + + /* + u32 EncounterLockState; + u64 LockID; + */ + + buffer.WriteUInt32(0); + buffer.WriteUInt64(0); + + //u32 SeeInvis[3]; + if (emu->NPC) { + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + } + + if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) || + (emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin) + ) + { + /* + u32 ArmorColor[9]; + */ + for (int k = EQ::textures::textureBegin; k < EQ::textures::materialCount; ++k) + { + buffer.WriteUInt32(emu->equipment_tint.Slot[k].Color); + } + + /* + Armor Armor[9]; + */ + for (int k = EQ::textures::textureBegin; k < EQ::textures::materialCount; k++) { + buffer.WriteUInt32(emu->equipment.Slot[k].Material); + buffer.WriteUInt32(emu->equipment.Slot[k].Unknown1); + buffer.WriteUInt32(emu->equipment.Slot[k].EliteModel); + buffer.WriteUInt32(emu->equipment.Slot[k].HerosForgeModel); + buffer.WriteUInt32(emu->equipment.Slot[k].Unknown2); + } + } + else + { + //Armor Armor[3]; + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + + buffer.WriteUInt32(emu->equipment.Primary.Material); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + + buffer.WriteUInt32(emu->equipment.Secondary.Material); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + } + + //u8 CPhysicsData[20]; - //structs::Spawn_Struct_Bitfields flags; - //flags.gender = emu->gender; - //flags.gender = emu->gender; - //flags.ispet = emu->is_pet; - //flags.afk = emu->afk; - //flags.anon = emu->anon; - //flags.gm = emu->gm; - //flags.sneak = 0; - //flags.lfg = emu->lfg; - //flags.invis = emu->invis; - //flags.linkdead = 0; - //flags.showhelm = emu->showhelm; - //flags.trader = emu->trader ? 1 : 0; - //flags.targetable = 1; - //flags.targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0; - //flags.showname = emu->show_name; } + } // DECODE methods diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 209e6dea3..acc463c27 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -193,37 +193,42 @@ namespace Larion { struct Spawn_Struct_Bitfields { - // byte 1 - /*00*/ unsigned gender : 2; // Gender (0=male, 1=female, 2=monster) - /*02*/ unsigned ispet : 1; // Guessed based on observing live spawns - /*03*/ unsigned afk : 1; // 0=no, 1=afk - /*04*/ unsigned anon : 2; // 0=normal, 1=anon, 2=roleplay - /*06*/ unsigned gm : 1; - /*07*/ unsigned sneak : 1; - // byte 2 - /*08*/ unsigned lfg : 1; - /*09*/ unsigned unk9 : 1; - /*10*/ unsigned invis : 12; // there are 3000 different (non-GM) invis levels - /*22*/ unsigned linkdead : 1; // 1 Toggles LD on or off after name. Correct for RoF2 - /*23*/ unsigned showhelm : 1; - // byte 4 - /*24*/ unsigned betabuffed : 1; // Prefixes name with ! - /*25*/ unsigned trader : 1; - /*26*/ unsigned animationonpop : 1; - /*27*/ unsigned targetable : 1; - /*28*/ unsigned targetable_with_hotkey : 1; - /*29*/ unsigned showname : 1; - /*30*/ unsigned idleanimationsoff : 1; // what we called statue? - /*31*/ unsigned untargetable : 1; // bClickThrough - // byte 5 - /*32*/ unsigned buyer : 1; - /*33*/ unsigned offline : 1; - /*34*/ unsigned interactiveobject : 1; - /*35*/ unsigned missile : 1; - /*36*/ unsigned title : 1; - /*37*/ unsigned suffix : 1; - /*38*/ unsigned unk38 : 1; - /*39*/ unsigned unk39 : 1; + union { + struct { + // byte 1 + /*00*/ unsigned gender : 2; // Gender (0=male, 1=female, 2=monster) + /*02*/ unsigned ispet : 1; // Guessed based on observing live spawns + /*03*/ unsigned afk : 1; // 0=no, 1=afk + /*04*/ unsigned anon : 2; // 0=normal, 1=anon, 2=roleplay + /*06*/ unsigned gm : 1; + /*07*/ unsigned sneak : 1; + // byte 2 + /*08*/ unsigned lfg : 1; + /*09*/ unsigned unk9 : 1; + /*10*/ unsigned invis : 12; // there are 3000 different (non-GM) invis levels + /*22*/ unsigned linkdead : 1; // 1 Toggles LD on or off after name. Correct for RoF2 + /*23*/ unsigned showhelm : 1; + // byte 4 + /*24*/ unsigned betabuffed : 1; // Prefixes name with ! + /*25*/ unsigned trader : 1; + /*26*/ unsigned animationonpop : 1; + /*27*/ unsigned targetable : 1; + /*28*/ unsigned targetable_with_hotkey : 1; + /*29*/ unsigned showname : 1; + /*30*/ unsigned idleanimationsoff : 1; // what we called statue? + /*31*/ unsigned untargetable : 1; // bClickThrough + // byte 5 + /*32*/ unsigned buyer : 1; + /*33*/ unsigned offline : 1; + /*34*/ unsigned interactiveobject : 1; + /*35*/ unsigned missile : 1; + /*36*/ unsigned title : 1; + /*37*/ unsigned suffix : 1; + /*38*/ unsigned unk38 : 1; + /*39*/ unsigned unk39 : 1; + }; + uint8_t data[5]; + }; }; #pragma pack() From 45d39f44f2d1f55dfc80ba6d9c165335a2a6dcdb Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 15 Nov 2024 23:53:08 -0800 Subject: [PATCH 27/93] WIP on the larion phsyics struct --- common/patches/larion_structs.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index acc463c27..bc8b851c1 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -230,6 +230,27 @@ namespace Larion { uint8_t data[5]; }; }; + + struct Spawn_Struct_Position + { + signed y : 19; + signed deltaX : 13; + + unsigned heading : 12; + signed z : 19; + unsigned pad1 : 1; + + unsigned pitch : 12; + signed animation : 10; //these might be swapped + signed deltaHeading : 10; //these might be swapped + + signed deltaY : 13; + signed deltaZ : 13; + unsigned pad3 : 6; + + signed x : 19; + unsigned pad4 : 13; + }; #pragma pack() }; //end namespace structs From 7888fb26550f47b36f22809aca40d2376f827054 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 16 Nov 2024 11:52:06 -0800 Subject: [PATCH 28/93] initial implementation of zone entry packet. --- common/patches/larion.cpp | 104 ++++++++++++++++++++++++++++++-- common/patches/larion_structs.h | 67 ++++++++++---------- common/serialize_buffer.h | 15 +++++ 3 files changed, 144 insertions(+), 42 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 03faaecca..33d0348d4 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -1626,11 +1626,7 @@ namespace Larion } //write flags - buffer.WriteUInt8(flags.data[0]); - buffer.WriteUInt8(flags.data[1]); - buffer.WriteUInt8(flags.data[2]); - buffer.WriteUInt8(flags.data[3]); - buffer.WriteUInt8(flags.data[4]); + buffer.WriteStructure(flags); /* float EmitterScalingRadius; @@ -1871,10 +1867,106 @@ namespace Larion } //u8 CPhysicsData[20]; + structs::Spawn_Struct_Position position; + position.y = emu->y; + position.deltaZ = emu->deltaZ; + position.deltaX = emu->deltaX; + position.x = emu->x; + position.heading = emu->heading; + position.deltaHeading = emu->deltaHeading; + position.z = emu->z; + position.animation = emu->animation; + position.deltaY = emu->deltaY; + buffer.WriteStructure(position); + + /* + if(Flags.title) { + char Title[]; + } + */ + if (flags.title) { + buffer.WriteString(emu->title); + } + + /* + if(Flags.suffix) { + char Suffix[]; + } + */ + if (flags.suffix) { + buffer.WriteString(emu->suffix); + } + + /* + u32 Unknown0x0164; + s32 SplineID; + */ + + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + + /* + u8 Mercenary; + */ + buffer.WriteUInt8(emu->IsMercenary); + + /* + char realEstateItemGuid[]; + s32 RealEstateID; + s32 RealEstateItemId; + */ + + buffer.WriteString("0000000000000000"); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + + /* + s32 MercId; + s32 ContractorID; + u32 Birthdate; + u8 bAlwaysShowAura; + */ + + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt8(0); + + /* + u32 physicsEffectCount; + PhysicsEffect physicsEffects[physicsEffectCount]; + */ + buffer.WriteUInt32(0); + + //s32 SpawnStatus[6]; + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + + if (flags.interactiveobject && emu->DestructibleUnk9 == 4) { + /* + s32 BannerIndex0; + s32 BannerIndex1; + s32 BannerTint0; + s32 BannerTint1; + */ + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + buffer.WriteUInt32(0); + } + + auto outapp = new EQApplicationPacket(OP_ZoneEntry, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); } - + + delete in; } // DECODE methods diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index bc8b851c1..eb69dfce5 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -193,42 +193,37 @@ namespace Larion { struct Spawn_Struct_Bitfields { - union { - struct { - // byte 1 - /*00*/ unsigned gender : 2; // Gender (0=male, 1=female, 2=monster) - /*02*/ unsigned ispet : 1; // Guessed based on observing live spawns - /*03*/ unsigned afk : 1; // 0=no, 1=afk - /*04*/ unsigned anon : 2; // 0=normal, 1=anon, 2=roleplay - /*06*/ unsigned gm : 1; - /*07*/ unsigned sneak : 1; - // byte 2 - /*08*/ unsigned lfg : 1; - /*09*/ unsigned unk9 : 1; - /*10*/ unsigned invis : 12; // there are 3000 different (non-GM) invis levels - /*22*/ unsigned linkdead : 1; // 1 Toggles LD on or off after name. Correct for RoF2 - /*23*/ unsigned showhelm : 1; - // byte 4 - /*24*/ unsigned betabuffed : 1; // Prefixes name with ! - /*25*/ unsigned trader : 1; - /*26*/ unsigned animationonpop : 1; - /*27*/ unsigned targetable : 1; - /*28*/ unsigned targetable_with_hotkey : 1; - /*29*/ unsigned showname : 1; - /*30*/ unsigned idleanimationsoff : 1; // what we called statue? - /*31*/ unsigned untargetable : 1; // bClickThrough - // byte 5 - /*32*/ unsigned buyer : 1; - /*33*/ unsigned offline : 1; - /*34*/ unsigned interactiveobject : 1; - /*35*/ unsigned missile : 1; - /*36*/ unsigned title : 1; - /*37*/ unsigned suffix : 1; - /*38*/ unsigned unk38 : 1; - /*39*/ unsigned unk39 : 1; - }; - uint8_t data[5]; - }; + // byte 1 + /*00*/ unsigned gender : 2; // Gender (0=male, 1=female, 2=monster) + /*02*/ unsigned ispet : 1; // Guessed based on observing live spawns + /*03*/ unsigned afk : 1; // 0=no, 1=afk + /*04*/ unsigned anon : 2; // 0=normal, 1=anon, 2=roleplay + /*06*/ unsigned gm : 1; + /*07*/ unsigned sneak : 1; + // byte 2 + /*08*/ unsigned lfg : 1; + /*09*/ unsigned unk9 : 1; + /*10*/ unsigned invis : 12; // there are 3000 different (non-GM) invis levels + /*22*/ unsigned linkdead : 1; // 1 Toggles LD on or off after name. Correct for RoF2 + /*23*/ unsigned showhelm : 1; + // byte 4 + /*24*/ unsigned betabuffed : 1; // Prefixes name with ! + /*25*/ unsigned trader : 1; + /*26*/ unsigned animationonpop : 1; + /*27*/ unsigned targetable : 1; + /*28*/ unsigned targetable_with_hotkey : 1; + /*29*/ unsigned showname : 1; + /*30*/ unsigned idleanimationsoff : 1; // what we called statue? + /*31*/ unsigned untargetable : 1; // bClickThrough + // byte 5 + /*32*/ unsigned buyer : 1; + /*33*/ unsigned offline : 1; + /*34*/ unsigned interactiveobject : 1; + /*35*/ unsigned missile : 1; + /*36*/ unsigned title : 1; + /*37*/ unsigned suffix : 1; + /*38*/ unsigned unk38 : 1; + /*39*/ unsigned unk39 : 1; }; struct Spawn_Struct_Position diff --git a/common/serialize_buffer.h b/common/serialize_buffer.h index 65ae36f70..1eda4da80 100644 --- a/common/serialize_buffer.h +++ b/common/serialize_buffer.h @@ -181,6 +181,21 @@ public: m_pos += len; } + template + void WriteStructurePtr(T *value) { + auto type_size = sizeof(T); + + if (m_pos + type_size > m_capacity) + Grow(m_capacity + type_size); + memcpy(m_buffer + m_pos, value, type_size); + m_pos += sizeof(type_size); + } + + template + void WriteStructure(T& value) { + WriteStructurePtr(&value); + } + size_t size() const { return m_pos; } size_t length() const { return size(); } size_t capacity() const { return m_capacity; } From da9a95fd833eccb48b9d7f3774a2ef97948d37c0 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 16 Nov 2024 12:13:55 -0800 Subject: [PATCH 29/93] WIP zone header --- common/patches/larion.cpp | 115 ++++++++++++++++++++++++++++++++ common/patches/larion_ops.h | 2 + common/patches/larion_structs.h | 2 + 3 files changed, 119 insertions(+) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 33d0348d4..91539da7c 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -1969,6 +1969,121 @@ namespace Larion delete in; } + ENCODE(OP_CharInventory) { + //consume the packet + EQApplicationPacket* in = *p; + *p = nullptr; + + if (!in->size) { + in->size = 4; + in->pBuffer = new uchar[in->size]; + memset(in->pBuffer, 0, in->size); + + dest->FastQueuePacket(&in, ack_req); + return; + } + + //store away the emu struct + uchar* __emu_buffer = in->pBuffer; + + int item_count = in->size / sizeof(EQ::InternalSerializedItem_Struct); + if (!item_count || (in->size % sizeof(EQ::InternalSerializedItem_Struct)) != 0) { + Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(EQ::InternalSerializedItem_Struct)); + + delete in; + return; + } + + EQ::InternalSerializedItem_Struct* eq = (EQ::InternalSerializedItem_Struct*)in->pBuffer; + + auto outapp = new EQApplicationPacket(OP_CharInventory, 4); + dest->FastQueuePacket(&outapp, ack_req); + + delete in; + } + + ENCODE(OP_NewZone) { + EQApplicationPacket* in = *p; + *p = nullptr; + + unsigned char* __emu_buffer = in->pBuffer; + NewZone_Struct* emu = (NewZone_Struct*)__emu_buffer; + + SerializeBuffer buffer; + /* + char Shortname[]; + char Longname[]; + char WeatherType[]; + char WeatherTypeOverride[]; + char SkyType[]; + char SkyTypeOverride[]; + */ + buffer.WriteString(emu->zone_short_name); + buffer.WriteString(emu->zone_long_name); + buffer.WriteString(""); + buffer.WriteString(""); + buffer.WriteString(""); + buffer.WriteString(""); + + /* + s32 ZoneType; + s16 ZoneId; + s16 ZoneInstance; + float ZoneExpModifier; + s32 GroupLvlExpRelated; + s32 FilterID; + s32 Unknown1; + */ + buffer.WriteInt32(emu->ztype); + buffer.WriteInt16(emu->zone_id); + buffer.WriteInt16(emu->zone_instance); + buffer.WriteFloat(emu->zone_exp_multiplier); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + + //float FogDensity; + buffer.WriteFloat(emu->fog_density); + + //WeatherState state[4]; + for (int i = 0; i < 4; ++i) { + /* + float FogStart; + float FogEnd; + u8 FogRed; + u8 FogGreen; + u8 FogBlue; + u8 RainChance; + u8 RainDuration; + u8 SnowChance; + u8 SnowDuration; + */ + + buffer.WriteFloat(emu->fog_minclip[i]); + buffer.WriteFloat(emu->fog_maxclip[i]); + buffer.WriteUInt8(emu->fog_red[i]); + buffer.WriteUInt8(emu->fog_green[i]); + buffer.WriteUInt8(emu->fog_blue[i]); + buffer.WriteUInt8(emu->rain_chance[i]); + buffer.WriteUInt8(emu->rain_duration[i]); + buffer.WriteUInt8(emu->snow_chance[i]); + buffer.WriteUInt8(emu->snow_duration[i]); + + } + + /* + u8 PrecipitationType; + float BloomIntensity; + float ZoneGravity; + s32 LavaDamage; + s32 MinLavaDamage; + */ + //buffer.WriteUInt8(emu->); + + delete in; + } + // DECODE methods DECODE(OP_ZoneEntry) diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 6be56c322..157913f6b 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -11,6 +11,8 @@ E(OP_SpawnAppearance) E(OP_PlayerProfile) E(OP_ZoneEntry) E(OP_ZoneSpawns) +E(OP_CharInventory) +E(OP_NewZone) //list of packets we need to decode on the way in: D(OP_ZoneEntry) diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index eb69dfce5..06c541982 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -246,6 +246,8 @@ namespace Larion { signed x : 19; unsigned pad4 : 13; }; + + #pragma pack() }; //end namespace structs From 29066cf847b51fc7a2eb7fdaa6e7dfebeed42443 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 16 Nov 2024 14:05:31 -0800 Subject: [PATCH 30/93] Zone header needs a lot of love later, hopefully this is close enough --- common/patches/larion.cpp | 166 +++++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 1 deletion(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 91539da7c..bb797bcce 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2079,7 +2079,171 @@ namespace Larion s32 LavaDamage; s32 MinLavaDamage; */ - //buffer.WriteUInt8(emu->); + buffer.WriteUInt8(emu->sky); + buffer.WriteFloat(1.0f); + buffer.WriteFloat(emu->gravity); + buffer.WriteInt32(emu->lava_damage); + buffer.WriteInt32(emu->min_lava_damage); + + /* + s32 TimeStringID; + s32 Unknown3; + s32 SkyLock; + s32 SkyLockOverride; + */ + buffer.WriteInt32(0); + buffer.WriteInt32(1); + buffer.WriteInt32(0); + buffer.WriteInt32(-1); + + /* + float SafeY; + float SafeX; + float SafeZ; + float SafeHeading; + float Ceiling; + float Floor; + */ + + buffer.WriteFloat(emu->safe_y); + buffer.WriteFloat(emu->safe_x); + buffer.WriteFloat(emu->safe_z); + buffer.WriteFloat(emu->safe_heading); + buffer.WriteFloat(emu->max_z); + buffer.WriteFloat(emu->underworld); + + /* + float MinClip; + float MaxClip; + s32 FallThroughWorldTeleportID; + */ + buffer.WriteFloat(emu->minclip); + buffer.WriteFloat(emu->maxclip); + buffer.WriteInt32(emu->underworld_teleport_index); + + /* + s32 Unknown4; + s32 ScriptIDHour; + s32 ScriptIDMinute; + s32 ScriptIDTick; + s32 ScriptIDOnPlayerDeath; + s32 ScriptIDOnNPCDeath; + s32 ScriptIDPlayerEnteringZone; + s32 ScriptIDOnZonePop; + s32 ScriptIDNPCLoot; + s32 Unknown4b; + s32 ScriptIDOnFishing; + s32 ScriptIDOnForage; + s32 Unknown4c; + */ + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + + /* + s32 NPCAgroMaxDist; + */ + buffer.WriteInt32(600); + + /* + s32 ForageLow; + s32 ForageMedium; + s32 ForageHigh; + s32 ForageSpecial; + s32 FishingLow; + s32 FishingMedium; + s32 FishingHigh; + s32 FishingRelated; + */ + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + + /* + s32 CanPlaceCampsite; + s32 CanPlaceGuildBanner; + s32 Unknown4d; + */ + + buffer.WriteInt32(2); + buffer.WriteInt32(2); + buffer.WriteInt32(0); + + /* + s32 FastRegenHP; + s32 FastRegenMana; + s32 FastRegenEndurance; + */ + buffer.WriteInt32(emu->fast_regen_hp); + buffer.WriteInt32(emu->fast_regen_mana); + buffer.WriteInt32(emu->fast_regen_endurance); + + /* + u8 NewEngineZone; + u8 SkyEnabled; + u8 FogOnOff; + u8 ClimateType; + u8 bNoPlayerLight; + */ + buffer.WriteUInt8(0); //not sure what happens if we set this incorrectly but we probably need to add this to the zone database + buffer.WriteUInt8(1); + buffer.WriteUInt8(1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + + /* + u8 bUnknown5; + u8 bNoAttack; + u8 bAllowPVP; + u8 bNoEncumber; + u8 Unknown6; + u8 Unknown7; + */ + buffer.WriteUInt8(1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + + /* + u8 bNoLevitate; + u8 bNoBuffExpiration; + u8 bDisallowManaStone; + u8 bNoBind; + u8 bNoCallOfTheHero; + u8 bUnknown8; + u8 bNoFear; + u8 bUnknown9; + */ + + buffer.WriteUInt8(0); + buffer.WriteUInt8(emu->suspend_buffs); + buffer.WriteUInt8(1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + + auto outapp = new EQApplicationPacket(OP_NewZone, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); delete in; } From 018308bfca84c24da239e57ee5327d9f30e45adb Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 16 Nov 2024 17:08:24 -0800 Subject: [PATCH 31/93] Going to work on op mapping; we're making it past add player at least. --- common/patches/larion.cpp | 93 +++++++++++++++++++++++-------------- common/patches/larion_ops.h | 4 +- 2 files changed, 60 insertions(+), 37 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index bb797bcce..d434464c6 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -489,42 +489,42 @@ namespace Larion FINISH_ENCODE(); } - ENCODE(OP_SpawnAppearance) - { - EQApplicationPacket* in = *p; - *p = nullptr; - - unsigned char* emu_buffer = in->pBuffer; - - SpawnAppearance_Struct* sas = (SpawnAppearance_Struct*)emu_buffer; - - if (sas->type != AppearanceType::Size) - { - //larion struct is different than rof2's but the idea is the same - auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); - structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; - - eq->spawn_id = sas->spawn_id; - eq->type = sas->type; - eq->parameter = sas->parameter; - - dest->FastQueuePacket(&outapp, ack_req); - delete in; - return; - } - - auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); - - ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer; - - css->EntityID = sas->spawn_id; - css->Size = (float)sas->parameter; - css->Unknown08 = 0; - css->Unknown12 = 1.0f; - - dest->FastQueuePacket(&outapp, ack_req); - delete in; - } + //ENCODE(OP_SpawnAppearance) + //{ + // EQApplicationPacket* in = *p; + // *p = nullptr; + // + // unsigned char* emu_buffer = in->pBuffer; + // + // SpawnAppearance_Struct* sas = (SpawnAppearance_Struct*)emu_buffer; + // + // if (sas->type != AppearanceType::Size) + // { + // //larion struct is different than rof2's but the idea is the same + // auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); + // structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; + // + // eq->spawn_id = sas->spawn_id; + // eq->type = sas->type; + // eq->parameter = sas->parameter; + // + // dest->FastQueuePacket(&outapp, ack_req); + // delete in; + // return; + // } + // + // auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); + // + // ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer; + // + // css->EntityID = sas->spawn_id; + // css->Size = (float)sas->parameter; + // css->Unknown08 = 0; + // css->Unknown12 = 1.0f; + // + // dest->FastQueuePacket(&outapp, ack_req); + // delete in; + //} ENCODE(OP_PlayerProfile) { EQApplicationPacket* in = *p; @@ -2248,6 +2248,27 @@ namespace Larion delete in; } + ENCODE(OP_SpawnAppearance) + { + EQApplicationPacket* in = *p; + *p = nullptr; + delete in; + } + + ENCODE(OP_SpawnDoor) + { + EQApplicationPacket* in = *p; + *p = nullptr; + delete in; + } + + ENCODE(OP_GroundSpawn) + { + EQApplicationPacket* in = *p; + *p = nullptr; + delete in; + } + // DECODE methods DECODE(OP_ZoneEntry) diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 157913f6b..1716f8590 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -6,13 +6,15 @@ E(OP_SendMembershipDetails) E(OP_SendMaxCharacters) E(OP_SendCharInfo) E(OP_ExpansionInfo) -E(OP_SpawnAppearance) //E(OP_SendAATable) E(OP_PlayerProfile) E(OP_ZoneEntry) E(OP_ZoneSpawns) E(OP_CharInventory) E(OP_NewZone) +E(OP_SpawnAppearance) +E(OP_SpawnDoor) +E(OP_GroundSpawn) //list of packets we need to decode on the way in: D(OP_ZoneEntry) From b85344f7795bba17c296b13c28b2ac3fbc6d820a Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 16 Nov 2024 22:04:44 -0800 Subject: [PATCH 32/93] Fix for OP_ZoneEntry spawn struct sometimes containing garbage data in flags causing random crashes. --- common/patches/larion.cpp | 13 ++++- common/patches/larion_structs.h | 98 ++++++++++++++++++--------------- common/serialize_buffer.h | 15 ----- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index d434464c6..1432fd4ba 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -1603,6 +1603,8 @@ namespace Larion buffer.WriteUInt8(emu->NPC); structs::Spawn_Struct_Bitfields flags; + memset(&flags, 0, sizeof(structs::Spawn_Struct_Bitfields)); + flags.gender = emu->gender; flags.ispet = emu->is_pet; flags.afk = emu->afk; @@ -1626,7 +1628,10 @@ namespace Larion } //write flags - buffer.WriteStructure(flags); + //buffer.WriteStructure(flags); + for (int j = 0; j < 5; ++j) { + buffer.WriteUInt8(flags.raw[j]); + } /* float EmitterScalingRadius; @@ -1868,6 +1873,7 @@ namespace Larion //u8 CPhysicsData[20]; structs::Spawn_Struct_Position position; + memset(&position, 0, sizeof(structs::Spawn_Struct_Position)); position.y = emu->y; position.deltaZ = emu->deltaZ; @@ -1879,7 +1885,10 @@ namespace Larion position.animation = emu->animation; position.deltaY = emu->deltaY; - buffer.WriteStructure(position); + //buffer.WriteStructure(position); + for (int j = 0; j < 5; ++j) { + buffer.WriteUInt32(position.raw[j]); + } /* if(Flags.title) { diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 06c541982..156cc0078 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -193,58 +193,68 @@ namespace Larion { struct Spawn_Struct_Bitfields { - // byte 1 - /*00*/ unsigned gender : 2; // Gender (0=male, 1=female, 2=monster) - /*02*/ unsigned ispet : 1; // Guessed based on observing live spawns - /*03*/ unsigned afk : 1; // 0=no, 1=afk - /*04*/ unsigned anon : 2; // 0=normal, 1=anon, 2=roleplay - /*06*/ unsigned gm : 1; - /*07*/ unsigned sneak : 1; - // byte 2 - /*08*/ unsigned lfg : 1; - /*09*/ unsigned unk9 : 1; - /*10*/ unsigned invis : 12; // there are 3000 different (non-GM) invis levels - /*22*/ unsigned linkdead : 1; // 1 Toggles LD on or off after name. Correct for RoF2 - /*23*/ unsigned showhelm : 1; - // byte 4 - /*24*/ unsigned betabuffed : 1; // Prefixes name with ! - /*25*/ unsigned trader : 1; - /*26*/ unsigned animationonpop : 1; - /*27*/ unsigned targetable : 1; - /*28*/ unsigned targetable_with_hotkey : 1; - /*29*/ unsigned showname : 1; - /*30*/ unsigned idleanimationsoff : 1; // what we called statue? - /*31*/ unsigned untargetable : 1; // bClickThrough - // byte 5 - /*32*/ unsigned buyer : 1; - /*33*/ unsigned offline : 1; - /*34*/ unsigned interactiveobject : 1; - /*35*/ unsigned missile : 1; - /*36*/ unsigned title : 1; - /*37*/ unsigned suffix : 1; - /*38*/ unsigned unk38 : 1; - /*39*/ unsigned unk39 : 1; + union { + struct { + // byte 1 + /*00*/ unsigned gender : 2; // Gender (0=male, 1=female, 2=monster) + /*02*/ unsigned ispet : 1; // Guessed based on observing live spawns + /*03*/ unsigned afk : 1; // 0=no, 1=afk + /*04*/ unsigned anon : 2; // 0=normal, 1=anon, 2=roleplay + /*06*/ unsigned gm : 1; + /*07*/ unsigned sneak : 1; + // byte 2 + /*08*/ unsigned lfg : 1; + /*09*/ unsigned unk9 : 1; + /*10*/ unsigned invis : 12; // there are 3000 different (non-GM) invis levels + /*22*/ unsigned linkdead : 1; // 1 Toggles LD on or off after name. Correct for RoF2 + /*23*/ unsigned showhelm : 1; + // byte 4 + /*24*/ unsigned betabuffed : 1; // Prefixes name with ! + /*25*/ unsigned trader : 1; + /*26*/ unsigned animationonpop : 1; + /*27*/ unsigned targetable : 1; + /*28*/ unsigned targetable_with_hotkey : 1; + /*29*/ unsigned showname : 1; + /*30*/ unsigned idleanimationsoff : 1; // what we called statue? + /*31*/ unsigned untargetable : 1; // bClickThrough + // byte 5 + /*32*/ unsigned buyer : 1; + /*33*/ unsigned offline : 1; + /*34*/ unsigned interactiveobject : 1; + /*35*/ unsigned missile : 1; + /*36*/ unsigned title : 1; + /*37*/ unsigned suffix : 1; + /*38*/ unsigned unk38 : 1; + /*39*/ unsigned unk39 : 1; + }; + uint8 raw[5]; + }; }; struct Spawn_Struct_Position { - signed y : 19; - signed deltaX : 13; + union { + struct { + signed y : 19; + signed deltaX : 13; - unsigned heading : 12; - signed z : 19; - unsigned pad1 : 1; + unsigned heading : 12; + signed z : 19; + unsigned pad1 : 1; - unsigned pitch : 12; - signed animation : 10; //these might be swapped - signed deltaHeading : 10; //these might be swapped + unsigned pitch : 12; + signed animation : 10; //these might be swapped + signed deltaHeading : 10; //these might be swapped - signed deltaY : 13; - signed deltaZ : 13; - unsigned pad3 : 6; + signed deltaY : 13; + signed deltaZ : 13; + unsigned pad3 : 6; - signed x : 19; - unsigned pad4 : 13; + signed x : 19; + unsigned pad4 : 13; + }; + uint32_t raw[5]; + }; }; diff --git a/common/serialize_buffer.h b/common/serialize_buffer.h index 1eda4da80..65ae36f70 100644 --- a/common/serialize_buffer.h +++ b/common/serialize_buffer.h @@ -181,21 +181,6 @@ public: m_pos += len; } - template - void WriteStructurePtr(T *value) { - auto type_size = sizeof(T); - - if (m_pos + type_size > m_capacity) - Grow(m_capacity + type_size); - memcpy(m_buffer + m_pos, value, type_size); - m_pos += sizeof(type_size); - } - - template - void WriteStructure(T& value) { - WriteStructurePtr(&value); - } - size_t size() const { return m_pos; } size_t length() const { return size(); } size_t capacity() const { return m_capacity; } From 027d95bbb8e3cd69c577b08a8ccb69522e8d7a8a Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 17 Nov 2024 15:44:05 -0800 Subject: [PATCH 33/93] WIP on door struct --- common/patches/larion.cpp | 111 +++++++++++++++++++------------- common/patches/larion_ops.h | 2 +- common/patches/larion_structs.h | 40 ++++++++++++ 3 files changed, 106 insertions(+), 47 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 1432fd4ba..81566ac4c 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -489,42 +489,42 @@ namespace Larion FINISH_ENCODE(); } - //ENCODE(OP_SpawnAppearance) - //{ - // EQApplicationPacket* in = *p; - // *p = nullptr; - // - // unsigned char* emu_buffer = in->pBuffer; - // - // SpawnAppearance_Struct* sas = (SpawnAppearance_Struct*)emu_buffer; - // - // if (sas->type != AppearanceType::Size) - // { - // //larion struct is different than rof2's but the idea is the same - // auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); - // structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; - // - // eq->spawn_id = sas->spawn_id; - // eq->type = sas->type; - // eq->parameter = sas->parameter; - // - // dest->FastQueuePacket(&outapp, ack_req); - // delete in; - // return; - // } - // - // auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); - // - // ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer; - // - // css->EntityID = sas->spawn_id; - // css->Size = (float)sas->parameter; - // css->Unknown08 = 0; - // css->Unknown12 = 1.0f; - // - // dest->FastQueuePacket(&outapp, ack_req); - // delete in; - //} + ENCODE(OP_SpawnAppearance) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + unsigned char* emu_buffer = in->pBuffer; + + SpawnAppearance_Struct* sas = (SpawnAppearance_Struct*)emu_buffer; + + if (sas->type != AppearanceType::Size) + { + //larion struct is different than rof2's but the idea is the same + auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); + structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; + + eq->spawn_id = sas->spawn_id; + eq->type = sas->type; + eq->parameter = sas->parameter; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + return; + } + + auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); + + ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer; + + css->EntityID = sas->spawn_id; + css->Size = (float)sas->parameter; + css->Unknown08 = 0; + css->Unknown12 = 1.0f; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } ENCODE(OP_PlayerProfile) { EQApplicationPacket* in = *p; @@ -2257,18 +2257,37 @@ namespace Larion delete in; } - ENCODE(OP_SpawnAppearance) - { - EQApplicationPacket* in = *p; - *p = nullptr; - delete in; - } - ENCODE(OP_SpawnDoor) { - EQApplicationPacket* in = *p; - *p = nullptr; - delete in; + SETUP_VAR_ENCODE(Door_Struct); + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + + int r; + for (r = 0; r < door_count; r++) { + // strncpy(eq[r].name, emu[r].name, sizeof(eq[r].name)); + // eq[r].xPos = emu[r].xPos; + // eq[r].yPos = emu[r].yPos; + // eq[r].zPos = emu[r].zPos; + // eq[r].heading = emu[r].heading; + // eq[r].incline = emu[r].incline; + // eq[r].size = emu[r].size; + // eq[r].doorId = emu[r].doorId; + // eq[r].opentype = emu[r].opentype; + // eq[r].state_at_spawn = emu[r].state_at_spawn; + // eq[r].invert_state = emu[r].invert_state; + // eq[r].door_param = emu[r].door_param; + // eq[r].unknown0080 = 0; + // eq[r].unknown0081 = 1; // Both must be 1 to allow clicking doors + // eq[r].unknown0082 = 0; + // eq[r].unknown0083 = 1; // Both must be 1 to allow clicking doors + // eq[r].unknown0084 = 0; + // eq[r].unknown0085 = 0; + // eq[r].unknown0086 = 0; + } + + FINISH_ENCODE(); } ENCODE(OP_GroundSpawn) diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 1716f8590..5d07d4538 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -6,13 +6,13 @@ E(OP_SendMembershipDetails) E(OP_SendMaxCharacters) E(OP_SendCharInfo) E(OP_ExpansionInfo) +E(OP_SpawnAppearance) //E(OP_SendAATable) E(OP_PlayerProfile) E(OP_ZoneEntry) E(OP_ZoneSpawns) E(OP_CharInventory) E(OP_NewZone) -E(OP_SpawnAppearance) E(OP_SpawnDoor) E(OP_GroundSpawn) diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 156cc0078..dd42cd1bf 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -257,6 +257,46 @@ namespace Larion { }; }; + struct Door_Struct + { + /*000*/ char name[32]; + /*032*/ float DefaultY; + /*036*/ float DefaultX; + /*040*/ float DefaultZ; + /*044*/ float DefaultHeading; + /*048*/ uint32 DefaultDoorAngle; //rof2's incline + /*052*/ float Y; //most (all I've seen?) doors match the defaults here + /*056*/ float X; + /*060*/ float Z; + /*064*/ float Heading; + /*068*/ float DoorAngle; //not sure if this is actually a float; it might be a uint32 like DefaultDoorAngle + /*072*/ uint32 ScaleFactor; //rof2's size + /*076*/ uint32 Unknown76; //client doesn't seem to read this + /*080*/ uint8 Id; //doorid + /*081*/ uint8 Type; //opentype + /*082*/ uint8 State; //state_at_spawn + /*083*/ uint8 DefaultState; //invert_state + /*084*/ int32 SpellId; //door_param + /*088*/ uint32 AdventureDoorId; + /*092*/ uint32 DynDoorID; + /*096*/ uint32 RealEstateDoorID; + /*100*/ uint8 bHasScript; + /*101*/ uint8 bUsable; //1 if clickable + /*102*/ uint8 bRemainOpen; + /*103*/ uint8 bVisible; //1 is visible + /*104*/ uint8 bHeadingChanged; + /*105*/ uint8 padding1[3]; + /*108*/ float TopSpeed1; + /*112*/ float TopSpeed2; + /*116*/ uint8 bNeedsTimeStampSet; + /*117*/ uint8 padding2[3]; + /*120*/ float unknownFloat1; + /*124*/ float unknownFloat2; + /*128*/ uint8 unknownByte1; + /*129*/ uint8 padding3[3]; + /*132*/ + }; + #pragma pack() From 8ff0e5614c739c9e106f041b3b24931a2736222e Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 17 Nov 2024 16:42:50 -0800 Subject: [PATCH 34/93] Add door encode. --- common/patches/larion.cpp | 41 ++++++++++++++++++--------------- common/patches/larion_structs.h | 2 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 81566ac4c..ade76eca1 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2266,25 +2266,28 @@ namespace Larion int r; for (r = 0; r < door_count; r++) { - // strncpy(eq[r].name, emu[r].name, sizeof(eq[r].name)); - // eq[r].xPos = emu[r].xPos; - // eq[r].yPos = emu[r].yPos; - // eq[r].zPos = emu[r].zPos; - // eq[r].heading = emu[r].heading; - // eq[r].incline = emu[r].incline; - // eq[r].size = emu[r].size; - // eq[r].doorId = emu[r].doorId; - // eq[r].opentype = emu[r].opentype; - // eq[r].state_at_spawn = emu[r].state_at_spawn; - // eq[r].invert_state = emu[r].invert_state; - // eq[r].door_param = emu[r].door_param; - // eq[r].unknown0080 = 0; - // eq[r].unknown0081 = 1; // Both must be 1 to allow clicking doors - // eq[r].unknown0082 = 0; - // eq[r].unknown0083 = 1; // Both must be 1 to allow clicking doors - // eq[r].unknown0084 = 0; - // eq[r].unknown0085 = 0; - // eq[r].unknown0086 = 0; + strncpy(eq[r].name, emu[r].name, 32); + eq[r].DefaultY = emu[r].yPos; + eq[r].DefaultX = emu[r].xPos; + eq[r].DefaultZ = emu[r].zPos; + eq[r].DefaultHeading = emu[r].heading; + eq[r].DefaultDoorAngle = emu[r].incline; + eq[r].Y = emu[r].yPos; + eq[r].X = emu[r].xPos; + eq[r].Z = emu[r].zPos; + eq[r].Heading = emu[r].heading; + //there's a door angle here but im not sure if / what we set it to since ive literally never seen it as anything but 0 on live + //based on pattern it probably is supposed to match the default angle? + //I'm not 100% sure this is a float it might be a uint32 + eq[r].DoorAngle = emu[r].incline; + eq[r].ScaleFactor = emu[r].size; + eq[r].Id = emu[r].doorId; + eq[r].Type = emu[r].opentype; + eq[r].State = emu[r].state_at_spawn; + eq[r].DefaultState = emu[r].invert_state; + eq[r].Param = emu[r].door_param; + eq[r].bVisible = 1; + eq[r].bUsable = 1; } FINISH_ENCODE(); diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index dd42cd1bf..5e8feda62 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -276,7 +276,7 @@ namespace Larion { /*081*/ uint8 Type; //opentype /*082*/ uint8 State; //state_at_spawn /*083*/ uint8 DefaultState; //invert_state - /*084*/ int32 SpellId; //door_param + /*084*/ int32 Param; //door_param /*088*/ uint32 AdventureDoorId; /*092*/ uint32 DynDoorID; /*096*/ uint32 RealEstateDoorID; From 004b0e1176352af04cffe73fcae2246f82695a34 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 17 Nov 2024 17:10:41 -0800 Subject: [PATCH 35/93] Ground spawn for larion --- common/patches/larion.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index ade76eca1..d78256ce1 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2297,6 +2297,28 @@ namespace Larion { EQApplicationPacket* in = *p; *p = nullptr; + Object_Struct* emu = (Object_Struct*)in->pBuffer; + + SerializeBuffer buffer; + buffer.WriteUInt32(emu->drop_id); + buffer.WriteString(emu->object_name); + buffer.WriteUInt16(emu->zone_id); + buffer.WriteUInt16(emu->zone_instance); + buffer.WriteUInt32(emu->drop_id); //this is some other sub but it's okay to duplicate + buffer.WriteUInt32(0); //expires + buffer.WriteFloat(emu->heading); + buffer.WriteFloat(emu->tilt_x); + buffer.WriteFloat(emu->tilt_y); + buffer.WriteFloat(emu->size != 0 && (float)emu->size < 5000.f ? (float)((float)emu->size / 100.0f) : 1.f); //size, with weird peq hack + buffer.WriteFloat(emu->y); + buffer.WriteFloat(emu->x); + buffer.WriteFloat(emu->z); + buffer.WriteFloat(emu->object_type); //weight + + auto outapp = new EQApplicationPacket(OP_GroundSpawn, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + delete in; } From f6f9d275e8e9fbb166c04f722aaa959c8b40737f Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 17 Nov 2024 19:53:09 -0800 Subject: [PATCH 36/93] Close to zone; spawn struct isn't right. --- common/patches/larion.cpp | 20 ++++++++++ common/patches/larion_ops.h | 1 + common/patches/larion_structs.h | 17 ++++++++ utils/patches/patch_Larion.conf | 70 ++++++++++++++++----------------- 4 files changed, 73 insertions(+), 35 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index d78256ce1..a60a0a82a 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2322,6 +2322,26 @@ namespace Larion delete in; } + ENCODE(OP_SendZonepoints) + { + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); + + eq->count = emu->count; + for (uint32 i = 0; i < emu->count; ++i) + { + eq->zpe[i].iterator = emu->zpe[i].iterator; + eq->zpe[i].x = emu->zpe[i].x; + eq->zpe[i].y = emu->zpe[i].y; + eq->zpe[i].z = emu->zpe[i].z; + eq->zpe[i].heading = emu->zpe[i].heading; + eq->zpe[i].zoneid = emu->zpe[i].zoneid; + eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; + } + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_ZoneEntry) diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 5d07d4538..6a6771550 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -15,6 +15,7 @@ E(OP_CharInventory) E(OP_NewZone) E(OP_SpawnDoor) E(OP_GroundSpawn) +E(OP_SendZonepoints) //list of packets we need to decode on the way in: D(OP_ZoneEntry) diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 5e8feda62..a571e3ad2 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -297,6 +297,23 @@ namespace Larion { /*132*/ }; + struct ZonePoint_Entry { + /*00*/ uint32 iterator; + /*04*/ float y; + /*08*/ float x; + /*12*/ float z; + /*16*/ float heading; + /*20*/ uint16 zoneid; + /*22*/ uint16 zoneinstance; + /*24*/ uint32 unknown024; + /*28*/ uint32 unknown028; + /*32*/ + }; + + struct ZonePoints { + /*00*/ uint32 count; + /*04*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints + }; #pragma pack() diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Larion.conf index 5fd29482f..43c036774 100644 --- a/utils/patches/patch_Larion.conf +++ b/utils/patches/patch_Larion.conf @@ -32,11 +32,11 @@ OP_SendSkillCapsChecksum=0x0000 OP_SendMaxCharacters=0x13af OP_SendMembership=0x2aca OP_SendMembershipDetails=0x2608 -OP_CharacterCreateRequest=0x0000 -OP_CharacterCreate=0x0000 -OP_DeleteCharacter=0x0000 -OP_RandomNameGenerator=0x0000 -OP_ApproveName=0x0000 +OP_CharacterCreateRequest=0x2df4 +OP_CharacterCreate=0x6a3c +OP_DeleteCharacter=x67d7 +OP_RandomNameGenerator=0x49d9 +OP_ApproveName=0x11e5 OP_MOTD=0x0000 OP_SetChatServer=0x2726 OP_SetChatServer2=0x0000 @@ -47,7 +47,7 @@ OP_FloatListThing=0x0000 # Reasons for Disconnect: OP_ZoneUnavail=0x0000 -OP_WorldClientReady=0x0000 +OP_WorldClientReady=0x7ed8 OP_CharacterStillInZone=0x0000 OP_WorldChecksumFailure=0x0000 OP_WorldLoginFailed=0x0000 @@ -60,16 +60,16 @@ OP_SendBaseDataChecksum=0x0000 # Zone in opcodes OP_AckPacket=0x77c9 -OP_ZoneEntry=0x0000 -OP_ReqNewZone=0x0000 -OP_NewZone=0x0000 -OP_ZoneSpawns=0x0000 -OP_PlayerProfile=0x0000 -OP_TimeOfDay=0x0000 -OP_LevelUpdate=0x0000 -OP_Stamina=0x0000 -OP_RequestClientZoneChange=0x0000 -OP_ZoneChange=0x0000 +OP_ZoneEntry=0x784a +OP_ReqNewZone=0x3895 +OP_NewZone=0x4341 +OP_ZoneSpawns=0x17d9 +OP_PlayerProfile=0x1c76 +OP_TimeOfDay=0x3736 +OP_LevelUpdate=0x0000 #0x0eb2 +OP_Stamina=0x0000 #0x1563 +OP_RequestClientZoneChange=0x0000 #0x0191 +OP_ZoneChange=0x0000 #0x17a3 OP_LockoutTimerInfo=0x0000 OP_ZoneServerReady=0x0000 OP_ZoneInUnknown=0x0000 @@ -77,42 +77,42 @@ OP_LogoutReply=0x0000 OP_PreLogoutReply=0x0000 # Required to fully log in -OP_SpawnAppearance=0x0000 -OP_ChangeSize=0x0000 +OP_SpawnAppearance=0x4eb0 +OP_ChangeSize=0x0000 #0x2fdc OP_TributeUpdate=0x0000 OP_TributeTimer=0x0000 OP_SendTributes=0x0000 OP_RequestGuildTributes=0x0000 OP_TributeInfo=0x0000 -OP_Weather=0x0000 -OP_ReqClientSpawn=0x0000 -OP_SpawnDoor=0x0000 -OP_GroundSpawn=0x0000 -OP_SendZonepoints=0x0000 +OP_Weather=0x6fe6 +OP_ReqClientSpawn=0x6732 +OP_SpawnDoor=0x4273 +OP_GroundSpawn=0x49c5 +OP_SendZonepoints=0x279f OP_BlockedBuffs=0x0000 OP_RemoveBlockedBuffs=0x0000 OP_ClearBlockedBuffs=0x0000 -OP_WorldObjectsSent=0x0000 -OP_SendExpZonein=0x0000 +OP_WorldObjectsSent=0x2879 +OP_SendExpZonein=0x02b4 OP_SendAATable=0x0000 OP_ClearAA=0x0000 OP_ClearLeadershipAbilities=0x0000 OP_RespondAA=0x0000 OP_UpdateAA=0x0000 OP_SendAAStats=0x0000 -OP_AAExpUpdate=0x0000 -OP_ExpUpdate=0x0000 -OP_HPUpdate=0x0000 -OP_ManaChange=0x0000 +OP_AAExpUpdate=0x0000 #0x642f +OP_ExpUpdate=0x0000 #0x611d +OP_HPUpdate=0x0000 #0x775c +OP_ManaChange=0x0000 #0x0606 OP_TGB=0x0000 -OP_SpecialMesg=0x0000 +OP_SpecialMesg=0x0000 #0x7d93 OP_GuildMemberList=0x0000 OP_GuildMOTD=0x0000 -OP_CharInventory=0x0000 -OP_WearChange=0x0000 -OP_ClientUpdate=0x0000 -OP_ClientReady=0x0000 -OP_SetServerFilter=0x0000 +OP_CharInventory=0x21d6 +OP_WearChange=0x0000 #0x44c0 +OP_ClientUpdate=0x0000 #0x3a4b +OP_ClientReady=0x0831 +OP_SetServerFilter=0x0000 #0x6b7f # Guild Opcodes OP_GetGuildMOTD=0x0000 From b167f050068afd66a8b725d409d45b26d033ba39 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 17 Nov 2024 21:58:27 -0800 Subject: [PATCH 37/93] Missing fields from spawn struct --- common/patches/larion.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index a60a0a82a..d9af91f34 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -1826,6 +1826,13 @@ namespace Larion buffer.WriteUInt32(0); } + /* + s32 Primary; + s32 Secondary; + */ + buffer.WriteUInt32(0xffffffff); + buffer.WriteUInt32(0xffffffff); + if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) || (emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin) ) From 3a7afb48cbc91d597aeed1f91ecfe93410f9bd2e Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 17 Nov 2024 22:26:16 -0800 Subject: [PATCH 38/93] Issue entering zone is they changed op_enterworld a bit; right now ignore the go home and tutorial features for now --- common/patches/larion.cpp | 12 ++++++++++++ common/patches/larion_ops.h | 1 + common/patches/larion_structs.h | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index d9af91f34..269fe28ea 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2351,6 +2351,18 @@ namespace Larion // DECODE methods + DECODE(OP_EnterWorld) + { + DECODE_LENGTH_EXACT(structs::EnterWorld_Struct); + SETUP_DIRECT_DECODE(EnterWorld_Struct, structs::EnterWorld_Struct); + + memcpy(emu->name, eq->name, sizeof(emu->name)); + emu->return_home = 0; + emu->tutorial = 0; + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ZoneEntry) { DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct); diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 6a6771550..862fcae9e 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -18,6 +18,7 @@ E(OP_GroundSpawn) E(OP_SendZonepoints) //list of packets we need to decode on the way in: +D(OP_EnterWorld) D(OP_ZoneEntry) #undef E diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index a571e3ad2..606f7bf06 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -315,6 +315,12 @@ namespace Larion { /*04*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints }; + struct EnterWorld_Struct { + /*000*/ char name[64]; + /*064*/ int32 unknown1; + /*068*/ int32 unknown2; //larion handles these differently so for now im just going to ignore them till i figure it out + }; + #pragma pack() }; //end namespace structs From 2c5c28b808ba7f3e582d9caf2f703c2637807f52 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 18 Nov 2024 20:00:03 -0800 Subject: [PATCH 39/93] Movement works, mostly now. --- common/emu_oplist.h | 1 + common/patches/larion.cpp | 97 +++++++++++++++++++++++++++++++++ common/patches/larion_ops.h | 6 +- common/patches/larion_structs.h | 64 ++++++++++++++++++++++ utils/patches/patch_Larion.conf | 6 +- 5 files changed, 170 insertions(+), 4 deletions(-) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index aa8190720..83b7d2df0 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -374,6 +374,7 @@ N(OP_MultiLineMsg), N(OP_NewSpawn), N(OP_NewTitlesAvailable), N(OP_NewZone), +N(OP_NPCMoveUpdate), N(OP_OnLevelMessage), N(OP_OpenContainer), N(OP_OpenDiscordMerchant), diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 269fe28ea..4ed644b4d 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2349,6 +2349,64 @@ namespace Larion FINISH_ENCODE(); } + ENCODE(OP_RequestClientZoneChange) + { + ENCODE_LENGTH_EXACT(RequestClientZoneChange_Struct); + SETUP_DIRECT_ENCODE(RequestClientZoneChange_Struct, structs::RequestClientZoneChange_Struct); + + OUT(zone_id); + OUT(instance_id); + OUT(y); + OUT(x); + OUT(z); + OUT(heading); + eq->type = 0x0b; + eq->unknown004 = 0xffffffff; + eq->unknown172 = 0x0168b500; + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneChange) + { + ENCODE_LENGTH_EXACT(ZoneChange_Struct); + SETUP_DIRECT_ENCODE(ZoneChange_Struct, structs::ZoneChange_Struct); + + memcpy(eq->char_name, emu->char_name, sizeof(emu->char_name)); + OUT(zoneID); + OUT(instanceID); + OUT(y); + OUT(x); + OUT(z) + OUT(zone_reason); + OUT(success); + + if (eq->success < 0) + eq->success -= 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(vehicle_id); + eq->position.x = emu->x_pos; + eq->position.y = emu->y_pos; + eq->position.z = emu->z_pos; + eq->position.heading = emu->heading; + eq->position.deltaX = emu->delta_x; + eq->position.deltaY = emu->delta_y; + eq->position.deltaZ = emu->delta_z; + eq->position.deltaHeading = emu->delta_heading; + eq->position.animation = emu->animation; + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -2372,5 +2430,44 @@ namespace Larion FINISH_DIRECT_DECODE(); } + + DECODE(OP_ZoneChange) + { + DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); + SETUP_DIRECT_DECODE(ZoneChange_Struct, structs::ZoneChange_Struct); + + memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); + IN(zoneID); + IN(instanceID); + IN(y); + IN(x); + IN(z) + IN(zone_reason); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(vehicle_id); + IN(sequence); + emu->x_pos = eq->position.x; + emu->y_pos = eq->position.y; + emu->z_pos = eq->position.z; + emu->heading = eq->position.heading; + emu->delta_x = eq->position.delta_x; + emu->delta_y = eq->position.delta_y; + emu->delta_z = eq->position.delta_z; + emu->delta_heading = eq->position.delta_heading; + emu->animation = eq->position.animation; + + FINISH_DIRECT_DECODE(); + } } /*Larion*/ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 862fcae9e..7c801405e 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -16,10 +16,14 @@ E(OP_NewZone) E(OP_SpawnDoor) E(OP_GroundSpawn) E(OP_SendZonepoints) - +E(OP_RequestClientZoneChange) +E(OP_ZoneChange) +E(OP_ClientUpdate) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) +D(OP_ZoneChange) +D(OP_ClientUpdate) #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 606f7bf06..8b238062f 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -257,6 +257,41 @@ namespace Larion { }; }; + struct Client_Position + { + /*00*/ float delta_x; + /*04*/ float x; + /*08*/ float z; + /*12*/ signed animation : 10; + signed padding1 : 22; + /*16*/ unsigned pitch : 12; + signed padding2 : 20; + /*20*/ float delta_y; + /*24*/ float y; + /*28*/ signed delta_heading : 10; + signed padding3 : 22; + /*32*/ signed heading : 12; + signed padding4 : 20; + /*36*/ float delta_z; + /*40*/ + }; + + struct PlayerPositionUpdateServer_Struct + { + /*00*/ uint16 spawn_id; + /*02*/ uint16 vehicle_id; + /*04*/ Spawn_Struct_Position position; + /*24*/ + }; + + struct PlayerPositionUpdateClient_Struct { + /*00*/ uint16 sequence; + /*02*/ uint16 spawn_id; + /*04*/ uint16 vehicle_id; + /*06*/ Client_Position position; + /*46*/ + }; + struct Door_Struct { /*000*/ char name[32]; @@ -321,6 +356,35 @@ namespace Larion { /*068*/ int32 unknown2; //larion handles these differently so for now im just going to ignore them till i figure it out }; + struct ZoneChange_Struct { + /*000*/ char char_name[64]; // Character Name + /*064*/ uint16 zoneID; + /*066*/ uint16 instanceID; + /*068*/ uint32 Unknown068; + /*072*/ uint32 Unknown072; + /*076*/ float y; + /*080*/ float x; + /*084*/ float z; + /*088*/ uint32 zone_reason; //0x0A == death, I think + /*092*/ int32 success; // =0 client->server, =1 server->client, -X=specific error + /*096*/ uint32 Unknown096; // Not sure the extra 4 bytes goes here or earlier in the struct. + /*100*/ + }; + + struct RequestClientZoneChange_Struct { + /*000*/ uint16 zone_id; + /*002*/ uint16 instance_id; + /*004*/ uint32 unknown004; + /*008*/ float y; + /*012*/ float x; + /*016*/ float z; + /*020*/ float heading; + /*024*/ uint32 type; //unknown... values + /*032*/ uint8 unknown032[144]; + /*172*/ uint32 unknown172; + /*176*/ + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Larion.conf index 43c036774..d88718599 100644 --- a/utils/patches/patch_Larion.conf +++ b/utils/patches/patch_Larion.conf @@ -68,8 +68,8 @@ OP_PlayerProfile=0x1c76 OP_TimeOfDay=0x3736 OP_LevelUpdate=0x0000 #0x0eb2 OP_Stamina=0x0000 #0x1563 -OP_RequestClientZoneChange=0x0000 #0x0191 -OP_ZoneChange=0x0000 #0x17a3 +OP_RequestClientZoneChange=0x0191 +OP_ZoneChange=0x17a3 OP_LockoutTimerInfo=0x0000 OP_ZoneServerReady=0x0000 OP_ZoneInUnknown=0x0000 @@ -110,7 +110,7 @@ OP_GuildMemberList=0x0000 OP_GuildMOTD=0x0000 OP_CharInventory=0x21d6 OP_WearChange=0x0000 #0x44c0 -OP_ClientUpdate=0x0000 #0x3a4b +OP_ClientUpdate=0x3a4b OP_ClientReady=0x0831 OP_SetServerFilter=0x0000 #0x6b7f From ebb657153aa741a8482ad6242327f7c11b7c9619 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 19 Nov 2024 21:06:53 -0800 Subject: [PATCH 40/93] Zoning works but saving and player movement packets dont so it's a little limited, wear change works. Chat is a work in progress but not yet working. --- common/patches/larion.cpp | 121 ++++++++++++++++++++++++++++++++ common/patches/larion_ops.h | 5 ++ common/patches/larion_structs.h | 25 +++++++ utils/patches/patch_Larion.conf | 8 +-- zone/client.cpp | 27 ++++--- 5 files changed, 173 insertions(+), 13 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 4ed644b4d..9c5db909a 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2407,6 +2407,70 @@ namespace Larion FINISH_ENCODE(); } + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + eq->wear_slot_id = emu->wear_slot_id; + eq->armor_id = emu->material; + eq->variation = emu->unknown06; + eq->material = emu->elite_material; + eq->new_armor_id = emu->hero_forge_model; + eq->new_armor_type = emu->unknown18; + eq->color = emu->color.Color; + + FINISH_ENCODE(); + } + + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id + + std::string name; + in->ReadString(name); // NPC names max out at 63 chars + + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; + std::string new_message; + + in->ReadString(old_message); + + //ServerToRoF2SayLink(new_message, old_message); + + buf.WriteString(new_message); + + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_DeleteSpawn) + { + ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); + + OUT(spawn_id); + eq->unknown04 = 1; // Observed + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -2469,5 +2533,62 @@ namespace Larion FINISH_DIRECT_DECODE(); } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + emu->wear_slot_id = eq->wear_slot_id; + emu->material = eq->armor_id; + emu->unknown06 = eq->variation; + emu->elite_material = eq->material; + emu->hero_forge_model = eq->new_armor_id; + emu->unknown18 = eq->new_armor_type; + emu->color.Color = eq->color; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ChannelMessage) + { + unsigned char* __eq_buffer = __packet->pBuffer; + + char* InBuffer = (char*)__eq_buffer; + + char Sender[64]; + char Target[64]; + + VARSTRUCT_DECODE_STRING(Sender, InBuffer); + VARSTRUCT_DECODE_STRING(Target, InBuffer); + + //packet seems the same as rof2 with 5 more empty bytes before language + InBuffer += 9; + + uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + + InBuffer += 5; + + uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + + std::string old_message = InBuffer; + std::string new_message; + //RoF2ToServerSayLink(new_message, old_message); + + __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + ChannelMessage_Struct* emu = (ChannelMessage_Struct*)__packet->pBuffer; + + strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); + strn0cpy(emu->sender, Target, sizeof(emu->sender)); + emu->language = Language; + emu->chan_num = Channel; + emu->skill_in_language = Skill; + strcpy(emu->message, new_message.c_str()); + + delete[] __eq_buffer; + } } /*Larion*/ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 7c801405e..1567272d8 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -19,11 +19,16 @@ E(OP_SendZonepoints) E(OP_RequestClientZoneChange) E(OP_ZoneChange) E(OP_ClientUpdate) +E(OP_WearChange) +E(OP_SpecialMesg) +E(OP_DeleteSpawn) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) D(OP_ZoneChange) D(OP_ClientUpdate) +D(OP_WearChange) +D(OP_ChannelMessage) #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 8b238062f..1afe6cb82 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -385,6 +385,31 @@ namespace Larion { /*176*/ }; + struct WearChange_Struct { + /*000*/ uint32 spawn_id; + /*004*/ uint32 wear_slot_id; + /*008*/ uint32 armor_id; + /*012*/ uint32 variation; + /*016*/ uint32 material; + /*020*/ uint32 new_armor_id; + /*024*/ uint32 new_armor_type; + /*028*/ uint32 color; + /*032*/ + }; + + struct ExpUpdate_Struct + { + /*000*/ uint64 exp; //This is exp % / 1000 now; eg 69250 = 69.25% + /*008*/ uint64 unknown; //unclear, I didn't see the client actually read this value but i might have missed it + }; + + struct DeleteSpawn_Struct + { + /*00*/ uint32 spawn_id; // Spawn ID to delete + /*04*/ uint8 unknown04; // Seen 1 + /*05*/ + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Larion.conf index d88718599..89e62fbab 100644 --- a/utils/patches/patch_Larion.conf +++ b/utils/patches/patch_Larion.conf @@ -105,11 +105,11 @@ OP_ExpUpdate=0x0000 #0x611d OP_HPUpdate=0x0000 #0x775c OP_ManaChange=0x0000 #0x0606 OP_TGB=0x0000 -OP_SpecialMesg=0x0000 #0x7d93 +OP_SpecialMesg=0x7d93 OP_GuildMemberList=0x0000 OP_GuildMOTD=0x0000 OP_CharInventory=0x21d6 -OP_WearChange=0x0000 #0x44c0 +OP_WearChange=0x44c0 OP_ClientUpdate=0x3a4b OP_ClientReady=0x0831 OP_SetServerFilter=0x0000 #0x6b7f @@ -218,7 +218,7 @@ OP_Bug=0x0000 OP_Feedback=0x0000 OP_Report=0x0000 OP_Damage=0x0000 -OP_ChannelMessage=0x0000 +OP_ChannelMessage=0x6adc OP_Assist=0x0000 OP_AssistGroup=0x0000 OP_MoveCoin=0x0000 @@ -249,7 +249,7 @@ OP_XTargetOpen=0x0000 OP_XTargetOpenResponse=0x0000 OP_BuffCreate=0x0000 OP_BuffRemoveRequest=0x0000 -OP_DeleteSpawn=0x0000 +OP_DeleteSpawn=0x7712 OP_AutoAttack=0x0000 OP_AutoAttack2=0x0000 OP_Consume=0x0000 diff --git a/zone/client.cpp b/zone/client.cpp index d0a8d97b0..268d37c8a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -534,20 +534,29 @@ void Client::SendZoneInPackets() if (GetLevel() >= 51) SendAlternateAdvancementStats(); - // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + auto tmpxp2 = GetEXPForLevel(GetLevel() + 1); + auto tmpxp1 = GetEXPForLevel(GetLevel()); + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); + + //Larion uses a more granular exp bar than other clients + if (m_ClientVersion >= EQ::versions::ClientVersion::Larion) { + eu->exp = (uint32)(100000.0f * tmpxp); + } + else { + eu->exp = (uint32)(330.0f * tmpxp); + } + + FastQueuePacket(&outapp); + } + else { + eu->exp = 0; + FastQueuePacket(&outapp); } - safe_delete(outapp); SendAlternateAdvancementTimers(); From 63331b678bbf9b0b6cb73174c797b6755524501a Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 20 Nov 2024 20:31:32 -0800 Subject: [PATCH 41/93] Fix movement (client->server) packets --- common/patches/larion_structs.h | 22 +++++++++---------- .../common/Daybreak/Connection.cs | 8 +++---- utils/stream_parser/common/Daybreak/Parser.cs | 2 +- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 1afe6cb82..2f613310c 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -263,17 +263,15 @@ namespace Larion { /*04*/ float x; /*08*/ float z; /*12*/ signed animation : 10; - signed padding1 : 22; - /*16*/ unsigned pitch : 12; - signed padding2 : 20; - /*20*/ float delta_y; - /*24*/ float y; - /*28*/ signed delta_heading : 10; - signed padding3 : 22; - /*32*/ signed heading : 12; - signed padding4 : 20; - /*36*/ float delta_z; - /*40*/ + unsigned pitch : 12; + signed padding1 : 10; + /*16*/ float delta_y; + /*20*/ float y; + /*24*/ signed delta_heading : 10; + signed heading : 12; + signed padding2 : 10; + /*28*/ float delta_z; + /*32*/ }; struct PlayerPositionUpdateServer_Struct @@ -289,7 +287,7 @@ namespace Larion { /*02*/ uint16 spawn_id; /*04*/ uint16 vehicle_id; /*06*/ Client_Position position; - /*46*/ + /*38*/ }; struct Door_Struct diff --git a/utils/stream_parser/common/Daybreak/Connection.cs b/utils/stream_parser/common/Daybreak/Connection.cs index 98fa93b5a..f4ba7fce0 100644 --- a/utils/stream_parser/common/Daybreak/Connection.cs +++ b/utils/stream_parser/common/Daybreak/Connection.cs @@ -176,10 +176,10 @@ namespace StreamParser.Common.Daybreak case Opcode.SessionResponse: if (_connect_code == 0) { - if(data.Length != 21) - { - return; - } + //if(data.Length != 21) + //{ + // return; + //} _connect_code = BitConverter.ToUInt32(data.Slice(2, 4)); _encode_key = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data.Slice(6, 4))); diff --git a/utils/stream_parser/common/Daybreak/Parser.cs b/utils/stream_parser/common/Daybreak/Parser.cs index be5d24e96..4bf7da653 100644 --- a/utils/stream_parser/common/Daybreak/Parser.cs +++ b/utils/stream_parser/common/Daybreak/Parser.cs @@ -47,7 +47,7 @@ namespace StreamParser.Common.Daybreak private void OnPacketCapture(object sender, PacketCapture capture) { var raw = capture.GetPacket(); - if (raw.LinkLayerType == PacketDotNet.LinkLayers.Ethernet) + if (raw.LinkLayerType == PacketDotNet.LinkLayers.Ethernet || raw.LinkLayerType == PacketDotNet.LinkLayers.Null) { var packet = PacketDotNet.Packet.ParsePacket(raw.LinkLayerType, raw.Data); var ipPacket = packet.Extract(); From 0eedbea0600053224b4789e0ffcae1562d036d65 Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 20 Nov 2024 22:35:09 -0800 Subject: [PATCH 42/93] Added alternate command sequence for clients that don't support # commands. Incoming channel messages work I think. Outgoing special msg still pretty screwed up but working on it. --- common/patches/larion.cpp | 4 ++-- zone/client.cpp | 4 ++-- zone/command.h | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 9c5db909a..3ab49ff8f 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2563,8 +2563,8 @@ namespace Larion VARSTRUCT_DECODE_STRING(Sender, InBuffer); VARSTRUCT_DECODE_STRING(Target, InBuffer); - //packet seems the same as rof2 with 5 more empty bytes before language - InBuffer += 9; + //packet seems the same as rof2 with 4 more empty bytes before language + InBuffer += 8; uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); diff --git a/zone/client.cpp b/zone/client.cpp index 268d37c8a..a8b57286d 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1177,7 +1177,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } } - if (message[0] == COMMAND_CHAR) { + if (message[0] == COMMAND_CHAR || message[0] == COMMAND_CHAR_NON_HASH) { if (command_dispatch(this, message, false) == -2) { if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0); @@ -7527,7 +7527,7 @@ void Client::GarbleMessage(char *message, uint8 variance) int delimiter_count = 0; // Don't garble # commands - if (message[0] == COMMAND_CHAR || message[0] == BOT_COMMAND_CHAR) { + if (message[0] == COMMAND_CHAR || message[0] == COMMAND_CHAR_NON_HASH || message[0] == BOT_COMMAND_CHAR) { return; } diff --git a/zone/command.h b/zone/command.h index d59fdf9a5..3f03a19ab 100644 --- a/zone/command.h +++ b/zone/command.h @@ -8,6 +8,7 @@ class Seperator; #include #define COMMAND_CHAR '#' +#define COMMAND_CHAR_NON_HASH '.' typedef void (*CmdFuncPtr)(Client *, const Seperator *); From 429d6fd87d603ad8845e6ee5a10c60f84be8b950 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 21 Nov 2024 23:06:44 -0800 Subject: [PATCH 43/93] WIP chat message work --- common/patches/larion.cpp | 43 ++++++++++++++++++++++++++++++++++++- common/patches/larion_ops.h | 1 + 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 3ab49ff8f..b5101c334 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2424,6 +2424,46 @@ namespace Larion FINISH_ENCODE(); } + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + ChannelMessage_Struct* emu = (ChannelMessage_Struct*)in->pBuffer; + + unsigned char* __emu_buffer = in->pBuffer; + + std::string old_message = emu->message; + std::string new_message = old_message; + //ServerToRoF2SayLink(new_message, old_message); + + in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 43; + + in->pBuffer = new unsigned char[in->size]; + + char* OutBuffer = (char*)in->pBuffer; + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_SpecialMesg) { EQApplicationPacket* in = *p; @@ -2451,6 +2491,7 @@ namespace Larion in->ReadString(old_message); //ServerToRoF2SayLink(new_message, old_message); + new_message = old_message; buf.WriteString(new_message); @@ -2574,7 +2615,7 @@ namespace Larion uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); std::string old_message = InBuffer; - std::string new_message; + std::string new_message = old_message; //RoF2ToServerSayLink(new_message, old_message); __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 1567272d8..000ca0bed 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -20,6 +20,7 @@ E(OP_RequestClientZoneChange) E(OP_ZoneChange) E(OP_ClientUpdate) E(OP_WearChange) +E(OP_ChannelMessage) E(OP_SpecialMesg) E(OP_DeleteSpawn) //list of packets we need to decode on the way in: From 89470584658dc8034054200a863944cf20cf4db2 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 21 Nov 2024 23:51:20 -0800 Subject: [PATCH 44/93] NewSpawn support --- common/patches/larion.cpp | 2 ++ common/patches/larion_ops.h | 1 + utils/patches/patch_Larion.conf | 8 ++++---- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index b5101c334..afdecdb99 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2019,6 +2019,8 @@ namespace Larion delete in; } + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + ENCODE(OP_NewZone) { EQApplicationPacket* in = *p; *p = nullptr; diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index 000ca0bed..b5316b5f3 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -12,6 +12,7 @@ E(OP_PlayerProfile) E(OP_ZoneEntry) E(OP_ZoneSpawns) E(OP_CharInventory) +E(OP_NewSpawn) E(OP_NewZone) E(OP_SpawnDoor) E(OP_GroundSpawn) diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Larion.conf index 89e62fbab..557d8b305 100644 --- a/utils/patches/patch_Larion.conf +++ b/utils/patches/patch_Larion.conf @@ -101,7 +101,7 @@ OP_RespondAA=0x0000 OP_UpdateAA=0x0000 OP_SendAAStats=0x0000 OP_AAExpUpdate=0x0000 #0x642f -OP_ExpUpdate=0x0000 #0x611d +OP_ExpUpdate=0x611d OP_HPUpdate=0x0000 #0x775c OP_ManaChange=0x0000 #0x0606 OP_TGB=0x0000 @@ -230,14 +230,14 @@ OP_FriendsWho=0x0000 OP_ConfirmDelete=0x0000 OP_Logout=0x0000 OP_Rewind=0x0000 -OP_TargetCommand=0x0000 +OP_TargetCommand=0x3b18 OP_Hide=0x0000 OP_Jump=0x0000 OP_Camp=0x0000 OP_Emote=0x0000 OP_SetRunMode=0x0000 OP_BankerChange=0x0000 -OP_TargetMouse=0x0000 +OP_TargetMouse=0x5741 OP_MobHealth=0x0000 OP_InitialMobHealth=0x0000 # Unused? OP_TargetHoTT=0x0000 @@ -261,7 +261,7 @@ OP_ItemPacket=0x0000 OP_ItemLinkResponse=0x0000 OP_ItemLinkClick=0x0000 OP_ItemPreview=0x0000 -OP_NewSpawn=0x0000 +OP_NewSpawn=0x3ea8 OP_Track=0x0000 OP_TrackTarget=0x0000 OP_TrackUnknown=0x0000 From d713ff69bfe06a1b342d4a23358169df91931e5c Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 22 Nov 2024 19:06:18 -0800 Subject: [PATCH 45/93] WIP items --- common/patches/larion.cpp | 1061 +++++++++++++++++++++++++++++++ common/patches/larion_limits.h | 18 +- common/patches/larion_ops.h | 2 + common/patches/larion_structs.h | 30 + utils/patches/patch_Larion.conf | 16 +- 5 files changed, 1118 insertions(+), 9 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index afdecdb99..ce3c6388d 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -49,6 +49,20 @@ namespace Larion static OpcodeManager* opcodes = nullptr; static Strategy struct_strategy; + void SerializeItem(SerializeBuffer &buffer, const EQ::ItemInstance* inst, int16 slot_id, uint8 depth, ItemPacketType packet_type); + + // server to client inventory location converters + static inline structs::InventorySlot_Struct ServerToLarionSlot(uint32 server_slot); + static inline structs::InventorySlot_Struct ServerToLarionCorpseSlot(uint32 server_corpse_slot); + static inline uint32 ServerToLarionCorpseMainSlot(uint32 server_corpse_slot); + static inline structs::TypelessInventorySlot_Struct ServerToLarionTypelessSlot(uint32 server_slot, int16 server_type); + + // client to server inventory location converters + static inline uint32 LarionToServerSlot(structs::InventorySlot_Struct larion_slot); + static inline uint32 LarionToServerCorpseSlot(structs::InventorySlot_Struct larion_corpse_slot); + static inline uint32 LarionToServerCorpseMainSlot(uint32 larion_corpse_slot); + static inline uint32 LarionToServerTypelessSlot(structs::TypelessInventorySlot_Struct larion_slot, int16 larion_type); + void Register(EQStreamIdentifier& into) { //create our opcode manager if we havent already @@ -2514,6 +2528,56 @@ namespace Larion FINISH_ENCODE(); } + ENCODE(OP_FormattedMessage) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + FormattedMessage_Struct* emu = (FormattedMessage_Struct*)in->pBuffer; + + unsigned char* __emu_buffer = in->pBuffer; + + char* old_message_ptr = (char*)in->pBuffer; + old_message_ptr += sizeof(FormattedMessage_Struct); + + std::string old_message_array[9]; + + for (int i = 0; i < 9; ++i) { + if (*old_message_ptr == 0) { break; } + old_message_array[i] = old_message_ptr; + old_message_ptr += old_message_array[i].length() + 1; + } + + uint32 new_message_size = 0; + std::string new_message_array[9]; + + for (int i = 0; i < 9; ++i) { + if (old_message_array[i].length() == 0) { break; } + new_message_array[i] = old_message_array[i]; + //ServerToRoF2SayLink(new_message_array[i], old_message_array[i]); + new_message_size += new_message_array[i].length() + 1; + } + + in->size = sizeof(FormattedMessage_Struct) + new_message_size + 1; + in->pBuffer = new unsigned char[in->size]; + + char* OutBuffer = (char*)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->unknown0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->string_id); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->type); + + for (int i = 0; i < 9; ++i) { + if (new_message_array[i].length() == 0) { break; } + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message_array[i].c_str()); + } + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -2633,5 +2697,1002 @@ namespace Larion delete[] __eq_buffer; } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + // Size 68 in Larion + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + //Naive version but should work well enough for now + int ExtractIDFile(const std::string& input) { + std::string number; + for (char ch : input) { + if (std::isdigit(static_cast(ch))) { + number += ch; + } + } + + if (number.empty()) { + return 0; + } + + return std::stoi(number); + } + + // Helper Functions + void SerializeItemDefinition(SerializeBuffer& buffer, const EQ::ItemData* item) { + //u8 Type; + buffer.WriteUInt8(item->ItemClass); + //char Name[]; + buffer.WriteString(item->Name); + + //char LoreName[]; + buffer.WriteString(item->Lore); + + //we need to parse id file + //s32 IDFile; + int32 idfile = ExtractIDFile(item->IDFile); + + //s32 IDFile2; + buffer.WriteUInt32(0); //unsupported atm + + /* + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = item->Slots; + ibs.price = item->Price; + ibs.icon = item->Icon; + */ + + //s32 ItemNumber; + buffer.WriteUInt32(item->ID); + + //s32 Weight; + buffer.WriteInt32(item->Weight); + + //bool NoRent; + buffer.WriteUInt8(item->NoRent); + + //bool IsDroppable; + buffer.WriteUInt8(item->NoDrop); + + //bool Attuneable; + buffer.WriteUInt8(item->Attuneable); + + //u8 Size; + buffer.WriteUInt8(item->Size); + + //u32 EquipSlots; + buffer.WriteUInt32(item->Slots); + + //u32 Cost; + buffer.WriteUInt32(item->Price); + + //s32 IconNumber; + buffer.WriteInt32(item->Icon); + + //bool eGMRequirement; + buffer.WriteUInt8(0); //unsupported atm + + //bool Tradeskills; + buffer.WriteUInt8(0); //unsupported atm + + //s8 SvCold; + //s8 SvDisease; + //s8 SvPoison; + //s8 SvMagic; + //s8 SvFire; + //s8 SvCorruption; + buffer.WriteInt8(item->CR); + buffer.WriteInt8(item->DR); + buffer.WriteInt8(item->PR); + buffer.WriteInt8(item->MR); + buffer.WriteInt8(item->FR); + buffer.WriteInt8(item->SVCorruption); + + //s8 STR; + //s8 STA; + //s8 AGI; + //s8 DEX; + //s8 CHA; + //s8 INT; + //s8 WIS; + buffer.WriteInt8(item->AStr); + buffer.WriteInt8(item->ASta); + buffer.WriteInt8(item->AAgi); + buffer.WriteInt8(item->ADex); + buffer.WriteInt8(item->ACha); + buffer.WriteInt8(item->AInt); + buffer.WriteInt8(item->AWis); + + //s32 HP; + //s32 Mana; + //s32 Endurance; + //s32 AC; + buffer.WriteInt32(item->HP); + buffer.WriteInt32(item->Mana); + buffer.WriteInt32(item->Endur); + buffer.WriteInt32(item->AC); + + //s32 HPRegen; + //s32 ManaRegen; + //s32 EnduranceRegen; + buffer.WriteInt32(item->Regen); + buffer.WriteInt32(item->ManaRegen); + buffer.WriteInt32(item->EnduranceRegen); + + //u32 Classes; + //u32 Races; + //u32 Deity; + buffer.WriteUInt32(item->Classes); + buffer.WriteUInt32(item->Races); + buffer.WriteUInt32(item->Deity); + + //u32 SkillModValue; + //u32 SkillModMax; + //s32 SkillModType; + //s32 SkillModBonus; + buffer.WriteInt32(item->SkillModValue); + buffer.WriteUInt32(item->SkillModMax); + buffer.WriteInt32(item->SkillModType); + buffer.WriteInt32(0); //unsupported atm + + //s32 BaneDMGRace; + //s32 BaneDMGBodyType; + //s32 BaneDMGRaceValue; + //s32 BaneDMGBodyTypeValue; + buffer.WriteInt32(item->BaneDmgRace); + buffer.WriteInt32(item->BaneDmgBody); + buffer.WriteInt32(item->BaneDmgRaceAmt); + buffer.WriteInt32(item->BaneDmgAmt); + + //bool Magic; + buffer.WriteUInt8(item->Magic); + + //s32 FoodDuration; + buffer.WriteInt32(item->CastTime_); + + //s32 RequiredLevel; + buffer.WriteInt32(item->ReqLevel > 125 ? 125 : item->ReqLevel); + + //s32 RecommendedLevel; + buffer.WriteInt32(item->RecLevel > 125 ? 125 : item->RecLevel); + + //s32 InstrumentType; + //s32 InstrumentMod; + buffer.WriteInt32(item->BardType); + buffer.WriteInt32(item->BardValue); + + //u8 Light; + buffer.WriteUInt8(item->Light); + + //u8 Delay; + buffer.WriteUInt8(item->Delay); + + //u8 ElementalFlag; + //u8 ElementalDamage; + buffer.WriteUInt8(item->ElemDmgType); + buffer.WriteUInt8(item->ElemDmgAmt); + + //u8 Range; + buffer.WriteUInt8(item->Range); + + //u32 Damage; + buffer.WriteUInt32(item->Damage); + + //u32 MaterialTintIndex; + //u32 Prestige; + buffer.WriteUInt32(item->Color); + buffer.WriteUInt32(0); //unsupported atm + + //u8 ItemClass; + buffer.WriteUInt8(item->ItemType); + + //ArmorProperties properties; + //s32 Type; + //s32 Material; + //s32 Variation; + //s32 NewArmorId; + //s32 NewArmorType; + buffer.WriteUInt32(item->Material); + buffer.WriteUInt32(0); //unsupported atm + buffer.WriteUInt32(item->EliteMaterial); + buffer.WriteUInt32(item->HerosForgeModel); + buffer.WriteUInt32(0); //unsupported atm + + //float MerchantGreedMod; + buffer.WriteFloat(item->SellRate); + + //s32 DmgBonusSkill; + //s32 DmgBonusValue; + buffer.WriteInt32(item->ExtraDmgSkill); + buffer.WriteInt32(item->ExtraDmgAmt); + + //s32 ScriptID; + //char CharmFile[]; + buffer.WriteUInt32(item->CharmFileID); + buffer.WriteString(item->CharmFile); + + //s32 AugType; + //u32 AugSkinTypeMask; + //u32 AugRestrictions; + buffer.WriteUInt32(item->AugType); + buffer.WriteInt32(-1); //unsupported atm + buffer.WriteUInt32(item->AugRestrict); + + //ItemAugmentationSocket AugData[6]; + for (int j = 0; j < 6; ++j) { + /* + s32 Type; + bool Visible; + bool Infusible; + */ + + buffer.WriteInt32(item->AugSlotType[j]); + buffer.WriteUInt8(item->AugSlotVisible[j]); + buffer.WriteUInt8(item->AugSlotUnk2[j]); //not entirely supported atm + } + + //s32 LDType; + //s32 LDTheme; + //s32 LDCost; + //s32 PointBuyBackPercent; + //s32 NeedAdventureCompleted; + buffer.WriteUInt32(item->PointType); + buffer.WriteUInt32(item->LDoNTheme); + buffer.WriteUInt32(item->LDoNPrice); + buffer.WriteUInt32(item->LDoNSellBackRate); + buffer.WriteUInt32(item->LDoNSold); + + //u8 ContainerType; + //u8 Slots; + //u8 SizeCapacity; + //u8 WeightReduction; + buffer.WriteUInt8(item->BagType); + buffer.WriteUInt8(item->BagSlots); + buffer.WriteUInt8(item->BagSize); + buffer.WriteUInt8(item->BagWR); + + //u8 BookType; + //u8 BookLang; + //char BookFile[]; + buffer.WriteUInt8(item->Book); + buffer.WriteUInt8(item->BookType); //doesn't match the name for eqlib + buffer.WriteString(item->Filename); + + //s32 Lore; + buffer.WriteInt32(item->LoreGroup); + + //bool Artifact; + buffer.WriteUInt8(item->ArtifactFlag); + + //s32 Favor; + buffer.WriteUInt32(item->Favor); + + //bool bIsFVNoDrop; + buffer.WriteUInt8(item->FVNoDrop); + + //s32 Attack; + //s32 Haste; + buffer.WriteInt32(item->Attack); + buffer.WriteInt32(item->Haste); + + //s32 GuildFavor; + buffer.WriteUInt32(item->GuildFavor); + + //s32 SolventItemID; + buffer.WriteUInt32(item->AugDistiller); + + //s32 AnimationOverride; + buffer.WriteInt32(-1); //unsupported atm + + //u32 PaletteTintIndex; + buffer.WriteInt32(0); //unsupported atm + + //bool bNoPetGive; + buffer.WriteUInt8(item->NoPet); + + //bool bSomeProfile; + buffer.WriteUInt8(0); //unsupported atm + + //u32 StackSize; + buffer.WriteUInt32(item->ID == PARCEL_MONEY_ITEM_ID ? 0x7FFFFFFF : ((item->Stackable ? item->StackSize : 0))); + + //bool bNoStorage; + buffer.WriteUInt8(item->NoTransfer); + + //bool Expendable; + uint8 expendable = item->ExpendableArrow; + + if (item->ItemType == EQ::item::ItemTypeFishingPole && item->SubType == 0) { + expendable = 1; + } + + buffer.WriteUInt8(expendable); + + //u8 SpellDataSkillMask[78]; + for (int j = 0; j < 78; ++j) { + buffer.WriteUInt8(0); //unsure what this is exactly + } + /* + ItemSpellType_Clicky = 0, + ItemSpellType_Proc, + ItemSpellType_Worn, + ItemSpellType_Focus, + ItemSpellType_Scroll, + ItemSpellType_Focus2, + ItemSpellType_Blessing, + */ + + /* + struct ProcEffectStruct + { + int32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; // poison? + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 procrate; + //uint8 effect_string; + //uint32 unknown5; + }; + */ + + /* + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; + */ + //SpellData spell_data[7]; + + /* + s32 SpellId; + u8 RequiredLevel; + u8 EffectType; + s32 EffectiveCasterLevel; + s32 MaxCharges; + s32 CastTime; + s32 RecastTime; + s32 RecastType; + s32 ProcRate; + char OverrideName[]; + s32 OverrideDesc; + */ + + + //s32 RightClickScriptID; + //bool QuestItem; + //s32 MaxPower; + //s32 Purity; + //s32 BackstabDamage; + //s32 HeroicSTR; + //s32 HeroicINT; + //s32 HeroicWIS; + //s32 HeroicAGI; + //s32 HeroicDEX; + //s32 HeroicSTA; + //s32 HeroicCHA; + //s32 HealAmount; + //s32 SpellDamage; + //s32 Clairvoyance; + //s32 SubClass; + //bool bLoginRegReqItem; + //s32 ItemLaunchScriptID; + //bool Heirloom; + //s32 Placeable; + //bool bPlaceableIgnoreCollisions; + //s32 PlacementType; + //s32 RealEstateDefID; + //float PlaceableScaleRangeMin; + //float PlaceableScaleRangeMax; + //s32 RealEstateUpkeepID; + //s32 MaxPerRealEstate; + //char HousepetFileName[]; + //bool bInteractiveObject; + //s32 TrophyBenefitID; + //bool bDisablePlacementRotation; + //bool bDisableFreePlacement; + //s32 NpcRespawnInterval; + //float PlaceableDefScale; + //float PlaceableDefHeading; + //float PlaceableDefPitch; + //float PlaceableDefRoll; + //u8 SocketSubClassCount; + //s32 SocketSubClass[SocketSubClassCount]; + //bool Collectible; + //bool NoDestroy; + //bool bNoNPC; + //bool NoZone; + //s32 MakerId; + //bool NoGround; + //bool bNoLoot; + //bool MarketPlace; + //bool bFreeSlot; + //bool bAutoUse; + //s32 Unknown0x0e4; + //s32 MinLuck; + //s32 MaxLuck; + //s32 LoreEquipped; + } + + void SerializeItem(SerializeBuffer& buffer, const EQ::ItemInstance* inst, int16 slot_id_in, uint8 depth, ItemPacketType packet_type) { + const EQ::ItemData* item = inst->GetUnscaledItem(); + + //char ItemGUID[]; + auto item_guid = fmt::format("{:016}", inst->GetSerialNumber()); + buffer.WriteString(item_guid); + + //u32 StackCount; + auto stacksize = + item->ID == PARCEL_MONEY_ITEM_ID ? inst->GetPrice() : (inst->IsStackable() ? ((inst->GetCharges() > 1000) + ? 0xFFFFFFFF : inst->GetCharges()) : 1); + buffer.WriteUInt32(stacksize); + + structs::InventorySlot_Struct slot_id{}; + switch (packet_type) { + case ItemPacketLoot: + slot_id = ServerToLarionCorpseSlot(slot_id_in); + break; + default: + slot_id = ServerToLarionSlot(slot_id_in); + break; + } + + //u32 slot_type; + buffer.WriteUInt32(inst->GetMerchantSlot() ? invtype::typeMerchant : slot_id.Type); + //s16 main_slot; + //s16 sub_slot; + //s16 aug_slot; + buffer.WriteInt16(inst->GetMerchantSlot() ? inst->GetMerchantSlot() : slot_id.Slot); + buffer.WriteInt16(inst->GetMerchantSlot() ? 0xffff : slot_id.SubIndex); + buffer.WriteInt16(inst->GetMerchantSlot() ? 0xffff : slot_id.AugIndex); + + //u64 price; + buffer.WriteUInt64(inst->GetPrice()); + + //u32 MerchantQuantity; + buffer.WriteUInt32(inst->GetMerchantSlot() ? inst->GetMerchantCount() : 1); + + //u32 ScriptIndex; + buffer.WriteUInt32(inst->IsScaling() ? (inst->GetExp() / 100) : 0); + + //u64 MerchantSlot; + buffer.WriteUInt64(inst->GetMerchantSlot() ? inst->GetMerchantSlot() : inst->GetSerialNumber()); + + //u32 LastCastTime; + buffer.WriteUInt32(inst->GetRecastTimestamp()); + + //s32 Charges; + auto charges = (inst->IsStackable() ? (item->MaxCharges ? 1 : 0) : ((inst->GetCharges() > 254) + ? -1 + : inst->GetCharges())); + + buffer.WriteInt32(charges); + + //s32 NoDropFlag; + buffer.WriteInt32(inst->IsAttuned() ? 1 : 0); + + //s32 Power; + buffer.WriteInt32(0); + + //s32 AugFlag; + buffer.WriteInt32(0); + + //bool bConvertable; + buffer.WriteInt8(0); + + //u32 ConvertItemNameLength; + buffer.WriteInt32(0); + + //char ConvertItemName[ConvertItemNameLength]; + + //u32 Open; + buffer.WriteInt32(0); + + //bool EvolvingItem; + buffer.WriteInt8(item->EvolvingItem); + + //EvoData evoData; + if (item->EvolvingItem > 0) { + //s32 GroupId; + buffer.WriteInt32(0); + + //s32 EvolvingCurrentLevel; + buffer.WriteInt32(item->EvolvingLevel); + + //double EvolvingExpPct; + buffer.WriteDouble(0.0); + + //s32 EvolvingMaxLevel; + buffer.WriteInt32(item->EvolvingMax); + + //s32 LastEquipped; + buffer.WriteInt32(0); + } + + uint32 ornamentation_icon = (inst->GetOrnamentationIcon() ? inst->GetOrnamentationIcon() : 0); + uint32 hero_model = 0; + + //s32 ActorTag1; + //s32 ActorTag2; + if (inst->GetOrnamentationIDFile()) { + hero_model = inst->GetOrnamentHeroModel(EQ::InventoryProfile::CalcMaterialFromSlot(slot_id_in)); + + buffer.WriteInt32(inst->GetOrnamentationIDFile()); + buffer.WriteInt32(inst->GetOrnamentationIDFile()); + } + else { + buffer.WriteInt32(0); + buffer.WriteInt32(0); + } + + //s32 OrnamentationIcon; + //s32 ArmorType; + //s32 NewArmorID; + //u32 Tint; + buffer.WriteInt32(ornamentation_icon); + buffer.WriteInt32(-1); + buffer.WriteInt32(hero_model); + buffer.WriteInt32(0); + + //bool bCopied; + buffer.WriteUInt8(0); + + //s32 RealEstateID; + buffer.WriteInt32(-1); + //s32 RespawnTime; + buffer.WriteInt32(0); + + //ItemDefinition Item; + SerializeItemDefinition(buffer, item); + + //u32 RealEstateArrayCount; + buffer.WriteInt32(0); + //s32 RealEstateArray[RealEstateArrayCount]; + + //bool bRealEstateItemPlaceable; + buffer.WriteInt8(0); + + //u32 SubContentSize; + } + + static inline structs::InventorySlot_Struct ServerToLarionSlot(uint32 server_slot) + { + structs::InventorySlot_Struct LarionSlot; + LarionSlot.Type = invtype::TYPE_INVALID; + LarionSlot.Slot = invslot::SLOT_INVALID; + LarionSlot.SubIndex = invbag::SLOT_INVALID; + LarionSlot.AugIndex = invaug::SOCKET_INVALID; + + uint32 TempSlot = EQ::invslot::SLOT_INVALID; + + if (server_slot < EQ::invtype::POSSESSIONS_SIZE) { + LarionSlot.Type = invtype::typePossessions; + LarionSlot.Slot = server_slot; + } + + else if (server_slot <= EQ::invbag::CURSOR_BAG_END && server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { + TempSlot = server_slot - EQ::invbag::GENERAL_BAGS_BEGIN; + + LarionSlot.Type = invtype::typePossessions; + LarionSlot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQ::invbag::SLOT_COUNT); + LarionSlot.SubIndex = TempSlot - ((LarionSlot.Slot - invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQ::invslot::TRIBUTE_END && server_slot >= EQ::invslot::TRIBUTE_BEGIN) { + LarionSlot.Type = invtype::typeTribute; + LarionSlot.Slot = server_slot - EQ::invslot::TRIBUTE_BEGIN; + } + + else if (server_slot <= EQ::invslot::GUILD_TRIBUTE_END && server_slot >= EQ::invslot::GUILD_TRIBUTE_BEGIN) { + LarionSlot.Type = invtype::typeGuildTribute; + LarionSlot.Slot = server_slot - EQ::invslot::GUILD_TRIBUTE_BEGIN; + } + + else if (server_slot == EQ::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { + LarionSlot.Type = invtype::typeWorld; + } + + else if (server_slot <= EQ::invslot::BANK_END && server_slot >= EQ::invslot::BANK_BEGIN) { + LarionSlot.Type = invtype::typeBank; + LarionSlot.Slot = server_slot - EQ::invslot::BANK_BEGIN; + } + + else if (server_slot <= EQ::invbag::BANK_BAGS_END && server_slot >= EQ::invbag::BANK_BAGS_BEGIN) { + TempSlot = server_slot - EQ::invbag::BANK_BAGS_BEGIN; + + LarionSlot.Type = invtype::typeBank; + LarionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; + LarionSlot.SubIndex = TempSlot - (LarionSlot.Slot * EQ::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQ::invslot::SHARED_BANK_END && server_slot >= EQ::invslot::SHARED_BANK_BEGIN) { + LarionSlot.Type = invtype::typeSharedBank; + LarionSlot.Slot = server_slot - EQ::invslot::SHARED_BANK_BEGIN; + } + + else if (server_slot <= EQ::invbag::SHARED_BANK_BAGS_END && server_slot >= EQ::invbag::SHARED_BANK_BAGS_BEGIN) { + TempSlot = server_slot - EQ::invbag::SHARED_BANK_BAGS_BEGIN; + + LarionSlot.Type = invtype::typeSharedBank; + LarionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; + LarionSlot.SubIndex = TempSlot - (LarionSlot.Slot * EQ::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQ::invslot::TRADE_END && server_slot >= EQ::invslot::TRADE_BEGIN) { + LarionSlot.Type = invtype::typeTrade; + LarionSlot.Slot = server_slot - EQ::invslot::TRADE_BEGIN; + } + + else if (server_slot <= EQ::invbag::TRADE_BAGS_END && server_slot >= EQ::invbag::TRADE_BAGS_BEGIN) { + TempSlot = server_slot - EQ::invbag::TRADE_BAGS_BEGIN; + + LarionSlot.Type = invtype::typeTrade; + LarionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; + LarionSlot.SubIndex = TempSlot - (LarionSlot.Slot * EQ::invbag::SLOT_COUNT); + } + + else if (server_slot <= EQ::invslot::WORLD_END && server_slot >= EQ::invslot::WORLD_BEGIN) { + LarionSlot.Type = invtype::typeWorld; + LarionSlot.Slot = server_slot - EQ::invslot::WORLD_BEGIN; + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to Larion Slot [%i, %i, %i, %i]", + server_slot, LarionSlot.Type, LarionSlot.Slot, LarionSlot.SubIndex, LarionSlot.AugIndex); + + return LarionSlot; + } + + static inline structs::InventorySlot_Struct ServerToLarionCorpseSlot(uint32 server_corpse_slot) + { + structs::InventorySlot_Struct LarionSlot; + LarionSlot.Type = invtype::TYPE_INVALID; + LarionSlot.Slot = ServerToLarionCorpseMainSlot(server_corpse_slot); + LarionSlot.SubIndex = invbag::SLOT_INVALID; + LarionSlot.AugIndex = invaug::SOCKET_INVALID; + + if (LarionSlot.Slot != invslot::SLOT_INVALID) + LarionSlot.Type = invtype::typeCorpse; + + Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to Larion Corpse Slot [%i, %i, %i, %i]", + server_corpse_slot, LarionSlot.Type, LarionSlot.Slot, LarionSlot.SubIndex, LarionSlot.AugIndex); + + return LarionSlot; + } + static inline uint32 ServerToLarionCorpseMainSlot(uint32 server_corpse_slot) + { + uint32 LarionSlot = invslot::SLOT_INVALID; + + if (server_corpse_slot <= EQ::invslot::CORPSE_END && server_corpse_slot >= EQ::invslot::CORPSE_BEGIN) { + LarionSlot = server_corpse_slot; + } + + LogNetcode("Convert Server Corpse Slot [{}] to Larion Corpse Main Slot [{}]", server_corpse_slot, LarionSlot); + + return LarionSlot; + } + + static inline structs::TypelessInventorySlot_Struct ServerToLarionTypelessSlot(uint32 server_slot, int16 server_type) + { + structs::TypelessInventorySlot_Struct LarionSlot; + LarionSlot.Slot = invslot::SLOT_INVALID; + LarionSlot.SubIndex = invbag::SLOT_INVALID; + LarionSlot.AugIndex = invaug::SOCKET_INVALID; + + uint32 TempSlot = EQ::invslot::SLOT_INVALID; + + if (server_type == EQ::invtype::typePossessions) { + if (server_slot < EQ::invtype::POSSESSIONS_SIZE) { + LarionSlot.Slot = server_slot; + } + + else if (server_slot <= EQ::invbag::CURSOR_BAG_END && server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { + TempSlot = server_slot - EQ::invbag::GENERAL_BAGS_BEGIN; + + LarionSlot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQ::invbag::SLOT_COUNT); + LarionSlot.SubIndex = TempSlot - ((LarionSlot.Slot - invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT); + } + } + + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to Larion Typeless Slot [%i, %i, %i] (implied type: %i)", + server_slot, LarionSlot.Slot, LarionSlot.SubIndex, LarionSlot.AugIndex, server_type); + + return LarionSlot; + } + + static inline uint32 LarionToServerSlot(structs::InventorySlot_Struct larion_slot) + { + if (larion_slot.AugIndex < invaug::SOCKET_INVALID || larion_slot.AugIndex >= invaug::SOCKET_COUNT) { + Log(Logs::Detail, Logs::Netcode, "Convert Larion Slot [%i, %i, %i, %i] to Server Slot %i", + larion_slot.Type, larion_slot.Slot, larion_slot.SubIndex, larion_slot.AugIndex, EQ::invslot::SLOT_INVALID); + + return EQ::invslot::SLOT_INVALID; + } + + uint32 server_slot = EQ::invslot::SLOT_INVALID; + uint32 temp_slot = invslot::SLOT_INVALID; + + switch (larion_slot.Type) { + case invtype::typePossessions: { + if (larion_slot.Slot >= invslot::POSSESSIONS_BEGIN && larion_slot.Slot <= invslot::POSSESSIONS_END) { + if (larion_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = larion_slot.Slot; + } + + else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { + if (larion_slot.Slot < invslot::GENERAL_BEGIN) + return EQ::invslot::SLOT_INVALID; + + temp_slot = (larion_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; + server_slot = EQ::invbag::GENERAL_BAGS_BEGIN + temp_slot + larion_slot.SubIndex; + } + } + + break; + } + case invtype::typeBank: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::BANK_SIZE) { + if (larion_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQ::invslot::BANK_BEGIN + larion_slot.Slot; + } + + else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = larion_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQ::invbag::BANK_BAGS_BEGIN + temp_slot + larion_slot.SubIndex; + } + } + + break; + } + case invtype::typeSharedBank: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::SHARED_BANK_SIZE) { + if (larion_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQ::invslot::SHARED_BANK_BEGIN + larion_slot.Slot; + } + + else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = larion_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQ::invbag::SHARED_BANK_BAGS_BEGIN + temp_slot + larion_slot.SubIndex; + } + } + + break; + } + case invtype::typeTrade: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::TRADE_SIZE) { + if (larion_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQ::invslot::TRADE_BEGIN + larion_slot.Slot; + } + + else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = larion_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQ::invbag::TRADE_BAGS_BEGIN + temp_slot + larion_slot.SubIndex; + } + } + + break; + } + case invtype::typeWorld: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::WORLD_SIZE) { + server_slot = EQ::invslot::WORLD_BEGIN + larion_slot.Slot; + } + + else if (larion_slot.Slot == invslot::SLOT_INVALID) { + server_slot = EQ::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; + } + + break; + } + case invtype::typeLimbo: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::LIMBO_SIZE) { + server_slot = EQ::invslot::slotCursor; + } + + break; + } + case invtype::typeTribute: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::TRIBUTE_SIZE) { + server_slot = EQ::invslot::TRIBUTE_BEGIN + larion_slot.Slot; + } + + break; + } + case invtype::typeGuildTribute: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { + server_slot = EQ::invslot::GUILD_TRIBUTE_BEGIN + larion_slot.Slot; + } + + break; + } + case invtype::typeCorpse: { + if (larion_slot.Slot >= invslot::CORPSE_BEGIN && larion_slot.Slot <= invslot::CORPSE_END) { + server_slot = larion_slot.Slot; + } + + break; + } + default: { + + break; + } + } + + Log(Logs::Detail, Logs::Netcode, "Convert Larion Slot [%i, %i, %i, %i] to Server Slot %i", + larion_slot.Type, larion_slot.Slot, larion_slot.SubIndex, larion_slot.AugIndex, server_slot); + + return server_slot; + } + + static inline uint32 LarionToServerCorpseSlot(structs::InventorySlot_Struct larion_corpse_slot) + { + uint32 ServerSlot = EQ::invslot::SLOT_INVALID; + + if (larion_corpse_slot.Type != invtype::typeCorpse || larion_corpse_slot.SubIndex != invbag::SLOT_INVALID || larion_corpse_slot.AugIndex != invaug::SOCKET_INVALID) { + ServerSlot = EQ::invslot::SLOT_INVALID; + } + + else { + ServerSlot = LarionToServerCorpseMainSlot(larion_corpse_slot.Slot); + } + + Log(Logs::Detail, Logs::Netcode, "Convert Larion Slot [%i, %i, %i, %i] to Server Slot %i", + larion_corpse_slot.Type, larion_corpse_slot.Slot, larion_corpse_slot.SubIndex, larion_corpse_slot.AugIndex, ServerSlot); + + return ServerSlot; + } + + static inline uint32 LarionToServerCorpseMainSlot(uint32 larion_corpse_slot) + { + uint32 ServerSlot = EQ::invslot::SLOT_INVALID; + + if (larion_corpse_slot <= invslot::CORPSE_END && larion_corpse_slot >= invslot::CORPSE_BEGIN) { + ServerSlot = larion_corpse_slot; + } + + LogNetcode("Convert Larion Corpse Main Slot [{}] to Server Corpse Slot [{}]", larion_corpse_slot, ServerSlot); + + return ServerSlot; + } + + static inline uint32 LarionToServerTypelessSlot(structs::TypelessInventorySlot_Struct larion_slot, int16 larion_type) + { + if (larion_slot.AugIndex < invaug::SOCKET_INVALID || larion_slot.AugIndex >= invaug::SOCKET_COUNT) { + Log(Logs::Detail, Logs::Netcode, "Convert Larion Typeless Slot [%i, %i, %i] (implied type: %i) to Server Slot %i", + larion_slot.Slot, larion_slot.SubIndex, larion_slot.AugIndex, larion_type, EQ::invslot::SLOT_INVALID); + + return EQ::invslot::SLOT_INVALID; + } + + uint32 ServerSlot = EQ::invslot::SLOT_INVALID; + uint32 TempSlot = invslot::SLOT_INVALID; + + switch (larion_type) { + case invtype::typePossessions: { + if (larion_slot.Slot >= invslot::POSSESSIONS_BEGIN && larion_slot.Slot <= invslot::POSSESSIONS_END) { + if (larion_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = larion_slot.Slot; + } + + else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { + if (larion_slot.Slot < invslot::GENERAL_BEGIN) + return EQ::invslot::SLOT_INVALID; + + TempSlot = (larion_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; + ServerSlot = EQ::invbag::GENERAL_BAGS_BEGIN + TempSlot + larion_slot.SubIndex; + } + } + + break; + } + case invtype::typeBank: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::BANK_SIZE) { + if (larion_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQ::invslot::BANK_BEGIN + larion_slot.Slot; + } + + else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = larion_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQ::invbag::BANK_BAGS_BEGIN + TempSlot + larion_slot.SubIndex; + } + } + + break; + } + case invtype::typeSharedBank: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::SHARED_BANK_SIZE) { + if (larion_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQ::invslot::SHARED_BANK_BEGIN + larion_slot.Slot; + } + + else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = larion_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQ::invbag::SHARED_BANK_BAGS_BEGIN + TempSlot + larion_slot.SubIndex; + } + } + + break; + } + case invtype::typeTrade: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::TRADE_SIZE) { + if (larion_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQ::invslot::TRADE_BEGIN + larion_slot.Slot; + } + + else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = larion_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQ::invbag::TRADE_BAGS_BEGIN + TempSlot + larion_slot.SubIndex; + } + } + + break; + } + case invtype::typeWorld: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::WORLD_SIZE) { + ServerSlot = EQ::invslot::WORLD_BEGIN + larion_slot.Slot; + } + + else if (larion_slot.Slot == invslot::SLOT_INVALID) { + ServerSlot = EQ::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; + } + + break; + } + case invtype::typeLimbo: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::LIMBO_SIZE) { + ServerSlot = EQ::invslot::slotCursor; + } + + break; + } + case invtype::typeTribute: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::TRIBUTE_SIZE) { + ServerSlot = EQ::invslot::TRIBUTE_BEGIN + larion_slot.Slot; + } + + break; + } + case invtype::typeGuildTribute: { + if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { + ServerSlot = EQ::invslot::GUILD_TRIBUTE_BEGIN + larion_slot.Slot; + } + + break; + } + case invtype::typeCorpse: { + if (larion_slot.Slot >= invslot::CORPSE_BEGIN && larion_slot.Slot <= invslot::CORPSE_END) { + ServerSlot = larion_slot.Slot; + } + + break; + } + default: { + + break; + } + } + + Log(Logs::Detail, Logs::Netcode, "Convert Larion Typeless Slot [%i, %i, %i] (implied type: %i) to Server Slot %i", + larion_slot.Slot, larion_slot.SubIndex, larion_slot.AugIndex, larion_type, ServerSlot); + + return ServerSlot; + } } /*Larion*/ diff --git a/common/patches/larion_limits.h b/common/patches/larion_limits.h index b6598c20f..c1cfca615 100644 --- a/common/patches/larion_limits.h +++ b/common/patches/larion_limits.h @@ -70,7 +70,23 @@ namespace Larion typeMail, typeGuildTrophyTribute, typeKrono, - typeOther + typeOther, + typeMercenaryItems, + typeViewModMercenaryItems, + typeMountKeyRingItems, + typeViewModMountKeyRingItems, + typeIllusionKeyRingItems, + typeViewModIllusionKeyRingItems, + typeFamiliarKeyRingItems, + typeViewModFamiliarKeyRingItems, + typeHeroForgeKeyRingItems, + typeViewModHeroForgeKeyRingItems, + typeTeleportationKeyRingItems, + typeViewModTeleportationKeyRingItems, + typeOverflow, + typeDragonHoard, + typeTradeskillDepot, + typeGuildTradeskillDepot }; } // namespace enum_ diff --git a/common/patches/larion_ops.h b/common/patches/larion_ops.h index b5316b5f3..abc09ca3a 100644 --- a/common/patches/larion_ops.h +++ b/common/patches/larion_ops.h @@ -24,6 +24,7 @@ E(OP_WearChange) E(OP_ChannelMessage) E(OP_SpecialMesg) E(OP_DeleteSpawn) +E(OP_FormattedMessage) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) @@ -31,6 +32,7 @@ D(OP_ZoneChange) D(OP_ClientUpdate) D(OP_WearChange) D(OP_ChannelMessage) +D(OP_SetServerFilter) #undef E #undef D diff --git a/common/patches/larion_structs.h b/common/patches/larion_structs.h index 2f613310c..927527048 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/larion_structs.h @@ -408,6 +408,36 @@ namespace Larion { /*05*/ }; + //OP_SetServerFilter + struct SetServerFilter_Struct { + uint32 filters[68]; + }; + + // Was new to RoF2, doesn't look changed + // The padding is because these structs are padded to the default 4 bytes + struct InventorySlot_Struct + { + /*000*/ int16 Type; + /*002*/ int16 Padding1; + /*004*/ int16 Slot; + /*006*/ int16 SubIndex; + /*008*/ int16 AugIndex; + /*010*/ int16 Padding2; + /*012*/ + }; + + // Was new for RoF2 - Used for Merchant_Purchase_Struct, doesn't look changed + // Can't sellfrom other than main inventory so Slot Type is not needed. + // The padding is because these structs are padded to the default 4 bytes + struct TypelessInventorySlot_Struct + { + /*000*/ int16 Slot; + /*002*/ int16 SubIndex; + /*004*/ int16 AugIndex; + /*006*/ int16 Padding; + /*008*/ + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Larion.conf index 557d8b305..52a738ce7 100644 --- a/utils/patches/patch_Larion.conf +++ b/utils/patches/patch_Larion.conf @@ -46,7 +46,7 @@ OP_WorldUnknown001=0x2049 OP_FloatListThing=0x0000 # Reasons for Disconnect: -OP_ZoneUnavail=0x0000 +OP_ZoneUnavail=0x582d OP_WorldClientReady=0x7ed8 OP_CharacterStillInZone=0x0000 OP_WorldChecksumFailure=0x0000 @@ -66,8 +66,8 @@ OP_NewZone=0x4341 OP_ZoneSpawns=0x17d9 OP_PlayerProfile=0x1c76 OP_TimeOfDay=0x3736 -OP_LevelUpdate=0x0000 #0x0eb2 -OP_Stamina=0x0000 #0x1563 +OP_LevelUpdate=0x0eb2 +OP_Stamina=0x1563 OP_RequestClientZoneChange=0x0191 OP_ZoneChange=0x17a3 OP_LockoutTimerInfo=0x0000 @@ -112,7 +112,7 @@ OP_CharInventory=0x21d6 OP_WearChange=0x44c0 OP_ClientUpdate=0x3a4b OP_ClientReady=0x0831 -OP_SetServerFilter=0x0000 #0x6b7f +OP_SetServerFilter=0x6b7f # Guild Opcodes OP_GetGuildMOTD=0x0000 @@ -191,7 +191,7 @@ OP_DenyResponse=0x0000 OP_SkillUpdate=0x04c OP_GMTrainSkillConfirm=0x0000 OP_RandomReq=0x0000 -OP_Death=0x0000 +OP_Death=0x429a OP_GMTraining=0x0000 OP_GMEndTraining=0x0000 OP_GMTrainSkill=0x0000 @@ -265,9 +265,9 @@ OP_NewSpawn=0x3ea8 OP_Track=0x0000 OP_TrackTarget=0x0000 OP_TrackUnknown=0x0000 -OP_ClickDoor=0x0000 -OP_MoveDoor=0x0000 -OP_RemoveAllDoors=0x0000 +OP_ClickDoor=0x733c +OP_MoveDoor=0x567c +OP_RemoveAllDoors=0x73e8 OP_EnvDamage=0x0000 OP_BoardBoat=0x0000 OP_LeaveBoat=0x0000 From 9d5a9ee6dfac47c99c769e37aef6a4d362134591 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 22 Nov 2024 22:58:43 -0800 Subject: [PATCH 46/93] More work in progress --- common/patches/larion.cpp | 270 +++++++++++++++++++++++++++++++------- 1 file changed, 224 insertions(+), 46 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index ce3c6388d..7a2443296 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -3027,62 +3027,152 @@ namespace Larion for (int j = 0; j < 78; ++j) { buffer.WriteUInt8(0); //unsure what this is exactly } - /* - ItemSpellType_Clicky = 0, - ItemSpellType_Proc, - ItemSpellType_Worn, - ItemSpellType_Focus, - ItemSpellType_Scroll, - ItemSpellType_Focus2, - ItemSpellType_Blessing, + + /* SpellData: + s32 SpellId; + u8 RequiredLevel; + u8 EffectType; + s32 EffectiveCasterLevel; + s32 MaxCharges; + s32 CastTime; + s32 RecastTime; + s32 RecastType; + s32 ProcRate; + char OverrideName[]; + s32 OverrideDesc; */ - /* - struct ProcEffectStruct - { - int32 effect; - uint8 level2; - uint32 type; - uint8 level; - uint32 unknown1; // poison? - uint32 unknown2; - uint32 unknown3; - uint32 unknown4; - uint32 procrate; - //uint8 effect_string; - //uint32 unknown5; - }; - */ + //SpellData SpellDataClicky; + buffer.WriteInt32(item->Click.Effect); + buffer.WriteUInt8(item->Click.Level2); + buffer.WriteUInt8(item->Click.Type); + buffer.WriteInt32(item->Click.Level); + buffer.WriteInt32(item->MaxCharges); + buffer.WriteInt32(item->CastTime); + buffer.WriteInt32(item->RecastDelay); + buffer.WriteInt32(item->RecastType); + buffer.WriteInt32(0); //unsupported atm + if (strlen(item->ClickName) > 0) { + buffer.WriteString(item->ClickName); + } + else { + buffer.WriteString(""); + } + buffer.WriteInt32(0); //unsupported atm - /* - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; - */ - //SpellData spell_data[7]; + //SpellData SpellDataProc; + buffer.WriteInt32(item->Proc.Effect); + buffer.WriteUInt8(item->Proc.Level2); + buffer.WriteUInt8(item->Proc.Type); + buffer.WriteInt32(item->Proc.Level); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); //unsupported atm; even live sets this to 0 for procs + if (strlen(item->ProcName) > 0) { + buffer.WriteString(item->ProcName); + } + else { + buffer.WriteString(""); + } + buffer.WriteInt32(0); //unsupported atm - /* - s32 SpellId; - u8 RequiredLevel; - u8 EffectType; - s32 EffectiveCasterLevel; - s32 MaxCharges; - s32 CastTime; - s32 RecastTime; - s32 RecastType; - s32 ProcRate; - char OverrideName[]; - s32 OverrideDesc; - */ - + //SpellData SpellDataWorn; + buffer.WriteInt32(item->Worn.Effect); + buffer.WriteUInt8(item->Worn.Level2); + buffer.WriteUInt8(item->Worn.Type); + buffer.WriteInt32(item->Worn.Level); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + if (strlen(item->WornName) > 0) { + buffer.WriteString(item->WornName); + } + else { + buffer.WriteString(""); + } + buffer.WriteInt32(0); //unsupported atm + + //SpellData SpellDataFocus; + buffer.WriteInt32(item->Focus.Effect); + buffer.WriteUInt8(item->Focus.Level2); + buffer.WriteUInt8(item->Focus.Type); + buffer.WriteInt32(item->Focus.Level); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + if (strlen(item->FocusName) > 0) { + buffer.WriteString(item->FocusName); + } + else { + buffer.WriteString(""); + } + buffer.WriteInt32(0); //unsupported atm + + //SpellData SpellDataScroll; + buffer.WriteInt32(item->Scroll.Effect); + buffer.WriteUInt8(item->Scroll.Level2); + buffer.WriteUInt8(item->Scroll.Type); + buffer.WriteInt32(item->Scroll.Level); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + if (strlen(item->ScrollName) > 0) { + buffer.WriteString(item->ScrollName); + } + else { + buffer.WriteString(""); + } + buffer.WriteInt32(0); //unsupported atm + + //SpellData SpellDataFocus2; //unsupported atm + buffer.WriteInt32(-1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteString(""); + buffer.WriteInt32(0); + + //SpellData SpellDataBlessing; //unsupported atm + buffer.WriteInt32(-1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteString(""); + buffer.WriteInt32(0); //s32 RightClickScriptID; + buffer.WriteInt32(0); //unsupported atm + //bool QuestItem; + buffer.WriteInt8(item->QuestItemFlag); + //s32 MaxPower; + buffer.WriteInt32(0); //unsupported atm + //s32 Purity; + buffer.WriteInt32(item->Purity); + //s32 BackstabDamage; + buffer.WriteInt32(item->BackstabDmg); + //s32 HeroicSTR; //s32 HeroicINT; //s32 HeroicWIS; @@ -3090,47 +3180,126 @@ namespace Larion //s32 HeroicDEX; //s32 HeroicSTA; //s32 HeroicCHA; + buffer.WriteInt32(item->HeroicStr); + buffer.WriteInt32(item->HeroicInt); + buffer.WriteInt32(item->HeroicWis); + buffer.WriteInt32(item->HeroicAgi); + buffer.WriteInt32(item->HeroicDex); + buffer.WriteInt32(item->HeroicSta); + buffer.WriteInt32(item->HeroicCha); + //s32 HealAmount; //s32 SpellDamage; //s32 Clairvoyance; + buffer.WriteInt32(item->HealAmt); + buffer.WriteInt32(item->SpellDmg); + buffer.WriteInt32(item->Clairvoyance); + //s32 SubClass; + buffer.WriteInt32(item->SubType); + //bool bLoginRegReqItem; + buffer.WriteUInt8(0); //unsupported atm + //s32 ItemLaunchScriptID; + buffer.WriteInt32(0); //unsupported atm + //bool Heirloom; + buffer.WriteUInt8(0); //unsupported atm + //s32 Placeable; + buffer.WriteInt32(0); //unsupported atm + //bool bPlaceableIgnoreCollisions; + buffer.WriteUInt8(0); + //s32 PlacementType; + buffer.WriteInt32(0); //unsupported atm + //s32 RealEstateDefID; + buffer.WriteInt32(0); //unsupported atm + //float PlaceableScaleRangeMin; //float PlaceableScaleRangeMax; + buffer.WriteFloat(0.0f); //unsupported atm + buffer.WriteFloat(0.0f); //unsupported atm + //s32 RealEstateUpkeepID; + buffer.WriteInt32(0); //unsupported atm + //s32 MaxPerRealEstate; + buffer.WriteInt32(-1); //unsupported atm + //char HousepetFileName[]; + buffer.WriteString(""); //unsupported atm + //bool bInteractiveObject; + buffer.WriteUInt8(0); //unsupported atm + //s32 TrophyBenefitID; + buffer.WriteInt32(-1); //unsupported atm + //bool bDisablePlacementRotation; //bool bDisableFreePlacement; + buffer.WriteUInt8(0); //unsupported atm + buffer.WriteUInt8(0); //unsupported atm + //s32 NpcRespawnInterval; + buffer.WriteInt32(0); //unsupported atm + //float PlaceableDefScale; //float PlaceableDefHeading; //float PlaceableDefPitch; //float PlaceableDefRoll; + buffer.WriteFloat(0.0f); + buffer.WriteFloat(0.0f); + buffer.WriteFloat(0.0f); + buffer.WriteFloat(0.0f); + //u8 SocketSubClassCount; //s32 SocketSubClass[SocketSubClassCount]; + buffer.WriteUInt8(0); //unsupported atm + //bool Collectible; + buffer.WriteUInt8(0); //unsupported atm + //bool NoDestroy; + buffer.WriteUInt8(0); //unsupported atm + //bool bNoNPC; + buffer.WriteUInt8(0); //unsupported atm + //bool NoZone; + buffer.WriteUInt8(0); //unsupported atm + //s32 MakerId; + buffer.WriteInt32(0); //unsupported atm + //bool NoGround; + buffer.WriteUInt8(0); //unsupported atm + //bool bNoLoot; + buffer.WriteUInt8(0); //unsupported atm + //bool MarketPlace; + buffer.WriteUInt8(0); //unsupported atm + //bool bFreeSlot; + buffer.WriteUInt8(0); //unsupported atm + //bool bAutoUse; + buffer.WriteUInt8(0); //unsupported atm + //s32 Unknown0x0e4; + buffer.WriteInt32(-1); //unsupported atm + //s32 MinLuck; //s32 MaxLuck; + buffer.WriteUInt32(0); //unsupported atm + buffer.WriteUInt32(0); //unsupported atm + //s32 LoreEquipped; + buffer.WriteUInt32(0); //unsupported atm } void SerializeItem(SerializeBuffer& buffer, const EQ::ItemInstance* inst, int16 slot_id_in, uint8 depth, ItemPacketType packet_type) { @@ -3272,6 +3441,15 @@ namespace Larion buffer.WriteInt8(0); //u32 SubContentSize; + + //SubItems + + //bool bCollected; + buffer.WriteInt8(0); //unsupported atm + //u64 DontKnow; + buffer.WriteUInt64(0); //unsupported atm + //s32 Luck; + buffer.WriteInt32(0); //unsupported atm } static inline structs::InventorySlot_Struct ServerToLarionSlot(uint32 server_slot) From 1ab0e78f005f662686d4da93316be6351f014875 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 22 Nov 2024 23:15:20 -0800 Subject: [PATCH 47/93] WIP; close if not there already. --- common/patches/larion.cpp | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 7a2443296..4125c990b 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -3441,8 +3441,42 @@ namespace Larion buffer.WriteInt8(0); //u32 SubContentSize; + uint32 subitem_count = 0; - //SubItems + int16 SubSlotNumber = EQ::invbag::SLOT_INVALID; + + if (slot_id_in <= EQ::invslot::GENERAL_END && slot_id_in >= EQ::invslot::GENERAL_BEGIN) + SubSlotNumber = EQ::invbag::GENERAL_BAGS_BEGIN + ((slot_id_in - EQ::invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT); + else if (slot_id_in == EQ::invslot::slotCursor) + SubSlotNumber = EQ::invbag::CURSOR_BAG_BEGIN; + else if (slot_id_in <= EQ::invslot::BANK_END && slot_id_in >= EQ::invslot::BANK_BEGIN) + SubSlotNumber = EQ::invbag::BANK_BAGS_BEGIN + ((slot_id_in - EQ::invslot::BANK_BEGIN) * EQ::invbag::SLOT_COUNT); + else if (slot_id_in <= EQ::invslot::SHARED_BANK_END && slot_id_in >= EQ::invslot::SHARED_BANK_BEGIN) + SubSlotNumber = EQ::invbag::SHARED_BANK_BAGS_BEGIN + ((slot_id_in - EQ::invslot::SHARED_BANK_BEGIN) * EQ::invbag::SLOT_COUNT); + else + SubSlotNumber = slot_id_in; // not sure if this is the best way to handle this..leaving for now + + if (SubSlotNumber != EQ::invbag::SLOT_INVALID) { + for (uint32 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; ++index) { + EQ::ItemInstance* sub = inst->GetItem(index); + if (!sub) + continue; + + ++subitem_count; + } + + buffer.WriteUInt32(subitem_count); + + for (uint32 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; ++index) { + EQ::ItemInstance* sub = inst->GetItem(index); + if (!sub) + continue; + + buffer.WriteUInt32(index); + + SerializeItem(buffer, sub, SubSlotNumber, (depth + 1), packet_type); + } + } //bool bCollected; buffer.WriteInt8(0); //unsupported atm From 233a0dda6a5a55923af0c09dc04e5f9af9873ccf Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 23 Nov 2024 22:28:17 -0800 Subject: [PATCH 48/93] Fix op_charinventory (mostly) --- common/patches/larion.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 4125c990b..4a0d2dc2b 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -2026,11 +2026,20 @@ namespace Larion } EQ::InternalSerializedItem_Struct* eq = (EQ::InternalSerializedItem_Struct*)in->pBuffer; + SerializeBuffer buffer; + buffer.WriteUInt32(item_count); - auto outapp = new EQApplicationPacket(OP_CharInventory, 4); - dest->FastQueuePacket(&outapp, ack_req); + for (int index = 0; index < item_count; ++index, ++eq) { + SerializeItem(buffer, (const EQ::ItemInstance*)eq->inst, eq->slot_id, 0, ItemPacketCharInventory); + } - delete in; + in->size = buffer.size(); + in->pBuffer = new unsigned char[buffer.size()]; + memcpy(in->pBuffer, buffer.buffer(), buffer.size()); + + delete[] __emu_buffer; + + dest->FastQueuePacket(&in, ack_req); } ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } @@ -2741,7 +2750,8 @@ namespace Larion //we need to parse id file //s32 IDFile; int32 idfile = ExtractIDFile(item->IDFile); - + buffer.WriteUInt32(idfile); + //s32 IDFile2; buffer.WriteUInt32(0); //unsupported atm @@ -3373,6 +3383,9 @@ namespace Larion //char ConvertItemName[ConvertItemNameLength]; + //u32 ConvertItemID; + buffer.WriteInt32(0); + //u32 Open; buffer.WriteInt32(0); From 4e53f5464f36b33ea163bec26c2cef2a339d4b6c Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 23 Nov 2024 22:53:22 -0800 Subject: [PATCH 49/93] Set focus effects enabled to 1 in op_logserver --- common/patches/larion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/patches/larion.cpp b/common/patches/larion.cpp index 4a0d2dc2b..24dcf1683 100644 --- a/common/patches/larion.cpp +++ b/common/patches/larion.cpp @@ -212,7 +212,7 @@ namespace Larion *(char*)&__packet->pBuffer[0x705] = 0; //Something about item restrictions, seems to always be set to 1 - *(char*)&__packet->pBuffer[0x710] = 0; + *(char*)&__packet->pBuffer[0x710] = 1; //This and 0x724 are often multiplied together in guild favor calcs, live and test send 1.0f *(float*)&__packet->pBuffer[0x720] = 1.0f; From 11636225b10bccfb6d8fa1fcadd98bfd16239131 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 25 Nov 2024 20:25:09 -0800 Subject: [PATCH 50/93] Rename Larion -> Laurion; later we'll rename the branch too. Make sure to update your patch file name. --- common/CMakeLists.txt | 24 +- common/emu_versions.cpp | 44 +-- common/emu_versions.h | 18 +- common/eq_limits.cpp | 32 +- common/eq_limits.h | 2 +- common/patches/{larion.cpp => laurion.cpp} | 350 +++++++++--------- common/patches/{larion.h => laurion.h} | 12 +- .../{larion_limits.cpp => laurion_limits.cpp} | 16 +- .../{larion_limits.h => laurion_limits.h} | 32 +- .../patches/{larion_ops.h => laurion_ops.h} | 0 .../{larion_structs.h => laurion_structs.h} | 12 +- common/patches/patches.cpp | 6 +- loginserver/client.cpp | 2 +- loginserver/client_manager.cpp | 28 +- loginserver/client_manager.h | 4 +- loginserver/login_types.h | 2 +- loginserver/world_server.cpp | 6 +- loginserver/world_server.h | 2 +- .../{patch_Larion.conf => patch_Laurion.conf} | 0 zone/client.cpp | 4 +- 20 files changed, 298 insertions(+), 298 deletions(-) rename common/patches/{larion.cpp => laurion.cpp} (88%) rename common/patches/{larion.h => laurion.h} (91%) rename common/patches/{larion_limits.cpp => laurion_limits.cpp} (93%) rename common/patches/{larion_limits.h => laurion_limits.h} (94%) rename common/patches/{larion_ops.h => laurion_ops.h} (100%) rename common/patches/{larion_structs.h => laurion_structs.h} (97%) rename utils/patches/{patch_Larion.conf => patch_Laurion.conf} (100%) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 3bbaecdb4..ca9af892a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -112,8 +112,8 @@ SET(common_sources net/websocket_server.cpp net/websocket_server_connection.cpp patches/patches.cpp - patches/larion.cpp - patches/larion_limits.cpp + patches/laurion.cpp + patches/laurion_limits.cpp patches/sod.cpp patches/sod_limits.cpp patches/sof.cpp @@ -657,10 +657,10 @@ SET(common_headers net/websocket_server.h net/websocket_server_connection.h patches/patches.h - patches/larion.h - patches/larion_limits.h - patches/larion_ops.h - patches/larion_structs.h + patches/laurion.h + patches/laurion_limits.h + patches/laurion_ops.h + patches/laurion_structs.h patches/sod.h patches/sod_limits.h patches/sod_ops.h @@ -747,10 +747,10 @@ SOURCE_GROUP(Net FILES SOURCE_GROUP(Patches FILES patches/patches.h - patches/larion.h - patches/larion_limits.h - patches/larion_ops.h - patches/larion_structs.h + patches/laurion.h + patches/laurion_limits.h + patches/laurion_ops.h + patches/laurion_structs.h patches/sod.h patches/sod_limits.h patches/sod_ops.h @@ -779,8 +779,8 @@ SOURCE_GROUP(Patches FILES patches/uf_ops.h patches/uf_structs.h patches/patches.cpp - patches/larion.cpp - patches/larion_limits.cpp + patches/laurion.cpp + patches/laurion_limits.cpp patches/sod.cpp patches/sod_limits.cpp patches/sof.cpp diff --git a/common/emu_versions.cpp b/common/emu_versions.cpp index ebde39ddf..7cdbf2575 100644 --- a/common/emu_versions.cpp +++ b/common/emu_versions.cpp @@ -56,8 +56,8 @@ const char* EQ::versions::ClientVersionName(ClientVersion client_version) return "RoF"; case ClientVersion::RoF2: return "RoF2"; - case ClientVersion::Larion: - return "Larion"; + case ClientVersion::Laurion: + return "Laurion"; default: return "Invalid Version"; }; @@ -78,8 +78,8 @@ uint32 EQ::versions::ConvertClientVersionToClientVersionBit(ClientVersion client return bitRoF; case ClientVersion::RoF2: return bitRoF2; - case ClientVersion::Larion: - return bitLarion; + case ClientVersion::Laurion: + return bitLaurion; default: return bitUnknown; } @@ -100,8 +100,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertClientVersionBitToClientVersion return ClientVersion::RoF; case ((uint32)1 << (static_cast(ClientVersion::RoF2) - 1)) : return ClientVersion::RoF2; - case ((uint32)1 << (static_cast(ClientVersion::Larion) - 1)): - return ClientVersion::Larion; + case ((uint32)1 << (static_cast(ClientVersion::Laurion) - 1)): + return ClientVersion::Laurion; default: return ClientVersion::Unknown; } @@ -190,8 +190,8 @@ const char* EQ::versions::MobVersionName(MobVersion mob_version) return "RoF"; case MobVersion::RoF2: return "RoF2"; - case MobVersion::Larion: - return "Larion"; + case MobVersion::Laurion: + return "Laurion"; case MobVersion::NPC: return "NPC"; case MobVersion::NPCMerchant: @@ -220,8 +220,8 @@ const char* EQ::versions::MobVersionName(MobVersion mob_version) return "Offline RoF"; case MobVersion::OfflineRoF2: return "Offline RoF2"; - case MobVersion::OfflineLarion: - return "Offline Larion"; + case MobVersion::OfflineLaurion: + return "Offline Laurion"; default: return "Invalid Version"; }; @@ -245,8 +245,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertMobVersionToClientVersion(MobVe return ClientVersion::RoF; case MobVersion::RoF2: return ClientVersion::RoF2; - case MobVersion::Larion: - return ClientVersion::Larion; + case MobVersion::Laurion: + return ClientVersion::Laurion; default: return ClientVersion::Unknown; } @@ -270,8 +270,8 @@ EQ::versions::MobVersion EQ::versions::ConvertClientVersionToMobVersion(ClientVe return MobVersion::RoF; case ClientVersion::RoF2: return MobVersion::RoF2; - case ClientVersion::Larion: - return MobVersion::Larion; + case ClientVersion::Laurion: + return MobVersion::Laurion; default: return MobVersion::Unknown; } @@ -292,8 +292,8 @@ EQ::versions::MobVersion EQ::versions::ConvertPCMobVersionToOfflinePCMobVersion( return MobVersion::OfflineRoF; case MobVersion::RoF2: return MobVersion::OfflineRoF2; - case MobVersion::Larion: - return MobVersion::OfflineLarion; + case MobVersion::Laurion: + return MobVersion::OfflineLaurion; default: return MobVersion::Unknown; } @@ -314,8 +314,8 @@ EQ::versions::MobVersion EQ::versions::ConvertOfflinePCMobVersionToPCMobVersion( return MobVersion::RoF; case MobVersion::OfflineRoF2: return MobVersion::RoF2; - case MobVersion::OfflineLarion: - return MobVersion::Larion; + case MobVersion::OfflineLaurion: + return MobVersion::Laurion; default: return MobVersion::Unknown; } @@ -336,8 +336,8 @@ EQ::versions::ClientVersion EQ::versions::ConvertOfflinePCMobVersionToClientVers return ClientVersion::RoF; case MobVersion::OfflineRoF2: return ClientVersion::RoF2; - case MobVersion::OfflineLarion: - return ClientVersion::Larion; + case MobVersion::OfflineLaurion: + return ClientVersion::Laurion; default: return ClientVersion::Unknown; } @@ -358,8 +358,8 @@ EQ::versions::MobVersion EQ::versions::ConvertClientVersionToOfflinePCMobVersion return MobVersion::OfflineRoF; case ClientVersion::RoF2: return MobVersion::OfflineRoF2; - case ClientVersion::Larion: - return MobVersion::OfflineLarion; + case ClientVersion::Laurion: + return MobVersion::OfflineLaurion; default: return MobVersion::Unknown; } diff --git a/common/emu_versions.h b/common/emu_versions.h index 5a2027770..f22c0121b 100644 --- a/common/emu_versions.h +++ b/common/emu_versions.h @@ -37,7 +37,7 @@ namespace EQ UF, // Build: 'Jun 8 2010 16:44:32' RoF, // Build: 'Dec 10 2012 17:35:44' RoF2, // Build: 'May 10 2013 23:30:08' - Larion + Laurion }; enum ClientVersionBitmask : uint32 { @@ -49,7 +49,7 @@ namespace EQ bitUF = 0x00000010, bitRoF = 0x00000020, bitRoF2 = 0x00000040, - bitLarion = 0x00000080, + bitLaurion = 0x00000080, maskUnknown = 0x00000000, maskTitaniumAndEarlier = 0x00000003, maskSoFAndEarlier = 0x00000007, @@ -61,11 +61,11 @@ namespace EQ maskUFAndLater = 0xFFFFFFF0, maskRoFAndLater = 0xFFFFFFE0, maskRoF2AndLater = 0xFFFFFFC0, - maskLarionAndLater = 0xFFFFFF80, + maskLaurionAndLater = 0xFFFFFF80, maskAllClients = 0xFFFFFFFF }; - const ClientVersion LastClientVersion = ClientVersion::Larion; + const ClientVersion LastClientVersion = ClientVersion::Laurion; const size_t ClientVersionCount = (static_cast(LastClientVersion) + 1); bool IsValidClientVersion(ClientVersion client_version); @@ -83,7 +83,7 @@ namespace EQ UF, RoF, RoF2, - Larion, + Laurion, NPC, NPCMerchant, Merc, @@ -98,13 +98,13 @@ namespace EQ OfflineUF, OfflineRoF, OfflineRoF2, - OfflineLarion + OfflineLaurion }; - const MobVersion LastMobVersion = MobVersion::OfflineLarion; - const MobVersion LastPCMobVersion = MobVersion::Larion; + const MobVersion LastMobVersion = MobVersion::OfflineLaurion; + const MobVersion LastPCMobVersion = MobVersion::Laurion; const MobVersion LastNonPCMobVersion = MobVersion::BotPet; - const MobVersion LastOfflinePCMobVersion = MobVersion::OfflineLarion; + const MobVersion LastOfflinePCMobVersion = MobVersion::OfflineLaurion; const size_t MobVersionCount = (static_cast(LastMobVersion) + 1); bool IsValidMobVersion(MobVersion mob_version); diff --git a/common/eq_limits.cpp b/common/eq_limits.cpp index 7c0f72038..21b03cddf 100644 --- a/common/eq_limits.cpp +++ b/common/eq_limits.cpp @@ -105,13 +105,13 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers RoF2::constants::CHARACTER_CREATION_LIMIT, RoF2::constants::SAY_LINK_BODY_SIZE ), - /*[ClientVersion::Larion] =*/ + /*[ClientVersion::Laurion] =*/ EQ::constants::LookupEntry( - Larion::constants::EXPANSION, - Larion::constants::EXPANSION_BIT, - Larion::constants::EXPANSIONS_MASK, - Larion::constants::CHARACTER_CREATION_LIMIT, - Larion::constants::SAY_LINK_BODY_SIZE + Laurion::constants::EXPANSION, + Laurion::constants::EXPANSION_BIT, + Laurion::constants::EXPANSIONS_MASK, + Laurion::constants::CHARACTER_CREATION_LIMIT, + Laurion::constants::SAY_LINK_BODY_SIZE ) }; @@ -1273,18 +1273,18 @@ static const EQ::spells::LookupEntry spells_static_lookup_entries[EQ::versions:: RoF2::spells::PET_BUFFS, RoF2::spells::MERC_BUFFS ), - /*[ClientVersion::Larion] =*/ + /*[ClientVersion::Laurion] =*/ EQ::spells::LookupEntry( - Larion::spells::SPELL_ID_MAX, - Larion::spells::SPELLBOOK_SIZE, + Laurion::spells::SPELL_ID_MAX, + Laurion::spells::SPELLBOOK_SIZE, UF::spells::SPELL_GEM_COUNT, // client translators are setup to allow the max value a client supports..however, the top 4 indices are not valid in this case - Larion::spells::LONG_BUFFS, - Larion::spells::SHORT_BUFFS, - Larion::spells::DISC_BUFFS, - Larion::spells::TOTAL_BUFFS, - Larion::spells::NPC_BUFFS, - Larion::spells::PET_BUFFS, - Larion::spells::MERC_BUFFS + Laurion::spells::LONG_BUFFS, + Laurion::spells::SHORT_BUFFS, + Laurion::spells::DISC_BUFFS, + Laurion::spells::TOTAL_BUFFS, + Laurion::spells::NPC_BUFFS, + Laurion::spells::PET_BUFFS, + Laurion::spells::MERC_BUFFS ) }; diff --git a/common/eq_limits.h b/common/eq_limits.h index e1e20aec6..c51d5d714 100644 --- a/common/eq_limits.h +++ b/common/eq_limits.h @@ -29,7 +29,7 @@ #include "../common/patches/uf_limits.h" #include "../common/patches/rof_limits.h" #include "../common/patches/rof2_limits.h" -#include "../common/patches/larion_limits.h" +#include "../common/patches/laurion_limits.h" namespace EQ { diff --git a/common/patches/larion.cpp b/common/patches/laurion.cpp similarity index 88% rename from common/patches/larion.cpp rename to common/patches/laurion.cpp index 24dcf1683..40d5f3e6c 100644 --- a/common/patches/larion.cpp +++ b/common/patches/laurion.cpp @@ -20,7 +20,7 @@ #include "../global_define.h" #include "../eqemu_config.h" #include "../eqemu_logsys.h" -#include "larion.h" +#include "laurion.h" #include "../opcodemgr.h" #include "../eq_stream_ident.h" @@ -30,7 +30,7 @@ #include "../misc_functions.h" #include "../strings.h" #include "../inventory_profile.h" -#include "larion_structs.h" +#include "laurion_structs.h" #include "../rulesys.h" #include "../path_manager.h" #include "../classes.h" @@ -43,25 +43,25 @@ #include #include -namespace Larion +namespace Laurion { - static const char* name = "Larion"; + static const char* name = "Laurion"; static OpcodeManager* opcodes = nullptr; static Strategy struct_strategy; void SerializeItem(SerializeBuffer &buffer, const EQ::ItemInstance* inst, int16 slot_id, uint8 depth, ItemPacketType packet_type); // server to client inventory location converters - static inline structs::InventorySlot_Struct ServerToLarionSlot(uint32 server_slot); - static inline structs::InventorySlot_Struct ServerToLarionCorpseSlot(uint32 server_corpse_slot); - static inline uint32 ServerToLarionCorpseMainSlot(uint32 server_corpse_slot); - static inline structs::TypelessInventorySlot_Struct ServerToLarionTypelessSlot(uint32 server_slot, int16 server_type); + static inline structs::InventorySlot_Struct ServerToLaurionSlot(uint32 server_slot); + static inline structs::InventorySlot_Struct ServerToLaurionCorpseSlot(uint32 server_corpse_slot); + static inline uint32 ServerToLaurionCorpseMainSlot(uint32 server_corpse_slot); + static inline structs::TypelessInventorySlot_Struct ServerToLaurionTypelessSlot(uint32 server_slot, int16 server_type); // client to server inventory location converters - static inline uint32 LarionToServerSlot(structs::InventorySlot_Struct larion_slot); - static inline uint32 LarionToServerCorpseSlot(structs::InventorySlot_Struct larion_corpse_slot); - static inline uint32 LarionToServerCorpseMainSlot(uint32 larion_corpse_slot); - static inline uint32 LarionToServerTypelessSlot(structs::TypelessInventorySlot_Struct larion_slot, int16 larion_type); + static inline uint32 LaurionToServerSlot(structs::InventorySlot_Struct laurion_slot); + static inline uint32 LaurionToServerCorpseSlot(structs::InventorySlot_Struct laurion_corpse_slot); + static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot); + static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type); void Register(EQStreamIdentifier& into) { @@ -120,7 +120,7 @@ namespace Larion { //all opcodes default to passthrough. #include "ss_register.h" -#include "larion_ops.h" +#include "laurion_ops.h" } std::string Strategy::Describe() const @@ -133,7 +133,7 @@ namespace Larion const EQ::versions::ClientVersion Strategy::ClientVersion() const { - return EQ::versions::ClientVersion::Larion; + return EQ::versions::ClientVersion::Laurion; } #include "ss_define.h" @@ -514,7 +514,7 @@ namespace Larion if (sas->type != AppearanceType::Size) { - //larion struct is different than rof2's but the idea is the same + //laurion struct is different than rof2's but the idea is the same auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; @@ -2714,7 +2714,7 @@ namespace Larion int r; for (r = 0; r < 29; r++) { - // Size 68 in Larion + // Size 68 in Laurion IN(filters[r]); } @@ -3328,10 +3328,10 @@ namespace Larion structs::InventorySlot_Struct slot_id{}; switch (packet_type) { case ItemPacketLoot: - slot_id = ServerToLarionCorpseSlot(slot_id_in); + slot_id = ServerToLaurionCorpseSlot(slot_id_in); break; default: - slot_id = ServerToLarionSlot(slot_id_in); + slot_id = ServerToLaurionSlot(slot_id_in); break; } @@ -3499,156 +3499,156 @@ namespace Larion buffer.WriteInt32(0); //unsupported atm } - static inline structs::InventorySlot_Struct ServerToLarionSlot(uint32 server_slot) + static inline structs::InventorySlot_Struct ServerToLaurionSlot(uint32 server_slot) { - structs::InventorySlot_Struct LarionSlot; - LarionSlot.Type = invtype::TYPE_INVALID; - LarionSlot.Slot = invslot::SLOT_INVALID; - LarionSlot.SubIndex = invbag::SLOT_INVALID; - LarionSlot.AugIndex = invaug::SOCKET_INVALID; + structs::InventorySlot_Struct LaurionSlot; + LaurionSlot.Type = invtype::TYPE_INVALID; + LaurionSlot.Slot = invslot::SLOT_INVALID; + LaurionSlot.SubIndex = invbag::SLOT_INVALID; + LaurionSlot.AugIndex = invaug::SOCKET_INVALID; uint32 TempSlot = EQ::invslot::SLOT_INVALID; if (server_slot < EQ::invtype::POSSESSIONS_SIZE) { - LarionSlot.Type = invtype::typePossessions; - LarionSlot.Slot = server_slot; + LaurionSlot.Type = invtype::typePossessions; + LaurionSlot.Slot = server_slot; } else if (server_slot <= EQ::invbag::CURSOR_BAG_END && server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { TempSlot = server_slot - EQ::invbag::GENERAL_BAGS_BEGIN; - LarionSlot.Type = invtype::typePossessions; - LarionSlot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQ::invbag::SLOT_COUNT); - LarionSlot.SubIndex = TempSlot - ((LarionSlot.Slot - invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT); + LaurionSlot.Type = invtype::typePossessions; + LaurionSlot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQ::invbag::SLOT_COUNT); + LaurionSlot.SubIndex = TempSlot - ((LaurionSlot.Slot - invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT); } else if (server_slot <= EQ::invslot::TRIBUTE_END && server_slot >= EQ::invslot::TRIBUTE_BEGIN) { - LarionSlot.Type = invtype::typeTribute; - LarionSlot.Slot = server_slot - EQ::invslot::TRIBUTE_BEGIN; + LaurionSlot.Type = invtype::typeTribute; + LaurionSlot.Slot = server_slot - EQ::invslot::TRIBUTE_BEGIN; } else if (server_slot <= EQ::invslot::GUILD_TRIBUTE_END && server_slot >= EQ::invslot::GUILD_TRIBUTE_BEGIN) { - LarionSlot.Type = invtype::typeGuildTribute; - LarionSlot.Slot = server_slot - EQ::invslot::GUILD_TRIBUTE_BEGIN; + LaurionSlot.Type = invtype::typeGuildTribute; + LaurionSlot.Slot = server_slot - EQ::invslot::GUILD_TRIBUTE_BEGIN; } else if (server_slot == EQ::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE) { - LarionSlot.Type = invtype::typeWorld; + LaurionSlot.Type = invtype::typeWorld; } else if (server_slot <= EQ::invslot::BANK_END && server_slot >= EQ::invslot::BANK_BEGIN) { - LarionSlot.Type = invtype::typeBank; - LarionSlot.Slot = server_slot - EQ::invslot::BANK_BEGIN; + LaurionSlot.Type = invtype::typeBank; + LaurionSlot.Slot = server_slot - EQ::invslot::BANK_BEGIN; } else if (server_slot <= EQ::invbag::BANK_BAGS_END && server_slot >= EQ::invbag::BANK_BAGS_BEGIN) { TempSlot = server_slot - EQ::invbag::BANK_BAGS_BEGIN; - LarionSlot.Type = invtype::typeBank; - LarionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; - LarionSlot.SubIndex = TempSlot - (LarionSlot.Slot * EQ::invbag::SLOT_COUNT); + LaurionSlot.Type = invtype::typeBank; + LaurionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; + LaurionSlot.SubIndex = TempSlot - (LaurionSlot.Slot * EQ::invbag::SLOT_COUNT); } else if (server_slot <= EQ::invslot::SHARED_BANK_END && server_slot >= EQ::invslot::SHARED_BANK_BEGIN) { - LarionSlot.Type = invtype::typeSharedBank; - LarionSlot.Slot = server_slot - EQ::invslot::SHARED_BANK_BEGIN; + LaurionSlot.Type = invtype::typeSharedBank; + LaurionSlot.Slot = server_slot - EQ::invslot::SHARED_BANK_BEGIN; } else if (server_slot <= EQ::invbag::SHARED_BANK_BAGS_END && server_slot >= EQ::invbag::SHARED_BANK_BAGS_BEGIN) { TempSlot = server_slot - EQ::invbag::SHARED_BANK_BAGS_BEGIN; - LarionSlot.Type = invtype::typeSharedBank; - LarionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; - LarionSlot.SubIndex = TempSlot - (LarionSlot.Slot * EQ::invbag::SLOT_COUNT); + LaurionSlot.Type = invtype::typeSharedBank; + LaurionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; + LaurionSlot.SubIndex = TempSlot - (LaurionSlot.Slot * EQ::invbag::SLOT_COUNT); } else if (server_slot <= EQ::invslot::TRADE_END && server_slot >= EQ::invslot::TRADE_BEGIN) { - LarionSlot.Type = invtype::typeTrade; - LarionSlot.Slot = server_slot - EQ::invslot::TRADE_BEGIN; + LaurionSlot.Type = invtype::typeTrade; + LaurionSlot.Slot = server_slot - EQ::invslot::TRADE_BEGIN; } else if (server_slot <= EQ::invbag::TRADE_BAGS_END && server_slot >= EQ::invbag::TRADE_BAGS_BEGIN) { TempSlot = server_slot - EQ::invbag::TRADE_BAGS_BEGIN; - LarionSlot.Type = invtype::typeTrade; - LarionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; - LarionSlot.SubIndex = TempSlot - (LarionSlot.Slot * EQ::invbag::SLOT_COUNT); + LaurionSlot.Type = invtype::typeTrade; + LaurionSlot.Slot = TempSlot / EQ::invbag::SLOT_COUNT; + LaurionSlot.SubIndex = TempSlot - (LaurionSlot.Slot * EQ::invbag::SLOT_COUNT); } else if (server_slot <= EQ::invslot::WORLD_END && server_slot >= EQ::invslot::WORLD_BEGIN) { - LarionSlot.Type = invtype::typeWorld; - LarionSlot.Slot = server_slot - EQ::invslot::WORLD_BEGIN; + LaurionSlot.Type = invtype::typeWorld; + LaurionSlot.Slot = server_slot - EQ::invslot::WORLD_BEGIN; } - Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to Larion Slot [%i, %i, %i, %i]", - server_slot, LarionSlot.Type, LarionSlot.Slot, LarionSlot.SubIndex, LarionSlot.AugIndex); + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to Laurion Slot [%i, %i, %i, %i]", + server_slot, LaurionSlot.Type, LaurionSlot.Slot, LaurionSlot.SubIndex, LaurionSlot.AugIndex); - return LarionSlot; + return LaurionSlot; } - static inline structs::InventorySlot_Struct ServerToLarionCorpseSlot(uint32 server_corpse_slot) + static inline structs::InventorySlot_Struct ServerToLaurionCorpseSlot(uint32 server_corpse_slot) { - structs::InventorySlot_Struct LarionSlot; - LarionSlot.Type = invtype::TYPE_INVALID; - LarionSlot.Slot = ServerToLarionCorpseMainSlot(server_corpse_slot); - LarionSlot.SubIndex = invbag::SLOT_INVALID; - LarionSlot.AugIndex = invaug::SOCKET_INVALID; + structs::InventorySlot_Struct LaurionSlot; + LaurionSlot.Type = invtype::TYPE_INVALID; + LaurionSlot.Slot = ServerToLaurionCorpseMainSlot(server_corpse_slot); + LaurionSlot.SubIndex = invbag::SLOT_INVALID; + LaurionSlot.AugIndex = invaug::SOCKET_INVALID; - if (LarionSlot.Slot != invslot::SLOT_INVALID) - LarionSlot.Type = invtype::typeCorpse; + if (LaurionSlot.Slot != invslot::SLOT_INVALID) + LaurionSlot.Type = invtype::typeCorpse; - Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to Larion Corpse Slot [%i, %i, %i, %i]", - server_corpse_slot, LarionSlot.Type, LarionSlot.Slot, LarionSlot.SubIndex, LarionSlot.AugIndex); + Log(Logs::Detail, Logs::Netcode, "Convert Server Corpse Slot %i to Laurion Corpse Slot [%i, %i, %i, %i]", + server_corpse_slot, LaurionSlot.Type, LaurionSlot.Slot, LaurionSlot.SubIndex, LaurionSlot.AugIndex); - return LarionSlot; + return LaurionSlot; } - static inline uint32 ServerToLarionCorpseMainSlot(uint32 server_corpse_slot) + static inline uint32 ServerToLaurionCorpseMainSlot(uint32 server_corpse_slot) { - uint32 LarionSlot = invslot::SLOT_INVALID; + uint32 LaurionSlot = invslot::SLOT_INVALID; if (server_corpse_slot <= EQ::invslot::CORPSE_END && server_corpse_slot >= EQ::invslot::CORPSE_BEGIN) { - LarionSlot = server_corpse_slot; + LaurionSlot = server_corpse_slot; } - LogNetcode("Convert Server Corpse Slot [{}] to Larion Corpse Main Slot [{}]", server_corpse_slot, LarionSlot); + LogNetcode("Convert Server Corpse Slot [{}] to Laurion Corpse Main Slot [{}]", server_corpse_slot, LaurionSlot); - return LarionSlot; + return LaurionSlot; } - static inline structs::TypelessInventorySlot_Struct ServerToLarionTypelessSlot(uint32 server_slot, int16 server_type) + static inline structs::TypelessInventorySlot_Struct ServerToLaurionTypelessSlot(uint32 server_slot, int16 server_type) { - structs::TypelessInventorySlot_Struct LarionSlot; - LarionSlot.Slot = invslot::SLOT_INVALID; - LarionSlot.SubIndex = invbag::SLOT_INVALID; - LarionSlot.AugIndex = invaug::SOCKET_INVALID; + structs::TypelessInventorySlot_Struct LaurionSlot; + LaurionSlot.Slot = invslot::SLOT_INVALID; + LaurionSlot.SubIndex = invbag::SLOT_INVALID; + LaurionSlot.AugIndex = invaug::SOCKET_INVALID; uint32 TempSlot = EQ::invslot::SLOT_INVALID; if (server_type == EQ::invtype::typePossessions) { if (server_slot < EQ::invtype::POSSESSIONS_SIZE) { - LarionSlot.Slot = server_slot; + LaurionSlot.Slot = server_slot; } else if (server_slot <= EQ::invbag::CURSOR_BAG_END && server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { TempSlot = server_slot - EQ::invbag::GENERAL_BAGS_BEGIN; - LarionSlot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQ::invbag::SLOT_COUNT); - LarionSlot.SubIndex = TempSlot - ((LarionSlot.Slot - invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT); + LaurionSlot.Slot = invslot::GENERAL_BEGIN + (TempSlot / EQ::invbag::SLOT_COUNT); + LaurionSlot.SubIndex = TempSlot - ((LaurionSlot.Slot - invslot::GENERAL_BEGIN) * EQ::invbag::SLOT_COUNT); } } - Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to Larion Typeless Slot [%i, %i, %i] (implied type: %i)", - server_slot, LarionSlot.Slot, LarionSlot.SubIndex, LarionSlot.AugIndex, server_type); + Log(Logs::Detail, Logs::Netcode, "Convert Server Slot %i to Laurion Typeless Slot [%i, %i, %i] (implied type: %i)", + server_slot, LaurionSlot.Slot, LaurionSlot.SubIndex, LaurionSlot.AugIndex, server_type); - return LarionSlot; + return LaurionSlot; } - static inline uint32 LarionToServerSlot(structs::InventorySlot_Struct larion_slot) + static inline uint32 LaurionToServerSlot(structs::InventorySlot_Struct laurion_slot) { - if (larion_slot.AugIndex < invaug::SOCKET_INVALID || larion_slot.AugIndex >= invaug::SOCKET_COUNT) { - Log(Logs::Detail, Logs::Netcode, "Convert Larion Slot [%i, %i, %i, %i] to Server Slot %i", - larion_slot.Type, larion_slot.Slot, larion_slot.SubIndex, larion_slot.AugIndex, EQ::invslot::SLOT_INVALID); + if (laurion_slot.AugIndex < invaug::SOCKET_INVALID || laurion_slot.AugIndex >= invaug::SOCKET_COUNT) { + Log(Logs::Detail, Logs::Netcode, "Convert Laurion Slot [%i, %i, %i, %i] to Server Slot %i", + laurion_slot.Type, laurion_slot.Slot, laurion_slot.SubIndex, laurion_slot.AugIndex, EQ::invslot::SLOT_INVALID); return EQ::invslot::SLOT_INVALID; } @@ -3656,101 +3656,101 @@ namespace Larion uint32 server_slot = EQ::invslot::SLOT_INVALID; uint32 temp_slot = invslot::SLOT_INVALID; - switch (larion_slot.Type) { + switch (laurion_slot.Type) { case invtype::typePossessions: { - if (larion_slot.Slot >= invslot::POSSESSIONS_BEGIN && larion_slot.Slot <= invslot::POSSESSIONS_END) { - if (larion_slot.SubIndex == invbag::SLOT_INVALID) { - server_slot = larion_slot.Slot; + if (laurion_slot.Slot >= invslot::POSSESSIONS_BEGIN && laurion_slot.Slot <= invslot::POSSESSIONS_END) { + if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = laurion_slot.Slot; } - else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { - if (larion_slot.Slot < invslot::GENERAL_BEGIN) + else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { + if (laurion_slot.Slot < invslot::GENERAL_BEGIN) return EQ::invslot::SLOT_INVALID; - temp_slot = (larion_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; - server_slot = EQ::invbag::GENERAL_BAGS_BEGIN + temp_slot + larion_slot.SubIndex; + temp_slot = (laurion_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; + server_slot = EQ::invbag::GENERAL_BAGS_BEGIN + temp_slot + laurion_slot.SubIndex; } } break; } case invtype::typeBank: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::BANK_SIZE) { - if (larion_slot.SubIndex == invbag::SLOT_INVALID) { - server_slot = EQ::invslot::BANK_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::BANK_SIZE) { + if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQ::invslot::BANK_BEGIN + laurion_slot.Slot; } - else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { - temp_slot = larion_slot.Slot * invbag::SLOT_COUNT; - server_slot = EQ::invbag::BANK_BAGS_BEGIN + temp_slot + larion_slot.SubIndex; + else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = laurion_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQ::invbag::BANK_BAGS_BEGIN + temp_slot + laurion_slot.SubIndex; } } break; } case invtype::typeSharedBank: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::SHARED_BANK_SIZE) { - if (larion_slot.SubIndex == invbag::SLOT_INVALID) { - server_slot = EQ::invslot::SHARED_BANK_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::SHARED_BANK_SIZE) { + if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQ::invslot::SHARED_BANK_BEGIN + laurion_slot.Slot; } - else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { - temp_slot = larion_slot.Slot * invbag::SLOT_COUNT; - server_slot = EQ::invbag::SHARED_BANK_BAGS_BEGIN + temp_slot + larion_slot.SubIndex; + else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = laurion_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQ::invbag::SHARED_BANK_BAGS_BEGIN + temp_slot + laurion_slot.SubIndex; } } break; } case invtype::typeTrade: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::TRADE_SIZE) { - if (larion_slot.SubIndex == invbag::SLOT_INVALID) { - server_slot = EQ::invslot::TRADE_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::TRADE_SIZE) { + if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { + server_slot = EQ::invslot::TRADE_BEGIN + laurion_slot.Slot; } - else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { - temp_slot = larion_slot.Slot * invbag::SLOT_COUNT; - server_slot = EQ::invbag::TRADE_BAGS_BEGIN + temp_slot + larion_slot.SubIndex; + else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { + temp_slot = laurion_slot.Slot * invbag::SLOT_COUNT; + server_slot = EQ::invbag::TRADE_BAGS_BEGIN + temp_slot + laurion_slot.SubIndex; } } break; } case invtype::typeWorld: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::WORLD_SIZE) { - server_slot = EQ::invslot::WORLD_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::WORLD_SIZE) { + server_slot = EQ::invslot::WORLD_BEGIN + laurion_slot.Slot; } - else if (larion_slot.Slot == invslot::SLOT_INVALID) { + else if (laurion_slot.Slot == invslot::SLOT_INVALID) { server_slot = EQ::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; } break; } case invtype::typeLimbo: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::LIMBO_SIZE) { + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::LIMBO_SIZE) { server_slot = EQ::invslot::slotCursor; } break; } case invtype::typeTribute: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::TRIBUTE_SIZE) { - server_slot = EQ::invslot::TRIBUTE_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::TRIBUTE_SIZE) { + server_slot = EQ::invslot::TRIBUTE_BEGIN + laurion_slot.Slot; } break; } case invtype::typeGuildTribute: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { - server_slot = EQ::invslot::GUILD_TRIBUTE_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { + server_slot = EQ::invslot::GUILD_TRIBUTE_BEGIN + laurion_slot.Slot; } break; } case invtype::typeCorpse: { - if (larion_slot.Slot >= invslot::CORPSE_BEGIN && larion_slot.Slot <= invslot::CORPSE_END) { - server_slot = larion_slot.Slot; + if (laurion_slot.Slot >= invslot::CORPSE_BEGIN && laurion_slot.Slot <= invslot::CORPSE_END) { + server_slot = laurion_slot.Slot; } break; @@ -3761,48 +3761,48 @@ namespace Larion } } - Log(Logs::Detail, Logs::Netcode, "Convert Larion Slot [%i, %i, %i, %i] to Server Slot %i", - larion_slot.Type, larion_slot.Slot, larion_slot.SubIndex, larion_slot.AugIndex, server_slot); + Log(Logs::Detail, Logs::Netcode, "Convert Laurion Slot [%i, %i, %i, %i] to Server Slot %i", + laurion_slot.Type, laurion_slot.Slot, laurion_slot.SubIndex, laurion_slot.AugIndex, server_slot); return server_slot; } - static inline uint32 LarionToServerCorpseSlot(structs::InventorySlot_Struct larion_corpse_slot) + static inline uint32 LaurionToServerCorpseSlot(structs::InventorySlot_Struct laurion_corpse_slot) { uint32 ServerSlot = EQ::invslot::SLOT_INVALID; - if (larion_corpse_slot.Type != invtype::typeCorpse || larion_corpse_slot.SubIndex != invbag::SLOT_INVALID || larion_corpse_slot.AugIndex != invaug::SOCKET_INVALID) { + if (laurion_corpse_slot.Type != invtype::typeCorpse || laurion_corpse_slot.SubIndex != invbag::SLOT_INVALID || laurion_corpse_slot.AugIndex != invaug::SOCKET_INVALID) { ServerSlot = EQ::invslot::SLOT_INVALID; } else { - ServerSlot = LarionToServerCorpseMainSlot(larion_corpse_slot.Slot); + ServerSlot = LaurionToServerCorpseMainSlot(laurion_corpse_slot.Slot); } - Log(Logs::Detail, Logs::Netcode, "Convert Larion Slot [%i, %i, %i, %i] to Server Slot %i", - larion_corpse_slot.Type, larion_corpse_slot.Slot, larion_corpse_slot.SubIndex, larion_corpse_slot.AugIndex, ServerSlot); + Log(Logs::Detail, Logs::Netcode, "Convert Laurion Slot [%i, %i, %i, %i] to Server Slot %i", + laurion_corpse_slot.Type, laurion_corpse_slot.Slot, laurion_corpse_slot.SubIndex, laurion_corpse_slot.AugIndex, ServerSlot); return ServerSlot; } - static inline uint32 LarionToServerCorpseMainSlot(uint32 larion_corpse_slot) + static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot) { uint32 ServerSlot = EQ::invslot::SLOT_INVALID; - if (larion_corpse_slot <= invslot::CORPSE_END && larion_corpse_slot >= invslot::CORPSE_BEGIN) { - ServerSlot = larion_corpse_slot; + if (laurion_corpse_slot <= invslot::CORPSE_END && laurion_corpse_slot >= invslot::CORPSE_BEGIN) { + ServerSlot = laurion_corpse_slot; } - LogNetcode("Convert Larion Corpse Main Slot [{}] to Server Corpse Slot [{}]", larion_corpse_slot, ServerSlot); + LogNetcode("Convert Laurion Corpse Main Slot [{}] to Server Corpse Slot [{}]", laurion_corpse_slot, ServerSlot); return ServerSlot; } - static inline uint32 LarionToServerTypelessSlot(structs::TypelessInventorySlot_Struct larion_slot, int16 larion_type) + static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type) { - if (larion_slot.AugIndex < invaug::SOCKET_INVALID || larion_slot.AugIndex >= invaug::SOCKET_COUNT) { - Log(Logs::Detail, Logs::Netcode, "Convert Larion Typeless Slot [%i, %i, %i] (implied type: %i) to Server Slot %i", - larion_slot.Slot, larion_slot.SubIndex, larion_slot.AugIndex, larion_type, EQ::invslot::SLOT_INVALID); + if (laurion_slot.AugIndex < invaug::SOCKET_INVALID || laurion_slot.AugIndex >= invaug::SOCKET_COUNT) { + Log(Logs::Detail, Logs::Netcode, "Convert Laurion Typeless Slot [%i, %i, %i] (implied type: %i) to Server Slot %i", + laurion_slot.Slot, laurion_slot.SubIndex, laurion_slot.AugIndex, laurion_type, EQ::invslot::SLOT_INVALID); return EQ::invslot::SLOT_INVALID; } @@ -3810,101 +3810,101 @@ namespace Larion uint32 ServerSlot = EQ::invslot::SLOT_INVALID; uint32 TempSlot = invslot::SLOT_INVALID; - switch (larion_type) { + switch (laurion_type) { case invtype::typePossessions: { - if (larion_slot.Slot >= invslot::POSSESSIONS_BEGIN && larion_slot.Slot <= invslot::POSSESSIONS_END) { - if (larion_slot.SubIndex == invbag::SLOT_INVALID) { - ServerSlot = larion_slot.Slot; + if (laurion_slot.Slot >= invslot::POSSESSIONS_BEGIN && laurion_slot.Slot <= invslot::POSSESSIONS_END) { + if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = laurion_slot.Slot; } - else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { - if (larion_slot.Slot < invslot::GENERAL_BEGIN) + else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { + if (laurion_slot.Slot < invslot::GENERAL_BEGIN) return EQ::invslot::SLOT_INVALID; - TempSlot = (larion_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; - ServerSlot = EQ::invbag::GENERAL_BAGS_BEGIN + TempSlot + larion_slot.SubIndex; + TempSlot = (laurion_slot.Slot - invslot::GENERAL_BEGIN) * invbag::SLOT_COUNT; + ServerSlot = EQ::invbag::GENERAL_BAGS_BEGIN + TempSlot + laurion_slot.SubIndex; } } break; } case invtype::typeBank: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::BANK_SIZE) { - if (larion_slot.SubIndex == invbag::SLOT_INVALID) { - ServerSlot = EQ::invslot::BANK_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::BANK_SIZE) { + if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQ::invslot::BANK_BEGIN + laurion_slot.Slot; } - else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { - TempSlot = larion_slot.Slot * invbag::SLOT_COUNT; - ServerSlot = EQ::invbag::BANK_BAGS_BEGIN + TempSlot + larion_slot.SubIndex; + else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = laurion_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQ::invbag::BANK_BAGS_BEGIN + TempSlot + laurion_slot.SubIndex; } } break; } case invtype::typeSharedBank: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::SHARED_BANK_SIZE) { - if (larion_slot.SubIndex == invbag::SLOT_INVALID) { - ServerSlot = EQ::invslot::SHARED_BANK_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::SHARED_BANK_SIZE) { + if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQ::invslot::SHARED_BANK_BEGIN + laurion_slot.Slot; } - else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { - TempSlot = larion_slot.Slot * invbag::SLOT_COUNT; - ServerSlot = EQ::invbag::SHARED_BANK_BAGS_BEGIN + TempSlot + larion_slot.SubIndex; + else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = laurion_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQ::invbag::SHARED_BANK_BAGS_BEGIN + TempSlot + laurion_slot.SubIndex; } } break; } case invtype::typeTrade: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::TRADE_SIZE) { - if (larion_slot.SubIndex == invbag::SLOT_INVALID) { - ServerSlot = EQ::invslot::TRADE_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::TRADE_SIZE) { + if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { + ServerSlot = EQ::invslot::TRADE_BEGIN + laurion_slot.Slot; } - else if (larion_slot.SubIndex >= invbag::SLOT_BEGIN && larion_slot.SubIndex <= invbag::SLOT_END) { - TempSlot = larion_slot.Slot * invbag::SLOT_COUNT; - ServerSlot = EQ::invbag::TRADE_BAGS_BEGIN + TempSlot + larion_slot.SubIndex; + else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { + TempSlot = laurion_slot.Slot * invbag::SLOT_COUNT; + ServerSlot = EQ::invbag::TRADE_BAGS_BEGIN + TempSlot + laurion_slot.SubIndex; } } break; } case invtype::typeWorld: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::WORLD_SIZE) { - ServerSlot = EQ::invslot::WORLD_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::WORLD_SIZE) { + ServerSlot = EQ::invslot::WORLD_BEGIN + laurion_slot.Slot; } - else if (larion_slot.Slot == invslot::SLOT_INVALID) { + else if (laurion_slot.Slot == invslot::SLOT_INVALID) { ServerSlot = EQ::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE; } break; } case invtype::typeLimbo: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::LIMBO_SIZE) { + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::LIMBO_SIZE) { ServerSlot = EQ::invslot::slotCursor; } break; } case invtype::typeTribute: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::TRIBUTE_SIZE) { - ServerSlot = EQ::invslot::TRIBUTE_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::TRIBUTE_SIZE) { + ServerSlot = EQ::invslot::TRIBUTE_BEGIN + laurion_slot.Slot; } break; } case invtype::typeGuildTribute: { - if (larion_slot.Slot >= invslot::SLOT_BEGIN && larion_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { - ServerSlot = EQ::invslot::GUILD_TRIBUTE_BEGIN + larion_slot.Slot; + if (laurion_slot.Slot >= invslot::SLOT_BEGIN && laurion_slot.Slot < invtype::GUILD_TRIBUTE_SIZE) { + ServerSlot = EQ::invslot::GUILD_TRIBUTE_BEGIN + laurion_slot.Slot; } break; } case invtype::typeCorpse: { - if (larion_slot.Slot >= invslot::CORPSE_BEGIN && larion_slot.Slot <= invslot::CORPSE_END) { - ServerSlot = larion_slot.Slot; + if (laurion_slot.Slot >= invslot::CORPSE_BEGIN && laurion_slot.Slot <= invslot::CORPSE_END) { + ServerSlot = laurion_slot.Slot; } break; @@ -3915,9 +3915,9 @@ namespace Larion } } - Log(Logs::Detail, Logs::Netcode, "Convert Larion Typeless Slot [%i, %i, %i] (implied type: %i) to Server Slot %i", - larion_slot.Slot, larion_slot.SubIndex, larion_slot.AugIndex, larion_type, ServerSlot); + Log(Logs::Detail, Logs::Netcode, "Convert Laurion Typeless Slot [%i, %i, %i] (implied type: %i) to Server Slot %i", + laurion_slot.Slot, laurion_slot.SubIndex, laurion_slot.AugIndex, laurion_type, ServerSlot); return ServerSlot; } -} /*Larion*/ +} /*Laurion*/ diff --git a/common/patches/larion.h b/common/patches/laurion.h similarity index 91% rename from common/patches/larion.h rename to common/patches/laurion.h index 7d7b83e32..f378e9e05 100644 --- a/common/patches/larion.h +++ b/common/patches/laurion.h @@ -17,14 +17,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef COMMON_LARION_H -#define COMMON_LARION_H +#ifndef COMMON_LAURION_H +#define COMMON_LAURION_H #include "../struct_strategy.h" class EQStreamIdentifier; -namespace Larion +namespace Laurion { //these are the only public member of this namespace. @@ -47,9 +47,9 @@ namespace Larion //magic macro to declare our opcode processors #include "ss_declare.h" -#include "larion_ops.h" +#include "laurion_ops.h" }; -}; /*Larion*/ +}; /*Laurion*/ -#endif /*COMMON_LARION_H*/ +#endif /*COMMON_LAURION_H*/ diff --git a/common/patches/larion_limits.cpp b/common/patches/laurion_limits.cpp similarity index 93% rename from common/patches/larion_limits.cpp rename to common/patches/laurion_limits.cpp index 49ea7a396..9716d083b 100644 --- a/common/patches/larion_limits.cpp +++ b/common/patches/laurion_limits.cpp @@ -17,12 +17,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "larion_limits.h" +#include "laurion_limits.h" #include "../strings.h" -int16 Larion::invtype::GetInvTypeSize(int16 inv_type) +int16 Laurion::invtype::GetInvTypeSize(int16 inv_type) { switch (inv_type) { case invtype::typePossessions: @@ -80,7 +80,7 @@ int16 Larion::invtype::GetInvTypeSize(int16 inv_type) } } -const char* Larion::invtype::GetInvTypeName(int16 inv_type) +const char* Laurion::invtype::GetInvTypeName(int16 inv_type) { switch (inv_type) { case invtype::TYPE_INVALID: @@ -140,7 +140,7 @@ const char* Larion::invtype::GetInvTypeName(int16 inv_type) } } -bool Larion::invtype::IsInvTypePersistent(int16 inv_type) +bool Laurion::invtype::IsInvTypePersistent(int16 inv_type) { switch (inv_type) { case invtype::typePossessions: @@ -158,7 +158,7 @@ bool Larion::invtype::IsInvTypePersistent(int16 inv_type) } } -const char* Larion::invslot::GetInvPossessionsSlotName(int16 inv_slot) +const char* Laurion::invslot::GetInvPossessionsSlotName(int16 inv_slot) { switch (inv_slot) { case invslot::SLOT_INVALID: @@ -236,7 +236,7 @@ const char* Larion::invslot::GetInvPossessionsSlotName(int16 inv_slot) } } -const char* Larion::invslot::GetInvSlotName(int16 inv_type, int16 inv_slot) +const char* Laurion::invslot::GetInvSlotName(int16 inv_type, int16 inv_slot) { if (inv_type == invtype::typePossessions) return invslot::GetInvPossessionsSlotName(inv_slot); @@ -255,7 +255,7 @@ const char* Larion::invslot::GetInvSlotName(int16 inv_type, int16 inv_slot) return ret_str.c_str(); } -const char* Larion::invbag::GetInvBagIndexName(int16 bag_index) +const char* Laurion::invbag::GetInvBagIndexName(int16 bag_index) { if (bag_index == invbag::SLOT_INVALID) return "Invalid Bag"; @@ -269,7 +269,7 @@ const char* Larion::invbag::GetInvBagIndexName(int16 bag_index) return ret_str.c_str(); } -const char* Larion::invaug::GetInvAugIndexName(int16 aug_index) +const char* Laurion::invaug::GetInvAugIndexName(int16 aug_index) { if (aug_index == invaug::SOCKET_INVALID) return "Invalid Augment"; diff --git a/common/patches/larion_limits.h b/common/patches/laurion_limits.h similarity index 94% rename from common/patches/larion_limits.h rename to common/patches/laurion_limits.h index c1cfca615..f5035c06c 100644 --- a/common/patches/larion_limits.h +++ b/common/patches/laurion_limits.h @@ -17,20 +17,20 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef COMMON_LARION_LIMITS_H -#define COMMON_LARION_LIMITS_H +#ifndef COMMON_LAURION_LIMITS_H +#define COMMON_LAURION_LIMITS_H #include "../types.h" #include "../emu_versions.h" #include "../skills.h" -namespace Larion +namespace Laurion { const int16 IINVALID = -1; const int16 INULL = 0; namespace inventory { - inline EQ::versions::ClientVersion GetInventoryRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetInventoryRef() { return EQ::versions::ClientVersion::Laurion; } const bool ConcatenateInvTypeLimbo = false; @@ -42,7 +42,7 @@ namespace Larion } /*inventory*/ namespace invtype { - inline EQ::versions::ClientVersion GetInvTypeRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetInvTypeRef() { return EQ::versions::ClientVersion::Laurion; } namespace enum_ { enum InventoryTypes : int16 { @@ -133,7 +133,7 @@ namespace Larion } /*invtype*/ namespace invslot { - inline EQ::versions::ClientVersion GetInvSlotRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetInvSlotRef() { return EQ::versions::ClientVersion::Laurion; } namespace enum_ { enum InventorySlots : int16 { @@ -212,7 +212,7 @@ namespace Larion } /*invslot*/ namespace invbag { - inline EQ::versions::ClientVersion GetInvBagRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetInvBagRef() { return EQ::versions::ClientVersion::Laurion; } const int16 SLOT_INVALID = IINVALID; const int16 SLOT_BEGIN = INULL; @@ -224,7 +224,7 @@ namespace Larion } /*invbag*/ namespace invaug { - inline EQ::versions::ClientVersion GetInvAugRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetInvAugRef() { return EQ::versions::ClientVersion::Laurion; } const int16 SOCKET_INVALID = IINVALID; const int16 SOCKET_BEGIN = INULL; @@ -236,7 +236,7 @@ namespace Larion } /*invaug*/ namespace item { - inline EQ::versions::ClientVersion GetItemRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetItemRef() { return EQ::versions::ClientVersion::Laurion; } //enum Unknown : int { // looks like item class..but, RoF has it too - nothing in UF- // Unknown1 = 0, @@ -265,7 +265,7 @@ namespace Larion } /*item*/ namespace profile { - inline EQ::versions::ClientVersion GetProfileRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetProfileRef() { return EQ::versions::ClientVersion::Laurion; } const int16 BANDOLIERS_SIZE = 20; // number of bandolier instances const int16 BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance @@ -277,7 +277,7 @@ namespace Larion } /*profile*/ namespace constants { - inline EQ::versions::ClientVersion GetConstantsRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetConstantsRef() { return EQ::versions::ClientVersion::Laurion; } const EQ::expansions::Expansion EXPANSION = EQ::expansions::Expansion::LS; const uint32 EXPANSION_BIT = EQ::expansions::bitLS; @@ -291,21 +291,21 @@ namespace Larion } /*constants*/ namespace behavior { - inline EQ::versions::ClientVersion GetBehaviorRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetBehaviorRef() { return EQ::versions::ClientVersion::Laurion; } const bool CoinHasWeight = false; } /*behavior*/ namespace skills { - inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::Laurion; } const size_t LastUsableSkill = EQ::skills::Skill2HPiercing; } /*skills*/ namespace spells { - inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::Larion; } + inline EQ::versions::ClientVersion GetSkillsRef() { return EQ::versions::ClientVersion::Laurion; } enum class CastingSlot : uint32 { Gem1 = 0, @@ -341,6 +341,6 @@ namespace Larion } /*spells*/ -}; /* Larion */ +}; /* Laurion */ -#endif /*COMMON_LARION_LIMITS_H*/ +#endif /*COMMON_LAURION_LIMITS_H*/ diff --git a/common/patches/larion_ops.h b/common/patches/laurion_ops.h similarity index 100% rename from common/patches/larion_ops.h rename to common/patches/laurion_ops.h diff --git a/common/patches/larion_structs.h b/common/patches/laurion_structs.h similarity index 97% rename from common/patches/larion_structs.h rename to common/patches/laurion_structs.h index 927527048..ab9724baf 100644 --- a/common/patches/larion_structs.h +++ b/common/patches/laurion_structs.h @@ -1,7 +1,7 @@ -#ifndef LARION_STRUCTS_H_ -#define LARION_STRUCTS_H_ +#ifndef LAURION_STRUCTS_H_ +#define LAURION_STRUCTS_H_ -namespace Larion { +namespace Laurion { namespace structs { // constants static const uint32 MAX_PP_AA_ARRAY = 300; @@ -351,7 +351,7 @@ namespace Larion { struct EnterWorld_Struct { /*000*/ char name[64]; /*064*/ int32 unknown1; - /*068*/ int32 unknown2; //larion handles these differently so for now im just going to ignore them till i figure it out + /*068*/ int32 unknown2; //laurion handles these differently so for now im just going to ignore them till i figure it out }; struct ZoneChange_Struct { @@ -441,6 +441,6 @@ namespace Larion { #pragma pack() }; //end namespace structs -}; //end namespace larion +}; //end namespace laurion -#endif /*LARION_STRUCTS_H_*/ +#endif /*LAURION_STRUCTS_H_*/ diff --git a/common/patches/patches.cpp b/common/patches/patches.cpp index 3a1a0fb6e..c8a17e6a2 100644 --- a/common/patches/patches.cpp +++ b/common/patches/patches.cpp @@ -26,7 +26,7 @@ #include "sod.h" #include "rof.h" #include "rof2.h" -#include "larion.h" +#include "laurion.h" void RegisterAllPatches(EQStreamIdentifier &into) { @@ -36,7 +36,7 @@ void RegisterAllPatches(EQStreamIdentifier &into) UF::Register(into); RoF::Register(into); RoF2::Register(into); - Larion::Register(into); + Laurion::Register(into); } void ReloadAllPatches() @@ -47,5 +47,5 @@ void ReloadAllPatches() UF::Reload(); RoF::Reload(); RoF2::Reload(); - Larion::Reload(); + Laurion::Reload(); } diff --git a/loginserver/client.cpp b/loginserver/client.cpp index dc1701959..45ceb8f38 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -587,7 +587,7 @@ void Client::SendExpansionPacketData(PlayerLoginReply_Struct& plrs) int32_t expansion = server.options.GetMaxExpansions(); int32_t owned_expansion = (expansion << 1) | 1; - if (m_client_version == cv_larion) { + if (m_client_version == cv_laurion) { buf.WriteInt32(0x00); buf.WriteInt32(0x00); buf.WriteInt16(0x00); diff --git a/loginserver/client_manager.cpp b/loginserver/client_manager.cpp index c82eb0de8..062872cd0 100644 --- a/loginserver/client_manager.cpp +++ b/loginserver/client_manager.cpp @@ -56,7 +56,7 @@ void CheckSoDOpcodeFile(const std::string& path) { } } -void CheckLarionOpcodeFile(const std::string& path) { +void CheckLaurionOpcodeFile(const std::string& path) { if (File::Exists(path)) { return; } @@ -158,40 +158,40 @@ ClientManager::ClientManager() } ); - int larion_port = server.config.GetVariableInt("client_configuration", "larion_port", 15900); + int laurion_port = server.config.GetVariableInt("client_configuration", "laurion_port", 15900); - EQStreamManagerInterfaceOptions larion_opts(larion_port, false, false); + EQStreamManagerInterfaceOptions laurion_opts(laurion_port, false, false); - larion_stream = new EQ::Net::EQStreamManager(larion_opts); - larion_ops = new RegularOpcodeManager; + laurion_stream = new EQ::Net::EQStreamManager(laurion_opts); + laurion_ops = new RegularOpcodeManager; opcodes_path = fmt::format( "{}/{}", path.GetOpcodePath(), - "login_opcodes_larion.conf" + "login_opcodes_laurion.conf" ); - CheckLarionOpcodeFile(opcodes_path); + CheckLaurionOpcodeFile(opcodes_path); - if (!larion_ops->LoadOpcodes(opcodes_path.c_str())) { + if (!laurion_ops->LoadOpcodes(opcodes_path.c_str())) { LogError( - "ClientManager fatal error: couldn't load opcodes for Larion file [{0}]", - server.config.GetVariableString("client_configuration", "larion_opcodes", "login_opcodes.conf") + "ClientManager fatal error: couldn't load opcodes for Laurion file [{0}]", + server.config.GetVariableString("client_configuration", "laurion_opcodes", "login_opcodes.conf") ); run_server = false; } - larion_stream->OnNewConnection( + laurion_stream->OnNewConnection( [this](std::shared_ptr stream) { LogInfo( - "New Larion client connection from [{0}:{1}]", + "New Laurion client connection from [{0}:{1}]", long2ip(stream->GetRemoteIP()), stream->GetRemotePort() ); - stream->SetOpcodeManager(&larion_ops); - Client* c = new Client(stream, cv_larion); + stream->SetOpcodeManager(&laurion_ops); + Client* c = new Client(stream, cv_laurion); clients.push_back(c); } ); diff --git a/loginserver/client_manager.h b/loginserver/client_manager.h index 23aff232e..27d14c2bd 100644 --- a/loginserver/client_manager.h +++ b/loginserver/client_manager.h @@ -55,8 +55,8 @@ private: EQ::Net::EQStreamManager *titanium_stream; OpcodeManager *sod_ops; EQ::Net::EQStreamManager *sod_stream; - OpcodeManager *larion_ops; - EQ::Net::EQStreamManager* larion_stream; + OpcodeManager *laurion_ops; + EQ::Net::EQStreamManager* laurion_stream; }; #endif diff --git a/loginserver/login_types.h b/loginserver/login_types.h index 1c8054a37..35707fef5 100644 --- a/loginserver/login_types.h +++ b/loginserver/login_types.h @@ -84,7 +84,7 @@ struct PlayEverquestResponse_Struct { enum LSClientVersion { cv_titanium, cv_sod, - cv_larion + cv_laurion }; enum LSClientStatus { diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index ece3f16d5..47a16e4e8 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -1020,7 +1020,7 @@ void WorldServer::SerializeForClientServerListLegacy(class SerializeBuffer& out, out.WriteUInt32(GetPlayersOnline()); } -void WorldServer::SerializeForClientServerListLarion(class SerializeBuffer& out, bool use_local_ip) const { +void WorldServer::SerializeForClientServerListLaurion(class SerializeBuffer& out, bool use_local_ip) const { if (use_local_ip) { out.WriteString(GetLocalIP()); } @@ -1067,8 +1067,8 @@ void WorldServer::SerializeForClientServerListLarion(class SerializeBuffer& out, void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const { - if (version == cv_larion) { - SerializeForClientServerListLarion(out, use_local_ip); + if (version == cv_laurion) { + SerializeForClientServerListLaurion(out, use_local_ip); } else { SerializeForClientServerListLegacy(out, use_local_ip); diff --git a/loginserver/world_server.h b/loginserver/world_server.h index 0b8da5851..a78235f6f 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -151,7 +151,7 @@ public: bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration); private: void SerializeForClientServerListLegacy(class SerializeBuffer& out, bool use_local_ip) const; - void SerializeForClientServerListLarion(class SerializeBuffer& out, bool use_local_ip) const; + void SerializeForClientServerListLaurion(class SerializeBuffer& out, bool use_local_ip) const; public: void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const; diff --git a/utils/patches/patch_Larion.conf b/utils/patches/patch_Laurion.conf similarity index 100% rename from utils/patches/patch_Larion.conf rename to utils/patches/patch_Laurion.conf diff --git a/zone/client.cpp b/zone/client.cpp index a8b57286d..d80ae4270 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -543,8 +543,8 @@ void Client::SendZoneInPackets() if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - //Larion uses a more granular exp bar than other clients - if (m_ClientVersion >= EQ::versions::ClientVersion::Larion) { + //Laurion uses a more granular exp bar than other clients + if (m_ClientVersion >= EQ::versions::ClientVersion::Laurion) { eu->exp = (uint32)(100000.0f * tmpxp); } else { From e74d4b6e672ecc89efd9ad08dd3629eaa6081bb8 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 25 Nov 2024 22:48:20 -0800 Subject: [PATCH 51/93] Add formatted and simple message. npc say should work now; though we should consider moving them to what live uses "OP_SpecialMesg" --- common/patches/laurion.cpp | 39 ++++++++++---------------------- utils/patches/patch_Laurion.conf | 4 ++-- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 40d5f3e6c..f443ce3fb 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2544,47 +2544,32 @@ namespace Laurion FormattedMessage_Struct* emu = (FormattedMessage_Struct*)in->pBuffer; - unsigned char* __emu_buffer = in->pBuffer; - char* old_message_ptr = (char*)in->pBuffer; old_message_ptr += sizeof(FormattedMessage_Struct); - + std::string old_message_array[9]; - + for (int i = 0; i < 9; ++i) { if (*old_message_ptr == 0) { break; } old_message_array[i] = old_message_ptr; old_message_ptr += old_message_array[i].length() + 1; } - uint32 new_message_size = 0; - std::string new_message_array[9]; + SerializeBuffer buffer; + buffer.WriteUInt32(emu->unknown0); + buffer.WriteUInt8(0); // Observed + buffer.WriteUInt32(emu->string_id); + buffer.WriteUInt32(emu->type); for (int i = 0; i < 9; ++i) { - if (old_message_array[i].length() == 0) { break; } - new_message_array[i] = old_message_array[i]; - //ServerToRoF2SayLink(new_message_array[i], old_message_array[i]); - new_message_size += new_message_array[i].length() + 1; + buffer.WriteLengthString(old_message_array[i]); } - in->size = sizeof(FormattedMessage_Struct) + new_message_size + 1; - in->pBuffer = new unsigned char[in->size]; + auto outapp = new EQApplicationPacket(OP_FormattedMessage, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); - char* OutBuffer = (char*)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->unknown0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->string_id); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->type); - - for (int i = 0; i < 9; ++i) { - if (new_message_array[i].length() == 0) { break; } - VARSTRUCT_ENCODE_STRING(OutBuffer, new_message_array[i].c_str()); - } - - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); + delete in; } // DECODE methods diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 52a738ce7..e721d691a 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -181,8 +181,8 @@ OP_LinkedReuse=0x0000 OP_SwapSpell=0x0000 OP_CastSpell=0x0000 OP_Consider=0x0000 -OP_FormattedMessage=0x0000 -OP_SimpleMessage=0x0000 +OP_FormattedMessage=0x7f7f +OP_SimpleMessage=0x1943 OP_Buff=0x0000 OP_Illusion=0x0000 OP_MoneyOnCorpse=0x0000 From f9918d47d718a8cd0a345898a7cc37e38564b7ef Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 26 Nov 2024 22:52:45 -0800 Subject: [PATCH 52/93] Work in progress on some packets; consider and HP update look right but I'm still testing. --- common/patches/laurion.cpp | 42 ++++++++++++++++++++++++++++++++ common/patches/laurion_ops.h | 4 +++ common/patches/laurion_structs.h | 30 +++++++++++++++++++++++ world/client.cpp | 7 ++++-- 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index f443ce3fb..e35a61cda 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2572,6 +2572,30 @@ namespace Laurion delete in; } + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + + FINISH_ENCODE(); + } + + ENCODE(OP_HPUpdate) + { + SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct, structs::SpawnHPUpdate_Struct); + + OUT(spawn_id); + OUT(cur_hp); + OUT(max_hp); + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -2706,6 +2730,24 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index abc09ca3a..110a6ae1a 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -25,6 +25,8 @@ E(OP_ChannelMessage) E(OP_SpecialMesg) E(OP_DeleteSpawn) E(OP_FormattedMessage) +E(OP_Consider) +E(OP_HPUpdate) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) @@ -33,6 +35,8 @@ D(OP_ClientUpdate) D(OP_WearChange) D(OP_ChannelMessage) D(OP_SetServerFilter) +D(OP_Consider) +D(OP_ConsiderCorpse) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index ab9724baf..fade3e428 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -438,6 +438,36 @@ namespace Laurion { /*008*/ }; + struct Consider_Struct { + /*000*/ uint32 playerid; // PlayerID + /*004*/ uint32 targetid; // TargetID + /*008*/ uint32 faction; // Faction + /*012*/ uint32 level; // Level + /*016*/ uint32 report_mode; // 0 normally, 4 will do a more detailed report that only works if you have GM flag set + /*020*/ uint8 rare_creature; // Will do the rare creature string + /*021*/ uint8 loot_locked; // Will list the target as (loot locked) + /*022*/ uint8 unknown022; // Padding probably + /*023*/ uint8 unknown023; // Padding probably + /*024*/ + }; + + struct ChangeSize_Struct + { + /*00*/ uint32 EntityID; + /*04*/ float Size; + /*08*/ uint32 Unknown08; // Observed 0 + /*12*/ float Unknown12; // Observed 1.0f + /*16*/ + }; + + struct SpawnHPUpdate_Struct + { + /*00*/ int16 spawn_id; + /*02*/ int64 cur_hp; + /*10*/ int64 max_hp; + /*18*/ + }; + #pragma pack() }; //end namespace structs diff --git a/world/client.cpp b/world/client.cpp index a6f5cfb5f..9b2069d40 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1209,8 +1209,11 @@ bool Client::Process() { } if(connect.Check()){ - SendGuildList();// Send OPCode: OP_GuildsList - SendApproveWorld(); + if (!(m_ClientVersionBit & EQ::versions::maskLaurionAndLater)) { + SendGuildList();// Send OPCode: OP_GuildsList + SendApproveWorld(); + } + connect.Disable(); } From 2d8b77712062658ff0bd32b536f5d6a1af0346cc Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 30 Nov 2024 15:17:46 -0800 Subject: [PATCH 53/93] Fix for door clicking, add a few more opcodes. --- common/emu_oplist.h | 2 -- common/patches/laurion.cpp | 11 +++++++++++ common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 10 ++++++++++ utils/patches/patch_Laurion.conf | 24 ++++++++++++------------ zone/client_packet.cpp | 15 --------------- zone/client_packet.h | 2 -- zone/lua_packet.cpp | 2 -- 8 files changed, 34 insertions(+), 33 deletions(-) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 83b7d2df0..236def1f8 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -1,8 +1,6 @@ // system use N(OP_ExploreUnknown), // start (please add new opcodes in descending order and re-order any name changes where applicable) -N(OP_0x0193), -N(OP_0x0347), N(OP_AAAction), N(OP_AAExpUpdate), N(OP_AcceptNewTask), diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index e35a61cda..7abfcab0c 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2748,6 +2748,17 @@ namespace Laurion DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + DECODE(OP_ClickDoor) + { + DECODE_LENGTH_EXACT(structs::ClickDoor_Struct); + SETUP_DIRECT_DECODE(ClickDoor_Struct, structs::ClickDoor_Struct); + + IN(doorid); + IN(player_id); + + FINISH_DIRECT_DECODE(); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 110a6ae1a..a4c2d7d32 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -37,6 +37,7 @@ D(OP_ChannelMessage) D(OP_SetServerFilter) D(OP_Consider) D(OP_ConsiderCorpse) +D(OP_ClickDoor) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index fade3e428..fa5cbf1d6 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -468,6 +468,16 @@ namespace Laurion { /*18*/ }; + struct ClickDoor_Struct { + /*00*/ uint16 player_id; + /*02*/ uint8 padding1[2]; + /*04*/ int32 unknown1; + /*08*/ int32 unknown2; + /*12*/ uint8 doorid; + /*13*/ uint8 padding2[3]; + + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index e721d691a..93016cca2 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -38,8 +38,8 @@ OP_DeleteCharacter=x67d7 OP_RandomNameGenerator=0x49d9 OP_ApproveName=0x11e5 OP_MOTD=0x0000 -OP_SetChatServer=0x2726 -OP_SetChatServer2=0x0000 +OP_SetChatServer=0x0000 +OP_SetChatServer2=0x2726 OP_ZoneServerInfo=0x2273 OP_WorldComplete=0x195c OP_WorldUnknown001=0x2049 @@ -78,7 +78,7 @@ OP_PreLogoutReply=0x0000 # Required to fully log in OP_SpawnAppearance=0x4eb0 -OP_ChangeSize=0x0000 #0x2fdc +OP_ChangeSize=0x2fdc OP_TributeUpdate=0x0000 OP_TributeTimer=0x0000 OP_SendTributes=0x0000 @@ -95,16 +95,16 @@ OP_ClearBlockedBuffs=0x0000 OP_WorldObjectsSent=0x2879 OP_SendExpZonein=0x02b4 OP_SendAATable=0x0000 -OP_ClearAA=0x0000 -OP_ClearLeadershipAbilities=0x0000 +OP_ClearAA=0x3498 +OP_ClearLeadershipAbilities=0x0000 #removed; leadership abilities are baked in and always on OP_RespondAA=0x0000 OP_UpdateAA=0x0000 OP_SendAAStats=0x0000 OP_AAExpUpdate=0x0000 #0x642f OP_ExpUpdate=0x611d -OP_HPUpdate=0x0000 #0x775c -OP_ManaChange=0x0000 #0x0606 -OP_TGB=0x0000 +OP_HPUpdate=0x775c +OP_ManaChange=0x0606 +OP_TGB=0x0000 #removed; tgb is baked in and always on OP_SpecialMesg=0x7d93 OP_GuildMemberList=0x0000 OP_GuildMOTD=0x0000 @@ -180,10 +180,10 @@ OP_MemorizeSpell=0x0000 OP_LinkedReuse=0x0000 OP_SwapSpell=0x0000 OP_CastSpell=0x0000 -OP_Consider=0x0000 +OP_Consider=0x53e3 OP_FormattedMessage=0x7f7f OP_SimpleMessage=0x1943 -OP_Buff=0x0000 +OP_Buff=0x0000 #0x6ce5 OP_Illusion=0x0000 OP_MoneyOnCorpse=0x0000 OP_RandomReply=0x0000 @@ -210,7 +210,7 @@ OP_FaceChange=0x0000 OP_SetFace=0x0000 OP_SenseHeading=0x0000 OP_Action=0x0000 -OP_ConsiderCorpse=0x0000 +OP_ConsiderCorpse=0x6092 OP_HideCorpse=0x0000 OP_CorpseDrag=0x0000 OP_CorpseDrop=0x0000 @@ -247,7 +247,7 @@ OP_XTargetRequest=0x0000 OP_XTargetAutoAddHaters=0x0000 OP_XTargetOpen=0x0000 OP_XTargetOpenResponse=0x0000 -OP_BuffCreate=0x0000 +OP_BuffCreate=0x0000 #0x27a1 OP_BuffRemoveRequest=0x0000 OP_DeleteSpawn=0x7712 OP_AutoAttack=0x0000 diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 14ee222f3..d083e1252 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -118,7 +118,6 @@ void MapOpcodes() ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry; // connected opcode handler assignments: - ConnectedOpcodes[OP_0x0193] = &Client::Handle_0x0193; ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction; ConnectedOpcodes[OP_AcceptNewTask] = &Client::Handle_OP_AcceptNewTask; ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest; @@ -1203,10 +1202,6 @@ void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app) { - auto outapp = new EQApplicationPacket(OP_0x0347, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; } void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) @@ -1834,16 +1829,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) return; } -// connected opcode handlers -void Client::Handle_0x0193(const EQApplicationPacket *app) -{ - // Not sure what this opcode does. It started being sent when OP_ClientUpdate was - // changed to pump OP_ClientUpdate back out instead of OP_MobUpdate - // 2 bytes: 00 00 - - return; -} - void Client::Handle_OP_AAAction(const EQApplicationPacket *app) { LogAA("Received OP_AAAction"); diff --git a/zone/client_packet.h b/zone/client_packet.h index 57a4bfcf4..42df65bd7 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -21,8 +21,6 @@ void Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app); void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app); /* Connected opcode handlers*/ - void Handle_0x0193(const EQApplicationPacket *app); - void Handle_0x01e7(const EQApplicationPacket *app); void Handle_OP_AAAction(const EQApplicationPacket *app); void Handle_OP_AcceptNewTask(const EQApplicationPacket *app); void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app); diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index c6a1ab6e2..47ceca13e 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -674,8 +674,6 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("ShopEndConfirm", static_cast(OP_ShopEndConfirm)), luabind::value("AdventureMerchantRequest", static_cast(OP_AdventureMerchantRequest)), luabind::value("Sound", static_cast(OP_Sound)), - luabind::value("0x0193", static_cast(OP_0x0193)), - luabind::value("0x0347", static_cast(OP_0x0347)), luabind::value("WorldComplete", static_cast(OP_WorldComplete)), luabind::value("MobRename", static_cast(OP_MobRename)), luabind::value("TaskDescription", static_cast(OP_TaskDescription)), From 1e35d30c8f7ec0854ef62aa70d27556ecca92985 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 30 Nov 2024 19:04:57 -0800 Subject: [PATCH 54/93] Now send membership for Laurion in zone right before player profile like live. --- zone/client.cpp | 38 ++++++++++++++++++++++++++++++++++++++ zone/client.h | 1 + zone/client_packet.cpp | 4 +--- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index d80ae4270..9ab53c593 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8602,6 +8602,44 @@ void Client::SendHPUpdateMarquee(){ SendMarqueeMessage(Chat::Yellow, 510, 0, 3000, 3000, health_update_notification); } +void Client::SendMembership() { + if (m_ClientVersion >= EQ::versions::ClientVersion::Laurion) { + auto outapp = new EQApplicationPacket(OP_SendMembership, sizeof(Membership_Struct)); + Membership_Struct* mc = (Membership_Struct*)outapp->pBuffer; + + mc->membership = 2; //Hardcode to gold for now. We don't use anything else. + mc->races = 0x1ffff; // Available Races (4110 for silver) + mc->classes = 0x1ffff; // Available Classes (4614 for silver) - Was 0x101ffff + mc->entrysize = 21; // Number of membership setting entries below + mc->entries[0] = 0xffffffff; // Max AA Restriction + mc->entries[1] = 0xffffffff; // Max Level Restriction + mc->entries[2] = 0xffffffff; // Max Char Slots per Account (not used by client?) + mc->entries[3] = 0xffffffff; // 1 for Silver + mc->entries[4] = 0xffffffff; // Main Inventory Size (0xffffffff on Live for Gold, but limiting to 8 until 10 is supported) + mc->entries[5] = 0xffffffff; // Max Platinum per level + mc->entries[6] = 1; // 0 for Silver + mc->entries[7] = 1; // 0 for Silver + mc->entries[8] = 1; // 1 for Silver + mc->entries[9] = 0xffffffff; // Unknown - Maybe Loyalty Points every 12 hours? 60 per week for Silver + mc->entries[10] = 1; // 1 for Silver + mc->entries[11] = 0xffffffff; // Shared Bank Slots + mc->entries[12] = 0xffffffff; // Unknown - Maybe Max Active Tasks? + mc->entries[13] = 1; // 1 for Silver + mc->entries[14] = 1; // 0 for Silver + mc->entries[15] = 1; // 0 for Silver + mc->entries[16] = 1; // 1 for Silver + mc->entries[17] = 1; // 0 for Silver + mc->entries[18] = 1; // 0 for Silver + mc->entries[19] = 0xffffffff; // 0 for Silver + mc->entries[20] = 0xffffffff; // 0 for Silver + mc->exit_url_length = 0; + //mc->exit_url = 0; // Used on Live: "http://www.everquest.com/free-to-play/exit-silver" + + QueuePacket(outapp); + safe_delete(outapp); + } +} + uint32 Client::GetMoney(uint8 type, uint8 subtype) { uint32 value = 0; diff --git a/zone/client.h b/zone/client.h index 5acc388b3..d08eec3a1 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1786,6 +1786,7 @@ public: void ResetHPUpdateTimer() { hpupdate_timer.Start(); } void SendHPUpdateMarquee(); + void SendMembership(); void CheckRegionTypeChanges(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d083e1252..dbd438295 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1650,9 +1650,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) m_pp.RestTimer = 0; - /* This checksum should disappear once dynamic structs are in... each struct strategy will do it */ // looks to be in place now - //CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct) - sizeof(m_pp.m_player_profile_version) - 4); - // m_pp.checksum = 0; // All server out-bound player profile packets are now translated - no need to waste cycles calculating this... + SendMembership(); outapp = new EQApplicationPacket(OP_PlayerProfile, sizeof(PlayerProfile_Struct)); From 591fa0eb1d0017d6d71d63a053c48176ec07eee9 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 30 Nov 2024 23:52:00 -0800 Subject: [PATCH 55/93] -Set laurion commands to $ from . since that's closer to muscle memory. -Add translation for OP_SpawnAppearance (later to support guilds we will need to extend it a bit server side.) --- common/patches/laurion.cpp | 150 ++++++++++++++++++++++++++++++- common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 55 +++++++++++- zone/command.h | 2 +- 4 files changed, 202 insertions(+), 6 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 7abfcab0c..967e10716 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -51,6 +51,10 @@ namespace Laurion void SerializeItem(SerializeBuffer &buffer, const EQ::ItemInstance* inst, int16 slot_id, uint8 depth, ItemPacketType packet_type); + //SpawnAppearance + static inline uint32 ServerToLaurionSpawnAppearanceType(uint32 server_type); + static inline uint32 LaurionToServerSpawnAppearanceType(uint32 laurion_type); + // server to client inventory location converters static inline structs::InventorySlot_Struct ServerToLaurionSlot(uint32 server_slot); static inline structs::InventorySlot_Struct ServerToLaurionCorpseSlot(uint32 server_corpse_slot); @@ -515,11 +519,12 @@ namespace Laurion if (sas->type != AppearanceType::Size) { //laurion struct is different than rof2's but the idea is the same + //we will probably want to better implement Laurion's structure later auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; eq->spawn_id = sas->spawn_id; - eq->type = sas->type; + eq->type = ServerToLaurionSpawnAppearanceType(sas->type); eq->parameter = sas->parameter; dest->FastQueuePacket(&outapp, ack_req); @@ -2759,6 +2764,17 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_SpawnAppearance) { + DECODE_LENGTH_EXACT(structs::SpawnAppearance_Struct); + SETUP_DIRECT_DECODE(SpawnAppearance_Struct, structs::SpawnAppearance_Struct); + + IN(spawn_id); + emu->type = LaurionToServerSpawnAppearanceType(eq->type); + IN(parameter); + + FINISH_DIRECT_DECODE(); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; @@ -3537,6 +3553,138 @@ namespace Laurion buffer.WriteInt32(0); //unsupported atm } + static inline uint32 ServerToLaurionSpawnAppearanceType(uint32 server_type) { + switch (server_type) + { + case AppearanceType::WhoLevel: + return structs::LaurionAppearance::WhoLevel; + case AppearanceType::MaxHealth: + return structs::LaurionAppearance::MaxHealth; + case AppearanceType::Invisibility: + return structs::LaurionAppearance::Invisibility; + case AppearanceType::PVP: + return structs::LaurionAppearance::PVP; + case AppearanceType::Light: + return structs::LaurionAppearance::Light; + case AppearanceType::Animation: + return structs::LaurionAppearance::Animation; + case AppearanceType::Sneak: + return structs::LaurionAppearance::Sneak; + case AppearanceType::SpawnID: + return structs::LaurionAppearance::SpawnID; + case AppearanceType::Health: + return structs::LaurionAppearance::Health; + case AppearanceType::Linkdead: + return structs::LaurionAppearance::Linkdead; + case AppearanceType::FlyMode: + return structs::LaurionAppearance::FlyMode; + case AppearanceType::GM: + return structs::LaurionAppearance::GM; + case AppearanceType::Anonymous: + return structs::LaurionAppearance::Anonymous; + case AppearanceType::GuildID: + return structs::LaurionAppearance::GuildID; + case AppearanceType::AFK: + return structs::LaurionAppearance::AFK; + case AppearanceType::Pet: + return structs::LaurionAppearance::Pet; + case AppearanceType::Summoned: + return structs::LaurionAppearance::Summoned; + case AppearanceType::SetType: + return structs::LaurionAppearance::NPCName; + case AppearanceType::CancelSneakHide: + return structs::LaurionAppearance::CancelSneakHide; + case AppearanceType::AreaHealthRegen: + return structs::LaurionAppearance::AreaHealthRegen; + case AppearanceType::AreaManaRegen: + return structs::LaurionAppearance::AreaManaRegen; + case AppearanceType::AreaEnduranceRegen: + return structs::LaurionAppearance::AreaEnduranceRegen; + case AppearanceType::FreezeBeneficialBuffs: + return structs::LaurionAppearance::FreezeBeneficialBuffs; + case AppearanceType::NPCTintIndex: + return structs::LaurionAppearance::NPCTintIndex; + case AppearanceType::ShowHelm: + return structs::LaurionAppearance::ShowHelm; + case AppearanceType::DamageState: + return structs::LaurionAppearance::DamageState; + case AppearanceType::TextureType: + return structs::LaurionAppearance::TextureType; + case AppearanceType::GuildShow: + return structs::LaurionAppearance::GuildShow; + case AppearanceType::OfflineMode: + return structs::LaurionAppearance::OfflineMode; + default: + return structs::LaurionAppearance::None; + } + } + + static inline uint32 LaurionToServerSpawnAppearanceType(uint32 laurion_type) { + switch (laurion_type) + { + case structs::LaurionAppearance::WhoLevel: + return AppearanceType::WhoLevel; + case structs::LaurionAppearance::MaxHealth: + return AppearanceType::MaxHealth; + case structs::LaurionAppearance::Invisibility: + return AppearanceType::Invisibility; + case structs::LaurionAppearance::PVP: + return AppearanceType::PVP; + case structs::LaurionAppearance::Light: + return AppearanceType::Light; + case structs::LaurionAppearance::Animation: + return AppearanceType::Animation; + case structs::LaurionAppearance::Sneak: + return AppearanceType::Sneak; + case structs::LaurionAppearance::SpawnID: + return AppearanceType::SpawnID; + case structs::LaurionAppearance::Health: + return AppearanceType::Health; + case structs::LaurionAppearance::Linkdead: + return AppearanceType::Linkdead; + case structs::LaurionAppearance::FlyMode: + return AppearanceType::FlyMode; + case structs::LaurionAppearance::GM: + return AppearanceType::GM; + case structs::LaurionAppearance::Anonymous: + return AppearanceType::Anonymous; + case structs::LaurionAppearance::GuildID: + return AppearanceType::GuildID; + case structs::LaurionAppearance::AFK: + return AppearanceType::AFK; + case structs::LaurionAppearance::Pet: + return AppearanceType::Pet; + case structs::LaurionAppearance::Summoned: + return AppearanceType::Summoned; + case structs::LaurionAppearance::SetType: + return AppearanceType::NPCName; + case structs::LaurionAppearance::CancelSneakHide: + return AppearanceType::CancelSneakHide; + case structs::LaurionAppearance::AreaHealthRegen: + return AppearanceType::AreaHealthRegen; + case structs::LaurionAppearance::AreaManaRegen: + return AppearanceType::AreaManaRegen; + case structs::LaurionAppearance::AreaEnduranceRegen: + return AppearanceType::AreaEnduranceRegen; + case structs::LaurionAppearance::FreezeBeneficialBuffs: + return AppearanceType::FreezeBeneficialBuffs; + case structs::LaurionAppearance::NPCTintIndex: + return AppearanceType::NPCTintIndex; + case structs::LaurionAppearance::ShowHelm: + return AppearanceType::ShowHelm; + case structs::LaurionAppearance::DamageState: + return AppearanceType::DamageState; + case structs::LaurionAppearance::TextureType: + return AppearanceType::TextureType; + case structs::LaurionAppearance::GuildShow: + return AppearanceType::GuildShow; + case structs::LaurionAppearance::OfflineMode: + return AppearanceType::OfflineMode; + default: + return AppearanceType::Die; + } + } + static inline structs::InventorySlot_Struct ServerToLaurionSlot(uint32 server_slot) { structs::InventorySlot_Struct LaurionSlot; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index a4c2d7d32..a8476d38d 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -38,6 +38,7 @@ D(OP_SetServerFilter) D(OP_Consider) D(OP_ConsiderCorpse) D(OP_ClickDoor) +D(OP_SpawnAppearance) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index fa5cbf1d6..fa83dd312 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -180,14 +180,61 @@ namespace Laurion { /*000*/ uint32 CharCount; //number of chars in this packet }; + enum LaurionAppearance : uint32 + { + None, + WhoLevel, + MaxHealth, + Invisibility, + PVP, + Light, + Animation, + Sneak, + SpawnID, + Health, + Linkdead, + FlyMode, + GM, + Anonymous, + GuildID, + AFK, + Pet, + Summoned, + Unknown18, + Unknown19, + SetType, + NPCName, + CancelSneakHide, + AreaHealthRegen, + AreaManaRegen, + AreaEnduranceRegen, + FreezeBeneficialBuffs, + NPCTintIndex, + Unknown28, + Unknown29, + Unknown30, + ShowHelm, + DamageState, + Unknown33, //Some virtual function call; based on location might be EQPlayers (my guess personally) or FindBits + TextureType, //Texture ID + Unknown35, + Unknown36, + GuildShow, + OfflineMode, + Unknown39, + Unknown40, + Unknown41, + Unknown42, + Birthdate, + EncounterLock + }; + struct SpawnAppearance_Struct { /*0000*/ uint32 spawn_id; // ID of the spawn /*0004*/ uint32 type; // Values associated with the type - /*0008*/ uint32 parameter; // Type of data sent - /*0012*/ uint32 unknown012; - /*0016*/ uint32 unknown016; - /*0020*/ uint32 unknown020; + /*0008*/ uint64 parameter; // Type of data sent + /*0016*/ uint64 lock_id; //the only place client uses this as far as I can tell is when you send 0x2c as type in which case it sets LockID = this /*0024*/ }; diff --git a/zone/command.h b/zone/command.h index 3f03a19ab..1c96758ee 100644 --- a/zone/command.h +++ b/zone/command.h @@ -8,7 +8,7 @@ class Seperator; #include #define COMMAND_CHAR '#' -#define COMMAND_CHAR_NON_HASH '.' +#define COMMAND_CHAR_NON_HASH '$' typedef void (*CmdFuncPtr)(Client *, const Seperator *); From 392998325bf40ccf2943b6ac55a918e811a2e66e Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 1 Dec 2024 23:42:28 -0800 Subject: [PATCH 56/93] -Damage mostly works -Death kinda sorta works; need to fix an ordering issue in source (we send death before damage on killing blow) -Fix animation -Fix auto attack -Camp --- common/patches/laurion.cpp | 56 +++++++++++++++++++++++++++++ common/patches/laurion_ops.h | 3 ++ common/patches/laurion_structs.h | 62 ++++++++++++++++++++++++++++++++ utils/patches/patch_Laurion.conf | 10 +++--- 4 files changed, 126 insertions(+), 5 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 967e10716..3bdc4b78b 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2601,6 +2601,62 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_Damage) { + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); + OUT(special); + + FINISH_ENCODE(); + } + + ENCODE(OP_Animation) + { + ENCODE_LENGTH_EXACT(Animation_Struct); + SETUP_DIRECT_ENCODE(Animation_Struct, structs::Animation_Struct); + + OUT(spawnid); + OUT(action); + OUT(speed); + + FINISH_ENCODE(); + } + + ENCODE(OP_Death) + { + ENCODE_LENGTH_EXACT(Death_Struct); + SETUP_DIRECT_ENCODE(Death_Struct, structs::Death_Struct); + + OUT(spawn_id); + OUT(killer_id); + OUT(spell_id); + OUT(attack_skill); + OUT(damage); + + //This is a hack, we need to actually fix the ordering in source as this wont respect filters etc + if (emu->attack_skill != 231) { + auto combat_packet = new EQApplicationPacket(OP_Damage, sizeof(structs::CombatDamage_Struct)); + structs::CombatDamage_Struct* cds = (structs::CombatDamage_Struct*)combat_packet->pBuffer; + + cds->target = emu->spawn_id; + cds->source = emu->killer_id; + cds->type = emu->attack_skill; + cds->damage = emu->damage; + cds->spellid = -1; + + dest->FastQueuePacket(&combat_packet, ack_req); + } + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index a8476d38d..177b7bcf6 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -27,6 +27,9 @@ E(OP_DeleteSpawn) E(OP_FormattedMessage) E(OP_Consider) E(OP_HPUpdate) +E(OP_Damage) +E(OP_Animation) +E(OP_Death) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index fa83dd312..0d3eeb4bf 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -525,6 +525,68 @@ namespace Laurion { }; + /* + Flags for special: + WildRampage: 0x1 + Rampage: 0x2 + NoCastOnText: 0x4 + DoubleBowShot: 0x8 + UnknownSpellFlag: 0x10 + Flurry: 0x20 + Riposte: 0x40 + Critical: 0x80 + Lucky: 0x100 + FinishingBlow: 0x200 + CripplingBlow: 0x400 + Assassinate: 0x800 + DeadlyStrike: 0x1000 + SlayUndead: 0x2000 + Headshot: 0x4000 + Strikethrough: 0x8000 + LuckyRiposte: 0x10000 + Twincast: 0x20000 + Might be more flags beyond this but I'm not sure + */ + + struct CombatDamage_Struct + { + /*000*/ uint16 target; + /*002*/ uint16 source; + /*004*/ uint32 unknown1; //not read by the client + /*008*/ int64 damage; + /*016*/ uint32 special; //flags; will document above + /*020*/ int32 spellid; + /*024*/ uint32 spell_level; //spell caster level (unconfirmed; it is used for the spell link) + /*028*/ float force; //I haven't actually been able to confirm these three yet + /*032*/ float hit_heading; + /*036*/ int32 hit_pitch; + /*040*/ uint8 type; + /*041*/ uint8 padding[3]; + /*044*/ uint32 unknown2; //not read by the client + /*048*/ + }; + + struct Animation_Struct { + /*00*/ uint16 spawnid; + /*02*/ uint8 action; + /*03*/ uint8 speed; + /*04*/ + }; + + struct Death_Struct + { + /*000*/ uint32 spawn_id; + /*004*/ uint32 killer_id; + /*008*/ uint32 corpseid; //not read by client + /*012*/ uint32 unknown1; //not read by client + /*016*/ uint32 spell_id; + /*020*/ uint32 attack_skill; + /*024*/ uint64 damage; + /*032*/ uint32 unknown2; //not read by client + /*036*/ uint32 unknown3; //not read by client + /*040*/ + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 93016cca2..e5e7042ea 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -195,7 +195,7 @@ OP_Death=0x429a OP_GMTraining=0x0000 OP_GMEndTraining=0x0000 OP_GMTrainSkill=0x0000 -OP_Animation=0x0000 +OP_Animation=0x79c7 OP_Begging=0x0000 OP_Consent=0x0000 OP_ConsentDeny=0x0000 @@ -217,7 +217,7 @@ OP_CorpseDrop=0x0000 OP_Bug=0x0000 OP_Feedback=0x0000 OP_Report=0x0000 -OP_Damage=0x0000 +OP_Damage=0x7d07 OP_ChannelMessage=0x6adc OP_Assist=0x0000 OP_AssistGroup=0x0000 @@ -233,7 +233,7 @@ OP_Rewind=0x0000 OP_TargetCommand=0x3b18 OP_Hide=0x0000 OP_Jump=0x0000 -OP_Camp=0x0000 +OP_Camp=0x326f OP_Emote=0x0000 OP_SetRunMode=0x0000 OP_BankerChange=0x0000 @@ -250,8 +250,8 @@ OP_XTargetOpenResponse=0x0000 OP_BuffCreate=0x0000 #0x27a1 OP_BuffRemoveRequest=0x0000 OP_DeleteSpawn=0x7712 -OP_AutoAttack=0x0000 -OP_AutoAttack2=0x0000 +OP_AutoAttack=0x3f03 +OP_AutoAttack2=0x1c31 OP_Consume=0x0000 OP_MoveItem=0x0000 OP_MoveMultipleItems=0x0000 From af8f85cfd9418b61795bbcaaa52def7d6d6185d5 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 2 Dec 2024 22:41:20 -0800 Subject: [PATCH 57/93] -Basic move item support. -Update some Laurion limits to better support new item slots. -Camping on Laurion will now work like it does on live. -Fixed a few of the Laurion exp messages (this will need some work). Packet calc is still all fked up. --- common/patches/laurion.cpp | 30 +++++++++++++++++++++++++++++- common/patches/laurion_limits.h | 11 +++++++---- common/patches/laurion_ops.h | 2 ++ common/patches/laurion_structs.h | 16 ++++++++++++++++ utils/patches/patch_Laurion.conf | 2 +- zone/client_packet.cpp | 22 ++++++++++++++++------ zone/exp.cpp | 21 ++++++++++++++++++--- 7 files changed, 89 insertions(+), 15 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 3bdc4b78b..94bd8f74d 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -1178,7 +1178,7 @@ namespace Laurion //u32 claim_count; out.WriteUInt32(0); - //Claim claims[claim_count]; + //Claim claims[claim_count]; //Tribute tribute; /* @@ -2657,6 +2657,20 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + Log(Logs::Detail, Logs::Netcode, "Laurion::ENCODE(OP_MoveItem)"); + + eq->from_slot = ServerToLaurionSlot(emu->from_slot); + eq->to_slot = ServerToLaurionSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -2831,6 +2845,20 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + Log(Logs::Detail, Logs::Netcode, "Laurion::DECODE(OP_MoveItem)"); + + emu->from_slot = LaurionToServerSlot(eq->from_slot); + emu->to_slot = LaurionToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; diff --git a/common/patches/laurion_limits.h b/common/patches/laurion_limits.h index f5035c06c..ab4eba11e 100644 --- a/common/patches/laurion_limits.h +++ b/common/patches/laurion_limits.h @@ -170,6 +170,8 @@ namespace Laurion slotGeneral8, slotGeneral9, slotGeneral10, + slotGeneral11, + slotGeneral12, slotCursor }; @@ -188,6 +190,7 @@ namespace Laurion const int16 EQUIPMENT_END = slotAmmo; const int16 EQUIPMENT_COUNT = (EQUIPMENT_END - EQUIPMENT_BEGIN) + 1; + //We support more if enabled but for now lets leave it at the 10 slots const int16 GENERAL_BEGIN = slotGeneral1; const int16 GENERAL_END = slotGeneral10; const int16 GENERAL_COUNT = (GENERAL_END - GENERAL_BEGIN) + 1; @@ -200,10 +203,10 @@ namespace Laurion const int16 CORPSE_END = invslot::slotGeneral1 + invslot::slotCursor; const uint64 EQUIPMENT_BITMASK = 0x00000000007FFFFF; - const uint64 GENERAL_BITMASK = 0x00000001FF800000; - const uint64 CURSOR_BITMASK = 0x0000000200000000; - const uint64 POSSESSIONS_BITMASK = (EQUIPMENT_BITMASK | GENERAL_BITMASK | CURSOR_BITMASK); // based on 34-slot count (RoF+) - const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 34)); // based on 34-slot count (RoF+) + const uint64 GENERAL_BITMASK = 0x00000007FF800000; + const uint64 CURSOR_BITMASK = 0x0000000800000000; + const uint64 POSSESSIONS_BITMASK = (EQUIPMENT_BITMASK | GENERAL_BITMASK | CURSOR_BITMASK); // based on 36-slot count (Laurion+) + const uint64 CORPSE_BITMASK = (GENERAL_BITMASK | CURSOR_BITMASK | (EQUIPMENT_BITMASK << 36)); // based on 36-slot count (Laurion+) const char* GetInvPossessionsSlotName(int16 inv_slot); diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 177b7bcf6..b1950bc7f 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -30,6 +30,7 @@ E(OP_HPUpdate) E(OP_Damage) E(OP_Animation) E(OP_Death) +E(OP_MoveItem) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) @@ -42,6 +43,7 @@ D(OP_Consider) D(OP_ConsiderCorpse) D(OP_ClickDoor) D(OP_SpawnAppearance) +D(OP_MoveItem) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 0d3eeb4bf..acc9ea78e 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -587,6 +587,22 @@ namespace Laurion { /*040*/ }; + struct DeleteItem_Struct + { + /*0000*/ InventorySlot_Struct from_slot; + /*0012*/ InventorySlot_Struct to_slot; + /*0024*/ uint32 number_in_stack; + /*0028*/ + }; + + struct MoveItem_Struct + { + /*0000*/ InventorySlot_Struct from_slot; + /*0012*/ InventorySlot_Struct to_slot; + /*0024*/ uint32 number_in_stack; + /*0028*/ + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index e5e7042ea..83d02608a 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -228,7 +228,7 @@ OP_WhoAllRequest=0x0000 OP_WhoAllResponse=0x0000 OP_FriendsWho=0x0000 OP_ConfirmDelete=0x0000 -OP_Logout=0x0000 +OP_Logout=0x771d OP_Rewind=0x0000 OP_TargetCommand=0x3b18 OP_Hide=0x0000 diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index dbd438295..fa6244829 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4317,13 +4317,23 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app) if (IsLFP()) worldserver.StopLFP(CharacterID()); - if (GetGM()) - { - OnDisconnect(true); - return; + if (ClientVersion() >= EQ::versions::ClientVersion::Laurion) { + if (!GetGM()) { + camp_timer.Start(29000, true); + } + + auto outapp = new EQApplicationPacket(OP_Camp, 1); + FastQueuePacket(&outapp); + } + else { + if (GetGM()) + { + OnDisconnect(true); + return; + } + + camp_timer.Start(29000, true); } - camp_timer.Start(29000, true); - return; } void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) diff --git a/zone/exp.cpp b/zone/exp.cpp index be6b8af2b..3fe8094db 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -657,7 +657,12 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool } else if (zone->IsHotzone()) { Message(Chat::Experience, "You gain party experience (with a bonus)!"); } else { - MessageString(Chat::Experience, GAIN_GROUPXP); + if (m_ClientVersion >= EQ::versions::ClientVersion::Laurion) { + MessageString(Chat::Experience, GAIN_GROUPXP, exp_percent_message.c_str()); + } + else { + MessageString(Chat::Experience, GAIN_GROUPXP); + } } } else if (IsRaidGrouped()) { if (RuleI(Character, ShowExpValues) > 0) { @@ -665,7 +670,12 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool } else if (zone->IsHotzone()) { Message(Chat::Experience, "You gained raid experience (with a bonus)!"); } else { - MessageString(Chat::Experience, GAIN_RAIDEXP); + if (m_ClientVersion >= EQ::versions::ClientVersion::Laurion) { + MessageString(Chat::Experience, GAIN_RAIDEXP, exp_percent_message.c_str()); + } + else { + MessageString(Chat::Experience, GAIN_RAIDEXP); + } } } else { if (RuleI(Character, ShowExpValues) > 0) { @@ -673,7 +683,12 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool } else if (zone->IsHotzone()) { Message(Chat::Experience, "You gain experience (with a bonus)!"); } else { - MessageString(Chat::Experience, GAIN_XP); + if (m_ClientVersion >= EQ::versions::ClientVersion::Laurion) { + MessageString(Chat::Experience, GAIN_XP, exp_percent_message.c_str()); + } + else { + MessageString(Chat::Experience, GAIN_XP); + } } } } From 32a1d7c43e35ff39ece7dae8357193ce41a5e263 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 2 Dec 2024 23:36:13 -0800 Subject: [PATCH 58/93] -Exp update almost works, but inital at zonein is broken -MoveItem has opcode now but is not translating correctly. --- common/patches/laurion.cpp | 11 +++++++++++ common/patches/laurion_ops.h | 1 + utils/patches/patch_Laurion.conf | 2 +- zone/client.cpp | 10 +--------- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 94bd8f74d..d340bd598 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2671,6 +2671,17 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_ExpUpdate) + { + SETUP_DIRECT_ENCODE(ExpUpdate_Struct, structs::ExpUpdate_Struct); + + //later we should change the underlying server to use this more accurate value + //and encode the 330 in the other patches + eq->exp = emu->exp * 100000 / 330; + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index b1950bc7f..4946bfcc2 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -31,6 +31,7 @@ E(OP_Damage) E(OP_Animation) E(OP_Death) E(OP_MoveItem) +E(OP_ExpUpdate) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 83d02608a..b22a189b4 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -253,7 +253,7 @@ OP_DeleteSpawn=0x7712 OP_AutoAttack=0x3f03 OP_AutoAttack2=0x1c31 OP_Consume=0x0000 -OP_MoveItem=0x0000 +OP_MoveItem=0x11e3 OP_MoveMultipleItems=0x0000 OP_DeleteItem=0x0000 OP_DeleteCharge=0x0000 diff --git a/zone/client.cpp b/zone/client.cpp index 9ab53c593..0b6d20b04 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -542,15 +542,7 @@ void Client::SendZoneInPackets() if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - - //Laurion uses a more granular exp bar than other clients - if (m_ClientVersion >= EQ::versions::ClientVersion::Laurion) { - eu->exp = (uint32)(100000.0f * tmpxp); - } - else { - eu->exp = (uint32)(330.0f * tmpxp); - } - + eu->exp = (uint32)(330.0f * tmpxp); FastQueuePacket(&outapp); } else { From 352f52e65d717294def181d7658ff417a119b2c9 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 3 Dec 2024 19:51:37 -0800 Subject: [PATCH 59/93] -Add function for converting laurion links (non functional) -Fix move items on cursor -Fill out more of the filters even if we don't use them atm --- common/eq_constants.h | 40 ++++++++++++++++++++++++++++++++++++++ common/patches/laurion.cpp | 39 +++++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/common/eq_constants.h b/common/eq_constants.h index 44beffd25..e199eaf0d 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -762,6 +762,46 @@ typedef enum { FilterStrikethrough = 26, //0=show, 1=hide // RoF2 Confirmed FilterStuns = 27, //0=show, 1=hide // RoF2 Confirmed FilterBardSongsOnPets = 28, //0=show, 1=hide // RoF2 Confirmed + FilterSwarmPetDeath = 29, + FilterFellowshipChat = 30, + FilterMercenaryMessages = 31, + FilterSpam = 32, + FilterAchievements = 33, + FilterPvPMessages = 34, + FilterSpellNameInCast = 35, + FilterRandomMine = 36, + FilterRandomGroupRaid = 37, + FilterRandomOthers = 38, + FilterEnvironmentalDamage = 39, + FilterMessages = 40, + FilterOverwriteDetrimental = 41, + FilterOverwriteBeneficial = 42, + FilterCantUseCommand = 43, + FilterCombatAbilityReuse = 44, + FilterAAAbilityReuse = 45, + FilterProcBeginCasting = 46, + FilterDestroyedItems = 47, + FilterYourAuras = 48, + FilterOtherAuras = 49, + FilterYourHeals = 50, + FilterOtherHeals = 51, + FilterYourDoTs = 52, + FilterOtherDoTs = 53, + FilterOtherDirectDamage = 54, + FilterSpellEmotes = 55, + FilterFactionMessages = 56, + FilterTauntMessages = 57, + FilterYourDisciplines = 58, + FilterOtherDisplines = 59, + FilterAchievementsOthers = 60, + FilterRaidVictory = 61, + FilterOtherDirectDamageCrits = 62, + FilterDoTYoursCritical = 63, + FilterDoTOthersCritical = 64, + FilterDoTDamageTaken = 65, + FilterHealsReceived = 66, + FilterHealsYoursCritical = 67, + FilterHealsOthersCritical = 68, _FilterCount } eqFilterType; diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index d340bd598..88b7a3d82 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -51,6 +51,10 @@ namespace Laurion void SerializeItem(SerializeBuffer &buffer, const EQ::ItemInstance* inst, int16 slot_id, uint8 depth, ItemPacketType packet_type); + // message link converters + static inline void ServerToLaurionConvertLinks(std::string& message_out, const std::string& message_in); + static inline void LaurionToServerConvertLinks(std::string& message_out, const std::string& message_in); + //SpawnAppearance static inline uint32 ServerToLaurionSpawnAppearanceType(uint32 server_type); static inline uint32 LaurionToServerSpawnAppearanceType(uint32 laurion_type); @@ -2464,8 +2468,8 @@ namespace Laurion unsigned char* __emu_buffer = in->pBuffer; std::string old_message = emu->message; - std::string new_message = old_message; - //ServerToRoF2SayLink(new_message, old_message); + std::string new_message; + ServerToLaurionConvertLinks(new_message, old_message); in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 43; @@ -2520,8 +2524,7 @@ namespace Laurion in->ReadString(old_message); - //ServerToRoF2SayLink(new_message, old_message); - new_message = old_message; + ServerToLaurionConvertLinks(new_message, old_message); buf.WriteString(new_message); @@ -2567,7 +2570,9 @@ namespace Laurion buffer.WriteUInt32(emu->type); for (int i = 0; i < 9; ++i) { - buffer.WriteLengthString(old_message_array[i]); + std::string new_message; + ServerToLaurionConvertLinks(new_message, old_message_array[i]); + buffer.WriteLengthString(new_message); } auto outapp = new EQApplicationPacket(OP_FormattedMessage, buffer.size()); @@ -2785,8 +2790,8 @@ namespace Laurion uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); std::string old_message = InBuffer; - std::string new_message = old_message; - //RoF2ToServerSayLink(new_message, old_message); + std::string new_message; + LaurionToServerConvertLinks(new_message, old_message); __packet->size = sizeof(ChannelMessage_Struct) + new_message.length() + 1; __packet->pBuffer = new unsigned char[__packet->size]; @@ -3648,6 +3653,15 @@ namespace Laurion buffer.WriteInt32(0); //unsupported atm } + static inline void ServerToLaurionConvertLinks(std::string& message_out, const std::string& message_in) + { + message_out = message_in; + } + + static inline void LaurionToServerConvertLinks(std::string& message_out, const std::string& message_in) { + message_out = message_in; + } + static inline uint32 ServerToLaurionSpawnAppearanceType(uint32 server_type) { switch (server_type) { @@ -3941,7 +3955,16 @@ namespace Laurion case invtype::typePossessions: { if (laurion_slot.Slot >= invslot::POSSESSIONS_BEGIN && laurion_slot.Slot <= invslot::POSSESSIONS_END) { if (laurion_slot.SubIndex == invbag::SLOT_INVALID) { - server_slot = laurion_slot.Slot; + if (laurion_slot.Slot == invslot::slotCursor) { + server_slot = EQ::invslot::slotCursor; + } + else if(laurion_slot.Slot == invslot::slotGeneral11 || laurion_slot.Slot == invslot::slotGeneral12) + { + return EQ::invslot::SLOT_INVALID; + } + else { + server_slot = laurion_slot.Slot; + } } else if (laurion_slot.SubIndex >= invbag::SLOT_BEGIN && laurion_slot.SubIndex <= invbag::SLOT_END) { From 2c48ec39efff89dc8b9ea1511445ea2ec89a6416 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 3 Dec 2024 20:37:44 -0800 Subject: [PATCH 60/93] Impl SendAATable --- common/patches/laurion.cpp | 163 +++++++++++++++++++++++++++++++ common/patches/laurion_ops.h | 1 + utils/patches/patch_Laurion.conf | 2 +- 3 files changed, 165 insertions(+), 1 deletion(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 88b7a3d82..f93aca8e8 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2687,6 +2687,169 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_SendAATable) + { + EQApplicationPacket* in = *p; + *p = nullptr; + AARankInfo_Struct* emu = (AARankInfo_Struct*)in->pBuffer; + + std::vector skill; + std::vector points; + in->SetReadPosition(sizeof(AARankInfo_Struct) + emu->total_effects * sizeof(AARankEffect_Struct)); + for (auto i = 0; i < emu->total_prereqs; ++i) { + skill.push_back(in->ReadUInt32()); + points.push_back(in->ReadUInt32()); + } + + SerializeBuffer buffer; + + /* + s32 AbilityId; + u8 ShowInAbilityWindow; + s32 ShortName; + s32 ShortName2; + s32 Name; + s32 Desc; + */ + + buffer.WriteUInt32(emu->id); + buffer.WriteUInt8(1); + buffer.WriteInt32(emu->upper_hotkey_sid); + buffer.WriteInt32(emu->lower_hotkey_sid); + buffer.WriteInt32(emu->title_sid); + buffer.WriteInt32(emu->desc_sid); + + /* + s32 MinLevel; + s32 Cost; + s32 GroupID; + s32 CurrentRank; + */ + buffer.WriteInt32(emu->level_req); + buffer.WriteInt32(emu->cost); + buffer.WriteUInt32(emu->seq); + buffer.WriteUInt32(emu->current_level); + + /* + u32 PrereqSkillCount; + s32 PrereqSkills[PrereqSkillCount]; + u32 PrereqLevelCount; + s32 PrereqLevels[PrereqLevelCount]; + */ + + if (emu->total_prereqs) { + buffer.WriteUInt32(emu->total_prereqs); + for (auto& e : skill) + buffer.WriteInt32(e); + buffer.WriteUInt32(emu->total_prereqs); + for (auto& e : points) + buffer.WriteInt32(e); + } + else { + buffer.WriteUInt32(1); + buffer.WriteUInt32(0); + buffer.WriteUInt32(1); + buffer.WriteUInt32(0); + } + + /* + u32 Type; + s32 SpellId; + */ + buffer.WriteInt32(emu->type); + buffer.WriteInt32(emu->spell); + + /* + u32 TimerIdCount; + s32 TimerIds[TimerIdCount]; + s32 ReuseTimer; + u32 Classes; + */ + buffer.WriteInt32(1); + buffer.WriteInt32(emu->spell_type); + buffer.WriteInt32(emu->spell_refresh); + buffer.WriteInt32(emu->classes); + + /* + s32 MaxRank; + s32 PrevAbilityId; + s32 NextAbilityId; + s32 TotalPoints; + */ + + buffer.WriteInt32(emu->max_level); + buffer.WriteInt32(emu->prev_id); + buffer.WriteInt32(emu->next_id); + buffer.WriteInt32(emu->total_cost); + + /* + u8 bRefund; + s32 QuestOnly; + u8 bIgnoreDeLevel; + */ + buffer.WriteUInt8(0); + buffer.WriteUInt32(emu->grant_only); + buffer.WriteUInt8(0); + + /* + s32 Charges; + s32 Expansion; + s32 Category; + */ + buffer.WriteUInt32(emu->charges); + buffer.WriteInt32(emu->expansion); + buffer.WriteInt32(emu->category); + + /* + u8 bShroud; + u8 bBetaOnly; + u8 bResetOnDeath; + u8 AutoGrant; + */ + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + + /* + s32 AutoGrantExpansion; + s32 Unknown098; + u8 Unknown09C; + */ + + buffer.WriteInt32(emu->expansion); + buffer.WriteInt32(0); + buffer.WriteUInt8(0); + + //u32 TotalEffects; + buffer.WriteUInt32(emu->total_effects); + in->SetReadPosition(sizeof(AARankInfo_Struct)); + for (auto i = 0; i < emu->total_effects; ++i) { + auto skill_id = in->ReadUInt32(); + auto base1 = in->ReadUInt32(); + auto base2 = in->ReadUInt32(); + auto slot = in->ReadUInt32(); + + /* + u32 effect_id; + s64 base1; + s64 base2; + u32 slot; + */ + + buffer.WriteUInt32(skill_id); + buffer.WriteInt64(base1); + buffer.WriteInt64(base2); + buffer.WriteUInt32(slot); + } + + auto outapp = new EQApplicationPacket(OP_SendAATable, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + + delete in; + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 4946bfcc2..003c95834 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -32,6 +32,7 @@ E(OP_Animation) E(OP_Death) E(OP_MoveItem) E(OP_ExpUpdate) +E(OP_SendAATable) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index b22a189b4..4c63c7d31 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -94,7 +94,7 @@ OP_RemoveBlockedBuffs=0x0000 OP_ClearBlockedBuffs=0x0000 OP_WorldObjectsSent=0x2879 OP_SendExpZonein=0x02b4 -OP_SendAATable=0x0000 +OP_SendAATable=0x5f30 OP_ClearAA=0x3498 OP_ClearLeadershipAbilities=0x0000 #removed; leadership abilities are baked in and always on OP_RespondAA=0x0000 From c4a7fcc063159c0159f7618d0cf67166cde5205e Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 4 Dec 2024 21:07:50 -0800 Subject: [PATCH 61/93] -Added several opcodes; not all confirmed working yet. -Partial support for item packet (several of the types have been shuffled so this will take a while to get working). --- common/patches/laurion.cpp | 63 +++++++++++++++++++++++++- common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 34 +++++++++++++- utils/patches/patch_Laurion.conf | 77 ++++++++++---------------------- 4 files changed, 119 insertions(+), 56 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index f93aca8e8..b7ebebf4e 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -71,6 +71,8 @@ namespace Laurion static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot); static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type); + structs::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); + void Register(EQStreamIdentifier& into) { //create our opcode manager if we havent already @@ -2850,6 +2852,39 @@ namespace Laurion delete in; } + ENCODE(OP_ItemPacket) + { + EQApplicationPacket* in = *p; + *p = nullptr; + uchar* __emu_buffer = in->pBuffer; + ItemPacket_Struct* old_item_pkt = (ItemPacket_Struct*)__emu_buffer; + + switch (old_item_pkt->PacketType) + { + case ItemPacketParcel: { + //parcels are significantly changed so we will need to figure this out + } + default: { + EQ::InternalSerializedItem_Struct* int_struct = (EQ::InternalSerializedItem_Struct*)(&__emu_buffer[4]); + + auto type = ServerToLaurionItemPacketType(old_item_pkt->PacketType); + if (type == structs::ItemPacketInvalid) { + break; + } + + SerializeBuffer buffer; + buffer.WriteInt32((int32_t)type); + SerializeItem(buffer, (const EQ::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); + + auto outapp = new EQApplicationPacket(OP_ItemPacket, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + } + } + + delete in; + } + // DECODE methods DECODE(OP_EnterWorld) @@ -3969,7 +4004,14 @@ namespace Laurion if (server_slot < EQ::invtype::POSSESSIONS_SIZE) { LaurionSlot.Type = invtype::typePossessions; - LaurionSlot.Slot = server_slot; + + if (server_slot == EQ::invslot::slotCursor) { + LaurionSlot.Slot = invslot::slotCursor; + } + else + { + LaurionSlot.Slot = server_slot; + } } else if (server_slot <= EQ::invbag::CURSOR_BAG_END && server_slot >= EQ::invbag::GENERAL_BAGS_BEGIN) { @@ -4387,4 +4429,23 @@ namespace Laurion return ServerSlot; } + + structs::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { + switch (server_type) { + case ItemPacketType::ItemPacketMerchant: + return structs::ItemPacketType::ItemPacketMerchant; + case ItemPacketType::ItemPacketTradeView: + return structs::ItemPacketType::ItemPacketTradeView; + case ItemPacketType::ItemPacketLoot: + return structs::ItemPacketType::ItemPacketLoot; + case ItemPacketType::ItemPacketTrade: + return structs::ItemPacketType::ItemPacketTrade; + case ItemPacketType::ItemPacketCharInventory: + return structs::ItemPacketType::ItemPacketCharInventory; + case ItemPacketType::ItemPacketLimbo: + return structs::ItemPacketType::ItemPacketLimbo; + default: + return structs::ItemPacketType::ItemPacketInvalid; + } + } } /*Laurion*/ diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 003c95834..3f6f31cef 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -33,6 +33,7 @@ E(OP_Death) E(OP_MoveItem) E(OP_ExpUpdate) E(OP_SendAATable) +E(OP_ItemPacket) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index acc9ea78e..69dc4c387 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -522,7 +522,6 @@ namespace Laurion { /*08*/ int32 unknown2; /*12*/ uint8 doorid; /*13*/ uint8 padding2[3]; - }; /* @@ -603,6 +602,39 @@ namespace Laurion { /*0028*/ }; + //These are significantly changed in Laurion from RoF2 + enum ItemPacketType { + //ItemPacketViewLink = 0x00, + ItemPacketMerchant = 0x64, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x6A, //Rof 0x69 -> Larion 0x6a (requires translation) + ItemPacketLimbo = 0x6B, //0x6A -> 0x6B + //ItemPacketWorldContainer = 0x6B, //These aren't found yet + //ItemPacketTributeItem = 0x6C, + //ItemPacketGuildTribute = 0x6D, + //ItemPacketCharmUpdate = 0x6E, // Larion has a specific packet for this + //ItemPacketRecovery = 0x71, + //ItemPacketParcel = 0x73, + ItemPacketInvalid = 0xFF + }; + + /* + enum ItemPacketType + { + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B + }; + */ + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 4c63c7d31..12ad33297 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -43,7 +43,7 @@ OP_SetChatServer2=0x2726 OP_ZoneServerInfo=0x2273 OP_WorldComplete=0x195c OP_WorldUnknown001=0x2049 -OP_FloatListThing=0x0000 +OP_FloatListThing=0x66fd # Reasons for Disconnect: OP_ZoneUnavail=0x582d @@ -231,8 +231,8 @@ OP_ConfirmDelete=0x0000 OP_Logout=0x771d OP_Rewind=0x0000 OP_TargetCommand=0x3b18 -OP_Hide=0x0000 -OP_Jump=0x0000 +OP_Hide=0x1cdf +OP_Jump=0x6fa0 OP_Camp=0x326f OP_Emote=0x0000 OP_SetRunMode=0x0000 @@ -254,10 +254,10 @@ OP_AutoAttack=0x3f03 OP_AutoAttack2=0x1c31 OP_Consume=0x0000 OP_MoveItem=0x11e3 -OP_MoveMultipleItems=0x0000 +OP_MoveMultipleItems=0x5205 OP_DeleteItem=0x0000 OP_DeleteCharge=0x0000 -OP_ItemPacket=0x0000 +OP_ItemPacket=0x7d43 OP_ItemLinkResponse=0x0000 OP_ItemLinkClick=0x0000 OP_ItemPreview=0x0000 @@ -280,7 +280,7 @@ OP_RezzAnswer=0x0000 OP_Shielding=0x0000 OP_RequestDuel=0x0000 OP_MobRename=0x0000 -OP_AugmentItem=0x0000 +OP_AugmentItem=0x3a1b OP_WeaponEquip1=0x0000 OP_PlayerStateAdd=0x0000 OP_PlayerStateRemove=0x0000 @@ -569,22 +569,21 @@ OP_MarkRaidNPC=0x0000 OP_RaidClearNPCMarks=0x0000 # Button-push commands -OP_Taunt=0x0000 -OP_CombatAbility=0x0000 -OP_SenseTraps=0x0000 -OP_PickPocket=0x0000 -OP_DisarmTraps=0x0000 -OP_Disarm=0x0000 -OP_Sneak=0x0000 -OP_Fishing=0x0000 -OP_InstillDoubt=0x0000 -OP_FeignDeath=0x0000 -OP_Mend=0x0000 -OP_Bind_Wound=0x0000 -OP_LDoNOpen=0x0000 -#OP_LDoNDisarmTraps= #Same as OP_DisarmTraps in RoF -OP_LDoNPickLock=0x0000 -OP_LDoNInspect=0x0000 +OP_Taunt=0x5064 +OP_CombatAbility=0xbf +OP_SenseTraps=0x579c +OP_PickPocket=0x53d1 +OP_DisarmTraps=0x21bf +OP_Disarm=0x31e9 +OP_Sneak=0x78a7 +OP_Fishing=0x57cc +OP_InstillDoubt=0x57cc +OP_FeignDeath=0x14b8 +OP_Mend=0x6b8 +OP_Bind_Wound=0x650e +OP_LDoNOpen=0x448 +OP_LDoNPickLock=0x61c8 +OP_LDoNInspect=0xc1c # Task packets OP_TaskDescription=0x0000 @@ -693,38 +692,8 @@ OP_PetitionRefresh=0x0000 OP_PetitionCheckout2=0x0000 OP_PetitionViewPetition=0x0000 -# Login opcodes -OP_SessionReady=0x0000 -OP_Login=0x0000 -OP_ServerListRequest=0x0000 -OP_PlayEverquestRequest=0x0000 -OP_PlayEverquestResponse=0x0000 -OP_ChatMessage=0x0000 -OP_LoginAccepted=0x0000 -OP_ServerListResponse=0x0000 -OP_Poll=0x0000 -OP_EnterChat=0x0000 -OP_PollResponse=0x0000 - -# raw opcodes -OP_RAWSessionRequest=0x0000 -OP_RAWSessionResponse=0x0000 -OP_RAWCombined=0x0000 -OP_RAWSessionDisconnect=0x0000 -OP_RAWKeepAlive=0x0000 -OP_RAWSessionStatRequest=0x0000 -OP_RAWSessionStatResponse=0x0000 -OP_RAWPacket=0x0000 -OP_RAWFragment=0x0000 -OP_RAWOutOfOrderAck=0x0000 -OP_RAWAck=0x0000 -OP_RAWAppCombined=0x0000 -OP_RAWOutOfSession=0x0000 - -# we need to document the differences between these packets to make identifying them easier -OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs -OP_InitialHPUpdate=0x0000 - #aura related OP_UpdateAura=0x0000 OP_RemoveTrap=0x0000 + +OP_Fingerprint=0x7a5b \ No newline at end of file From 2db1b1b9b0f66ea785a50cd18143b79a1567a075 Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 4 Dec 2024 22:28:07 -0800 Subject: [PATCH 62/93] More work on item packet --- common/patches/laurion.cpp | 47 ++++++++++++++++++++++++++------ common/patches/laurion_structs.h | 34 +++++++++-------------- 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index b7ebebf4e..1905ee880 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2859,19 +2859,40 @@ namespace Laurion uchar* __emu_buffer = in->pBuffer; ItemPacket_Struct* old_item_pkt = (ItemPacket_Struct*)__emu_buffer; - switch (old_item_pkt->PacketType) + auto type = ServerToLaurionItemPacketType(old_item_pkt->PacketType); + if (type == structs::ItemPacketInvalid) { + delete in; + return; + } + + switch (type) { - case ItemPacketParcel: { - //parcels are significantly changed so we will need to figure this out + case structs::ItemPacketType::ItemPacketParcel: { + ParcelMessaging_Struct pms{}; + EQ::Util::MemoryStreamReader ss(reinterpret_cast(in->pBuffer), in->size); + cereal::BinaryInputArchive ar(ss); + ar(pms); + + uint32 player_name_length = pms.player_name.length(); + uint32 note_length = pms.note.length(); + + auto* int_struct = (EQ::InternalSerializedItem_Struct*)pms.serialized_item.data(); + + SerializeBuffer buffer; + buffer.WriteInt32((int32_t)type); + SerializeItem(buffer, (const EQ::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); + + buffer.WriteUInt32(pms.sent_time); + buffer.WriteLengthString(pms.player_name); + buffer.WriteLengthString(pms.note); + + auto outapp = new EQApplicationPacket(OP_ItemPacket, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + break; } default: { EQ::InternalSerializedItem_Struct* int_struct = (EQ::InternalSerializedItem_Struct*)(&__emu_buffer[4]); - - auto type = ServerToLaurionItemPacketType(old_item_pkt->PacketType); - if (type == structs::ItemPacketInvalid) { - break; - } - SerializeBuffer buffer; buffer.WriteInt32((int32_t)type); SerializeItem(buffer, (const EQ::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); @@ -4444,6 +4465,14 @@ namespace Laurion return structs::ItemPacketType::ItemPacketCharInventory; case ItemPacketType::ItemPacketLimbo: return structs::ItemPacketType::ItemPacketLimbo; + case ItemPacketType::ItemPacketWorldContainer: + return structs::ItemPacketType::ItemPacketWorldContainer; + case ItemPacketType::ItemPacketTributeItem: + return structs::ItemPacketType::ItemPacketTributeItem; + case ItemPacketType::ItemPacketGuildTribute: + return structs::ItemPacketType::ItemPacketGuildTribute; + case ItemPacketType::ItemPacketCharmUpdate: + return structs::ItemPacketType::ItemPacketCharmUpdate; default: return structs::ItemPacketType::ItemPacketInvalid; } diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 69dc4c387..5546c4a57 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -609,32 +609,24 @@ namespace Laurion { ItemPacketTradeView = 0x65, ItemPacketLoot = 0x66, ItemPacketTrade = 0x67, + //looks like they added something at 0x68 that didn't exist before and shifted everything after it up by 1 + ItemPacketUnknown068 = 0x68, //Not sure but it seems to deal with the cursor somehow. ItemPacketCharInventory = 0x6A, //Rof 0x69 -> Larion 0x6a (requires translation) ItemPacketLimbo = 0x6B, //0x6A -> 0x6B - //ItemPacketWorldContainer = 0x6B, //These aren't found yet - //ItemPacketTributeItem = 0x6C, - //ItemPacketGuildTribute = 0x6D, - //ItemPacketCharmUpdate = 0x6E, // Larion has a specific packet for this - //ItemPacketRecovery = 0x71, - //ItemPacketParcel = 0x73, + ItemPacketWorldContainer = 0x6C, + ItemPacketTributeItem = 0x6D, + ItemPacketGuildTribute = 0x6E, + ItemPacketCharmUpdate = 0x6f, + ItemPacketRecovery = 0x72, + ItemPacketParcel = 0x74, + ItemPacketUnknown075 = 0x75, //Not sure but uses a lot of the same logic as the trade and char inventory types + ItemPacketOverflow = 0x76, + ItemPacketDragonHoard = 0x77, + ItemPacketTradeskill = 0x78, + ItemPacketTradeskillDepot = 0x79, ItemPacketInvalid = 0xFF }; - /* - enum ItemPacketType - { - ItemPacketViewLink = 0x00, - ItemPacketTradeView = 0x65, - ItemPacketLoot = 0x66, - ItemPacketTrade = 0x67, - ItemPacketCharInventory = 0x69, - ItemPacketSummonItem = 0x6A, - ItemPacketTributeItem = 0x6C, - ItemPacketMerchant = 0x64, - ItemPacketWorldContainer = 0x6B - }; - */ - #pragma pack() }; //end namespace structs From d3ac751dd1610ea5098f48886af323e4cb5b7939 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 5 Dec 2024 19:39:59 -0800 Subject: [PATCH 63/93] Merchant window startup --- common/classes.h | 3 + common/emu_oplist.h | 1 + common/patches/laurion.cpp | 104 ++++++++++++++++++++----------- common/patches/laurion_limits.h | 34 +++++----- common/patches/laurion_ops.h | 2 + common/patches/laurion_structs.h | 40 +++++------- common/patches/rof2.cpp | 30 +++++++++ common/patches/rof2_ops.h | 2 + utils/patches/patch_Laurion.conf | 6 +- zone/client_packet.cpp | 5 +- 10 files changed, 149 insertions(+), 78 deletions(-) diff --git a/common/classes.h b/common/classes.h index 96e5cad26..7371ccaa3 100644 --- a/common/classes.h +++ b/common/classes.h @@ -71,6 +71,9 @@ namespace Class { constexpr uint8 FellowshipMaster = 69; constexpr uint8 AlternateCurrencyMerchant = 70; constexpr uint8 MercenaryLiaison = 71; + constexpr uint8 RealEstateMerchant = 72; + constexpr uint8 LoyaltyMerchant = 73; + constexpr uint8 TributeMaster2 = 74; constexpr uint8 PLAYER_CLASS_COUNT = 16; constexpr uint16 ALL_CLASSES_BITMASK = 65535; diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 236def1f8..f71d0d916 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -354,6 +354,7 @@ N(OP_MercenaryTimer), N(OP_MercenaryTimerRequest), N(OP_MercenaryUnknown1), N(OP_MercenaryUnsuspendResponse), +N(OP_MerchantBulkItems), N(OP_MobEnduranceUpdate), N(OP_MobHealth), N(OP_MobManaUpdate), diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 1905ee880..0eb217637 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -71,7 +71,7 @@ namespace Laurion static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot); static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type); - structs::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); + item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); void Register(EQStreamIdentifier& into) { @@ -246,35 +246,35 @@ namespace Laurion eq->entries[0] = -1; // Max AA Restriction eq->entries[1] = -1; // Max Level Restriction eq->entries[2] = -1; // Max Char Slots per Account (not used by client?) - eq->entries[3] = -1; // 1 for Silver + eq->entries[3] = -1; // SpellTier eq->entries[4] = -1; // Main Inventory Size eq->entries[5] = -1; // Max Platinum per level eq->entries[6] = 1; // Send Mail - eq->entries[7] = 1; // Use Parcels? - eq->entries[8] = 1; // Voice Chat + eq->entries[7] = 1; // Use Parcels + eq->entries[8] = 1; // Loyalty eq->entries[9] = -1; // Merc Tiers - eq->entries[10] = 1; // Create Guilds + eq->entries[10] = 1; // Housing eq->entries[11] = -1; // Shared Bank Slots eq->entries[12] = -1; // Max Journal Quests - eq->entries[13] = 1; // Housing Enabled - eq->entries[14] = 1; // Prestiege - eq->entries[15] = 1; // Broker System + eq->entries[13] = 1; // CreateGuild + eq->entries[14] = 1; // Bazaar + eq->entries[15] = 1; // Barter eq->entries[16] = 1; // Chat - eq->entries[17] = 1; // Progression Server Access - eq->entries[18] = 1; // Customer Support - eq->entries[19] = -1; // Popup reminders? - eq->entries[20] = -1; // Exit Popup? - eq->entries[21] = 0; - eq->entries[22] = 0; - eq->entries[23] = 0; // This is the highest we actually see in detail entries - eq->entries[24] = 0; - eq->entries[25] = 0; - eq->entries[26] = 0; - eq->entries[27] = 0; - eq->entries[28] = 0; - eq->entries[29] = 0; - eq->entries[30] = 0; - eq->entries[31] = 0; + eq->entries[17] = 1; // Petition + eq->entries[18] = 1; // Advertising + eq->entries[19] = -1; // UseItem + eq->entries[20] = -1; // StartingCity + eq->entries[21] = 1; // Ornament + eq->entries[22] = 0; // HeroicCharacter + eq->entries[23] = 0; // AutoGrantAA + eq->entries[24] = 0; // MountKeyRingSlots + eq->entries[25] = 0; // IllusionKeyRingSlots + eq->entries[26] = 0; // FamiliarKeyRingSlots + eq->entries[27] = 0; // FamiliarAutoLeave + eq->entries[28] = 0; // HeroForgeKeyRingSlots + eq->entries[29] = 0; // DragonHoardSlots + eq->entries[30] = 0; // TeleportKeyRingSlots + eq->entries[31] = 0; // PersonalDepotSlots eq->entries[32] = 0; FINISH_ENCODE(); @@ -2860,14 +2860,14 @@ namespace Laurion ItemPacket_Struct* old_item_pkt = (ItemPacket_Struct*)__emu_buffer; auto type = ServerToLaurionItemPacketType(old_item_pkt->PacketType); - if (type == structs::ItemPacketInvalid) { + if (type == item::ItemPacketType::ItemPacketInvalid) { delete in; return; } switch (type) { - case structs::ItemPacketType::ItemPacketParcel: { + case item::ItemPacketType::ItemPacketParcel: { ParcelMessaging_Struct pms{}; EQ::Util::MemoryStreamReader ss(reinterpret_cast(in->pBuffer), in->size); cereal::BinaryInputArchive ar(ss); @@ -2906,6 +2906,26 @@ namespace Laurion delete in; } + ENCODE(OP_ShopRequest) + { + ENCODE_LENGTH_EXACT(MerchantClick_Struct); + SETUP_DIRECT_ENCODE(MerchantClick_Struct, structs::MerchantClickResponse_Struct); + + if (emu->command == 0) { + OUT(player_id); + eq->npc_id = 0; + } + else { + OUT(npc_id); + OUT(player_id); + OUT(rate); + OUT(tab_display); + eq->unknown028 = 256; + } + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -3094,6 +3114,16 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_ShopRequest) + { + DECODE_LENGTH_EXACT(structs::MerchantClickRequest_Struct); + SETUP_DIRECT_DECODE(MerchantClick_Struct, structs::MerchantClickRequest_Struct); + + IN(npc_id); + + FINISH_DIRECT_DECODE(); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; @@ -4451,30 +4481,30 @@ namespace Laurion return ServerSlot; } - structs::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { + item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { switch (server_type) { case ItemPacketType::ItemPacketMerchant: - return structs::ItemPacketType::ItemPacketMerchant; + return item::ItemPacketType::ItemPacketMerchant; case ItemPacketType::ItemPacketTradeView: - return structs::ItemPacketType::ItemPacketTradeView; + return item::ItemPacketType::ItemPacketTradeView; case ItemPacketType::ItemPacketLoot: - return structs::ItemPacketType::ItemPacketLoot; + return item::ItemPacketType::ItemPacketLoot; case ItemPacketType::ItemPacketTrade: - return structs::ItemPacketType::ItemPacketTrade; + return item::ItemPacketType::ItemPacketTrade; case ItemPacketType::ItemPacketCharInventory: - return structs::ItemPacketType::ItemPacketCharInventory; + return item::ItemPacketType::ItemPacketCharInventory; case ItemPacketType::ItemPacketLimbo: - return structs::ItemPacketType::ItemPacketLimbo; + return item::ItemPacketType::ItemPacketLimbo; case ItemPacketType::ItemPacketWorldContainer: - return structs::ItemPacketType::ItemPacketWorldContainer; + return item::ItemPacketType::ItemPacketWorldContainer; case ItemPacketType::ItemPacketTributeItem: - return structs::ItemPacketType::ItemPacketTributeItem; + return item::ItemPacketType::ItemPacketTributeItem; case ItemPacketType::ItemPacketGuildTribute: - return structs::ItemPacketType::ItemPacketGuildTribute; + return item::ItemPacketType::ItemPacketGuildTribute; case ItemPacketType::ItemPacketCharmUpdate: - return structs::ItemPacketType::ItemPacketCharmUpdate; + return item::ItemPacketType::ItemPacketCharmUpdate; default: - return structs::ItemPacketType::ItemPacketInvalid; + return item::ItemPacketType::ItemPacketInvalid; } } } /*Laurion*/ diff --git a/common/patches/laurion_limits.h b/common/patches/laurion_limits.h index ab4eba11e..587844ce2 100644 --- a/common/patches/laurion_limits.h +++ b/common/patches/laurion_limits.h @@ -249,20 +249,26 @@ namespace Laurion //}; enum ItemPacketType : int { - ItemPacketMerchant = 100, - ItemPacketTradeView = 101, - ItemPacketLoot = 102, - ItemPacketTrade = 103, - ItemPacketCharInventory = 105, - ItemPacketLimbo = 106, - ItemPacketWorldContainer = 107, - ItemPacketTributeItem = 108, - ItemPacketGuildTribute = 109, - ItemPacket10 = 110, - ItemPacket11 = 111, - ItemPacket12 = 112, - ItemPacketRecovery = 113, - ItemPacket14 = 115 // Parcel? adds to merchant window too + ItemPacketMerchant = 0x64, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + //looks like they added something at 0x68 that didn't exist before and shifted everything after it up by 1 + ItemPacketUnknown068 = 0x68, //Not sure but it seems to deal with the cursor somehow. + ItemPacketCharInventory = 0x6A, //Rof 0x69 -> Larion 0x6a (requires translation) + ItemPacketLimbo = 0x6B, //0x6A -> 0x6B + ItemPacketWorldContainer = 0x6C, + ItemPacketTributeItem = 0x6D, + ItemPacketGuildTribute = 0x6E, + ItemPacketCharmUpdate = 0x6f, + ItemPacketRecovery = 0x72, + ItemPacketParcel = 0x74, + ItemPacketUnknown075 = 0x75, //Not sure but uses a lot of the same logic as the trade and char inventory types + ItemPacketOverflow = 0x76, + ItemPacketDragonHoard = 0x77, + ItemPacketTradeskill = 0x78, + ItemPacketTradeskillDepot = 0x79, + ItemPacketInvalid = 0xFF }; } /*item*/ diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 3f6f31cef..2db186c25 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -34,6 +34,7 @@ E(OP_MoveItem) E(OP_ExpUpdate) E(OP_SendAATable) E(OP_ItemPacket) +E(OP_ShopRequest) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) @@ -47,6 +48,7 @@ D(OP_ConsiderCorpse) D(OP_ClickDoor) D(OP_SpawnAppearance) D(OP_MoveItem) +D(OP_ShopRequest) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 5546c4a57..c3c3e9794 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -602,29 +602,23 @@ namespace Laurion { /*0028*/ }; - //These are significantly changed in Laurion from RoF2 - enum ItemPacketType { - //ItemPacketViewLink = 0x00, - ItemPacketMerchant = 0x64, - ItemPacketTradeView = 0x65, - ItemPacketLoot = 0x66, - ItemPacketTrade = 0x67, - //looks like they added something at 0x68 that didn't exist before and shifted everything after it up by 1 - ItemPacketUnknown068 = 0x68, //Not sure but it seems to deal with the cursor somehow. - ItemPacketCharInventory = 0x6A, //Rof 0x69 -> Larion 0x6a (requires translation) - ItemPacketLimbo = 0x6B, //0x6A -> 0x6B - ItemPacketWorldContainer = 0x6C, - ItemPacketTributeItem = 0x6D, - ItemPacketGuildTribute = 0x6E, - ItemPacketCharmUpdate = 0x6f, - ItemPacketRecovery = 0x72, - ItemPacketParcel = 0x74, - ItemPacketUnknown075 = 0x75, //Not sure but uses a lot of the same logic as the trade and char inventory types - ItemPacketOverflow = 0x76, - ItemPacketDragonHoard = 0x77, - ItemPacketTradeskill = 0x78, - ItemPacketTradeskillDepot = 0x79, - ItemPacketInvalid = 0xFF + struct MerchantClickRequest_Struct + { + /*000*/ uint32 npc_id; // Merchant NPC's entity id + /*004*/ + }; + + struct MerchantClickResponse_Struct + { + /*000*/ uint32 npc_id; // Merchant NPC's entity id + /*004*/ uint32 player_id; + /*008*/ float rate; + /*012*/ uint32 tab_display; // bitmask b000 none, b001 Purchase/Sell, b010 Recover, b100 Parcels + /*016*/ uint32 ldon_category; // ldon cat for ldon merchants + /*020*/ uint32 alt_currency1; //These two usually match but I imagine they could be different? + /*024*/ uint32 alt_currency2; + /*028*/ uint32 unknown028; // Observed 256 + /*032*/ }; #pragma pack() diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 29c52f9ef..32ef8d5dd 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -3602,6 +3602,21 @@ namespace RoF2 FINISH_ENCODE(); } + + ENCODE(OP_ShopRequest) + { + ENCODE_LENGTH_EXACT(MerchantClick_Struct); + SETUP_DIRECT_ENCODE(MerchantClick_Struct, structs::MerchantClick_Struct); + + OUT(npc_id); + OUT(player_id); + OUT(command); + OUT(rate); + OUT(tab_display); + eq->unknown02 = emu->unknown020; + + FINISH_ENCODE(); + } ENCODE(OP_SkillUpdate) { @@ -5993,6 +6008,21 @@ namespace RoF2 FINISH_DIRECT_DECODE(); } + DECODE(OP_ShopRequest) + { + DECODE_LENGTH_EXACT(structs::MerchantClick_Struct); + SETUP_DIRECT_DECODE(MerchantClick_Struct, structs::MerchantClick_Struct); + + IN(npc_id); + IN(player_id); + IN(command); + IN(rate); + IN(tab_display); + emu->unknown020 = 0; + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_Save) { DECODE_LENGTH_EXACT(structs::Save_Struct); diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 08d8b9985..7c59851f6 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -118,6 +118,7 @@ E(OP_SendZonepoints) E(OP_SetGuildRank) E(OP_ShopPlayerBuy) E(OP_ShopPlayerSell) +E(OP_ShopRequest) E(OP_SkillUpdate) E(OP_SomeItemPacketMaybe) E(OP_SpawnAppearance) @@ -203,6 +204,7 @@ D(OP_Save) D(OP_SetServerFilter) D(OP_ShopPlayerBuy) D(OP_ShopPlayerSell) +D(OP_ShopRequest) D(OP_ShopSendParcel) D(OP_Trader) D(OP_TraderBuy) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 12ad33297..944338be1 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -464,9 +464,9 @@ OP_ItemAdvancedLoreText=0x0000 # merchant stuff OP_ShopPlayerSell=0x0000 -OP_ShopRequest=0x0000 -OP_ShopEnd=0x0000 -OP_ShopEndConfirm=0x0000 +OP_ShopRequest=0x840 +OP_ShopEnd=0x74bb +OP_ShopEndConfirm=0x2ed1 OP_ShopPlayerBuy=0x0000 OP_ShopDelItem=0x0000 OP_ShopSendParcel=0x0000 diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fa6244829..fe4091572 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14639,7 +14639,10 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) mco->rate = 1 / buy_cost_mod; } - outapp->priority = 6; + if (m_ClientVersion >= EQ::versions::ClientVersion::Laurion) { + mco->player_id = GetID(); + } + QueuePacket(outapp); safe_delete(outapp); From 7083a74b31162e747e08acfb23cdc801c5a0d464 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 7 Dec 2024 13:45:27 -0800 Subject: [PATCH 64/93] WIP spell casting for laurion --- common/patches/laurion.cpp | 239 ++++++++++++++++++++++++++++++- common/patches/laurion_limits.h | 4 +- common/patches/laurion_ops.h | 4 + common/patches/laurion_structs.h | 59 ++++++++ 4 files changed, 299 insertions(+), 7 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 0eb217637..ba9ecb2c9 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -55,7 +55,7 @@ namespace Laurion static inline void ServerToLaurionConvertLinks(std::string& message_out, const std::string& message_in); static inline void LaurionToServerConvertLinks(std::string& message_out, const std::string& message_in); - //SpawnAppearance + // SpawnAppearance static inline uint32 ServerToLaurionSpawnAppearanceType(uint32 server_type); static inline uint32 LaurionToServerSpawnAppearanceType(uint32 laurion_type); @@ -71,7 +71,16 @@ namespace Laurion static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot); static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type); - item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); + // Item packet types + static item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); + + // casting slots + static inline spells::CastingSlot ServerToLaurionCastingSlot(EQ::spells::CastingSlot slot); + static inline EQ::spells::CastingSlot LaurionToServerCastingSlot(spells::CastingSlot slot); + + // buff slots + static inline int ServerToLaurionBuffSlot(int index); + static inline int LaurionToServerBuffSlot(int index); void Register(EQStreamIdentifier& into) { @@ -1163,7 +1172,8 @@ namespace Laurion u8 status; */ - out.WriteUInt64(0); + out.WriteInt32(emu->guild_id); + out.WriteUInt32(0); out.WriteUInt8(0); out.WriteUInt8(5); @@ -1262,7 +1272,7 @@ namespace Laurion out.WriteUInt32(emu->careerEbonCrystals); /* - u32 momentum_balance; + u32 momentum_balance; u32 loyalty_reward_balance; u32 parcel_status; */ @@ -2926,6 +2936,104 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_BeginCast) + { + SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); + + OUT(spell_id); + OUT(caster_id); + OUT(cast_time); + eq->unknown0e = 1; //not sure what this is; but its usually 1 on live + + FINISH_ENCODE(); + } + + ENCODE(OP_BuffCreate) + { + SETUP_VAR_ENCODE(BuffIcon_Struct); + + //Laurion has one extra 0x00 byte before the end byte + uint32 sz = 13 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); + + __packet->WriteUInt32(emu->entity_id); + __packet->WriteUInt32(emu->tic_timer); + __packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff) + __packet->WriteUInt16(emu->count); + + for (int i = 0; i < emu->count; ++i) + { + __packet->WriteUInt32(emu->type == 0 ? ServerToLaurionBuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot); + __packet->WriteUInt32(emu->entries[i].spell_id); + __packet->WriteUInt32(emu->entries[i].tics_remaining); + __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown + __packet->WriteString(emu->entries[i].caster); + } + __packet->WriteUInt8(0); // Unknown1 + __packet->WriteUInt8(emu->type); // Unknown2 + + FINISH_ENCODE(); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct); + SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::EQAffectPacket_Struct); + + eq->entity_id = emu->entityid; + eq->unknown004 = 0; + + //fill in affect info + eq->affect.caster_id.Id = emu->buff.player_id; + eq->affect.flags = 0; + eq->affect.spell_id = emu->buff.spellid; + eq->affect.duration = emu->buff.duration; + eq->affect.initial_duration = emu->buff.duration; + eq->affect.hit_count = emu->buff.num_hits; + eq->affect.viral_timer = 0; + eq->affect.modifier = emu->buff.bard_modifier == 10 ? 1.0f : emu->buff.bard_modifier / 10.0f; + eq->affect.y = emu->buff.y; + eq->affect.x = emu->buff.x; + eq->affect.z = emu->buff.z; + eq->affect.level = emu->buff.level; + + eq->slot_id = ServerToLaurionBuffSlot(emu->slotid); + if (emu->bufffade == 1) + { + eq->buff_fade = 1; + } + else + { + eq->buff_fade = 2; + } + + EQApplicationPacket* outapp = nullptr; + if (emu->bufffade == 1) + { + // Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon + outapp = new EQApplicationPacket(OP_BuffCreate, 30); + outapp->WriteUInt32(emu->entityid); + outapp->WriteUInt32(0); // tic timer + outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ? + outapp->WriteUInt16(1); // 1 buff in this packet + outapp->WriteUInt32(ServerToLaurionBuffSlot(emu->slotid)); + outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove) + outapp->WriteUInt32(0); // Duration + outapp->WriteUInt32(0); // numhits + outapp->WriteUInt8(0); // Caster name + outapp->WriteUInt8(0); // Type + outapp->WriteUInt8(0); // Type + } + + FINISH_ENCODE(); + + if (outapp) { + dest->FastQueuePacket(&outapp); + } + } + // DECODE methods DECODE(OP_EnterWorld) @@ -3124,6 +3232,23 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + emu->slot = static_cast(LaurionToServerCastingSlot(static_cast(eq->slot))); + + //We need to figure out the x y z position stuff + IN(spell_id); + emu->inventoryslot = LaurionToServerSlot(eq->inventory_slot); + IN(target_id); + //IN(y_pos); + //IN(x_pos); + //IN(z_pos); + FINISH_DIRECT_DECODE(); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; @@ -4481,7 +4606,7 @@ namespace Laurion return ServerSlot; } - item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { + static item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { switch (server_type) { case ItemPacketType::ItemPacketMerchant: return item::ItemPacketType::ItemPacketMerchant; @@ -4507,4 +4632,108 @@ namespace Laurion return item::ItemPacketType::ItemPacketInvalid; } } + + //This stuff isn't right because they for one removed potion belt + //This will probably be enough to get casting working for now though + static inline spells::CastingSlot ServerToLaurionCastingSlot(EQ::spells::CastingSlot slot) { + switch (slot) { + case EQ::spells::CastingSlot::Gem1: + return spells::CastingSlot::Gem1; + case EQ::spells::CastingSlot::Gem2: + return spells::CastingSlot::Gem2; + case EQ::spells::CastingSlot::Gem3: + return spells::CastingSlot::Gem3; + case EQ::spells::CastingSlot::Gem4: + return spells::CastingSlot::Gem4; + case EQ::spells::CastingSlot::Gem5: + return spells::CastingSlot::Gem5; + case EQ::spells::CastingSlot::Gem6: + return spells::CastingSlot::Gem6; + case EQ::spells::CastingSlot::Gem7: + return spells::CastingSlot::Gem7; + case EQ::spells::CastingSlot::Gem8: + return spells::CastingSlot::Gem8; + case EQ::spells::CastingSlot::Gem9: + return spells::CastingSlot::Gem9; + case EQ::spells::CastingSlot::Gem10: + return spells::CastingSlot::Gem10; + case EQ::spells::CastingSlot::Gem11: + return spells::CastingSlot::Gem11; + case EQ::spells::CastingSlot::Gem12: + return spells::CastingSlot::Gem12; + case EQ::spells::CastingSlot::Item: + case EQ::spells::CastingSlot::PotionBelt: + return spells::CastingSlot::Item; + case EQ::spells::CastingSlot::Discipline: + return spells::CastingSlot::Discipline; + case EQ::spells::CastingSlot::AltAbility: + return spells::CastingSlot::AltAbility; + default: // we shouldn't have any issues with other slots ... just return something + return spells::CastingSlot::Discipline; + } + } + + static inline EQ::spells::CastingSlot LaurionToServerCastingSlot(spells::CastingSlot slot) { + switch (slot) { + case spells::CastingSlot::Gem1: + return EQ::spells::CastingSlot::Gem1; + case spells::CastingSlot::Gem2: + return EQ::spells::CastingSlot::Gem2; + case spells::CastingSlot::Gem3: + return EQ::spells::CastingSlot::Gem3; + case spells::CastingSlot::Gem4: + return EQ::spells::CastingSlot::Gem4; + case spells::CastingSlot::Gem5: + return EQ::spells::CastingSlot::Gem5; + case spells::CastingSlot::Gem6: + return EQ::spells::CastingSlot::Gem6; + case spells::CastingSlot::Gem7: + return EQ::spells::CastingSlot::Gem7; + case spells::CastingSlot::Gem8: + return EQ::spells::CastingSlot::Gem8; + case spells::CastingSlot::Gem9: + return EQ::spells::CastingSlot::Gem9; + case spells::CastingSlot::Gem10: + return EQ::spells::CastingSlot::Gem10; + case spells::CastingSlot::Gem11: + return EQ::spells::CastingSlot::Gem11; + case spells::CastingSlot::Gem12: + return EQ::spells::CastingSlot::Gem12; + case spells::CastingSlot::Discipline: + return EQ::spells::CastingSlot::Discipline; + case spells::CastingSlot::Item: + return EQ::spells::CastingSlot::Item; + case spells::CastingSlot::AltAbility: + return EQ::spells::CastingSlot::AltAbility; + default: // we shouldn't have any issues with other slots ... just return something + return EQ::spells::CastingSlot::Discipline; + } + } + + //Laurion has the same # of long buffs as rof2, but 10 more short buffs + static inline int ServerToLaurionBuffSlot(int index) + { + // we're a disc + if (index >= EQ::spells::LONG_BUFFS + EQ::spells::SHORT_BUFFS) + return index - EQ::spells::LONG_BUFFS - EQ::spells::SHORT_BUFFS + + spells::LONG_BUFFS + spells::SHORT_BUFFS; + // we're a song + if (index >= EQ::spells::LONG_BUFFS) + return index - EQ::spells::LONG_BUFFS + spells::LONG_BUFFS; + // we're a normal buff + return index; // as long as we guard against bad slots server side, we should be fine + } + + static inline int LaurionToServerBuffSlot(int index) + { + // we're a disc + if (index >= spells::LONG_BUFFS + spells::SHORT_BUFFS) + return index - spells::LONG_BUFFS - spells::SHORT_BUFFS + EQ::spells::LONG_BUFFS + + EQ::spells::SHORT_BUFFS; + // we're a song + if (index >= spells::LONG_BUFFS) + return index - spells::LONG_BUFFS + EQ::spells::LONG_BUFFS; + // we're a normal buff + return index; // as long as we guard against bad slots server side, we should be fine + } } /*Laurion*/ diff --git a/common/patches/laurion_limits.h b/common/patches/laurion_limits.h index 587844ce2..a0fc1be4e 100644 --- a/common/patches/laurion_limits.h +++ b/common/patches/laurion_limits.h @@ -341,10 +341,10 @@ namespace Laurion const int SPELL_GEM_RECAST_TIMER = 15; const int LONG_BUFFS = 42; - const int SHORT_BUFFS = 20; + const int SHORT_BUFFS = 30; const int DISC_BUFFS = 1; const int TOTAL_BUFFS = LONG_BUFFS + SHORT_BUFFS + DISC_BUFFS; - const int NPC_BUFFS = 97; + const int NPC_BUFFS = 400; const int PET_BUFFS = NPC_BUFFS; const int MERC_BUFFS = LONG_BUFFS; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 2db186c25..04dbe7ba0 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -35,6 +35,9 @@ E(OP_ExpUpdate) E(OP_SendAATable) E(OP_ItemPacket) E(OP_ShopRequest) +E(OP_BeginCast) +E(OP_BuffCreate) +E(OP_Buff) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) @@ -49,6 +52,7 @@ D(OP_ClickDoor) D(OP_SpawnAppearance) D(OP_MoveItem) D(OP_ShopRequest) +D(OP_CastSpell) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index c3c3e9794..39530e101 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -621,6 +621,65 @@ namespace Laurion { /*032*/ }; + struct BeginCast_Struct + { + /*000*/ uint32 spell_id; + /*004*/ uint16 caster_id; + /*006*/ uint32 cast_time; // in miliseconds + /*010*/ uint32 unknown0a; // I think this is caster effective level but im not sure. live always sends 0 + /*014*/ uint8 unknown0e; // 0 will short circuit the cast, seen 1 from live usually, maybe related to interrupts or particles or something + /*015*/ + }; + + struct CastSpell_Struct + { + /*00*/ uint32 slot; + /*04*/ uint32 spell_id; + /*08*/ InventorySlot_Struct inventory_slot; // slot for clicky item, Seen unknown of 131 = normal cast + /*20*/ uint32 spell_crc; // this is pinstSpellManager_x->SpellsCrc[spell_id] + /*24*/ uint32 target_id; + /*28*/ uint8 unknown2[11]; + /*39*/ + }; + + struct EQAffectSlot_Struct { + /*00*/ int32 slot; + /*04*/ int32 padding; + /*08*/ int64 value; + /*16*/ + }; + + struct EQAffect_Struct + { + /*000*/ EQAffectSlot_Struct slots[6]; + /*096*/ EqGuid caster_id; + /*104*/ uint32 flags; + /*108*/ uint32 spell_id; + /*112*/ uint32 duration; + /*116*/ uint32 initial_duration; + /*120*/ uint32 hit_count; + /*124*/ uint32 viral_timer; + /*128*/ float modifier; + /*132*/ float y; + /*136*/ float x; + /*140*/ float z; + /*144*/ uint8 level; + /*145*/ uint8 type; + /*146*/ uint8 charges; //no idea if these are right; eqlib doesn't seem to know either + /*147*/ uint8 activatable; + /*148*/ uint32 unknown1; //might be some timer, not sure though + /*152*/ + }; + + struct EQAffectPacket_Struct { + /*000*/ uint32 entity_id; + /*004*/ int32 unknown004; + /*008*/ EQAffect_Struct affect; + /*160*/ uint32 slot_id; + /*164*/ uint32 buff_fade; + /*168*/ + }; + #pragma pack() }; //end namespace structs From 13f57f3c5dde95d837d6efaa33e435570739b427 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 7 Dec 2024 19:42:40 -0800 Subject: [PATCH 65/93] WIP; buff doesn't seem to full work; need to fix op_action --- common/patches/laurion.cpp | 25 +++++++++++++++++++------ common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 32 ++++++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index ba9ecb2c9..f51c4f051 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2938,6 +2938,7 @@ namespace Laurion ENCODE(OP_BeginCast) { + ENCODE_LENGTH_EXACT(BeginCast_Struct); SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); OUT(spell_id); @@ -3034,6 +3035,19 @@ namespace Laurion } } + ENCODE(OP_ManaChange) { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + OUT(keepcasting); + OUT(slot); + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -3238,14 +3252,13 @@ namespace Laurion SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); emu->slot = static_cast(LaurionToServerCastingSlot(static_cast(eq->slot))); - - //We need to figure out the x y z position stuff + IN(spell_id); - emu->inventoryslot = LaurionToServerSlot(eq->inventory_slot); + emu->inventoryslot = -1; IN(target_id); - //IN(y_pos); - //IN(x_pos); - //IN(z_pos); + IN(y_pos); + IN(x_pos); + IN(z_pos); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 04dbe7ba0..979b8f673 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -38,6 +38,7 @@ E(OP_ShopRequest) E(OP_BeginCast) E(OP_BuffCreate) E(OP_Buff) +E(OP_ManaChange) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 39530e101..0f1d1c00d 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -631,14 +631,29 @@ namespace Laurion { /*015*/ }; + //I've observed 5 s16 that are all -1. + //Clicky items don't even trigger this as far as i can tell so not sure what this is for now. + //One of these could have changed to a s32 but im not sure. + struct CastSpellInventorySlot_Struct { + /*00*/ int16 type; + /*02*/ int16 slot; + /*04*/ int16 sub_index; + /*06*/ int16 aug_index; + /*08*/ int16 unknown1; + /*10*/ + }; + struct CastSpell_Struct { /*00*/ uint32 slot; /*04*/ uint32 spell_id; - /*08*/ InventorySlot_Struct inventory_slot; // slot for clicky item, Seen unknown of 131 = normal cast - /*20*/ uint32 spell_crc; // this is pinstSpellManager_x->SpellsCrc[spell_id] - /*24*/ uint32 target_id; - /*28*/ uint8 unknown2[11]; + /*08*/ CastSpellInventorySlot_Struct inventory_slot; + /*18*/ uint32 target_id; + /*22*/ uint32 spell_crc; + /*26*/ float y_pos; + /*30*/ float x_pos; + /*34*/ float z_pos; + /*38*/ uint8 unknown; //not sure, might also be before y_pos; only ever seen zero for both but should be easy to figure out later /*39*/ }; @@ -680,6 +695,15 @@ namespace Laurion { /*168*/ }; + struct ManaChange_Struct + { + uint32 new_mana; + uint32 stamina; + uint32 spell_id; + uint32 keepcasting; + int32 slot; + }; + #pragma pack() }; //end namespace structs From ce98118cc8eada38423c24899622b14ec97d0288 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 8 Dec 2024 22:56:34 -0800 Subject: [PATCH 66/93] Basic spellcasting works a bit. --- common/patches/laurion.cpp | 23 +++++++++++++++++++++++ common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 29 +++++++++++++++++++++++++++++ utils/patches/patch_Laurion.conf | 12 ++++++------ 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index f51c4f051..fbd13c71c 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3048,6 +3048,29 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_Action) { + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::MissileHitInfo); + + //This is mostly figured out; there's two unknowns, only unknown1 is read by the client + OUT(target); + OUT(source); + eq->spell_id = emu->spell; + eq->effect_type = emu->effect_flag; + eq->effective_casting_level = 0; //if you set this to != 0 it will use this level instead of calculating it + eq->unknown1 = 0; + eq->unknown2 = 0; + eq->damage = 0; //client doesn't read this but live sends it here, can just set 0 + eq->modifier = 1.0f + (emu->instrument_mod - 10) / 10.0f; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); + eq->skill = emu->type; + OUT(level); + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 979b8f673..39319461b 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -39,6 +39,7 @@ E(OP_BeginCast) E(OP_BuffCreate) E(OP_Buff) E(OP_ManaChange) +E(OP_Action) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 0f1d1c00d..de05afdb9 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -704,6 +704,35 @@ namespace Laurion { int32 slot; }; + //This is what we call OP_Action + //To the client though this is basically a missile hit though + //OP_Action is basically "instant missile hit" to the client + //@0x1401f0970 MissileHitInfo::Deserialize(CUnSerializeBuffer *buffer); + struct MissileHitInfo + { + uint16 target; + uint16 source; + uint32 spell_id; + //4 leaves a buff + uint32 effect_type; + uint32 effective_casting_level; + //Client does read this but only does something if it's negative + int64 unknown1; + //I don't see the client read this one outside basic serialization + int64 unknown2; + //I don't see the client read this one either but based on captures from live it seems to match spell damage + int64 damage; + float modifier; + float force; + float hit_heading; + float hit_pitch; + //same convention as damage + //231 for spell, otherwise it's skill in use + uint8 skill; + uint8 level; //the client doesn't actually deserialize anything past level + //live however has a lot more info here depending on packet type + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 944338be1..425e714c9 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -103,7 +103,7 @@ OP_SendAAStats=0x0000 OP_AAExpUpdate=0x0000 #0x642f OP_ExpUpdate=0x611d OP_HPUpdate=0x775c -OP_ManaChange=0x0606 +OP_ManaChange=0x700f OP_TGB=0x0000 #removed; tgb is baked in and always on OP_SpecialMesg=0x7d93 OP_GuildMemberList=0x0000 @@ -173,17 +173,17 @@ OP_QueryUCSServerStatus=0x0000 OP_InspectRequest=0x0000 OP_InspectAnswer=0x0000 OP_InspectMessageUpdate=0x0000 -OP_BeginCast=0x0000 +OP_BeginCast=0x31f9 OP_ColoredText=0x0000 OP_ConsentResponse=0x0000 OP_MemorizeSpell=0x0000 OP_LinkedReuse=0x0000 OP_SwapSpell=0x0000 -OP_CastSpell=0x0000 +OP_CastSpell=0x325b OP_Consider=0x53e3 OP_FormattedMessage=0x7f7f OP_SimpleMessage=0x1943 -OP_Buff=0x0000 #0x6ce5 +OP_Buff=0x6ce5 OP_Illusion=0x0000 OP_MoneyOnCorpse=0x0000 OP_RandomReply=0x0000 @@ -209,7 +209,7 @@ OP_ClearSurname=0x0000 OP_FaceChange=0x0000 OP_SetFace=0x0000 OP_SenseHeading=0x0000 -OP_Action=0x0000 +OP_Action=0x4c13 OP_ConsiderCorpse=0x6092 OP_HideCorpse=0x0000 OP_CorpseDrag=0x0000 @@ -247,7 +247,7 @@ OP_XTargetRequest=0x0000 OP_XTargetAutoAddHaters=0x0000 OP_XTargetOpen=0x0000 OP_XTargetOpenResponse=0x0000 -OP_BuffCreate=0x0000 #0x27a1 +OP_BuffCreate=0x27a1 OP_BuffRemoveRequest=0x0000 OP_DeleteSpawn=0x7712 OP_AutoAttack=0x3f03 From b761f1cdf9bd28b87aa725b7b74e211366d6b72c Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 9 Dec 2024 22:56:11 -0800 Subject: [PATCH 67/93] -Updated pp buff doc -Support for Server->Laurion item links. -Laurion->Server item links still do not work right. --- common/patches/laurion.cpp | 116 +++++++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 12 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index fbd13c71c..a2256158f 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -899,19 +899,24 @@ namespace Laurion out.WriteUInt32(42); //PackedEQAffect buffs[buff_count]; + //todo: fix for (int i = 0; i < 42; ++i) { /* - float modifier; - u32 caster; - u32 flags; - u32 duration; - u32 max_duration; - u8 level; - s32 spell_id; - s32 hitcount; - u8 unknown1; - u32 unknown2; - u32 unknown3; + struct EQAffect + { + float modifier; + EqGuid caster; + u32 duration; + u32 max_duration; + u8 level; + s32 spell_id; + s32 hitcount; + u32 flags; + u32 viral_timer; + u8 type; + SlotData slots[6]; + }; + */ out.WriteFloat(1.0f); out.WriteUInt32(0); @@ -4065,7 +4070,94 @@ namespace Laurion static inline void ServerToLaurionConvertLinks(std::string& message_out, const std::string& message_in) { - message_out = message_in; + if (message_in.find('\x12') == std::string::npos) { + message_out = message_in; + return; + } + + auto segments = Strings::Split(message_in, '\x12'); + for (size_t segment_iter = 0; segment_iter < segments.size(); ++segment_iter) { + if (segment_iter & 1) { + auto etag = std::stoi(segments[segment_iter].substr(0, 1)); + + switch (etag) { + case 0: + { + size_t index = 1; + auto item_id = segments[segment_iter].substr(index, 5); + index += 5; + + auto aug1 = segments[segment_iter].substr(index, 5); + index += 5; + + auto aug2 = segments[segment_iter].substr(index, 5); + index += 5; + + auto aug3 = segments[segment_iter].substr(index, 5); + index += 5; + + auto aug4 = segments[segment_iter].substr(index, 5); + index += 5; + + auto aug5 = segments[segment_iter].substr(index, 5); + index += 5; + + auto aug6 = segments[segment_iter].substr(index, 5); + index += 5; + + auto is_evolving = segments[segment_iter].substr(index, 1); + index += 1; + + auto evolutionGroup = segments[segment_iter].substr(index, 4); + index += 4; + + auto evolutionLevel = segments[segment_iter].substr(index, 2); + index += 2; + + auto ornamentationIconID = segments[segment_iter].substr(index, 5); + index += 5; + + auto itemHash = segments[segment_iter].substr(index, 8); + index += 8; + + auto text = segments[segment_iter].substr(index); + + message_out.push_back('\x12'); + message_out.push_back('0'); //etag item + message_out.append(item_id); + message_out.append(aug1); + message_out.append("00000"); + message_out.append(aug2); + message_out.append("00000"); + message_out.append(aug3); + message_out.append("00000"); + message_out.append(aug4); + message_out.append("00000"); + message_out.append(aug5); + message_out.append("00000"); + message_out.append(aug6); + message_out.append("00000"); + message_out.append(is_evolving); + message_out.append(evolutionGroup); + message_out.append(evolutionLevel); + message_out.append(ornamentationIconID); + message_out.append("00000"); + message_out.append(itemHash); + message_out.append(text); + message_out.push_back('\x12'); + + break; + } + default: + //unsupported etag right now; just pass it as is + message_out.append(segments[segment_iter]); + break; + } + } + else { + message_out.append(segments[segment_iter]); + } + } } static inline void LaurionToServerConvertLinks(std::string& message_out, const std::string& message_in) { From 0da381c27285b2b99a3d89b95035eedcc32b04d0 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 12 Dec 2024 19:56:13 -0800 Subject: [PATCH 68/93] Add OP_MobHealth encode. --- common/patches/laurion.cpp | 10 ++++++++++ common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index a2256158f..491396b7d 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3076,6 +3076,16 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_MobHealth) { + ENCODE_LENGTH_EXACT(SpawnHPUpdate_Struct2); + SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct2, structs::MobHealth_Struct); + + OUT(spawn_id); + OUT(hp); + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 39319461b..b2d8d8f30 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -40,6 +40,7 @@ E(OP_BuffCreate) E(OP_Buff) E(OP_ManaChange) E(OP_Action) +E(OP_MobHealth) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index de05afdb9..294c5aace 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -733,6 +733,12 @@ namespace Laurion { //live however has a lot more info here depending on packet type }; + struct MobHealth_Struct + { + /*01*/ int16 spawn_id; + /*00*/ uint32 hp; + }; + #pragma pack() }; //end namespace structs From 0fdfda9c53d05c23d6ff4ab59106a1308244d913 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 12 Dec 2024 20:02:26 -0800 Subject: [PATCH 69/93] updated patch file too --- utils/patches/patch_Laurion.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 425e714c9..c4ae8eebf 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -238,7 +238,7 @@ OP_Emote=0x0000 OP_SetRunMode=0x0000 OP_BankerChange=0x0000 OP_TargetMouse=0x5741 -OP_MobHealth=0x0000 +OP_MobHealth=0x5b77 OP_InitialMobHealth=0x0000 # Unused? OP_TargetHoTT=0x0000 OP_TargetBuffs=0x0000 From 6d5424e7b0379c085d9642e0ba51083a510477a3 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 12 Dec 2024 20:38:37 -0800 Subject: [PATCH 70/93] Update a few opcodes we don't check contents of. --- utils/patches/patch_Laurion.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index c4ae8eebf..b9d852dfe 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -169,7 +169,7 @@ OP_GMNameChange=0x0000 OP_GMLastName=0x0000 # Misc Opcodes -OP_QueryUCSServerStatus=0x0000 +OP_QueryUCSServerStatus=0x2570 OP_InspectRequest=0x0000 OP_InspectAnswer=0x0000 OP_InspectMessageUpdate=0x0000 @@ -285,7 +285,7 @@ OP_WeaponEquip1=0x0000 OP_PlayerStateAdd=0x0000 OP_PlayerStateRemove=0x0000 OP_ApplyPoison=0x0000 -OP_Save=0x0000 +OP_Save=0x6da2 OP_TestBuff=0x0000 OP_CustomTitles=0x0000 OP_Split=0x0000 @@ -295,7 +295,7 @@ OP_Bandolier=0x0000 OP_PotionBelt=0x0000 OP_DuelDecline=0x0000 OP_DuelAccept=0x0000 -OP_SaveOnZoneReq=0x0000 +OP_SaveOnZoneReq=0x3bfe OP_ReadBook=0x0000 OP_Dye=0x0000 OP_InterruptCast=0x0000 From ef5fdafdbee2a73d7ed95729b15e53c90bf0e057 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 12 Dec 2024 21:03:41 -0800 Subject: [PATCH 71/93] GM training request decode. --- common/patches/laurion.cpp | 15 +++++++++++++++ common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 9 +++++++++ 3 files changed, 25 insertions(+) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 491396b7d..495de99ca 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3300,6 +3300,21 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_GMTraining) + { + DECODE_LENGTH_EXACT(structs::GMTrainee_Struct); + SETUP_DIRECT_DECODE(GMTrainee_Struct, structs::GMTrainee_Struct); + + OUT(npcid); + OUT(playerid); + + for (int i = 0; i < 100; ++i) { + OUT(skills[i]); + } + + FINISH_DIRECT_DECODE(); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index b2d8d8f30..2938774dd 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -56,6 +56,7 @@ D(OP_SpawnAppearance) D(OP_MoveItem) D(OP_ShopRequest) D(OP_CastSpell) +D(OP_GMTraining) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 294c5aace..2e4364be9 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -739,6 +739,15 @@ namespace Laurion { /*00*/ uint32 hp; }; + struct GMTrainee_Struct + { + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[PACKET_SKILL_ARRAY_SIZE]; + /*408*/ uint8 unknown408[36]; + /*444*/ + }; + #pragma pack() }; //end namespace structs From 6070e73b162b378bc6188dd31aab52fd3e4b161f Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 12 Dec 2024 22:54:09 -0800 Subject: [PATCH 72/93] GM training wip --- common/patches/laurion.cpp | 57 ++++++++++++++++++++++++++++++-- common/patches/laurion_ops.h | 1 + utils/patches/patch_Laurion.conf | 2 +- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 495de99ca..a31f77d02 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3086,6 +3086,57 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_GMTraining) { + ENCODE_LENGTH_EXACT(GMTrainee_Struct); + SETUP_DIRECT_ENCODE(GMTrainee_Struct, structs::GMTrainee_Struct); + + OUT(npcid); + OUT(playerid); + + for (int i = 0; i < 100; ++i) { + OUT(skills[i]); + } + + eq->unknown408[0] = 1; + eq->unknown408[1] = 0xC9; + eq->unknown408[2] = 0xC9; + eq->unknown408[3] = 0xC9; + eq->unknown408[4] = 0xC9; + eq->unknown408[5] = 0xC9; + eq->unknown408[6] = 0xC9; + eq->unknown408[7] = 0xC9; + eq->unknown408[8] = 0xC9; + eq->unknown408[9] = 0xC9; + eq->unknown408[10] = 0xC9; + eq->unknown408[11] = 0xC9; + eq->unknown408[12] = 0xC9; + eq->unknown408[13] = 0xC9; + eq->unknown408[14] = 0xC9; + eq->unknown408[15] = 0xC9; + eq->unknown408[16] = 0xC9; + eq->unknown408[17] = 0xC9; + eq->unknown408[18] = 0xC9; + eq->unknown408[19] = 0xC9; + eq->unknown408[20] = 0xC9; + eq->unknown408[21] = 0xC9; + eq->unknown408[22] = 0xC9; + eq->unknown408[23] = 0xC9; + eq->unknown408[24] = 0xC9; + eq->unknown408[25] = 0xC9; + eq->unknown408[26] = 0xC9; + eq->unknown408[27] = 0xC9; + eq->unknown408[28] = 0xC9; + eq->unknown408[29] = 0xC9; + eq->unknown408[30] = 0xC9; + eq->unknown408[31] = 0xC9; + eq->unknown408[32] = 0xC9; + eq->unknown408[33] = 0xCA; //the client far as I can tell doesn't read past the 1 byte + 32 0xc9s, but still setting it to what we saw + eq->unknown408[34] = 0x8C; + eq->unknown408[35] = 0xEC; + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) @@ -3305,11 +3356,11 @@ namespace Laurion DECODE_LENGTH_EXACT(structs::GMTrainee_Struct); SETUP_DIRECT_DECODE(GMTrainee_Struct, structs::GMTrainee_Struct); - OUT(npcid); - OUT(playerid); + IN(npcid); + IN(playerid); for (int i = 0; i < 100; ++i) { - OUT(skills[i]); + IN(skills[i]); } FINISH_DIRECT_DECODE(); diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 2938774dd..b975c6561 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -41,6 +41,7 @@ E(OP_Buff) E(OP_ManaChange) E(OP_Action) E(OP_MobHealth) +E(OP_GMTraining) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index b9d852dfe..75ffa04cd 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -192,7 +192,7 @@ OP_SkillUpdate=0x04c OP_GMTrainSkillConfirm=0x0000 OP_RandomReq=0x0000 OP_Death=0x429a -OP_GMTraining=0x0000 +OP_GMTraining=0x7c7a OP_GMEndTraining=0x0000 OP_GMTrainSkill=0x0000 OP_Animation=0x79c7 From be0c6b5e0584d9f60729af49a47ac913842c46d2 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 14 Dec 2024 19:37:10 -0800 Subject: [PATCH 73/93] Work on skill updates. --- common/patches/laurion.cpp | 25 +++++++++++++++++++++++++ common/patches/laurion_ops.h | 2 ++ common/patches/laurion_structs.h | 17 +++++++++++++++++ utils/patches/patch_Laurion.conf | 14 +++++++------- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index a31f77d02..74cd716ef 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3137,6 +3137,31 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_GMTrainSkillConfirm) + { + ENCODE_LENGTH_EXACT(GMTrainSkillConfirm_Struct); + SETUP_DIRECT_ENCODE(GMTrainSkillConfirm_Struct, structs::GMTrainSkillConfirm_Struct); + + OUT(SkillID); + OUT(Cost); + OUT(NewSkill); + OUT_str(TrainerName); + + FINISH_ENCODE(); + } + + ENCODE(OP_SkillUpdate) + { + ENCODE_LENGTH_EXACT(SkillUpdate_Struct); + SETUP_DIRECT_ENCODE(SkillUpdate_Struct, structs::SkillUpdate_Struct); + + OUT(skillId); + OUT(value); + eq->active = 1; + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index b975c6561..fb0d63898 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -42,6 +42,8 @@ E(OP_ManaChange) E(OP_Action) E(OP_MobHealth) E(OP_GMTraining) +E(OP_GMTrainSkillConfirm) +E(OP_SkillUpdate) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 2e4364be9..0cd5c9a57 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -748,6 +748,23 @@ namespace Laurion { /*444*/ }; + struct GMTrainSkillConfirm_Struct { + /*000*/ uint32 SkillID; + /*004*/ uint32 Cost; + /*008*/ uint8 NewSkill; // Set to 1 for 'You have learned the basics' message. + /*009*/ char TrainerName[64]; + /*073*/ uint8 Unknown073[3]; + /*076*/ + }; + + struct SkillUpdate_Struct { + /*00*/ uint32 skillId; + /*04*/ uint32 value; + /*08*/ uint8 active; + /*09*/ uint8 padding[3]; + /*12*/ + }; + #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 75ffa04cd..ab7bb36aa 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -188,15 +188,15 @@ OP_Illusion=0x0000 OP_MoneyOnCorpse=0x0000 OP_RandomReply=0x0000 OP_DenyResponse=0x0000 -OP_SkillUpdate=0x04c -OP_GMTrainSkillConfirm=0x0000 +OP_SkillUpdate=0x6735 +OP_GMTrainSkillConfirm=0x6fbc OP_RandomReq=0x0000 OP_Death=0x429a OP_GMTraining=0x7c7a -OP_GMEndTraining=0x0000 -OP_GMTrainSkill=0x0000 +OP_GMEndTraining=0x3ec6 +OP_GMTrainSkill=0x54e1 OP_Animation=0x79c7 -OP_Begging=0x0000 +OP_Begging=0x7ded OP_Consent=0x0000 OP_ConsentDeny=0x0000 OP_AutoFire=0x0000 @@ -219,8 +219,8 @@ OP_Feedback=0x0000 OP_Report=0x0000 OP_Damage=0x7d07 OP_ChannelMessage=0x6adc -OP_Assist=0x0000 -OP_AssistGroup=0x0000 +OP_Assist=0x51f1 +OP_AssistGroup=0x3f23 OP_MoveCoin=0x0000 OP_ZonePlayerToBind=0x0000 OP_KeyRing=0x0000 From 33ab28c1e097907e80c67d4c8df6e9594e6d4640 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 14 Dec 2024 23:23:55 -0800 Subject: [PATCH 74/93] Fix for corpses not serializing correctly (hopefully). --- common/patches/laurion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 74cd716ef..01d756589 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -1860,7 +1860,7 @@ namespace Laurion buffer.WriteUInt64(0); //u32 SeeInvis[3]; - if (emu->NPC) { + if (emu->NPC == 1) { buffer.WriteUInt32(0); buffer.WriteUInt32(0); buffer.WriteUInt32(0); From 62a84388c00096e001193af1017ab30c22d7ab20 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 16 Dec 2024 18:08:27 -0800 Subject: [PATCH 75/93] AA struct work --- common/patches/laurion.cpp | 24 ++++++++++++++++++++++++ common/patches/laurion_ops.h | 2 +- common/patches/laurion_structs.h | 20 ++++++++++++++++++++ utils/patches/patch_Laurion.conf | 18 +++++++++--------- zone/client.cpp | 15 +++++++-------- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 01d756589..7ce971e39 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3162,6 +3162,30 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_RespondAA) + { + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + eq->aa_spent = emu->aa_spent; + // These fields may need to be correctly populated at some point + eq->aapoints_assigned[0] = emu->aa_spent; + eq->aapoints_assigned[1] = 0; + eq->aapoints_assigned[2] = 0; + eq->aapoints_assigned[3] = 0; + eq->aapoints_assigned[4] = 0; + eq->aapoints_assigned[5] = 0; + + for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) + { + eq->aa_list[i].AA = emu->aa_list[i].AA; + eq->aa_list[i].value = emu->aa_list[i].value; + eq->aa_list[i].charges = emu->aa_list[i].charges; + eq->aa_list[i].bUnknown0x0c = false; + } + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index fb0d63898..13a33ec71 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -7,7 +7,6 @@ E(OP_SendMaxCharacters) E(OP_SendCharInfo) E(OP_ExpansionInfo) E(OP_SpawnAppearance) -//E(OP_SendAATable) E(OP_PlayerProfile) E(OP_ZoneEntry) E(OP_ZoneSpawns) @@ -44,6 +43,7 @@ E(OP_MobHealth) E(OP_GMTraining) E(OP_GMTrainSkillConfirm) E(OP_SkillUpdate) +E(OP_RespondAA) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 0cd5c9a57..28a205366 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -765,6 +765,26 @@ namespace Laurion { /*12*/ }; + struct AA_Array + { + uint32 AA; + uint32 value; + uint32 charges; // expendable charges + bool bUnknown0x0c; // added test winter 2024; removed sometime in summer 2024 + }; + + struct AATable_Struct { + /*00*/ uint32 aa_spent; // Total AAs Spent + /*04*/ uint32 aapoints_assigned[6]; // none, general, arch, class, special, focus, merc + /*24*/ AA_Array aa_list[MAX_PP_AA_ARRAY]; + }; + + struct AltAdvStats_Struct { + /*000*/ uint32 experience; + /*004*/ uint32 unspent; + /*008*/ uint8 percentage; + /*009*/ uint8 unknown009[3]; + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index ab7bb36aa..5b0e6f09e 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -97,10 +97,10 @@ OP_SendExpZonein=0x02b4 OP_SendAATable=0x5f30 OP_ClearAA=0x3498 OP_ClearLeadershipAbilities=0x0000 #removed; leadership abilities are baked in and always on -OP_RespondAA=0x0000 -OP_UpdateAA=0x0000 -OP_SendAAStats=0x0000 -OP_AAExpUpdate=0x0000 #0x642f +OP_RespondAA=0x4c67 +OP_UpdateAA=0x3b30 +OP_SendAAStats=0x7d65 #i'll be honest i think this was removed at some point but this is the op at the spot in the list +OP_AAExpUpdate=0x642f #need to look into whether this has changed; exp did OP_ExpUpdate=0x611d OP_HPUpdate=0x775c OP_ManaChange=0x700f @@ -299,11 +299,11 @@ OP_SaveOnZoneReq=0x3bfe OP_ReadBook=0x0000 OP_Dye=0x0000 OP_InterruptCast=0x0000 -OP_AAAction=0x0000 +OP_AAAction=0x71BB OP_LeadershipExpToggle=0x0000 OP_LeadershipExpUpdate=0x0000 -OP_PurchaseLeadershipAA=0x0000 -OP_UpdateLeadershipAA=0x0000 +OP_PurchaseLeadershipAA=0x0000 #removed, these act as if all purchased now +OP_UpdateLeadershipAA=0x0000 #removed, these act as if all purchased now OP_MarkNPC=0x0000 OP_ClearNPCMarks=0x0000 OP_DelegateAbility=0x0000 @@ -366,7 +366,7 @@ OP_OpenContainer=0x0000 OP_Marquee=0x0000 OP_ItemRecastDelay=0x0000 #OP_OpenInventory=0x0000 # Likely does not exist in RoF -U -OP_ResetAA=0x0000 +OP_ResetAA=0x53c0 OP_Fling=0x0000 OP_CancelSneakHide=0x0000 OP_AggroMeterLockTarget=0x0000 @@ -546,7 +546,7 @@ OP_GroupLeaderChange=0x0000 OP_GroupRoles=0x0000 OP_GroupMakeLeader=0x0000 OP_DoGroupLeadershipAbility=0x0000 -OP_GroupLeadershipAAUpdate=0x0000 +OP_GroupLeadershipAAUpdate=0x0000 # removed these act as if you have always purchased them OP_GroupMentor=0x0000 OP_InspectBuffs=0x0000 diff --git a/zone/client.cpp b/zone/client.cpp index 0b6d20b04..57386917f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -537,18 +537,17 @@ void Client::SendZoneInPackets() outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - auto tmpxp2 = GetEXPForLevel(GetLevel() + 1); - auto tmpxp1 = GetEXPForLevel(GetLevel()); - + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + + // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); eu->exp = (uint32)(330.0f * tmpxp); - FastQueuePacket(&outapp); - } - else { - eu->exp = 0; - FastQueuePacket(&outapp); + outapp->priority = 6; + QueuePacket(outapp); } + safe_delete(outapp); SendAlternateAdvancementTimers(); From 852667016d02e69a1fae166f04f56561ad061169 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 16 Dec 2024 22:13:37 -0800 Subject: [PATCH 76/93] Blocked buffs --- common/patches/laurion.cpp | 39 ++++++++++++++++++++++++++++++++ common/patches/laurion_ops.h | 5 ++++ common/patches/laurion_structs.h | 10 ++++++++ utils/patches/patch_Laurion.conf | 16 ++++++------- 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 7ce971e39..7f32bcd02 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3186,6 +3186,27 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_BlockedBuffs) + { + ENCODE_LENGTH_EXACT(BlockedBuffs_Struct); + SETUP_DIRECT_ENCODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); + + for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) + eq->SpellID[i] = emu->SpellID[i]; + + for (uint32 i = BLOCKED_BUFF_COUNT; i < structs::BLOCKED_BUFF_COUNT; ++i) + eq->SpellID[i] = -1; + + OUT(Count); + OUT(Pet); + OUT(Initialise); + OUT(Flags); + + FINISH_ENCODE(); + } + + ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } + // DECODE methods DECODE(OP_EnterWorld) @@ -3415,6 +3436,24 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_BlockedBuffs) + { + DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); + SETUP_DIRECT_DECODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); + + for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) + emu->SpellID[i] = eq->SpellID[i]; + + IN(Count); + IN(Pet); + IN(Initialise); + IN(Flags); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RemoveBlockedBuffs) { DECODE_FORWARD(OP_BlockedBuffs); } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 13a33ec71..e9aa1d734 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -44,6 +44,9 @@ E(OP_GMTraining) E(OP_GMTrainSkillConfirm) E(OP_SkillUpdate) E(OP_RespondAA) +E(OP_BlockedBuffs) +E(OP_RemoveBlockedBuffs) + //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) @@ -60,6 +63,8 @@ D(OP_MoveItem) D(OP_ShopRequest) D(OP_CastSpell) D(OP_GMTraining) +D(OP_BlockedBuffs) +D(OP_RemoveBlockedBuffs) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 28a205366..2ee8d1b6a 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -12,6 +12,7 @@ namespace Laurion { static const uint32 MAX_PP_UNKNOWN_ABILITIES = 25; static const uint32 MAX_RECAST_TYPES = 25; static const uint32 MAX_ITEM_RECAST_TYPES = 100; + static const uint32 BLOCKED_BUFF_COUNT = 40; static const uint32 BUFF_COUNT = 62; static const uint32 MAX_PP_LANGUAGE = 32; #pragma pack(1) @@ -785,6 +786,15 @@ namespace Laurion { /*008*/ uint8 percentage; /*009*/ uint8 unknown009[3]; }; + + struct BlockedBuffs_Struct + { + /*000*/ int32 SpellID[BLOCKED_BUFF_COUNT]; + /*120*/ uint32 Count; + /*124*/ uint8 Pet; + /*125*/ uint8 Initialise; + /*126*/ uint16 Flags; + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 5b0e6f09e..0c77a42f7 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -19,7 +19,6 @@ OP_ApproveWorld=0x0000 OP_LogServer=0x6d4d OP_SendCharInfo=0x832 OP_ExpansionInfo=0x066d -OP_GuildsList=0x0000 OP_EnterWorld=0x6691 OP_PostEnterWorld=0x2062 OP_World_Client_CRC1=0x74c8 @@ -89,9 +88,9 @@ OP_ReqClientSpawn=0x6732 OP_SpawnDoor=0x4273 OP_GroundSpawn=0x49c5 OP_SendZonepoints=0x279f -OP_BlockedBuffs=0x0000 -OP_RemoveBlockedBuffs=0x0000 -OP_ClearBlockedBuffs=0x0000 +OP_BlockedBuffs=0x4fdb +OP_RemoveBlockedBuffs=0x53cd +OP_ClearBlockedBuffs=0x5752 OP_WorldObjectsSent=0x2879 OP_SendExpZonein=0x02b4 OP_SendAATable=0x5f30 @@ -106,8 +105,6 @@ OP_HPUpdate=0x775c OP_ManaChange=0x700f OP_TGB=0x0000 #removed; tgb is baked in and always on OP_SpecialMesg=0x7d93 -OP_GuildMemberList=0x0000 -OP_GuildMOTD=0x0000 OP_CharInventory=0x21d6 OP_WearChange=0x44c0 OP_ClientUpdate=0x3a4b @@ -115,6 +112,9 @@ OP_ClientReady=0x0831 OP_SetServerFilter=0x6b7f # Guild Opcodes +OP_GuildsList=0x0000 +OP_GuildMemberList=0x0000 +OP_GuildMOTD=0x0000 OP_GetGuildMOTD=0x0000 OP_GetGuildMOTDReply=0x0000 OP_GuildMemberUpdate=0x0000 @@ -174,9 +174,9 @@ OP_InspectRequest=0x0000 OP_InspectAnswer=0x0000 OP_InspectMessageUpdate=0x0000 OP_BeginCast=0x31f9 -OP_ColoredText=0x0000 +OP_ColoredText=0x0f3c OP_ConsentResponse=0x0000 -OP_MemorizeSpell=0x0000 +OP_MemorizeSpell=0x1d31 OP_LinkedReuse=0x0000 OP_SwapSpell=0x0000 OP_CastSpell=0x325b From f040bbdb0369fb9b9839b5ab17a7d1487e78846c Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 18 Dec 2024 22:16:09 -0800 Subject: [PATCH 77/93] -ZoneToBind struct -Added opcode for ZoneToBind -Added opcode for RespawnWnd (it looks like this is the same as rof2 so no struct work right now unless it doesn't end up working) --- common/patches/laurion.cpp | 21 +++++++++++++++++++++ common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 15 +++++++++++++++ utils/patches/patch_Laurion.conf | 4 ++-- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 7f32bcd02..f8a73c5b1 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3207,6 +3207,27 @@ namespace Laurion ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } + ENCODE(OP_ZonePlayerToBind) + { + SETUP_VAR_ENCODE(ZonePlayerToBind_Struct); + ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name)); + + __packet->SetWritePosition(0); + __packet->WriteUInt16(emu->bind_zone_id); + __packet->WriteUInt16(emu->bind_instance_id); + __packet->WriteFloat(emu->x); + __packet->WriteFloat(emu->y); + __packet->WriteFloat(emu->z); + __packet->WriteFloat(emu->heading); + __packet->WriteString(emu->zone_name); + __packet->WriteUInt32(60); + __packet->WriteUInt32(0); + __packet->WriteUInt32(51); + __packet->WriteUInt32(41); + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index e9aa1d734..32061a224 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -46,6 +46,7 @@ E(OP_SkillUpdate) E(OP_RespondAA) E(OP_BlockedBuffs) E(OP_RemoveBlockedBuffs) +E(OP_ZonePlayerToBind) //list of packets we need to decode on the way in: D(OP_EnterWorld) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 2ee8d1b6a..fdbed24b1 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -795,6 +795,21 @@ namespace Laurion { /*125*/ uint8 Initialise; /*126*/ uint16 Flags; }; + + struct ZonePlayerToBind_Struct { + //Same structure as the binds in PlayerProfile_Struct + //Assembly calls the same function + /*000*/ uint16 bind_zone_id; + /*002*/ uint16 bind_instance_id; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ char zone_name[1]; // Or "Bind Location" + /*021*/ uint32 unknown1; + /*025*/ uint32 unknown2; + /*029*/ uint32 unknown3; + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 0c77a42f7..1dec3d584 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -222,7 +222,7 @@ OP_ChannelMessage=0x6adc OP_Assist=0x51f1 OP_AssistGroup=0x3f23 OP_MoveCoin=0x0000 -OP_ZonePlayerToBind=0x0000 +OP_ZonePlayerToBind=0x5643 OP_KeyRing=0x0000 OP_WhoAllRequest=0x0000 OP_WhoAllResponse=0x0000 @@ -330,7 +330,7 @@ OP_PVPLeaderBoardReply=0x0000 OP_PVPLeaderBoardDetailsRequest=0x0000 OP_PVPLeaderBoardDetailsReply=0x0000 OP_RestState=0x0000 -OP_RespawnWindow=0x0000 +OP_RespawnWindow=0x55ed OP_LDoNButton=0x0000 OP_SetStartCity=0x0000 OP_VoiceMacroIn=0x0000 From 2cb4c5561338a51753dfdac39073030eb5b18dae Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 19 Dec 2024 19:15:27 -0800 Subject: [PATCH 78/93] Some opcodes. --- utils/patches/patch_Laurion.conf | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 1dec3d584..08ac9c370 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -36,7 +36,7 @@ OP_CharacterCreate=0x6a3c OP_DeleteCharacter=x67d7 OP_RandomNameGenerator=0x49d9 OP_ApproveName=0x11e5 -OP_MOTD=0x0000 +OP_MOTD=0x0be4 OP_SetChatServer=0x0000 OP_SetChatServer2=0x2726 OP_ZoneServerInfo=0x2273 @@ -177,8 +177,8 @@ OP_BeginCast=0x31f9 OP_ColoredText=0x0f3c OP_ConsentResponse=0x0000 OP_MemorizeSpell=0x1d31 -OP_LinkedReuse=0x0000 -OP_SwapSpell=0x0000 +OP_LinkedReuse=0x7a8e +OP_SwapSpell=0x63c7 OP_CastSpell=0x325b OP_Consider=0x53e3 OP_FormattedMessage=0x7f7f @@ -199,11 +199,11 @@ OP_Animation=0x79c7 OP_Begging=0x7ded OP_Consent=0x0000 OP_ConsentDeny=0x0000 -OP_AutoFire=0x0000 +OP_AutoFire=0x5280 OP_PetCommands=0x0000 OP_PetCommandState=0x0000 OP_PetHoTT=0x0000 -OP_DeleteSpell=0x0000 +OP_DeleteSpell=0x4281 OP_Surname=0x0000 OP_ClearSurname=0x0000 OP_FaceChange=0x0000 @@ -221,7 +221,7 @@ OP_Damage=0x7d07 OP_ChannelMessage=0x6adc OP_Assist=0x51f1 OP_AssistGroup=0x3f23 -OP_MoveCoin=0x0000 +OP_MoveCoin=0x4e6a OP_ZonePlayerToBind=0x5643 OP_KeyRing=0x0000 OP_WhoAllRequest=0x0000 @@ -269,9 +269,9 @@ OP_ClickDoor=0x733c OP_MoveDoor=0x567c OP_RemoveAllDoors=0x73e8 OP_EnvDamage=0x0000 -OP_BoardBoat=0x0000 -OP_LeaveBoat=0x0000 -OP_ControlBoat=0x0000 +OP_BoardBoat=0x7015 +OP_LeaveBoat=0x2486 +OP_ControlBoat=0x166f OP_Forage=0x0000 OP_SafeFallSuccess=0x0000 OP_RezzComplete=0x0000 @@ -282,14 +282,14 @@ OP_RequestDuel=0x0000 OP_MobRename=0x0000 OP_AugmentItem=0x3a1b OP_WeaponEquip1=0x0000 -OP_PlayerStateAdd=0x0000 -OP_PlayerStateRemove=0x0000 +OP_PlayerStateAdd=0x2178 +OP_PlayerStateRemove=0x178e OP_ApplyPoison=0x0000 OP_Save=0x6da2 OP_TestBuff=0x0000 OP_CustomTitles=0x0000 -OP_Split=0x0000 -OP_YellForHelp=0x0000 +OP_Split=0x7f6e +OP_YellForHelp=0x5fc9 OP_LoadSpellSet=0x0000 OP_Bandolier=0x0000 OP_PotionBelt=0x0000 @@ -298,7 +298,7 @@ OP_DuelAccept=0x0000 OP_SaveOnZoneReq=0x3bfe OP_ReadBook=0x0000 OP_Dye=0x0000 -OP_InterruptCast=0x0000 +OP_InterruptCast=0x1d71 OP_AAAction=0x71BB OP_LeadershipExpToggle=0x0000 OP_LeadershipExpUpdate=0x0000 @@ -308,7 +308,7 @@ OP_MarkNPC=0x0000 OP_ClearNPCMarks=0x0000 OP_DelegateAbility=0x0000 OP_SetGroupTarget=0x0000 -OP_Charm=0x0000 +OP_Charm=0x66bb OP_Stun=0x0000 OP_SendFindableNPCs=0x0000 OP_FindPersonRequest=0x0000 @@ -345,7 +345,7 @@ OP_BecomeCorpse=0x0000 # Unused? OP_Action2=0x0000 # Unused? OP_MobUpdate=0x0000 OP_NPCMoveUpdate=0x0000 -OP_CameraEffect=0x0000 +OP_CameraEffect=0x2f01 OP_SpellEffect=0x0000 OP_AddNimbusEffect=0x0000 OP_RemoveNimbusEffect=0x0000 @@ -367,7 +367,7 @@ OP_Marquee=0x0000 OP_ItemRecastDelay=0x0000 #OP_OpenInventory=0x0000 # Likely does not exist in RoF -U OP_ResetAA=0x53c0 -OP_Fling=0x0000 +OP_Fling=0x3731 OP_CancelSneakHide=0x0000 OP_AggroMeterLockTarget=0x0000 OP_AggroMeterTargetInfo=0x0000 From d9132e84ab4ef7303c32ef873a5e3c350d35e17c Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 24 Dec 2024 12:03:30 -0800 Subject: [PATCH 79/93] Illusion and money on corpse --- common/patches/laurion.cpp | 41 +++++++++++++++++++++++++ common/patches/laurion_ops.h | 3 +- common/patches/laurion_structs.h | 52 ++++++++++++++++++++++++++++++++ utils/patches/patch_Laurion.conf | 4 +-- 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index f8a73c5b1..b519b8f13 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3228,6 +3228,47 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + eq->unknown006[0] = 0; + eq->unknown006[1] = 0; + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + + FINISH_ENCODE(); + } + + ENCODE(OP_MoneyOnCorpse) + { + ENCODE_LENGTH_EXACT(moneyOnCorpseStruct); + SETUP_DIRECT_ENCODE(moneyOnCorpseStruct, structs::moneyOnCorpseStruct); + + eq->type = emu->response; + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + eq->flags = 0; + + FINISH_ENCODE(); + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 32061a224..4bd23d500 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -47,7 +47,8 @@ E(OP_RespondAA) E(OP_BlockedBuffs) E(OP_RemoveBlockedBuffs) E(OP_ZonePlayerToBind) - +E(OP_Illusion) +E(OP_MoneyOnCorpse) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index fdbed24b1..051b16a96 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -810,6 +810,58 @@ namespace Laurion { /*025*/ uint32 unknown2; /*029*/ uint32 unknown3; }; + + struct ArmorPropertyStruct + { + /*000*/ uint32 type; + /*004*/ uint32 variation; + /*008*/ uint32 material; + /*012*/ uint32 newArmorID; + /*016*/ uint32 newArmorType; + /*020*/ + }; + + struct Illusion_Struct { + /*000*/ uint32 spawnid; + /*004*/ char charname[64]; + /*068*/ uint16 race; //according to eqlib this is s32 + /*070*/ char unknown006[2]; + /*072*/ uint8 gender; + /*073*/ uint8 texture; + /*074*/ uint8 armorVariation; + /*075*/ uint8 armorMaterial; + /*076*/ uint8 helmtexture; + /*077*/ uint8 unknown077; //padding from this being a pack(4) struct actually + /*078*/ uint8 unknown078; + /*079*/ uint8 unknown079; + /*080*/ uint32 face; + /*084*/ uint8 hairstyle; + /*085*/ uint8 haircolor; + /*086*/ uint8 beard; + /*087*/ uint8 beardcolor; + /*088*/ float size; + /*092*/ uint32_t npc_tint; + /*096*/ bool keep_armor_properties; + /*097*/ uint8 unknown097[3]; //padding from this being a pack(4) struct actually + /*100*/ ArmorPropertyStruct armorProperties[9]; + /*280*/ uint32_t armorTints[9]; + /*316*/ int32 class_; + /*320*/ uint32 drakkin_heritage; + /*324*/ uint32 drakkin_tattoo; + /*328*/ uint32 drakkin_details; + /*332*/ + }; + + struct moneyOnCorpseStruct { + /*000*/ uint8 type; + /*001*/ uint8 padding1[3]; + /*004*/ uint32 flags; + /*008*/ uint32 platinum; + /*012*/ uint32 gold; + /*016*/ uint32 silver; + /*020*/ uint32 copper; + /*024*/ + } #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 08ac9c370..6c9d8c577 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -184,8 +184,8 @@ OP_Consider=0x53e3 OP_FormattedMessage=0x7f7f OP_SimpleMessage=0x1943 OP_Buff=0x6ce5 -OP_Illusion=0x0000 -OP_MoneyOnCorpse=0x0000 +OP_Illusion=0x5a3f +OP_MoneyOnCorpse=0x39d3 OP_RandomReply=0x0000 OP_DenyResponse=0x0000 OP_SkillUpdate=0x6735 From 45db09303f5f3d48588f354de67f3451ce3f7fa9 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 24 Dec 2024 14:29:51 -0800 Subject: [PATCH 80/93] Add a few opcodes Add onlevelmessage encode --- common/patches/laurion.cpp | 26 ++++++++++++++++++++++++++ common/patches/laurion_ops.h | 1 + utils/patches/patch_Laurion.conf | 20 ++++++++++---------- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index b519b8f13..660ea9fa0 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3269,6 +3269,32 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_OnLevelMessage) + { + EQApplicationPacket* in = *p; + *p = nullptr; + OnLevelMessage_Struct* emu = (OnLevelMessage_Struct*)in->pBuffer; + SerializeBuffer buffer; + + buffer.WriteLengthString(emu->Title); + buffer.WriteLengthString(emu->Text); + buffer.WriteLengthString(emu->ButtonName0); + buffer.WriteLengthString(emu->ButtonName1); + buffer.WriteUInt8(emu->Buttons); + buffer.WriteUInt8(emu->SoundControls); + buffer.WriteUInt32(emu->Duration); + buffer.WriteUInt32(emu->PopupID); + buffer.WriteUInt32(emu->NegativeID); + buffer.WriteUInt32(0); //seen -1 & 0 + buffer.WriteUInt32(0); //seen 0 + + auto outapp = new EQApplicationPacket(OP_OnLevelMessage, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + + delete in; + } + // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 4bd23d500..e2a86e651 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -49,6 +49,7 @@ E(OP_RemoveBlockedBuffs) E(OP_ZonePlayerToBind) E(OP_Illusion) E(OP_MoneyOnCorpse) +E(OP_OnLevelMessage) //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 6c9d8c577..b47132ca7 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -313,14 +313,14 @@ OP_Stun=0x0000 OP_SendFindableNPCs=0x0000 OP_FindPersonRequest=0x0000 OP_FindPersonReply=0x0000 -OP_Sound=0x0000 -OP_CashReward=0x0000 +OP_Sound=0x2fa8 +OP_CashReward=0x5e23 OP_PetBuffWindow=0x0000 -OP_LevelAppearance=0x0000 -OP_Translocate=0x0000 -OP_Sacrifice=0x0000 -OP_PopupResponse=0x0000 -OP_OnLevelMessage=0x0000 +OP_LevelAppearance=0x5d24 +OP_Translocate=0x2772 +OP_Sacrifice=0x2cbf +OP_PopupResponse=0x6be9 +OP_OnLevelMessage=0x2a41 OP_AugmentInfo=0x0000 OP_Petition=0x0000 OP_SomeItemPacketMaybe=0x0000 @@ -420,10 +420,10 @@ OP_MercenarySuspendResponse=0x0000 OP_MercenaryUnsuspendResponse=0x0000 # Looting -OP_LootRequest=0x0000 -OP_EndLootRequest=0x0000 +OP_LootRequest=0x60e5 +OP_EndLootRequest=0x35f6 OP_LootItem=0x0000 -OP_LootComplete=0x0000 +OP_LootComplete=0x1f5e # bazaar trader stuff: OP_BazaarSearch=0x0000 From 91bd9ccf266f93509076792b27f9f79464f1b645 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 24 Dec 2024 16:02:18 -0800 Subject: [PATCH 81/93] Minor group work --- common/patches/laurion.cpp | 27 +++++++++++++++++++++++++++ common/patches/laurion_ops.h | 4 +++- common/patches/laurion_structs.h | 15 ++++++++++++++- utils/patches/patch_Laurion.conf | 6 +++--- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 660ea9fa0..776e437ff 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3542,6 +3542,33 @@ namespace Laurion DECODE(OP_RemoveBlockedBuffs) { DECODE_FORWARD(OP_BlockedBuffs); } + DECODE(OP_GroupDisband) + { + DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite) + { + DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite2) + { + DECODE_FORWARD(OP_GroupInvite); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index e2a86e651..a0b5d38f2 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -68,6 +68,8 @@ D(OP_CastSpell) D(OP_GMTraining) D(OP_BlockedBuffs) D(OP_RemoveBlockedBuffs) - +D(OP_GroupDisband) +D(OP_GroupInvite) +D(OP_GroupInvite2) #undef E #undef D diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 051b16a96..d9ff005be 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -861,7 +861,20 @@ namespace Laurion { /*016*/ uint32 silver; /*020*/ uint32 copper; /*024*/ - } + }; + + struct GroupGeneric_Struct { + /*0000*/ char name1[64]; + /*0064*/ char name2[64]; + /*0128*/ uint32 unknown0128; + /*0132*/ uint32 unknown0132; + /*0136*/ uint32 unknown0136; + /*0140*/ uint32 unknown0140; + /*0144*/ uint32 unknown0144; + /*0148*/ uint32 unknown0148; + /*0152*/ uint16 unknown0152; + /*0154*/ + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index b47132ca7..8786552f8 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -529,8 +529,8 @@ OP_AdventureLeaderboardRequest=0x0000 OP_AdventureLeaderboardReply=0x0000 # Group Opcodes -OP_GroupDisband=0x0000 -OP_GroupInvite=0x0000 +OP_GroupDisband=0x78ef +OP_GroupInvite=0x1d90 OP_GroupFollow=0x0000 OP_GroupUpdate=0x0000 OP_GroupUpdateB=0x0000 @@ -539,7 +539,7 @@ OP_GroupAcknowledge=0x0000 OP_GroupDelete=0x0000 OP_CancelInvite=0x0000 OP_GroupFollow2=0x0000 -OP_GroupInvite2=0x0000 +OP_GroupInvite2=0x1e7e OP_GroupDisbandYou=0x0000 OP_GroupDisbandOther=0x0000 OP_GroupLeaderChange=0x0000 From 745eeb6eae9772a5b193aba74b020dae7f8bc758 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 27 Dec 2024 23:22:33 -0800 Subject: [PATCH 82/93] A few more ops; some need server support still. Reorder encodes to be alphabetical for better maintanability. --- common/patches/laurion.cpp | 3132 +++++++++++++++--------------- common/patches/laurion_ops.h | 89 +- utils/patches/patch_Laurion.conf | 16 +- 3 files changed, 1619 insertions(+), 1618 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 776e437ff..f304deb3b 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -158,6 +158,572 @@ namespace Laurion #include "ss_define.h" // ENCODE methods + ENCODE(OP_Action) { + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::MissileHitInfo); + + //This is mostly figured out; there's two unknowns, only unknown1 is read by the client + OUT(target); + OUT(source); + eq->spell_id = emu->spell; + eq->effect_type = emu->effect_flag; + eq->effective_casting_level = 0; //if you set this to != 0 it will use this level instead of calculating it + eq->unknown1 = 0; + eq->unknown2 = 0; + eq->damage = 0; //client doesn't read this but live sends it here, can just set 0 + eq->modifier = 1.0f + (emu->instrument_mod - 10) / 10.0f; + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); + eq->skill = emu->type; + OUT(level); + + FINISH_ENCODE(); + } + + ENCODE(OP_Animation) + { + ENCODE_LENGTH_EXACT(Animation_Struct); + SETUP_DIRECT_ENCODE(Animation_Struct, structs::Animation_Struct); + + OUT(spawnid); + OUT(action); + OUT(speed); + + FINISH_ENCODE(); + } + + ENCODE(OP_BeginCast) + { + ENCODE_LENGTH_EXACT(BeginCast_Struct); + SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); + + OUT(spell_id); + OUT(caster_id); + OUT(cast_time); + eq->unknown0e = 1; //not sure what this is; but its usually 1 on live + + FINISH_ENCODE(); + } + + ENCODE(OP_BlockedBuffs) + { + ENCODE_LENGTH_EXACT(BlockedBuffs_Struct); + SETUP_DIRECT_ENCODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); + + for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) + eq->SpellID[i] = emu->SpellID[i]; + + for (uint32 i = BLOCKED_BUFF_COUNT; i < structs::BLOCKED_BUFF_COUNT; ++i) + eq->SpellID[i] = -1; + + OUT(Count); + OUT(Pet); + OUT(Initialise); + OUT(Flags); + + FINISH_ENCODE(); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct); + SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::EQAffectPacket_Struct); + + eq->entity_id = emu->entityid; + eq->unknown004 = 0; + + //fill in affect info + eq->affect.caster_id.Id = emu->buff.player_id; + eq->affect.flags = 0; + eq->affect.spell_id = emu->buff.spellid; + eq->affect.duration = emu->buff.duration; + eq->affect.initial_duration = emu->buff.duration; + eq->affect.hit_count = emu->buff.num_hits; + eq->affect.viral_timer = 0; + eq->affect.modifier = emu->buff.bard_modifier == 10 ? 1.0f : emu->buff.bard_modifier / 10.0f; + eq->affect.y = emu->buff.y; + eq->affect.x = emu->buff.x; + eq->affect.z = emu->buff.z; + eq->affect.level = emu->buff.level; + + eq->slot_id = ServerToLaurionBuffSlot(emu->slotid); + if (emu->bufffade == 1) + { + eq->buff_fade = 1; + } + else + { + eq->buff_fade = 2; + } + + EQApplicationPacket* outapp = nullptr; + if (emu->bufffade == 1) + { + // Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon + outapp = new EQApplicationPacket(OP_BuffCreate, 30); + outapp->WriteUInt32(emu->entityid); + outapp->WriteUInt32(0); // tic timer + outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ? + outapp->WriteUInt16(1); // 1 buff in this packet + outapp->WriteUInt32(ServerToLaurionBuffSlot(emu->slotid)); + outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove) + outapp->WriteUInt32(0); // Duration + outapp->WriteUInt32(0); // numhits + outapp->WriteUInt8(0); // Caster name + outapp->WriteUInt8(0); // Type + outapp->WriteUInt8(0); // Type + } + + FINISH_ENCODE(); + + if (outapp) { + dest->FastQueuePacket(&outapp); + } + } + + ENCODE(OP_BuffCreate) + { + SETUP_VAR_ENCODE(BuffIcon_Struct); + + //Laurion has one extra 0x00 byte before the end byte + uint32 sz = 13 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); + + __packet->WriteUInt32(emu->entity_id); + __packet->WriteUInt32(emu->tic_timer); + __packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff) + __packet->WriteUInt16(emu->count); + + for (int i = 0; i < emu->count; ++i) + { + __packet->WriteUInt32(emu->type == 0 ? ServerToLaurionBuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot); + __packet->WriteUInt32(emu->entries[i].spell_id); + __packet->WriteUInt32(emu->entries[i].tics_remaining); + __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown + __packet->WriteString(emu->entries[i].caster); + } + __packet->WriteUInt8(0); // Unknown1 + __packet->WriteUInt8(emu->type); // Unknown2 + + FINISH_ENCODE(); + } + + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + ChannelMessage_Struct* emu = (ChannelMessage_Struct*)in->pBuffer; + + unsigned char* __emu_buffer = in->pBuffer; + + std::string old_message = emu->message; + std::string new_message; + ServerToLaurionConvertLinks(new_message, old_message); + + in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 43; + + in->pBuffer = new unsigned char[in->size]; + + char* OutBuffer = (char*)in->pBuffer; + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); + VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_CharInventory) { + //consume the packet + EQApplicationPacket* in = *p; + *p = nullptr; + + if (!in->size) { + in->size = 4; + in->pBuffer = new uchar[in->size]; + memset(in->pBuffer, 0, in->size); + + dest->FastQueuePacket(&in, ack_req); + return; + } + + //store away the emu struct + uchar* __emu_buffer = in->pBuffer; + + int item_count = in->size / sizeof(EQ::InternalSerializedItem_Struct); + if (!item_count || (in->size % sizeof(EQ::InternalSerializedItem_Struct)) != 0) { + Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(EQ::InternalSerializedItem_Struct)); + + delete in; + return; + } + + EQ::InternalSerializedItem_Struct* eq = (EQ::InternalSerializedItem_Struct*)in->pBuffer; + SerializeBuffer buffer; + buffer.WriteUInt32(item_count); + + for (int index = 0; index < item_count; ++index, ++eq) { + SerializeItem(buffer, (const EQ::ItemInstance*)eq->inst, eq->slot_id, 0, ItemPacketCharInventory); + } + + in->size = buffer.size(); + in->pBuffer = new unsigned char[buffer.size()]; + memcpy(in->pBuffer, buffer.buffer(), buffer.size()); + + delete[] __emu_buffer; + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(vehicle_id); + eq->position.x = emu->x_pos; + eq->position.y = emu->y_pos; + eq->position.z = emu->z_pos; + eq->position.heading = emu->heading; + eq->position.deltaX = emu->delta_x; + eq->position.deltaY = emu->delta_y; + eq->position.deltaZ = emu->delta_z; + eq->position.deltaHeading = emu->delta_heading; + eq->position.animation = emu->animation; + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) { + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + OUT(force); + OUT(hit_heading); + OUT(hit_pitch); + OUT(special); + + FINISH_ENCODE(); + } + + ENCODE(OP_Death) + { + ENCODE_LENGTH_EXACT(Death_Struct); + SETUP_DIRECT_ENCODE(Death_Struct, structs::Death_Struct); + + OUT(spawn_id); + OUT(killer_id); + OUT(spell_id); + OUT(attack_skill); + OUT(damage); + + //This is a hack, we need to actually fix the ordering in source as this wont respect filters etc + if (emu->attack_skill != 231) { + auto combat_packet = new EQApplicationPacket(OP_Damage, sizeof(structs::CombatDamage_Struct)); + structs::CombatDamage_Struct* cds = (structs::CombatDamage_Struct*)combat_packet->pBuffer; + + cds->target = emu->spawn_id; + cds->source = emu->killer_id; + cds->type = emu->attack_skill; + cds->damage = emu->damage; + cds->spellid = -1; + + dest->FastQueuePacket(&combat_packet, ack_req); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteSpawn) + { + ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); + + OUT(spawn_id); + eq->unknown04 = 1; // Observed + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpUpdate) + { + SETUP_DIRECT_ENCODE(ExpUpdate_Struct, structs::ExpUpdate_Struct); + + //later we should change the underlying server to use this more accurate value + //and encode the 330 in the other patches + eq->exp = emu->exp * 100000 / 330; + + FINISH_ENCODE(); + } + + ENCODE(OP_FormattedMessage) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + FormattedMessage_Struct* emu = (FormattedMessage_Struct*)in->pBuffer; + + char* old_message_ptr = (char*)in->pBuffer; + old_message_ptr += sizeof(FormattedMessage_Struct); + + std::string old_message_array[9]; + + for (int i = 0; i < 9; ++i) { + if (*old_message_ptr == 0) { break; } + old_message_array[i] = old_message_ptr; + old_message_ptr += old_message_array[i].length() + 1; + } + + SerializeBuffer buffer; + buffer.WriteUInt32(emu->unknown0); + buffer.WriteUInt8(0); // Observed + buffer.WriteUInt32(emu->string_id); + buffer.WriteUInt32(emu->type); + + for (int i = 0; i < 9; ++i) { + std::string new_message; + ServerToLaurionConvertLinks(new_message, old_message_array[i]); + buffer.WriteLengthString(new_message); + } + + auto outapp = new EQApplicationPacket(OP_FormattedMessage, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + + delete in; + } + + ENCODE(OP_GMTraining) { + ENCODE_LENGTH_EXACT(GMTrainee_Struct); + SETUP_DIRECT_ENCODE(GMTrainee_Struct, structs::GMTrainee_Struct); + + OUT(npcid); + OUT(playerid); + + for (int i = 0; i < 100; ++i) { + OUT(skills[i]); + } + + eq->unknown408[0] = 1; + eq->unknown408[1] = 0xC9; + eq->unknown408[2] = 0xC9; + eq->unknown408[3] = 0xC9; + eq->unknown408[4] = 0xC9; + eq->unknown408[5] = 0xC9; + eq->unknown408[6] = 0xC9; + eq->unknown408[7] = 0xC9; + eq->unknown408[8] = 0xC9; + eq->unknown408[9] = 0xC9; + eq->unknown408[10] = 0xC9; + eq->unknown408[11] = 0xC9; + eq->unknown408[12] = 0xC9; + eq->unknown408[13] = 0xC9; + eq->unknown408[14] = 0xC9; + eq->unknown408[15] = 0xC9; + eq->unknown408[16] = 0xC9; + eq->unknown408[17] = 0xC9; + eq->unknown408[18] = 0xC9; + eq->unknown408[19] = 0xC9; + eq->unknown408[20] = 0xC9; + eq->unknown408[21] = 0xC9; + eq->unknown408[22] = 0xC9; + eq->unknown408[23] = 0xC9; + eq->unknown408[24] = 0xC9; + eq->unknown408[25] = 0xC9; + eq->unknown408[26] = 0xC9; + eq->unknown408[27] = 0xC9; + eq->unknown408[28] = 0xC9; + eq->unknown408[29] = 0xC9; + eq->unknown408[30] = 0xC9; + eq->unknown408[31] = 0xC9; + eq->unknown408[32] = 0xC9; + eq->unknown408[33] = 0xCA; //the client far as I can tell doesn't read past the 1 byte + 32 0xc9s, but still setting it to what we saw + eq->unknown408[34] = 0x8C; + eq->unknown408[35] = 0xEC; + + FINISH_ENCODE(); + } + + ENCODE(OP_GMTrainSkillConfirm) + { + ENCODE_LENGTH_EXACT(GMTrainSkillConfirm_Struct); + SETUP_DIRECT_ENCODE(GMTrainSkillConfirm_Struct, structs::GMTrainSkillConfirm_Struct); + + OUT(SkillID); + OUT(Cost); + OUT(NewSkill); + OUT_str(TrainerName); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + EQApplicationPacket* in = *p; + *p = nullptr; + Object_Struct* emu = (Object_Struct*)in->pBuffer; + + SerializeBuffer buffer; + buffer.WriteUInt32(emu->drop_id); + buffer.WriteString(emu->object_name); + buffer.WriteUInt16(emu->zone_id); + buffer.WriteUInt16(emu->zone_instance); + buffer.WriteUInt32(emu->drop_id); //this is some other sub but it's okay to duplicate + buffer.WriteUInt32(0); //expires + buffer.WriteFloat(emu->heading); + buffer.WriteFloat(emu->tilt_x); + buffer.WriteFloat(emu->tilt_y); + buffer.WriteFloat(emu->size != 0 && (float)emu->size < 5000.f ? (float)((float)emu->size / 100.0f) : 1.f); //size, with weird peq hack + buffer.WriteFloat(emu->y); + buffer.WriteFloat(emu->x); + buffer.WriteFloat(emu->z); + buffer.WriteFloat(emu->object_type); //weight + + auto outapp = new EQApplicationPacket(OP_GroundSpawn, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + + delete in; + } + + ENCODE(OP_HPUpdate) + { + SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct, structs::SpawnHPUpdate_Struct); + + OUT(spawn_id); + OUT(cur_hp); + OUT(max_hp); + + FINISH_ENCODE(); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + eq->unknown006[0] = 0; + eq->unknown006[1] = 0; + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemPacket) + { + EQApplicationPacket* in = *p; + *p = nullptr; + uchar* __emu_buffer = in->pBuffer; + ItemPacket_Struct* old_item_pkt = (ItemPacket_Struct*)__emu_buffer; + + auto type = ServerToLaurionItemPacketType(old_item_pkt->PacketType); + if (type == item::ItemPacketType::ItemPacketInvalid) { + delete in; + return; + } + + switch (type) + { + case item::ItemPacketType::ItemPacketParcel: { + ParcelMessaging_Struct pms{}; + EQ::Util::MemoryStreamReader ss(reinterpret_cast(in->pBuffer), in->size); + cereal::BinaryInputArchive ar(ss); + ar(pms); + + uint32 player_name_length = pms.player_name.length(); + uint32 note_length = pms.note.length(); + + auto* int_struct = (EQ::InternalSerializedItem_Struct*)pms.serialized_item.data(); + + SerializeBuffer buffer; + buffer.WriteInt32((int32_t)type); + SerializeItem(buffer, (const EQ::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); + + buffer.WriteUInt32(pms.sent_time); + buffer.WriteLengthString(pms.player_name); + buffer.WriteLengthString(pms.note); + + auto outapp = new EQApplicationPacket(OP_ItemPacket, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + break; + } + default: { + EQ::InternalSerializedItem_Struct* int_struct = (EQ::InternalSerializedItem_Struct*)(&__emu_buffer[4]); + SerializeBuffer buffer; + buffer.WriteInt32((int32_t)type); + SerializeItem(buffer, (const EQ::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); + + auto outapp = new EQApplicationPacket(OP_ItemPacket, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + } + } + + delete in; + } + ENCODE(OP_LogServer) { SETUP_VAR_ENCODE(LogServer_Struct); ALLOC_LEN_ENCODE(1840); @@ -244,319 +810,328 @@ namespace Laurion FINISH_ENCODE(); } - ENCODE(OP_SendMembership) { - ENCODE_LENGTH_EXACT(Membership_Struct); - SETUP_DIRECT_ENCODE(Membership_Struct, structs::Membership_Struct); + ENCODE(OP_ManaChange) { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - eq->membership = emu->membership; - eq->races = emu->races; - eq->classes = emu->classes; - eq->entrysize = 33; - eq->entries[0] = -1; // Max AA Restriction - eq->entries[1] = -1; // Max Level Restriction - eq->entries[2] = -1; // Max Char Slots per Account (not used by client?) - eq->entries[3] = -1; // SpellTier - eq->entries[4] = -1; // Main Inventory Size - eq->entries[5] = -1; // Max Platinum per level - eq->entries[6] = 1; // Send Mail - eq->entries[7] = 1; // Use Parcels - eq->entries[8] = 1; // Loyalty - eq->entries[9] = -1; // Merc Tiers - eq->entries[10] = 1; // Housing - eq->entries[11] = -1; // Shared Bank Slots - eq->entries[12] = -1; // Max Journal Quests - eq->entries[13] = 1; // CreateGuild - eq->entries[14] = 1; // Bazaar - eq->entries[15] = 1; // Barter - eq->entries[16] = 1; // Chat - eq->entries[17] = 1; // Petition - eq->entries[18] = 1; // Advertising - eq->entries[19] = -1; // UseItem - eq->entries[20] = -1; // StartingCity - eq->entries[21] = 1; // Ornament - eq->entries[22] = 0; // HeroicCharacter - eq->entries[23] = 0; // AutoGrantAA - eq->entries[24] = 0; // MountKeyRingSlots - eq->entries[25] = 0; // IllusionKeyRingSlots - eq->entries[26] = 0; // FamiliarKeyRingSlots - eq->entries[27] = 0; // FamiliarAutoLeave - eq->entries[28] = 0; // HeroForgeKeyRingSlots - eq->entries[29] = 0; // DragonHoardSlots - eq->entries[30] = 0; // TeleportKeyRingSlots - eq->entries[31] = 0; // PersonalDepotSlots - eq->entries[32] = 0; + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + OUT(keepcasting); + OUT(slot); FINISH_ENCODE(); } - ENCODE(OP_SendMembershipDetails) { - ENCODE_LENGTH_EXACT(Membership_Details_Struct); - SETUP_DIRECT_ENCODE(Membership_Details_Struct, structs::Membership_Details_Struct); + ENCODE(OP_MobHealth) { + ENCODE_LENGTH_EXACT(SpawnHPUpdate_Struct2); + SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct2, structs::MobHealth_Struct); - int32 settings[96][3] = { - { 0, 0, 250 }, { 1, 0, 1000 }, { 0, 1, -1 }, { 1, 1, -1 }, - { 0, 2, 2 }, { 2, 0, -1 }, { 3, 0, -1 }, { 1, 2, 4 }, - { 0, 3, 1 }, { 2, 1, -1 }, { 3, 1, -1 }, { 1, 3, 1 }, - { 0, 4, -1 }, { 2, 2, -1 }, { 3, 2, -1 }, { 1, 4, -1 }, - { 0, 5, -1 }, { 2, 3, -1 }, { 3, 3, -1 }, { 1, 5, -1 }, - { 0, 6, 0 }, { 2, 4, -1 }, { 3, 4, -1 }, { 1, 6, 0 }, - { 0, 7, 1 }, { 2, 5, -1 }, { 3, 5, -1 }, { 1, 7, 1 }, - { 0, 8, 1 }, { 2, 6, 1 }, { 3, 6, 1 }, { 1, 8, 1 }, - { 0, 9, 5 }, { 2, 7, 1 }, { 3, 7, 1 }, { 1, 9, 5 }, - { 0, 10, 0 }, { 2, 8, 1 }, { 3, 8, 1 }, { 0, 11, -1 }, - { 1, 10, 1 }, { 2, 9, -1 }, { 3, 9, -1 }, { 0, 12, -1 }, - { 1, 11, -1 }, { 2, 10, 1 }, { 3, 10, 1 }, { 0, 13, 0 }, - { 1, 12, -1 }, { 2, 11, -1 }, { 3, 11, -1 }, { 0, 14, 0 }, - { 1, 13, 1 }, { 2, 12, -1 }, { 3, 12, -1 }, { 0, 15, 0 }, - { 1, 14, 0 }, { 2, 13, 1 }, { 3, 13, 1 }, { 0, 16, 0 }, - { 1, 15, 0 }, { 2, 14, 1 }, { 3, 14, 1 }, { 0, 17, 0 }, - { 1, 16, 1 }, { 2, 15, 1 }, { 3, 15, 1 }, { 0, 18, 0 }, - { 1, 17, 0 }, { 2, 16, 1 }, { 3, 16, 1 }, { 0, 19, 0 }, - { 1, 18, 0 }, { 2, 17, 1 }, { 3, 17, 1 }, { 0, 20, 0 }, - { 1, 19, 0 }, { 2, 18, 1 }, { 3, 18, 1 }, { 0, 21, 0 }, - { 1, 20, 0 }, { 2, 19, -1 }, { 3, 19, -1 }, { 0, 22, 0 }, - { 1, 21, 0 }, { 2, 20, -1 }, { 3, 20, -1 }, { 2, 21, 0 }, - { 0, 23, 0 }, { 1, 22, 0 }, { 3, 21, 0 }, { 2, 22, 0 }, - { 1, 23, 0 }, { 3, 22, 0 }, { 2, 23, 0 }, { 3, 23, 0 } - }; - - uint32 races[17][2] = { - { 1, 131071 }, - { 333, 131071 }, - { 90287, 131071 }, - { 90289, 16 }, - { 90290, 32 }, - { 90291, 64 }, - { 90292, 128 }, - { 90293, 256 }, - { 90294, 512 }, - { 90295, 1024 }, - { 90296, 2048 }, - { 90297, 8192 }, - { 90298, 16384 }, - { 90299, 32768 }, - { 90300, 65536 }, - { 2012271, 131071 }, - { 2012277, 131071 } - }; - - uint32 classes[17][2] = { - { 1, 131071 }, - { 333, 131071 }, - { 90287, 131071 }, - { 90301, 8 }, - { 90302, 16 }, - { 90303, 32 }, - { 90304, 64 }, - { 90305, 128 }, - { 90306, 256 }, - { 90307, 1024 }, - { 90308, 2048 }, - { 90309, 8192 }, - { 90310, 16384 }, - { 90311, 32768 }, - { 90312, 65536 }, - { 2012271, 131071 }, - { 2012277, 131071 } - }; - - eq->membership_setting_count = 96; - - for (int i = 0; i < 96; ++i) { - eq->settings[i].setting_index = (int8)settings[i][0]; - eq->settings[i].setting_id = settings[i][1]; - eq->settings[i].setting_value = settings[i][2]; - } - - eq->class_entry_count = 17; - for (int i = 0; i < 17; ++i) { - eq->membership_classes[i].purchase_id = classes[i][0]; - eq->membership_classes[i].bitwise_entry = classes[i][1]; - } - - eq->race_entry_count = 17; - for (int i = 0; i < 17; ++i) { - eq->membership_races[i].purchase_id = races[i][0]; - eq->membership_races[i].bitwise_entry = races[i][1]; - } - - eq->exit_url_length = 0; + OUT(spawn_id); + OUT(hp); FINISH_ENCODE(); } - ENCODE(OP_SendMaxCharacters) { - ENCODE_LENGTH_EXACT(MaxCharacters_Struct); - SETUP_DIRECT_ENCODE(MaxCharacters_Struct, structs::MaxCharacters_Struct); - - //OUT(max_chars); - eq->max_chars = 8; //needs to be fixed - eq->marketplace_chars = 0; - eq->unknown008 = -1; - eq->unknown00c = 196608; - eq->unknown010 = 0; - eq->unknown014 = 0; - eq->unknown018 = 0; - eq->unknown01c = 0; - eq->unknown020 = -1; - eq->unknown024 = 0; - eq->unknown028 = 0; - eq->unknown02c = 0; - eq->unknown030 = 0; - eq->unknown034 = 0; - - FINISH_ENCODE(); - } - - ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); - - // Zero-character count shunt - if (emu->CharCount == 0) { - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); - eq->CharCount = emu->CharCount; - - FINISH_ENCODE(); - return; - } - - unsigned char* emu_ptr = __emu_buffer; - emu_ptr += sizeof(CharacterSelect_Struct); - CharacterSelectEntry_Struct* emu_cse = (CharacterSelectEntry_Struct*)nullptr; - - size_t names_length = 0; - size_t character_count = 0; - for (; character_count < emu->CharCount; ++character_count) { - emu_cse = (CharacterSelectEntry_Struct*)emu_ptr; - names_length += strlen(emu_cse->Name); - emu_ptr += sizeof(CharacterSelectEntry_Struct); - } - - size_t total_length = sizeof(structs::CharacterSelect_Struct) - + character_count * sizeof(structs::CharacterSelectEntry_Struct) - + names_length; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - structs::CharacterSelectEntry_Struct* eq_cse = (structs::CharacterSelectEntry_Struct*)nullptr; - - eq->CharCount = character_count; - - emu_ptr = __emu_buffer; - emu_ptr += sizeof(CharacterSelect_Struct); - - unsigned char* eq_ptr = __packet->pBuffer; - eq_ptr += sizeof(structs::CharacterSelect_Struct); - - for (int counter = 0; counter < character_count; ++counter) { - emu_cse = (CharacterSelectEntry_Struct*)emu_ptr; - eq_cse = (structs::CharacterSelectEntry_Struct*)eq_ptr; // base address - - strcpy(eq_cse->Name, emu_cse->Name); - eq_ptr += strlen(emu_cse->Name); - - eq_cse = (structs::CharacterSelectEntry_Struct*)eq_ptr; - eq_cse->Name[0] = '\0'; - - eq_cse->Class = emu_cse->Class; - eq_cse->Race = emu_cse->Race; - eq_cse->Level = emu_cse->Level; - eq_cse->ShroudClass = emu_cse->ShroudClass; - eq_cse->ShroudRace = emu_cse->ShroudRace; - eq_cse->Zone = emu_cse->Zone; - eq_cse->Instance = emu_cse->Instance; - eq_cse->Gender = emu_cse->Gender; - eq_cse->Face = emu_cse->Face; - - for (int equip_index = 0; equip_index < EQ::textures::materialCount; equip_index++) { - eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material; - eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1; - eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteModel; - eq_cse->Equip[equip_index].HeroForgeModel = emu_cse->Equip[equip_index].HerosForgeModel; - eq_cse->Equip[equip_index].Material2 = emu_cse->Equip[equip_index].Unknown2; - eq_cse->Equip[equip_index].Color = emu_cse->Equip[equip_index].Color; - } - - eq_cse->Unknown1 = 255; - eq_cse->Unknown2 = 0; - eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo; - eq_cse->DrakkinDetails = emu_cse->DrakkinDetails; - eq_cse->Deity = emu_cse->Deity; - eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile; - eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile; - eq_cse->HairColor = emu_cse->HairColor; - eq_cse->BeardColor = emu_cse->BeardColor; - eq_cse->EyeColor1 = emu_cse->EyeColor1; - eq_cse->EyeColor2 = emu_cse->EyeColor2; - eq_cse->HairStyle = emu_cse->HairStyle; - eq_cse->Beard = emu_cse->Beard; - eq_cse->GoHome = emu_cse->GoHome; - eq_cse->Tutorial = emu_cse->Tutorial; - eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage; - eq_cse->Enabled = emu_cse->Enabled; - eq_cse->LastLogin = emu_cse->LastLogin; - eq_cse->Unknown3 = 0; - eq_cse->Unknown4 = 0; - eq_cse->Unknown5 = 0; - eq_cse->Unknown6 = 0; - eq_cse->Unknown7 = 0; - eq_cse->CharacterId = 0; - eq_cse->Unknown8 = 1; - - emu_ptr += sizeof(CharacterSelectEntry_Struct); - eq_ptr += sizeof(structs::CharacterSelectEntry_Struct); - } - - DumpPacket(__packet); - - FINISH_ENCODE(); - } - - ENCODE(OP_ExpansionInfo) + ENCODE(OP_MoneyOnCorpse) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + ENCODE_LENGTH_EXACT(moneyOnCorpseStruct); + SETUP_DIRECT_ENCODE(moneyOnCorpseStruct, structs::moneyOnCorpseStruct); - OUT(Expansions); + eq->type = emu->response; + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + eq->flags = 0; FINISH_ENCODE(); } - ENCODE(OP_SpawnAppearance) + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + Log(Logs::Detail, Logs::Netcode, "Laurion::ENCODE(OP_MoveItem)"); + + eq->from_slot = ServerToLaurionSlot(emu->from_slot); + eq->to_slot = ServerToLaurionSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) { + EQApplicationPacket* in = *p; + *p = nullptr; + + unsigned char* __emu_buffer = in->pBuffer; + NewZone_Struct* emu = (NewZone_Struct*)__emu_buffer; + + SerializeBuffer buffer; + /* + char Shortname[]; + char Longname[]; + char WeatherType[]; + char WeatherTypeOverride[]; + char SkyType[]; + char SkyTypeOverride[]; + */ + buffer.WriteString(emu->zone_short_name); + buffer.WriteString(emu->zone_long_name); + buffer.WriteString(""); + buffer.WriteString(""); + buffer.WriteString(""); + buffer.WriteString(""); + + /* + s32 ZoneType; + s16 ZoneId; + s16 ZoneInstance; + float ZoneExpModifier; + s32 GroupLvlExpRelated; + s32 FilterID; + s32 Unknown1; + */ + buffer.WriteInt32(emu->ztype); + buffer.WriteInt16(emu->zone_id); + buffer.WriteInt16(emu->zone_instance); + buffer.WriteFloat(emu->zone_exp_multiplier); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + + //float FogDensity; + buffer.WriteFloat(emu->fog_density); + + //WeatherState state[4]; + for (int i = 0; i < 4; ++i) { + /* + float FogStart; + float FogEnd; + u8 FogRed; + u8 FogGreen; + u8 FogBlue; + u8 RainChance; + u8 RainDuration; + u8 SnowChance; + u8 SnowDuration; + */ + + buffer.WriteFloat(emu->fog_minclip[i]); + buffer.WriteFloat(emu->fog_maxclip[i]); + buffer.WriteUInt8(emu->fog_red[i]); + buffer.WriteUInt8(emu->fog_green[i]); + buffer.WriteUInt8(emu->fog_blue[i]); + buffer.WriteUInt8(emu->rain_chance[i]); + buffer.WriteUInt8(emu->rain_duration[i]); + buffer.WriteUInt8(emu->snow_chance[i]); + buffer.WriteUInt8(emu->snow_duration[i]); + + } + + /* + u8 PrecipitationType; + float BloomIntensity; + float ZoneGravity; + s32 LavaDamage; + s32 MinLavaDamage; + */ + buffer.WriteUInt8(emu->sky); + buffer.WriteFloat(1.0f); + buffer.WriteFloat(emu->gravity); + buffer.WriteInt32(emu->lava_damage); + buffer.WriteInt32(emu->min_lava_damage); + + /* + s32 TimeStringID; + s32 Unknown3; + s32 SkyLock; + s32 SkyLockOverride; + */ + buffer.WriteInt32(0); + buffer.WriteInt32(1); + buffer.WriteInt32(0); + buffer.WriteInt32(-1); + + /* + float SafeY; + float SafeX; + float SafeZ; + float SafeHeading; + float Ceiling; + float Floor; + */ + + buffer.WriteFloat(emu->safe_y); + buffer.WriteFloat(emu->safe_x); + buffer.WriteFloat(emu->safe_z); + buffer.WriteFloat(emu->safe_heading); + buffer.WriteFloat(emu->max_z); + buffer.WriteFloat(emu->underworld); + + /* + float MinClip; + float MaxClip; + s32 FallThroughWorldTeleportID; + */ + buffer.WriteFloat(emu->minclip); + buffer.WriteFloat(emu->maxclip); + buffer.WriteInt32(emu->underworld_teleport_index); + + /* + s32 Unknown4; + s32 ScriptIDHour; + s32 ScriptIDMinute; + s32 ScriptIDTick; + s32 ScriptIDOnPlayerDeath; + s32 ScriptIDOnNPCDeath; + s32 ScriptIDPlayerEnteringZone; + s32 ScriptIDOnZonePop; + s32 ScriptIDNPCLoot; + s32 Unknown4b; + s32 ScriptIDOnFishing; + s32 ScriptIDOnForage; + s32 Unknown4c; + */ + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + buffer.WriteInt32(0); + + /* + s32 NPCAgroMaxDist; + */ + buffer.WriteInt32(600); + + /* + s32 ForageLow; + s32 ForageMedium; + s32 ForageHigh; + s32 ForageSpecial; + s32 FishingLow; + s32 FishingMedium; + s32 FishingHigh; + s32 FishingRelated; + */ + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + buffer.WriteInt32(-1); + + /* + s32 CanPlaceCampsite; + s32 CanPlaceGuildBanner; + s32 Unknown4d; + */ + + buffer.WriteInt32(2); + buffer.WriteInt32(2); + buffer.WriteInt32(0); + + /* + s32 FastRegenHP; + s32 FastRegenMana; + s32 FastRegenEndurance; + */ + buffer.WriteInt32(emu->fast_regen_hp); + buffer.WriteInt32(emu->fast_regen_mana); + buffer.WriteInt32(emu->fast_regen_endurance); + + /* + u8 NewEngineZone; + u8 SkyEnabled; + u8 FogOnOff; + u8 ClimateType; + u8 bNoPlayerLight; + */ + buffer.WriteUInt8(0); //not sure what happens if we set this incorrectly but we probably need to add this to the zone database + buffer.WriteUInt8(1); + buffer.WriteUInt8(1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + + /* + u8 bUnknown5; + u8 bNoAttack; + u8 bAllowPVP; + u8 bNoEncumber; + u8 Unknown6; + u8 Unknown7; + */ + buffer.WriteUInt8(1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + + /* + u8 bNoLevitate; + u8 bNoBuffExpiration; + u8 bDisallowManaStone; + u8 bNoBind; + u8 bNoCallOfTheHero; + u8 bUnknown8; + u8 bNoFear; + u8 bUnknown9; + */ + + buffer.WriteUInt8(0); + buffer.WriteUInt8(emu->suspend_buffs); + buffer.WriteUInt8(1); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + + auto outapp = new EQApplicationPacket(OP_NewZone, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + + delete in; + } + + ENCODE(OP_OnLevelMessage) { EQApplicationPacket* in = *p; *p = nullptr; - - unsigned char* emu_buffer = in->pBuffer; - - SpawnAppearance_Struct* sas = (SpawnAppearance_Struct*)emu_buffer; - - if (sas->type != AppearanceType::Size) - { - //laurion struct is different than rof2's but the idea is the same - //we will probably want to better implement Laurion's structure later - auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); - structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; - - eq->spawn_id = sas->spawn_id; - eq->type = ServerToLaurionSpawnAppearanceType(sas->type); - eq->parameter = sas->parameter; - - dest->FastQueuePacket(&outapp, ack_req); - delete in; - return; - } - - auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); - - ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer; - - css->EntityID = sas->spawn_id; - css->Size = (float)sas->parameter; - css->Unknown08 = 0; - css->Unknown12 = 1.0f; - + OnLevelMessage_Struct* emu = (OnLevelMessage_Struct*)in->pBuffer; + SerializeBuffer buffer; + + buffer.WriteLengthString(emu->Title); + buffer.WriteLengthString(emu->Text); + buffer.WriteLengthString(emu->ButtonName0); + buffer.WriteLengthString(emu->ButtonName1); + buffer.WriteUInt8(emu->Buttons); + buffer.WriteUInt8(emu->SoundControls); + buffer.WriteUInt32(emu->Duration); + buffer.WriteUInt32(emu->PopupID); + buffer.WriteUInt32(emu->NegativeID); + buffer.WriteUInt32(0); //seen -1 & 0 + buffer.WriteUInt32(0); //seen 0 + + auto outapp = new EQApplicationPacket(OP_OnLevelMessage, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); dest->FastQueuePacket(&outapp, ack_req); + delete in; } @@ -779,7 +1354,7 @@ namespace Laurion s32 index; s32 points_spent; s32 charges_spent; - u8 unknown1; + u8 unknown1; */ out.WriteUInt32(emu->aa_array[i].AA); out.WriteUInt32(emu->aa_array[i].value); @@ -1200,7 +1775,7 @@ namespace Laurion //u32 claim_count; out.WriteUInt32(0); //Claim claims[claim_count]; - + //Tribute tribute; /* u32 BenefitTimer; @@ -1277,7 +1852,7 @@ namespace Laurion out.WriteUInt32(emu->careerEbonCrystals); /* - u32 momentum_balance; + u32 momentum_balance; u32 loyalty_reward_balance; u32 parcel_status; */ @@ -1572,8 +2147,703 @@ namespace Laurion delete in; } + ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } + + ENCODE(OP_RespondAA) + { + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + eq->aa_spent = emu->aa_spent; + // These fields may need to be correctly populated at some point + eq->aapoints_assigned[0] = emu->aa_spent; + eq->aapoints_assigned[1] = 0; + eq->aapoints_assigned[2] = 0; + eq->aapoints_assigned[3] = 0; + eq->aapoints_assigned[4] = 0; + eq->aapoints_assigned[5] = 0; + + for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) + { + eq->aa_list[i].AA = emu->aa_list[i].AA; + eq->aa_list[i].value = emu->aa_list[i].value; + eq->aa_list[i].charges = emu->aa_list[i].charges; + eq->aa_list[i].bUnknown0x0c = false; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_RequestClientZoneChange) + { + ENCODE_LENGTH_EXACT(RequestClientZoneChange_Struct); + SETUP_DIRECT_ENCODE(RequestClientZoneChange_Struct, structs::RequestClientZoneChange_Struct); + + OUT(zone_id); + OUT(instance_id); + OUT(y); + OUT(x); + OUT(z); + OUT(heading); + eq->type = 0x0b; + eq->unknown004 = 0xffffffff; + eq->unknown172 = 0x0168b500; + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + EQApplicationPacket* in = *p; + *p = nullptr; + AARankInfo_Struct* emu = (AARankInfo_Struct*)in->pBuffer; + + std::vector skill; + std::vector points; + in->SetReadPosition(sizeof(AARankInfo_Struct) + emu->total_effects * sizeof(AARankEffect_Struct)); + for (auto i = 0; i < emu->total_prereqs; ++i) { + skill.push_back(in->ReadUInt32()); + points.push_back(in->ReadUInt32()); + } + + SerializeBuffer buffer; + + /* + s32 AbilityId; + u8 ShowInAbilityWindow; + s32 ShortName; + s32 ShortName2; + s32 Name; + s32 Desc; + */ + + buffer.WriteUInt32(emu->id); + buffer.WriteUInt8(1); + buffer.WriteInt32(emu->upper_hotkey_sid); + buffer.WriteInt32(emu->lower_hotkey_sid); + buffer.WriteInt32(emu->title_sid); + buffer.WriteInt32(emu->desc_sid); + + /* + s32 MinLevel; + s32 Cost; + s32 GroupID; + s32 CurrentRank; + */ + buffer.WriteInt32(emu->level_req); + buffer.WriteInt32(emu->cost); + buffer.WriteUInt32(emu->seq); + buffer.WriteUInt32(emu->current_level); + + /* + u32 PrereqSkillCount; + s32 PrereqSkills[PrereqSkillCount]; + u32 PrereqLevelCount; + s32 PrereqLevels[PrereqLevelCount]; + */ + + if (emu->total_prereqs) { + buffer.WriteUInt32(emu->total_prereqs); + for (auto& e : skill) + buffer.WriteInt32(e); + buffer.WriteUInt32(emu->total_prereqs); + for (auto& e : points) + buffer.WriteInt32(e); + } + else { + buffer.WriteUInt32(1); + buffer.WriteUInt32(0); + buffer.WriteUInt32(1); + buffer.WriteUInt32(0); + } + + /* + u32 Type; + s32 SpellId; + */ + buffer.WriteInt32(emu->type); + buffer.WriteInt32(emu->spell); + + /* + u32 TimerIdCount; + s32 TimerIds[TimerIdCount]; + s32 ReuseTimer; + u32 Classes; + */ + buffer.WriteInt32(1); + buffer.WriteInt32(emu->spell_type); + buffer.WriteInt32(emu->spell_refresh); + buffer.WriteInt32(emu->classes); + + /* + s32 MaxRank; + s32 PrevAbilityId; + s32 NextAbilityId; + s32 TotalPoints; + */ + + buffer.WriteInt32(emu->max_level); + buffer.WriteInt32(emu->prev_id); + buffer.WriteInt32(emu->next_id); + buffer.WriteInt32(emu->total_cost); + + /* + u8 bRefund; + s32 QuestOnly; + u8 bIgnoreDeLevel; + */ + buffer.WriteUInt8(0); + buffer.WriteUInt32(emu->grant_only); + buffer.WriteUInt8(0); + + /* + s32 Charges; + s32 Expansion; + s32 Category; + */ + buffer.WriteUInt32(emu->charges); + buffer.WriteInt32(emu->expansion); + buffer.WriteInt32(emu->category); + + /* + u8 bShroud; + u8 bBetaOnly; + u8 bResetOnDeath; + u8 AutoGrant; + */ + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + buffer.WriteUInt8(0); + + /* + s32 AutoGrantExpansion; + s32 Unknown098; + u8 Unknown09C; + */ + + buffer.WriteInt32(emu->expansion); + buffer.WriteInt32(0); + buffer.WriteUInt8(0); + + //u32 TotalEffects; + buffer.WriteUInt32(emu->total_effects); + in->SetReadPosition(sizeof(AARankInfo_Struct)); + for (auto i = 0; i < emu->total_effects; ++i) { + auto skill_id = in->ReadUInt32(); + auto base1 = in->ReadUInt32(); + auto base2 = in->ReadUInt32(); + auto slot = in->ReadUInt32(); + + /* + u32 effect_id; + s64 base1; + s64 base2; + u32 slot; + */ + + buffer.WriteUInt32(skill_id); + buffer.WriteInt64(base1); + buffer.WriteInt64(base2); + buffer.WriteUInt32(slot); + } + + auto outapp = new EQApplicationPacket(OP_SendAATable, buffer.size()); + outapp->WriteData(buffer.buffer(), buffer.size()); + dest->FastQueuePacket(&outapp, ack_req); + + delete in; + } + + ENCODE(OP_SendCharInfo) { + ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + // Zero-character count shunt + if (emu->CharCount == 0) { + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct)); + eq->CharCount = emu->CharCount; + + FINISH_ENCODE(); + return; + } + + unsigned char* emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + CharacterSelectEntry_Struct* emu_cse = (CharacterSelectEntry_Struct*)nullptr; + + size_t names_length = 0; + size_t character_count = 0; + for (; character_count < emu->CharCount; ++character_count) { + emu_cse = (CharacterSelectEntry_Struct*)emu_ptr; + names_length += strlen(emu_cse->Name); + emu_ptr += sizeof(CharacterSelectEntry_Struct); + } + + size_t total_length = sizeof(structs::CharacterSelect_Struct) + + character_count * sizeof(structs::CharacterSelectEntry_Struct) + + names_length; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + structs::CharacterSelectEntry_Struct* eq_cse = (structs::CharacterSelectEntry_Struct*)nullptr; + + eq->CharCount = character_count; + + emu_ptr = __emu_buffer; + emu_ptr += sizeof(CharacterSelect_Struct); + + unsigned char* eq_ptr = __packet->pBuffer; + eq_ptr += sizeof(structs::CharacterSelect_Struct); + + for (int counter = 0; counter < character_count; ++counter) { + emu_cse = (CharacterSelectEntry_Struct*)emu_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct*)eq_ptr; // base address + + strcpy(eq_cse->Name, emu_cse->Name); + eq_ptr += strlen(emu_cse->Name); + + eq_cse = (structs::CharacterSelectEntry_Struct*)eq_ptr; + eq_cse->Name[0] = '\0'; + + eq_cse->Class = emu_cse->Class; + eq_cse->Race = emu_cse->Race; + eq_cse->Level = emu_cse->Level; + eq_cse->ShroudClass = emu_cse->ShroudClass; + eq_cse->ShroudRace = emu_cse->ShroudRace; + eq_cse->Zone = emu_cse->Zone; + eq_cse->Instance = emu_cse->Instance; + eq_cse->Gender = emu_cse->Gender; + eq_cse->Face = emu_cse->Face; + + for (int equip_index = 0; equip_index < EQ::textures::materialCount; equip_index++) { + eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material; + eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1; + eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteModel; + eq_cse->Equip[equip_index].HeroForgeModel = emu_cse->Equip[equip_index].HerosForgeModel; + eq_cse->Equip[equip_index].Material2 = emu_cse->Equip[equip_index].Unknown2; + eq_cse->Equip[equip_index].Color = emu_cse->Equip[equip_index].Color; + } + + eq_cse->Unknown1 = 255; + eq_cse->Unknown2 = 0; + eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo; + eq_cse->DrakkinDetails = emu_cse->DrakkinDetails; + eq_cse->Deity = emu_cse->Deity; + eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile; + eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile; + eq_cse->HairColor = emu_cse->HairColor; + eq_cse->BeardColor = emu_cse->BeardColor; + eq_cse->EyeColor1 = emu_cse->EyeColor1; + eq_cse->EyeColor2 = emu_cse->EyeColor2; + eq_cse->HairStyle = emu_cse->HairStyle; + eq_cse->Beard = emu_cse->Beard; + eq_cse->GoHome = emu_cse->GoHome; + eq_cse->Tutorial = emu_cse->Tutorial; + eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage; + eq_cse->Enabled = emu_cse->Enabled; + eq_cse->LastLogin = emu_cse->LastLogin; + eq_cse->Unknown3 = 0; + eq_cse->Unknown4 = 0; + eq_cse->Unknown5 = 0; + eq_cse->Unknown6 = 0; + eq_cse->Unknown7 = 0; + eq_cse->CharacterId = 0; + eq_cse->Unknown8 = 1; + + emu_ptr += sizeof(CharacterSelectEntry_Struct); + eq_ptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + DumpPacket(__packet); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendMaxCharacters) { + ENCODE_LENGTH_EXACT(MaxCharacters_Struct); + SETUP_DIRECT_ENCODE(MaxCharacters_Struct, structs::MaxCharacters_Struct); + + //OUT(max_chars); + eq->max_chars = 8; //needs to be fixed + eq->marketplace_chars = 0; + eq->unknown008 = -1; + eq->unknown00c = 196608; + eq->unknown010 = 0; + eq->unknown014 = 0; + eq->unknown018 = 0; + eq->unknown01c = 0; + eq->unknown020 = -1; + eq->unknown024 = 0; + eq->unknown028 = 0; + eq->unknown02c = 0; + eq->unknown030 = 0; + eq->unknown034 = 0; + + FINISH_ENCODE(); + } + + ENCODE(OP_SendMembership) { + ENCODE_LENGTH_EXACT(Membership_Struct); + SETUP_DIRECT_ENCODE(Membership_Struct, structs::Membership_Struct); + + eq->membership = emu->membership; + eq->races = emu->races; + eq->classes = emu->classes; + eq->entrysize = 33; + eq->entries[0] = -1; // Max AA Restriction + eq->entries[1] = -1; // Max Level Restriction + eq->entries[2] = -1; // Max Char Slots per Account (not used by client?) + eq->entries[3] = -1; // SpellTier + eq->entries[4] = -1; // Main Inventory Size + eq->entries[5] = -1; // Max Platinum per level + eq->entries[6] = 1; // Send Mail + eq->entries[7] = 1; // Use Parcels + eq->entries[8] = 1; // Loyalty + eq->entries[9] = -1; // Merc Tiers + eq->entries[10] = 1; // Housing + eq->entries[11] = -1; // Shared Bank Slots + eq->entries[12] = -1; // Max Journal Quests + eq->entries[13] = 1; // CreateGuild + eq->entries[14] = 1; // Bazaar + eq->entries[15] = 1; // Barter + eq->entries[16] = 1; // Chat + eq->entries[17] = 1; // Petition + eq->entries[18] = 1; // Advertising + eq->entries[19] = -1; // UseItem + eq->entries[20] = -1; // StartingCity + eq->entries[21] = 1; // Ornament + eq->entries[22] = 0; // HeroicCharacter + eq->entries[23] = 0; // AutoGrantAA + eq->entries[24] = 0; // MountKeyRingSlots + eq->entries[25] = 0; // IllusionKeyRingSlots + eq->entries[26] = 0; // FamiliarKeyRingSlots + eq->entries[27] = 0; // FamiliarAutoLeave + eq->entries[28] = 0; // HeroForgeKeyRingSlots + eq->entries[29] = 0; // DragonHoardSlots + eq->entries[30] = 0; // TeleportKeyRingSlots + eq->entries[31] = 0; // PersonalDepotSlots + eq->entries[32] = 0; + + FINISH_ENCODE(); + } + + ENCODE(OP_SendMembershipDetails) { + ENCODE_LENGTH_EXACT(Membership_Details_Struct); + SETUP_DIRECT_ENCODE(Membership_Details_Struct, structs::Membership_Details_Struct); + + int32 settings[96][3] = { + { 0, 0, 250 }, { 1, 0, 1000 }, { 0, 1, -1 }, { 1, 1, -1 }, + { 0, 2, 2 }, { 2, 0, -1 }, { 3, 0, -1 }, { 1, 2, 4 }, + { 0, 3, 1 }, { 2, 1, -1 }, { 3, 1, -1 }, { 1, 3, 1 }, + { 0, 4, -1 }, { 2, 2, -1 }, { 3, 2, -1 }, { 1, 4, -1 }, + { 0, 5, -1 }, { 2, 3, -1 }, { 3, 3, -1 }, { 1, 5, -1 }, + { 0, 6, 0 }, { 2, 4, -1 }, { 3, 4, -1 }, { 1, 6, 0 }, + { 0, 7, 1 }, { 2, 5, -1 }, { 3, 5, -1 }, { 1, 7, 1 }, + { 0, 8, 1 }, { 2, 6, 1 }, { 3, 6, 1 }, { 1, 8, 1 }, + { 0, 9, 5 }, { 2, 7, 1 }, { 3, 7, 1 }, { 1, 9, 5 }, + { 0, 10, 0 }, { 2, 8, 1 }, { 3, 8, 1 }, { 0, 11, -1 }, + { 1, 10, 1 }, { 2, 9, -1 }, { 3, 9, -1 }, { 0, 12, -1 }, + { 1, 11, -1 }, { 2, 10, 1 }, { 3, 10, 1 }, { 0, 13, 0 }, + { 1, 12, -1 }, { 2, 11, -1 }, { 3, 11, -1 }, { 0, 14, 0 }, + { 1, 13, 1 }, { 2, 12, -1 }, { 3, 12, -1 }, { 0, 15, 0 }, + { 1, 14, 0 }, { 2, 13, 1 }, { 3, 13, 1 }, { 0, 16, 0 }, + { 1, 15, 0 }, { 2, 14, 1 }, { 3, 14, 1 }, { 0, 17, 0 }, + { 1, 16, 1 }, { 2, 15, 1 }, { 3, 15, 1 }, { 0, 18, 0 }, + { 1, 17, 0 }, { 2, 16, 1 }, { 3, 16, 1 }, { 0, 19, 0 }, + { 1, 18, 0 }, { 2, 17, 1 }, { 3, 17, 1 }, { 0, 20, 0 }, + { 1, 19, 0 }, { 2, 18, 1 }, { 3, 18, 1 }, { 0, 21, 0 }, + { 1, 20, 0 }, { 2, 19, -1 }, { 3, 19, -1 }, { 0, 22, 0 }, + { 1, 21, 0 }, { 2, 20, -1 }, { 3, 20, -1 }, { 2, 21, 0 }, + { 0, 23, 0 }, { 1, 22, 0 }, { 3, 21, 0 }, { 2, 22, 0 }, + { 1, 23, 0 }, { 3, 22, 0 }, { 2, 23, 0 }, { 3, 23, 0 } + }; + + uint32 races[17][2] = { + { 1, 131071 }, + { 333, 131071 }, + { 90287, 131071 }, + { 90289, 16 }, + { 90290, 32 }, + { 90291, 64 }, + { 90292, 128 }, + { 90293, 256 }, + { 90294, 512 }, + { 90295, 1024 }, + { 90296, 2048 }, + { 90297, 8192 }, + { 90298, 16384 }, + { 90299, 32768 }, + { 90300, 65536 }, + { 2012271, 131071 }, + { 2012277, 131071 } + }; + + uint32 classes[17][2] = { + { 1, 131071 }, + { 333, 131071 }, + { 90287, 131071 }, + { 90301, 8 }, + { 90302, 16 }, + { 90303, 32 }, + { 90304, 64 }, + { 90305, 128 }, + { 90306, 256 }, + { 90307, 1024 }, + { 90308, 2048 }, + { 90309, 8192 }, + { 90310, 16384 }, + { 90311, 32768 }, + { 90312, 65536 }, + { 2012271, 131071 }, + { 2012277, 131071 } + }; + + eq->membership_setting_count = 96; + + for (int i = 0; i < 96; ++i) { + eq->settings[i].setting_index = (int8)settings[i][0]; + eq->settings[i].setting_id = settings[i][1]; + eq->settings[i].setting_value = settings[i][2]; + } + + eq->class_entry_count = 17; + for (int i = 0; i < 17; ++i) { + eq->membership_classes[i].purchase_id = classes[i][0]; + eq->membership_classes[i].bitwise_entry = classes[i][1]; + } + + eq->race_entry_count = 17; + for (int i = 0; i < 17; ++i) { + eq->membership_races[i].purchase_id = races[i][0]; + eq->membership_races[i].bitwise_entry = races[i][1]; + } + + eq->exit_url_length = 0; + + FINISH_ENCODE(); + } + + ENCODE(OP_SendZonepoints) + { + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); + + eq->count = emu->count; + for (uint32 i = 0; i < emu->count; ++i) + { + eq->zpe[i].iterator = emu->zpe[i].iterator; + eq->zpe[i].x = emu->zpe[i].x; + eq->zpe[i].y = emu->zpe[i].y; + eq->zpe[i].z = emu->zpe[i].z; + eq->zpe[i].heading = emu->zpe[i].heading; + eq->zpe[i].zoneid = emu->zpe[i].zoneid; + eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopRequest) + { + ENCODE_LENGTH_EXACT(MerchantClick_Struct); + SETUP_DIRECT_ENCODE(MerchantClick_Struct, structs::MerchantClickResponse_Struct); + + if (emu->command == 0) { + OUT(player_id); + eq->npc_id = 0; + } + else { + OUT(npc_id); + OUT(player_id); + OUT(rate); + OUT(tab_display); + eq->unknown028 = 256; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SkillUpdate) + { + ENCODE_LENGTH_EXACT(SkillUpdate_Struct); + SETUP_DIRECT_ENCODE(SkillUpdate_Struct, structs::SkillUpdate_Struct); + + OUT(skillId); + OUT(value); + eq->active = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_SpecialMesg) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + SerializeBuffer buf(in->size); + buf.WriteInt8(in->ReadUInt8()); // speak mode + buf.WriteInt8(in->ReadUInt8()); // journal mode + buf.WriteInt8(in->ReadUInt8()); // language + buf.WriteInt32(in->ReadUInt32()); // message type + buf.WriteInt32(in->ReadUInt32()); // target spawn id + + std::string name; + in->ReadString(name); // NPC names max out at 63 chars + + buf.WriteString(name); + + buf.WriteInt32(in->ReadUInt32()); // loc + buf.WriteInt32(in->ReadUInt32()); + buf.WriteInt32(in->ReadUInt32()); + + std::string old_message; + std::string new_message; + + in->ReadString(old_message); + + ServerToLaurionConvertLinks(new_message, old_message); + + buf.WriteString(new_message); + + auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_SpawnAppearance) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + unsigned char* emu_buffer = in->pBuffer; + + SpawnAppearance_Struct* sas = (SpawnAppearance_Struct*)emu_buffer; + + if (sas->type != AppearanceType::Size) + { + //laurion struct is different than rof2's but the idea is the same + //we will probably want to better implement Laurion's structure later + auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(structs::SpawnAppearance_Struct)); + structs::SpawnAppearance_Struct *eq = (structs::SpawnAppearance_Struct*)outapp->pBuffer; + + eq->spawn_id = sas->spawn_id; + eq->type = ServerToLaurionSpawnAppearanceType(sas->type); + eq->parameter = sas->parameter; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + return; + } + + auto outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); + + ChangeSize_Struct* css = (ChangeSize_Struct*)outapp->pBuffer; + + css->EntityID = sas->spawn_id; + css->Size = (float)sas->parameter; + css->Unknown08 = 0; + css->Unknown12 = 1.0f; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + + int r; + for (r = 0; r < door_count; r++) { + strncpy(eq[r].name, emu[r].name, 32); + eq[r].DefaultY = emu[r].yPos; + eq[r].DefaultX = emu[r].xPos; + eq[r].DefaultZ = emu[r].zPos; + eq[r].DefaultHeading = emu[r].heading; + eq[r].DefaultDoorAngle = emu[r].incline; + eq[r].Y = emu[r].yPos; + eq[r].X = emu[r].xPos; + eq[r].Z = emu[r].zPos; + eq[r].Heading = emu[r].heading; + //there's a door angle here but im not sure if / what we set it to since ive literally never seen it as anything but 0 on live + //based on pattern it probably is supposed to match the default angle? + //I'm not 100% sure this is a float it might be a uint32 + eq[r].DoorAngle = emu[r].incline; + eq[r].ScaleFactor = emu[r].size; + eq[r].Id = emu[r].doorId; + eq[r].Type = emu[r].opentype; + eq[r].State = emu[r].state_at_spawn; + eq[r].DefaultState = emu[r].invert_state; + eq[r].Param = emu[r].door_param; + eq[r].bVisible = 1; + eq[r].bUsable = 1; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + eq->wear_slot_id = emu->wear_slot_id; + eq->armor_id = emu->material; + eq->variation = emu->unknown06; + eq->material = emu->elite_material; + eq->new_armor_id = emu->hero_forge_model; + eq->new_armor_type = emu->unknown18; + eq->color = emu->color.Color; + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneChange) + { + ENCODE_LENGTH_EXACT(ZoneChange_Struct); + SETUP_DIRECT_ENCODE(ZoneChange_Struct, structs::ZoneChange_Struct); + + memcpy(eq->char_name, emu->char_name, sizeof(emu->char_name)); + OUT(zoneID); + OUT(instanceID); + OUT(y); + OUT(x); + OUT(z) + OUT(zone_reason); + OUT(success); + + if (eq->success < 0) + eq->success -= 1; + + FINISH_ENCODE(); + } + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + ENCODE(OP_ZonePlayerToBind) + { + SETUP_VAR_ENCODE(ZonePlayerToBind_Struct); + ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name)); + + __packet->SetWritePosition(0); + __packet->WriteUInt16(emu->bind_zone_id); + __packet->WriteUInt16(emu->bind_instance_id); + __packet->WriteFloat(emu->x); + __packet->WriteFloat(emu->y); + __packet->WriteFloat(emu->z); + __packet->WriteFloat(emu->heading); + __packet->WriteString(emu->zone_name); + __packet->WriteUInt32(60); + __packet->WriteUInt32(0); + __packet->WriteUInt32(51); + __packet->WriteUInt32(41); + + FINISH_ENCODE(); + } + ENCODE(OP_ZoneSpawns) { EQApplicationPacket* in = *p; @@ -2025,1276 +3295,6 @@ namespace Laurion delete in; } - ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket* in = *p; - *p = nullptr; - - if (!in->size) { - in->size = 4; - in->pBuffer = new uchar[in->size]; - memset(in->pBuffer, 0, in->size); - - dest->FastQueuePacket(&in, ack_req); - return; - } - - //store away the emu struct - uchar* __emu_buffer = in->pBuffer; - - int item_count = in->size / sizeof(EQ::InternalSerializedItem_Struct); - if (!item_count || (in->size % sizeof(EQ::InternalSerializedItem_Struct)) != 0) { - Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(EQ::InternalSerializedItem_Struct)); - - delete in; - return; - } - - EQ::InternalSerializedItem_Struct* eq = (EQ::InternalSerializedItem_Struct*)in->pBuffer; - SerializeBuffer buffer; - buffer.WriteUInt32(item_count); - - for (int index = 0; index < item_count; ++index, ++eq) { - SerializeItem(buffer, (const EQ::ItemInstance*)eq->inst, eq->slot_id, 0, ItemPacketCharInventory); - } - - in->size = buffer.size(); - in->pBuffer = new unsigned char[buffer.size()]; - memcpy(in->pBuffer, buffer.buffer(), buffer.size()); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - } - - ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } - - ENCODE(OP_NewZone) { - EQApplicationPacket* in = *p; - *p = nullptr; - - unsigned char* __emu_buffer = in->pBuffer; - NewZone_Struct* emu = (NewZone_Struct*)__emu_buffer; - - SerializeBuffer buffer; - /* - char Shortname[]; - char Longname[]; - char WeatherType[]; - char WeatherTypeOverride[]; - char SkyType[]; - char SkyTypeOverride[]; - */ - buffer.WriteString(emu->zone_short_name); - buffer.WriteString(emu->zone_long_name); - buffer.WriteString(""); - buffer.WriteString(""); - buffer.WriteString(""); - buffer.WriteString(""); - - /* - s32 ZoneType; - s16 ZoneId; - s16 ZoneInstance; - float ZoneExpModifier; - s32 GroupLvlExpRelated; - s32 FilterID; - s32 Unknown1; - */ - buffer.WriteInt32(emu->ztype); - buffer.WriteInt16(emu->zone_id); - buffer.WriteInt16(emu->zone_instance); - buffer.WriteFloat(emu->zone_exp_multiplier); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - - //float FogDensity; - buffer.WriteFloat(emu->fog_density); - - //WeatherState state[4]; - for (int i = 0; i < 4; ++i) { - /* - float FogStart; - float FogEnd; - u8 FogRed; - u8 FogGreen; - u8 FogBlue; - u8 RainChance; - u8 RainDuration; - u8 SnowChance; - u8 SnowDuration; - */ - - buffer.WriteFloat(emu->fog_minclip[i]); - buffer.WriteFloat(emu->fog_maxclip[i]); - buffer.WriteUInt8(emu->fog_red[i]); - buffer.WriteUInt8(emu->fog_green[i]); - buffer.WriteUInt8(emu->fog_blue[i]); - buffer.WriteUInt8(emu->rain_chance[i]); - buffer.WriteUInt8(emu->rain_duration[i]); - buffer.WriteUInt8(emu->snow_chance[i]); - buffer.WriteUInt8(emu->snow_duration[i]); - - } - - /* - u8 PrecipitationType; - float BloomIntensity; - float ZoneGravity; - s32 LavaDamage; - s32 MinLavaDamage; - */ - buffer.WriteUInt8(emu->sky); - buffer.WriteFloat(1.0f); - buffer.WriteFloat(emu->gravity); - buffer.WriteInt32(emu->lava_damage); - buffer.WriteInt32(emu->min_lava_damage); - - /* - s32 TimeStringID; - s32 Unknown3; - s32 SkyLock; - s32 SkyLockOverride; - */ - buffer.WriteInt32(0); - buffer.WriteInt32(1); - buffer.WriteInt32(0); - buffer.WriteInt32(-1); - - /* - float SafeY; - float SafeX; - float SafeZ; - float SafeHeading; - float Ceiling; - float Floor; - */ - - buffer.WriteFloat(emu->safe_y); - buffer.WriteFloat(emu->safe_x); - buffer.WriteFloat(emu->safe_z); - buffer.WriteFloat(emu->safe_heading); - buffer.WriteFloat(emu->max_z); - buffer.WriteFloat(emu->underworld); - - /* - float MinClip; - float MaxClip; - s32 FallThroughWorldTeleportID; - */ - buffer.WriteFloat(emu->minclip); - buffer.WriteFloat(emu->maxclip); - buffer.WriteInt32(emu->underworld_teleport_index); - - /* - s32 Unknown4; - s32 ScriptIDHour; - s32 ScriptIDMinute; - s32 ScriptIDTick; - s32 ScriptIDOnPlayerDeath; - s32 ScriptIDOnNPCDeath; - s32 ScriptIDPlayerEnteringZone; - s32 ScriptIDOnZonePop; - s32 ScriptIDNPCLoot; - s32 Unknown4b; - s32 ScriptIDOnFishing; - s32 ScriptIDOnForage; - s32 Unknown4c; - */ - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - buffer.WriteInt32(0); - - /* - s32 NPCAgroMaxDist; - */ - buffer.WriteInt32(600); - - /* - s32 ForageLow; - s32 ForageMedium; - s32 ForageHigh; - s32 ForageSpecial; - s32 FishingLow; - s32 FishingMedium; - s32 FishingHigh; - s32 FishingRelated; - */ - buffer.WriteInt32(-1); - buffer.WriteInt32(-1); - buffer.WriteInt32(-1); - buffer.WriteInt32(-1); - buffer.WriteInt32(-1); - buffer.WriteInt32(-1); - buffer.WriteInt32(-1); - buffer.WriteInt32(-1); - - /* - s32 CanPlaceCampsite; - s32 CanPlaceGuildBanner; - s32 Unknown4d; - */ - - buffer.WriteInt32(2); - buffer.WriteInt32(2); - buffer.WriteInt32(0); - - /* - s32 FastRegenHP; - s32 FastRegenMana; - s32 FastRegenEndurance; - */ - buffer.WriteInt32(emu->fast_regen_hp); - buffer.WriteInt32(emu->fast_regen_mana); - buffer.WriteInt32(emu->fast_regen_endurance); - - /* - u8 NewEngineZone; - u8 SkyEnabled; - u8 FogOnOff; - u8 ClimateType; - u8 bNoPlayerLight; - */ - buffer.WriteUInt8(0); //not sure what happens if we set this incorrectly but we probably need to add this to the zone database - buffer.WriteUInt8(1); - buffer.WriteUInt8(1); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - - /* - u8 bUnknown5; - u8 bNoAttack; - u8 bAllowPVP; - u8 bNoEncumber; - u8 Unknown6; - u8 Unknown7; - */ - buffer.WriteUInt8(1); - buffer.WriteUInt8(0); - buffer.WriteUInt8(1); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - - /* - u8 bNoLevitate; - u8 bNoBuffExpiration; - u8 bDisallowManaStone; - u8 bNoBind; - u8 bNoCallOfTheHero; - u8 bUnknown8; - u8 bNoFear; - u8 bUnknown9; - */ - - buffer.WriteUInt8(0); - buffer.WriteUInt8(emu->suspend_buffs); - buffer.WriteUInt8(1); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - - auto outapp = new EQApplicationPacket(OP_NewZone, buffer.size()); - outapp->WriteData(buffer.buffer(), buffer.size()); - dest->FastQueuePacket(&outapp, ack_req); - - delete in; - } - - ENCODE(OP_SpawnDoor) - { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size / sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - - int r; - for (r = 0; r < door_count; r++) { - strncpy(eq[r].name, emu[r].name, 32); - eq[r].DefaultY = emu[r].yPos; - eq[r].DefaultX = emu[r].xPos; - eq[r].DefaultZ = emu[r].zPos; - eq[r].DefaultHeading = emu[r].heading; - eq[r].DefaultDoorAngle = emu[r].incline; - eq[r].Y = emu[r].yPos; - eq[r].X = emu[r].xPos; - eq[r].Z = emu[r].zPos; - eq[r].Heading = emu[r].heading; - //there's a door angle here but im not sure if / what we set it to since ive literally never seen it as anything but 0 on live - //based on pattern it probably is supposed to match the default angle? - //I'm not 100% sure this is a float it might be a uint32 - eq[r].DoorAngle = emu[r].incline; - eq[r].ScaleFactor = emu[r].size; - eq[r].Id = emu[r].doorId; - eq[r].Type = emu[r].opentype; - eq[r].State = emu[r].state_at_spawn; - eq[r].DefaultState = emu[r].invert_state; - eq[r].Param = emu[r].door_param; - eq[r].bVisible = 1; - eq[r].bUsable = 1; - } - - FINISH_ENCODE(); - } - - ENCODE(OP_GroundSpawn) - { - EQApplicationPacket* in = *p; - *p = nullptr; - Object_Struct* emu = (Object_Struct*)in->pBuffer; - - SerializeBuffer buffer; - buffer.WriteUInt32(emu->drop_id); - buffer.WriteString(emu->object_name); - buffer.WriteUInt16(emu->zone_id); - buffer.WriteUInt16(emu->zone_instance); - buffer.WriteUInt32(emu->drop_id); //this is some other sub but it's okay to duplicate - buffer.WriteUInt32(0); //expires - buffer.WriteFloat(emu->heading); - buffer.WriteFloat(emu->tilt_x); - buffer.WriteFloat(emu->tilt_y); - buffer.WriteFloat(emu->size != 0 && (float)emu->size < 5000.f ? (float)((float)emu->size / 100.0f) : 1.f); //size, with weird peq hack - buffer.WriteFloat(emu->y); - buffer.WriteFloat(emu->x); - buffer.WriteFloat(emu->z); - buffer.WriteFloat(emu->object_type); //weight - - auto outapp = new EQApplicationPacket(OP_GroundSpawn, buffer.size()); - outapp->WriteData(buffer.buffer(), buffer.size()); - dest->FastQueuePacket(&outapp, ack_req); - - delete in; - } - - ENCODE(OP_SendZonepoints) - { - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); - - eq->count = emu->count; - for (uint32 i = 0; i < emu->count; ++i) - { - eq->zpe[i].iterator = emu->zpe[i].iterator; - eq->zpe[i].x = emu->zpe[i].x; - eq->zpe[i].y = emu->zpe[i].y; - eq->zpe[i].z = emu->zpe[i].z; - eq->zpe[i].heading = emu->zpe[i].heading; - eq->zpe[i].zoneid = emu->zpe[i].zoneid; - eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; - } - - FINISH_ENCODE(); - } - - ENCODE(OP_RequestClientZoneChange) - { - ENCODE_LENGTH_EXACT(RequestClientZoneChange_Struct); - SETUP_DIRECT_ENCODE(RequestClientZoneChange_Struct, structs::RequestClientZoneChange_Struct); - - OUT(zone_id); - OUT(instance_id); - OUT(y); - OUT(x); - OUT(z); - OUT(heading); - eq->type = 0x0b; - eq->unknown004 = 0xffffffff; - eq->unknown172 = 0x0168b500; - - FINISH_ENCODE(); - } - - ENCODE(OP_ZoneChange) - { - ENCODE_LENGTH_EXACT(ZoneChange_Struct); - SETUP_DIRECT_ENCODE(ZoneChange_Struct, structs::ZoneChange_Struct); - - memcpy(eq->char_name, emu->char_name, sizeof(emu->char_name)); - OUT(zoneID); - OUT(instanceID); - OUT(y); - OUT(x); - OUT(z) - OUT(zone_reason); - OUT(success); - - if (eq->success < 0) - eq->success -= 1; - - FINISH_ENCODE(); - } - - ENCODE(OP_ClientUpdate) - { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - - OUT(spawn_id); - OUT(vehicle_id); - eq->position.x = emu->x_pos; - eq->position.y = emu->y_pos; - eq->position.z = emu->z_pos; - eq->position.heading = emu->heading; - eq->position.deltaX = emu->delta_x; - eq->position.deltaY = emu->delta_y; - eq->position.deltaZ = emu->delta_z; - eq->position.deltaHeading = emu->delta_heading; - eq->position.animation = emu->animation; - - FINISH_ENCODE(); - } - - ENCODE(OP_WearChange) - { - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - - OUT(spawn_id); - eq->wear_slot_id = emu->wear_slot_id; - eq->armor_id = emu->material; - eq->variation = emu->unknown06; - eq->material = emu->elite_material; - eq->new_armor_id = emu->hero_forge_model; - eq->new_armor_type = emu->unknown18; - eq->color = emu->color.Color; - - FINISH_ENCODE(); - } - - ENCODE(OP_ChannelMessage) - { - EQApplicationPacket* in = *p; - *p = nullptr; - - ChannelMessage_Struct* emu = (ChannelMessage_Struct*)in->pBuffer; - - unsigned char* __emu_buffer = in->pBuffer; - - std::string old_message = emu->message; - std::string new_message; - ServerToLaurionConvertLinks(new_message, old_message); - - in->size = strlen(emu->sender) + strlen(emu->targetname) + new_message.length() + 43; - - in->pBuffer = new unsigned char[in->size]; - - char* OutBuffer = (char*)in->pBuffer; - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); - } - - ENCODE(OP_SpecialMesg) - { - EQApplicationPacket* in = *p; - *p = nullptr; - - SerializeBuffer buf(in->size); - buf.WriteInt8(in->ReadUInt8()); // speak mode - buf.WriteInt8(in->ReadUInt8()); // journal mode - buf.WriteInt8(in->ReadUInt8()); // language - buf.WriteInt32(in->ReadUInt32()); // message type - buf.WriteInt32(in->ReadUInt32()); // target spawn id - - std::string name; - in->ReadString(name); // NPC names max out at 63 chars - - buf.WriteString(name); - - buf.WriteInt32(in->ReadUInt32()); // loc - buf.WriteInt32(in->ReadUInt32()); - buf.WriteInt32(in->ReadUInt32()); - - std::string old_message; - std::string new_message; - - in->ReadString(old_message); - - ServerToLaurionConvertLinks(new_message, old_message); - - buf.WriteString(new_message); - - auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); - - dest->FastQueuePacket(&outapp, ack_req); - delete in; - } - - ENCODE(OP_DeleteSpawn) - { - ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - - OUT(spawn_id); - eq->unknown04 = 1; // Observed - - FINISH_ENCODE(); - } - - ENCODE(OP_FormattedMessage) - { - EQApplicationPacket* in = *p; - *p = nullptr; - - FormattedMessage_Struct* emu = (FormattedMessage_Struct*)in->pBuffer; - - char* old_message_ptr = (char*)in->pBuffer; - old_message_ptr += sizeof(FormattedMessage_Struct); - - std::string old_message_array[9]; - - for (int i = 0; i < 9; ++i) { - if (*old_message_ptr == 0) { break; } - old_message_array[i] = old_message_ptr; - old_message_ptr += old_message_array[i].length() + 1; - } - - SerializeBuffer buffer; - buffer.WriteUInt32(emu->unknown0); - buffer.WriteUInt8(0); // Observed - buffer.WriteUInt32(emu->string_id); - buffer.WriteUInt32(emu->type); - - for (int i = 0; i < 9; ++i) { - std::string new_message; - ServerToLaurionConvertLinks(new_message, old_message_array[i]); - buffer.WriteLengthString(new_message); - } - - auto outapp = new EQApplicationPacket(OP_FormattedMessage, buffer.size()); - outapp->WriteData(buffer.buffer(), buffer.size()); - dest->FastQueuePacket(&outapp, ack_req); - - delete in; - } - - ENCODE(OP_Consider) - { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - - FINISH_ENCODE(); - } - - ENCODE(OP_HPUpdate) - { - SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct, structs::SpawnHPUpdate_Struct); - - OUT(spawn_id); - OUT(cur_hp); - OUT(max_hp); - - FINISH_ENCODE(); - } - - ENCODE(OP_Damage) { - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - OUT(force); - OUT(hit_heading); - OUT(hit_pitch); - OUT(special); - - FINISH_ENCODE(); - } - - ENCODE(OP_Animation) - { - ENCODE_LENGTH_EXACT(Animation_Struct); - SETUP_DIRECT_ENCODE(Animation_Struct, structs::Animation_Struct); - - OUT(spawnid); - OUT(action); - OUT(speed); - - FINISH_ENCODE(); - } - - ENCODE(OP_Death) - { - ENCODE_LENGTH_EXACT(Death_Struct); - SETUP_DIRECT_ENCODE(Death_Struct, structs::Death_Struct); - - OUT(spawn_id); - OUT(killer_id); - OUT(spell_id); - OUT(attack_skill); - OUT(damage); - - //This is a hack, we need to actually fix the ordering in source as this wont respect filters etc - if (emu->attack_skill != 231) { - auto combat_packet = new EQApplicationPacket(OP_Damage, sizeof(structs::CombatDamage_Struct)); - structs::CombatDamage_Struct* cds = (structs::CombatDamage_Struct*)combat_packet->pBuffer; - - cds->target = emu->spawn_id; - cds->source = emu->killer_id; - cds->type = emu->attack_skill; - cds->damage = emu->damage; - cds->spellid = -1; - - dest->FastQueuePacket(&combat_packet, ack_req); - } - - FINISH_ENCODE(); - } - - ENCODE(OP_MoveItem) - { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - Log(Logs::Detail, Logs::Netcode, "Laurion::ENCODE(OP_MoveItem)"); - - eq->from_slot = ServerToLaurionSlot(emu->from_slot); - eq->to_slot = ServerToLaurionSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); - } - - ENCODE(OP_ExpUpdate) - { - SETUP_DIRECT_ENCODE(ExpUpdate_Struct, structs::ExpUpdate_Struct); - - //later we should change the underlying server to use this more accurate value - //and encode the 330 in the other patches - eq->exp = emu->exp * 100000 / 330; - - FINISH_ENCODE(); - } - - ENCODE(OP_SendAATable) - { - EQApplicationPacket* in = *p; - *p = nullptr; - AARankInfo_Struct* emu = (AARankInfo_Struct*)in->pBuffer; - - std::vector skill; - std::vector points; - in->SetReadPosition(sizeof(AARankInfo_Struct) + emu->total_effects * sizeof(AARankEffect_Struct)); - for (auto i = 0; i < emu->total_prereqs; ++i) { - skill.push_back(in->ReadUInt32()); - points.push_back(in->ReadUInt32()); - } - - SerializeBuffer buffer; - - /* - s32 AbilityId; - u8 ShowInAbilityWindow; - s32 ShortName; - s32 ShortName2; - s32 Name; - s32 Desc; - */ - - buffer.WriteUInt32(emu->id); - buffer.WriteUInt8(1); - buffer.WriteInt32(emu->upper_hotkey_sid); - buffer.WriteInt32(emu->lower_hotkey_sid); - buffer.WriteInt32(emu->title_sid); - buffer.WriteInt32(emu->desc_sid); - - /* - s32 MinLevel; - s32 Cost; - s32 GroupID; - s32 CurrentRank; - */ - buffer.WriteInt32(emu->level_req); - buffer.WriteInt32(emu->cost); - buffer.WriteUInt32(emu->seq); - buffer.WriteUInt32(emu->current_level); - - /* - u32 PrereqSkillCount; - s32 PrereqSkills[PrereqSkillCount]; - u32 PrereqLevelCount; - s32 PrereqLevels[PrereqLevelCount]; - */ - - if (emu->total_prereqs) { - buffer.WriteUInt32(emu->total_prereqs); - for (auto& e : skill) - buffer.WriteInt32(e); - buffer.WriteUInt32(emu->total_prereqs); - for (auto& e : points) - buffer.WriteInt32(e); - } - else { - buffer.WriteUInt32(1); - buffer.WriteUInt32(0); - buffer.WriteUInt32(1); - buffer.WriteUInt32(0); - } - - /* - u32 Type; - s32 SpellId; - */ - buffer.WriteInt32(emu->type); - buffer.WriteInt32(emu->spell); - - /* - u32 TimerIdCount; - s32 TimerIds[TimerIdCount]; - s32 ReuseTimer; - u32 Classes; - */ - buffer.WriteInt32(1); - buffer.WriteInt32(emu->spell_type); - buffer.WriteInt32(emu->spell_refresh); - buffer.WriteInt32(emu->classes); - - /* - s32 MaxRank; - s32 PrevAbilityId; - s32 NextAbilityId; - s32 TotalPoints; - */ - - buffer.WriteInt32(emu->max_level); - buffer.WriteInt32(emu->prev_id); - buffer.WriteInt32(emu->next_id); - buffer.WriteInt32(emu->total_cost); - - /* - u8 bRefund; - s32 QuestOnly; - u8 bIgnoreDeLevel; - */ - buffer.WriteUInt8(0); - buffer.WriteUInt32(emu->grant_only); - buffer.WriteUInt8(0); - - /* - s32 Charges; - s32 Expansion; - s32 Category; - */ - buffer.WriteUInt32(emu->charges); - buffer.WriteInt32(emu->expansion); - buffer.WriteInt32(emu->category); - - /* - u8 bShroud; - u8 bBetaOnly; - u8 bResetOnDeath; - u8 AutoGrant; - */ - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - buffer.WriteUInt8(0); - - /* - s32 AutoGrantExpansion; - s32 Unknown098; - u8 Unknown09C; - */ - - buffer.WriteInt32(emu->expansion); - buffer.WriteInt32(0); - buffer.WriteUInt8(0); - - //u32 TotalEffects; - buffer.WriteUInt32(emu->total_effects); - in->SetReadPosition(sizeof(AARankInfo_Struct)); - for (auto i = 0; i < emu->total_effects; ++i) { - auto skill_id = in->ReadUInt32(); - auto base1 = in->ReadUInt32(); - auto base2 = in->ReadUInt32(); - auto slot = in->ReadUInt32(); - - /* - u32 effect_id; - s64 base1; - s64 base2; - u32 slot; - */ - - buffer.WriteUInt32(skill_id); - buffer.WriteInt64(base1); - buffer.WriteInt64(base2); - buffer.WriteUInt32(slot); - } - - auto outapp = new EQApplicationPacket(OP_SendAATable, buffer.size()); - outapp->WriteData(buffer.buffer(), buffer.size()); - dest->FastQueuePacket(&outapp, ack_req); - - delete in; - } - - ENCODE(OP_ItemPacket) - { - EQApplicationPacket* in = *p; - *p = nullptr; - uchar* __emu_buffer = in->pBuffer; - ItemPacket_Struct* old_item_pkt = (ItemPacket_Struct*)__emu_buffer; - - auto type = ServerToLaurionItemPacketType(old_item_pkt->PacketType); - if (type == item::ItemPacketType::ItemPacketInvalid) { - delete in; - return; - } - - switch (type) - { - case item::ItemPacketType::ItemPacketParcel: { - ParcelMessaging_Struct pms{}; - EQ::Util::MemoryStreamReader ss(reinterpret_cast(in->pBuffer), in->size); - cereal::BinaryInputArchive ar(ss); - ar(pms); - - uint32 player_name_length = pms.player_name.length(); - uint32 note_length = pms.note.length(); - - auto* int_struct = (EQ::InternalSerializedItem_Struct*)pms.serialized_item.data(); - - SerializeBuffer buffer; - buffer.WriteInt32((int32_t)type); - SerializeItem(buffer, (const EQ::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); - - buffer.WriteUInt32(pms.sent_time); - buffer.WriteLengthString(pms.player_name); - buffer.WriteLengthString(pms.note); - - auto outapp = new EQApplicationPacket(OP_ItemPacket, buffer.size()); - outapp->WriteData(buffer.buffer(), buffer.size()); - dest->FastQueuePacket(&outapp, ack_req); - break; - } - default: { - EQ::InternalSerializedItem_Struct* int_struct = (EQ::InternalSerializedItem_Struct*)(&__emu_buffer[4]); - SerializeBuffer buffer; - buffer.WriteInt32((int32_t)type); - SerializeItem(buffer, (const EQ::ItemInstance*)int_struct->inst, int_struct->slot_id, 0, old_item_pkt->PacketType); - - auto outapp = new EQApplicationPacket(OP_ItemPacket, buffer.size()); - outapp->WriteData(buffer.buffer(), buffer.size()); - dest->FastQueuePacket(&outapp, ack_req); - } - } - - delete in; - } - - ENCODE(OP_ShopRequest) - { - ENCODE_LENGTH_EXACT(MerchantClick_Struct); - SETUP_DIRECT_ENCODE(MerchantClick_Struct, structs::MerchantClickResponse_Struct); - - if (emu->command == 0) { - OUT(player_id); - eq->npc_id = 0; - } - else { - OUT(npc_id); - OUT(player_id); - OUT(rate); - OUT(tab_display); - eq->unknown028 = 256; - } - - FINISH_ENCODE(); - } - - ENCODE(OP_BeginCast) - { - ENCODE_LENGTH_EXACT(BeginCast_Struct); - SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); - - OUT(spell_id); - OUT(caster_id); - OUT(cast_time); - eq->unknown0e = 1; //not sure what this is; but its usually 1 on live - - FINISH_ENCODE(); - } - - ENCODE(OP_BuffCreate) - { - SETUP_VAR_ENCODE(BuffIcon_Struct); - - //Laurion has one extra 0x00 byte before the end byte - uint32 sz = 13 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - __packet->WriteUInt32(emu->entity_id); - __packet->WriteUInt32(emu->tic_timer); - __packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff) - __packet->WriteUInt16(emu->count); - - for (int i = 0; i < emu->count; ++i) - { - __packet->WriteUInt32(emu->type == 0 ? ServerToLaurionBuffSlot(emu->entries[i].buff_slot) : emu->entries[i].buff_slot); - __packet->WriteUInt32(emu->entries[i].spell_id); - __packet->WriteUInt32(emu->entries[i].tics_remaining); - __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown - __packet->WriteString(emu->entries[i].caster); - } - __packet->WriteUInt8(0); // Unknown1 - __packet->WriteUInt8(emu->type); // Unknown2 - - FINISH_ENCODE(); - } - - ENCODE(OP_Buff) - { - ENCODE_LENGTH_EXACT(SpellBuffPacket_Struct); - SETUP_DIRECT_ENCODE(SpellBuffPacket_Struct, structs::EQAffectPacket_Struct); - - eq->entity_id = emu->entityid; - eq->unknown004 = 0; - - //fill in affect info - eq->affect.caster_id.Id = emu->buff.player_id; - eq->affect.flags = 0; - eq->affect.spell_id = emu->buff.spellid; - eq->affect.duration = emu->buff.duration; - eq->affect.initial_duration = emu->buff.duration; - eq->affect.hit_count = emu->buff.num_hits; - eq->affect.viral_timer = 0; - eq->affect.modifier = emu->buff.bard_modifier == 10 ? 1.0f : emu->buff.bard_modifier / 10.0f; - eq->affect.y = emu->buff.y; - eq->affect.x = emu->buff.x; - eq->affect.z = emu->buff.z; - eq->affect.level = emu->buff.level; - - eq->slot_id = ServerToLaurionBuffSlot(emu->slotid); - if (emu->bufffade == 1) - { - eq->buff_fade = 1; - } - else - { - eq->buff_fade = 2; - } - - EQApplicationPacket* outapp = nullptr; - if (emu->bufffade == 1) - { - // Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon - outapp = new EQApplicationPacket(OP_BuffCreate, 30); - outapp->WriteUInt32(emu->entityid); - outapp->WriteUInt32(0); // tic timer - outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ? - outapp->WriteUInt16(1); // 1 buff in this packet - outapp->WriteUInt32(ServerToLaurionBuffSlot(emu->slotid)); - outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove) - outapp->WriteUInt32(0); // Duration - outapp->WriteUInt32(0); // numhits - outapp->WriteUInt8(0); // Caster name - outapp->WriteUInt8(0); // Type - outapp->WriteUInt8(0); // Type - } - - FINISH_ENCODE(); - - if (outapp) { - dest->FastQueuePacket(&outapp); - } - } - - ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - OUT(keepcasting); - OUT(slot); - - FINISH_ENCODE(); - } - - ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::MissileHitInfo); - - //This is mostly figured out; there's two unknowns, only unknown1 is read by the client - OUT(target); - OUT(source); - eq->spell_id = emu->spell; - eq->effect_type = emu->effect_flag; - eq->effective_casting_level = 0; //if you set this to != 0 it will use this level instead of calculating it - eq->unknown1 = 0; - eq->unknown2 = 0; - eq->damage = 0; //client doesn't read this but live sends it here, can just set 0 - eq->modifier = 1.0f + (emu->instrument_mod - 10) / 10.0f; - OUT(force); - OUT(hit_heading); - OUT(hit_pitch); - eq->skill = emu->type; - OUT(level); - - FINISH_ENCODE(); - } - - ENCODE(OP_MobHealth) { - ENCODE_LENGTH_EXACT(SpawnHPUpdate_Struct2); - SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct2, structs::MobHealth_Struct); - - OUT(spawn_id); - OUT(hp); - - FINISH_ENCODE(); - } - - ENCODE(OP_GMTraining) { - ENCODE_LENGTH_EXACT(GMTrainee_Struct); - SETUP_DIRECT_ENCODE(GMTrainee_Struct, structs::GMTrainee_Struct); - - OUT(npcid); - OUT(playerid); - - for (int i = 0; i < 100; ++i) { - OUT(skills[i]); - } - - eq->unknown408[0] = 1; - eq->unknown408[1] = 0xC9; - eq->unknown408[2] = 0xC9; - eq->unknown408[3] = 0xC9; - eq->unknown408[4] = 0xC9; - eq->unknown408[5] = 0xC9; - eq->unknown408[6] = 0xC9; - eq->unknown408[7] = 0xC9; - eq->unknown408[8] = 0xC9; - eq->unknown408[9] = 0xC9; - eq->unknown408[10] = 0xC9; - eq->unknown408[11] = 0xC9; - eq->unknown408[12] = 0xC9; - eq->unknown408[13] = 0xC9; - eq->unknown408[14] = 0xC9; - eq->unknown408[15] = 0xC9; - eq->unknown408[16] = 0xC9; - eq->unknown408[17] = 0xC9; - eq->unknown408[18] = 0xC9; - eq->unknown408[19] = 0xC9; - eq->unknown408[20] = 0xC9; - eq->unknown408[21] = 0xC9; - eq->unknown408[22] = 0xC9; - eq->unknown408[23] = 0xC9; - eq->unknown408[24] = 0xC9; - eq->unknown408[25] = 0xC9; - eq->unknown408[26] = 0xC9; - eq->unknown408[27] = 0xC9; - eq->unknown408[28] = 0xC9; - eq->unknown408[29] = 0xC9; - eq->unknown408[30] = 0xC9; - eq->unknown408[31] = 0xC9; - eq->unknown408[32] = 0xC9; - eq->unknown408[33] = 0xCA; //the client far as I can tell doesn't read past the 1 byte + 32 0xc9s, but still setting it to what we saw - eq->unknown408[34] = 0x8C; - eq->unknown408[35] = 0xEC; - - FINISH_ENCODE(); - } - - ENCODE(OP_GMTrainSkillConfirm) - { - ENCODE_LENGTH_EXACT(GMTrainSkillConfirm_Struct); - SETUP_DIRECT_ENCODE(GMTrainSkillConfirm_Struct, structs::GMTrainSkillConfirm_Struct); - - OUT(SkillID); - OUT(Cost); - OUT(NewSkill); - OUT_str(TrainerName); - - FINISH_ENCODE(); - } - - ENCODE(OP_SkillUpdate) - { - ENCODE_LENGTH_EXACT(SkillUpdate_Struct); - SETUP_DIRECT_ENCODE(SkillUpdate_Struct, structs::SkillUpdate_Struct); - - OUT(skillId); - OUT(value); - eq->active = 1; - - FINISH_ENCODE(); - } - - ENCODE(OP_RespondAA) - { - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - eq->aa_spent = emu->aa_spent; - // These fields may need to be correctly populated at some point - eq->aapoints_assigned[0] = emu->aa_spent; - eq->aapoints_assigned[1] = 0; - eq->aapoints_assigned[2] = 0; - eq->aapoints_assigned[3] = 0; - eq->aapoints_assigned[4] = 0; - eq->aapoints_assigned[5] = 0; - - for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) - { - eq->aa_list[i].AA = emu->aa_list[i].AA; - eq->aa_list[i].value = emu->aa_list[i].value; - eq->aa_list[i].charges = emu->aa_list[i].charges; - eq->aa_list[i].bUnknown0x0c = false; - } - - FINISH_ENCODE(); - } - - ENCODE(OP_BlockedBuffs) - { - ENCODE_LENGTH_EXACT(BlockedBuffs_Struct); - SETUP_DIRECT_ENCODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); - - for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) - eq->SpellID[i] = emu->SpellID[i]; - - for (uint32 i = BLOCKED_BUFF_COUNT; i < structs::BLOCKED_BUFF_COUNT; ++i) - eq->SpellID[i] = -1; - - OUT(Count); - OUT(Pet); - OUT(Initialise); - OUT(Flags); - - FINISH_ENCODE(); - } - - ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } - - ENCODE(OP_ZonePlayerToBind) - { - SETUP_VAR_ENCODE(ZonePlayerToBind_Struct); - ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name)); - - __packet->SetWritePosition(0); - __packet->WriteUInt16(emu->bind_zone_id); - __packet->WriteUInt16(emu->bind_instance_id); - __packet->WriteFloat(emu->x); - __packet->WriteFloat(emu->y); - __packet->WriteFloat(emu->z); - __packet->WriteFloat(emu->heading); - __packet->WriteString(emu->zone_name); - __packet->WriteUInt32(60); - __packet->WriteUInt32(0); - __packet->WriteUInt32(51); - __packet->WriteUInt32(41); - - FINISH_ENCODE(); - } - - ENCODE(OP_Illusion) - { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - - OUT(spawnid); - OUT_str(charname); - OUT(race); - eq->unknown006[0] = 0; - eq->unknown006[1] = 0; - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - - FINISH_ENCODE(); - } - - ENCODE(OP_MoneyOnCorpse) - { - ENCODE_LENGTH_EXACT(moneyOnCorpseStruct); - SETUP_DIRECT_ENCODE(moneyOnCorpseStruct, structs::moneyOnCorpseStruct); - - eq->type = emu->response; - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - eq->flags = 0; - - FINISH_ENCODE(); - } - - ENCODE(OP_OnLevelMessage) - { - EQApplicationPacket* in = *p; - *p = nullptr; - OnLevelMessage_Struct* emu = (OnLevelMessage_Struct*)in->pBuffer; - SerializeBuffer buffer; - - buffer.WriteLengthString(emu->Title); - buffer.WriteLengthString(emu->Text); - buffer.WriteLengthString(emu->ButtonName0); - buffer.WriteLengthString(emu->ButtonName1); - buffer.WriteUInt8(emu->Buttons); - buffer.WriteUInt8(emu->SoundControls); - buffer.WriteUInt32(emu->Duration); - buffer.WriteUInt32(emu->PopupID); - buffer.WriteUInt32(emu->NegativeID); - buffer.WriteUInt32(0); //seen -1 & 0 - buffer.WriteUInt32(0); //seen 0 - - auto outapp = new EQApplicationPacket(OP_OnLevelMessage, buffer.size()); - outapp->WriteData(buffer.buffer(), buffer.size()); - dest->FastQueuePacket(&outapp, ack_req); - - delete in; - } - // DECODE methods DECODE(OP_EnterWorld) diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index a0b5d38f2..e656956ac 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -1,55 +1,56 @@ //list of packets we need to encode on the way out: -E(OP_LogServer) -E(OP_SendMembership) -E(OP_SendMembershipDetails) -E(OP_SendMaxCharacters) -E(OP_SendCharInfo) -E(OP_ExpansionInfo) -E(OP_SpawnAppearance) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_ZoneSpawns) -E(OP_CharInventory) -E(OP_NewSpawn) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_RequestClientZoneChange) -E(OP_ZoneChange) -E(OP_ClientUpdate) -E(OP_WearChange) -E(OP_ChannelMessage) -E(OP_SpecialMesg) -E(OP_DeleteSpawn) -E(OP_FormattedMessage) -E(OP_Consider) -E(OP_HPUpdate) -E(OP_Damage) -E(OP_Animation) -E(OP_Death) -E(OP_MoveItem) -E(OP_ExpUpdate) -E(OP_SendAATable) -E(OP_ItemPacket) -E(OP_ShopRequest) -E(OP_BeginCast) -E(OP_BuffCreate) -E(OP_Buff) -E(OP_ManaChange) E(OP_Action) -E(OP_MobHealth) +E(OP_Animation) +E(OP_BeginCast) +E(OP_BlockedBuffs) +E(OP_Buff) +E(OP_BuffCreate) +E(OP_ChannelMessage) +E(OP_CharInventory) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_Death) +E(OP_DeleteSpawn) +E(OP_ExpansionInfo) +E(OP_ExpUpdate) +E(OP_FormattedMessage) E(OP_GMTraining) E(OP_GMTrainSkillConfirm) -E(OP_SkillUpdate) -E(OP_RespondAA) -E(OP_BlockedBuffs) -E(OP_RemoveBlockedBuffs) -E(OP_ZonePlayerToBind) +E(OP_GroundSpawn) +E(OP_HPUpdate) E(OP_Illusion) +E(OP_ItemPacket) +E(OP_LogServer) +E(OP_ManaChange) +E(OP_MobHealth) E(OP_MoneyOnCorpse) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) E(OP_OnLevelMessage) +E(OP_PlayerProfile) +E(OP_RemoveBlockedBuffs) +E(OP_RespondAA) +E(OP_RequestClientZoneChange) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendMaxCharacters) +E(OP_SendMembership) +E(OP_SendMembershipDetails) +E(OP_SendZonepoints) +E(OP_ShopRequest) +E(OP_SkillUpdate) +E(OP_SpecialMesg) +E(OP_SpawnAppearance) +E(OP_SpawnDoor) +E(OP_WearChange) +E(OP_ZoneChange) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneSpawns) + //list of packets we need to decode on the way in: D(OP_EnterWorld) D(OP_ZoneEntry) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 8786552f8..0b98ecc90 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -235,7 +235,7 @@ OP_Hide=0x1cdf OP_Jump=0x6fa0 OP_Camp=0x326f OP_Emote=0x0000 -OP_SetRunMode=0x0000 +OP_SetRunMode=0x1449 OP_BankerChange=0x0000 OP_TargetMouse=0x5741 OP_MobHealth=0x5b77 @@ -248,15 +248,15 @@ OP_XTargetAutoAddHaters=0x0000 OP_XTargetOpen=0x0000 OP_XTargetOpenResponse=0x0000 OP_BuffCreate=0x27a1 -OP_BuffRemoveRequest=0x0000 +OP_BuffRemoveRequest=0x4507 OP_DeleteSpawn=0x7712 OP_AutoAttack=0x3f03 OP_AutoAttack2=0x1c31 -OP_Consume=0x0000 +OP_Consume=0x5ef7 OP_MoveItem=0x11e3 OP_MoveMultipleItems=0x5205 -OP_DeleteItem=0x0000 -OP_DeleteCharge=0x0000 +OP_DeleteItem=0x0150 +OP_DeleteCharge=0x1b7e OP_ItemPacket=0x7d43 OP_ItemLinkResponse=0x0000 OP_ItemLinkClick=0x0000 @@ -346,9 +346,9 @@ OP_Action2=0x0000 # Unused? OP_MobUpdate=0x0000 OP_NPCMoveUpdate=0x0000 OP_CameraEffect=0x2f01 -OP_SpellEffect=0x0000 -OP_AddNimbusEffect=0x0000 -OP_RemoveNimbusEffect=0x0000 +OP_SpellEffect=0x7378 +OP_AddNimbusEffect=0x069f +OP_RemoveNimbusEffect=0x19ee OP_AltCurrency=0x0000 OP_AltCurrencyMerchantRequest=0x0000 OP_AltCurrencyMerchantReply=0x0000 From a42d6e8ee1b06ad5691ca9b191c04efe46fb0c5d Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 28 Dec 2024 09:24:32 -0800 Subject: [PATCH 83/93] Reorder decodes in alpha order too. --- common/patches/laurion.cpp | 297 +++++++++++++++++------------------ common/patches/laurion_ops.h | 26 +-- 2 files changed, 161 insertions(+), 162 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index f304deb3b..d5d55c303 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -3296,82 +3296,35 @@ namespace Laurion } // DECODE methods - - DECODE(OP_EnterWorld) + DECODE(OP_BlockedBuffs) { - DECODE_LENGTH_EXACT(structs::EnterWorld_Struct); - SETUP_DIRECT_DECODE(EnterWorld_Struct, structs::EnterWorld_Struct); + DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); + SETUP_DIRECT_DECODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); - memcpy(emu->name, eq->name, sizeof(emu->name)); - emu->return_home = 0; - emu->tutorial = 0; + for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) + emu->SpellID[i] = eq->SpellID[i]; + + IN(Count); + IN(Pet); + IN(Initialise); + IN(Flags); FINISH_DIRECT_DECODE(); } - DECODE(OP_ZoneEntry) + DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct); - SETUP_DIRECT_DECODE(ClientZoneEntry_Struct, structs::ClientZoneEntry_Struct); + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); - - FINISH_DIRECT_DECODE(); - } - - DECODE(OP_ZoneChange) - { - DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); - SETUP_DIRECT_DECODE(ZoneChange_Struct, structs::ZoneChange_Struct); - - memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); - IN(zoneID); - IN(instanceID); - IN(y); - IN(x); - IN(z) - IN(zone_reason); - IN(success); - - FINISH_DIRECT_DECODE(); - } - - DECODE(OP_ClientUpdate) - { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - - IN(spawn_id); - IN(vehicle_id); - IN(sequence); - emu->x_pos = eq->position.x; - emu->y_pos = eq->position.y; - emu->z_pos = eq->position.z; - emu->heading = eq->position.heading; - emu->delta_x = eq->position.delta_x; - emu->delta_y = eq->position.delta_y; - emu->delta_z = eq->position.delta_z; - emu->delta_heading = eq->position.delta_heading; - emu->animation = eq->position.animation; - - FINISH_DIRECT_DECODE(); - } - - DECODE(OP_WearChange) - { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - - IN(spawn_id); - emu->wear_slot_id = eq->wear_slot_id; - emu->material = eq->armor_id; - emu->unknown06 = eq->variation; - emu->elite_material = eq->material; - emu->hero_forge_model = eq->new_armor_id; - emu->unknown18 = eq->new_armor_type; - emu->color.Color = eq->color; + emu->slot = static_cast(LaurionToServerCastingSlot(static_cast(eq->slot))); + IN(spell_id); + emu->inventoryslot = -1; + IN(target_id); + IN(y_pos); + IN(x_pos); + IN(z_pos); FINISH_DIRECT_DECODE(); } @@ -3415,16 +3368,35 @@ namespace Laurion delete[] __eq_buffer; } - DECODE(OP_SetServerFilter) + DECODE(OP_ClickDoor) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + DECODE_LENGTH_EXACT(structs::ClickDoor_Struct); + SETUP_DIRECT_DECODE(ClickDoor_Struct, structs::ClickDoor_Struct); - int r; - for (r = 0; r < 29; r++) { - // Size 68 in Laurion - IN(filters[r]); - } + IN(doorid); + IN(player_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(vehicle_id); + IN(sequence); + emu->x_pos = eq->position.x; + emu->y_pos = eq->position.y; + emu->z_pos = eq->position.z; + emu->heading = eq->position.heading; + emu->delta_x = eq->position.delta_x; + emu->delta_y = eq->position.delta_y; + emu->delta_z = eq->position.delta_z; + emu->delta_heading = eq->position.delta_heading; + emu->animation = eq->position.animation; FINISH_DIRECT_DECODE(); } @@ -3447,68 +3419,18 @@ namespace Laurion DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } - DECODE(OP_ClickDoor) + DECODE(OP_EnterWorld) { - DECODE_LENGTH_EXACT(structs::ClickDoor_Struct); - SETUP_DIRECT_DECODE(ClickDoor_Struct, structs::ClickDoor_Struct); + DECODE_LENGTH_EXACT(structs::EnterWorld_Struct); + SETUP_DIRECT_DECODE(EnterWorld_Struct, structs::EnterWorld_Struct); - IN(doorid); - IN(player_id); + memcpy(emu->name, eq->name, sizeof(emu->name)); + emu->return_home = 0; + emu->tutorial = 0; FINISH_DIRECT_DECODE(); } - DECODE(OP_SpawnAppearance) { - DECODE_LENGTH_EXACT(structs::SpawnAppearance_Struct); - SETUP_DIRECT_DECODE(SpawnAppearance_Struct, structs::SpawnAppearance_Struct); - - IN(spawn_id); - emu->type = LaurionToServerSpawnAppearanceType(eq->type); - IN(parameter); - - FINISH_DIRECT_DECODE(); - } - - DECODE(OP_MoveItem) - { - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - Log(Logs::Detail, Logs::Netcode, "Laurion::DECODE(OP_MoveItem)"); - - emu->from_slot = LaurionToServerSlot(eq->from_slot); - emu->to_slot = LaurionToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); - } - - DECODE(OP_ShopRequest) - { - DECODE_LENGTH_EXACT(structs::MerchantClickRequest_Struct); - SETUP_DIRECT_DECODE(MerchantClick_Struct, structs::MerchantClickRequest_Struct); - - IN(npc_id); - - FINISH_DIRECT_DECODE(); - } - - DECODE(OP_CastSpell) - { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - emu->slot = static_cast(LaurionToServerCastingSlot(static_cast(eq->slot))); - - IN(spell_id); - emu->inventoryslot = -1; - IN(target_id); - IN(y_pos); - IN(x_pos); - IN(z_pos); - FINISH_DIRECT_DECODE(); - } - DECODE(OP_GMTraining) { DECODE_LENGTH_EXACT(structs::GMTrainee_Struct); @@ -3524,24 +3446,6 @@ namespace Laurion FINISH_DIRECT_DECODE(); } - DECODE(OP_BlockedBuffs) - { - DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); - SETUP_DIRECT_DECODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); - - for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) - emu->SpellID[i] = eq->SpellID[i]; - - IN(Count); - IN(Pet); - IN(Initialise); - IN(Flags); - - FINISH_DIRECT_DECODE(); - } - - DECODE(OP_RemoveBlockedBuffs) { DECODE_FORWARD(OP_BlockedBuffs); } - DECODE(OP_GroupDisband) { DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); @@ -3569,6 +3473,101 @@ namespace Laurion DECODE_FORWARD(OP_GroupInvite); } + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + Log(Logs::Detail, Logs::Netcode, "Laurion::DECODE(OP_MoveItem)"); + + emu->from_slot = LaurionToServerSlot(eq->from_slot); + emu->to_slot = LaurionToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RemoveBlockedBuffs) { DECODE_FORWARD(OP_BlockedBuffs); } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + // Size 68 in Laurion + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopRequest) + { + DECODE_LENGTH_EXACT(structs::MerchantClickRequest_Struct); + SETUP_DIRECT_DECODE(MerchantClick_Struct, structs::MerchantClickRequest_Struct); + + IN(npc_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SpawnAppearance) { + DECODE_LENGTH_EXACT(structs::SpawnAppearance_Struct); + SETUP_DIRECT_DECODE(SpawnAppearance_Struct, structs::SpawnAppearance_Struct); + + IN(spawn_id); + emu->type = LaurionToServerSpawnAppearanceType(eq->type); + IN(parameter); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + emu->wear_slot_id = eq->wear_slot_id; + emu->material = eq->armor_id; + emu->unknown06 = eq->variation; + emu->elite_material = eq->material; + emu->hero_forge_model = eq->new_armor_id; + emu->unknown18 = eq->new_armor_type; + emu->color.Color = eq->color; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ZoneEntry) + { + DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct); + SETUP_DIRECT_DECODE(ClientZoneEntry_Struct, structs::ClientZoneEntry_Struct); + + memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ZoneChange) + { + DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); + SETUP_DIRECT_DECODE(ZoneChange_Struct, structs::ZoneChange_Struct); + + memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); + IN(zoneID); + IN(instanceID); + IN(y); + IN(x); + IN(z) + IN(zone_reason); + IN(success); + + FINISH_DIRECT_DECODE(); + } + //Naive version but should work well enough for now int ExtractIDFile(const std::string& input) { std::string number; diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index e656956ac..f481d96dc 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -52,25 +52,25 @@ E(OP_ZonePlayerToBind) E(OP_ZoneSpawns) //list of packets we need to decode on the way in: -D(OP_EnterWorld) -D(OP_ZoneEntry) -D(OP_ZoneChange) -D(OP_ClientUpdate) -D(OP_WearChange) +D(OP_BlockedBuffs) +D(OP_CastSpell) D(OP_ChannelMessage) -D(OP_SetServerFilter) +D(OP_ClientUpdate) +D(OP_ClickDoor) D(OP_Consider) D(OP_ConsiderCorpse) -D(OP_ClickDoor) -D(OP_SpawnAppearance) -D(OP_MoveItem) -D(OP_ShopRequest) -D(OP_CastSpell) +D(OP_EnterWorld) D(OP_GMTraining) -D(OP_BlockedBuffs) -D(OP_RemoveBlockedBuffs) D(OP_GroupDisband) D(OP_GroupInvite) D(OP_GroupInvite2) +D(OP_MoveItem) +D(OP_RemoveBlockedBuffs) +D(OP_SetServerFilter) +D(OP_ShopRequest) +D(OP_SpawnAppearance) +D(OP_WearChange) +D(OP_ZoneEntry) +D(OP_ZoneChange) #undef E #undef D From bd2798f2cc1fcb492d1365c96f39de639430a77e Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 28 Dec 2024 22:59:32 -0800 Subject: [PATCH 84/93] Add support for augment items. --- common/patches/laurion.cpp | 38 ++++++++++++++++++++++++++++++++ common/patches/laurion_ops.h | 3 +++ common/patches/laurion_structs.h | 23 +++++++++++++++++++ utils/patches/patch_Laurion.conf | 2 +- 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index d5d55c303..c028db8ae 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -193,6 +193,18 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + FINISH_ENCODE(); + } + ENCODE(OP_BeginCast) { ENCODE_LENGTH_EXACT(BeginCast_Struct); @@ -3296,6 +3308,32 @@ namespace Laurion } // DECODE methods + DECODE(OP_AugmentInfo) + { + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = LaurionToServerSlot(eq->container_slot); + emu->augment_slot = LaurionToServerSlot(eq->augment_slot); + emu->container_index = eq->container_index; + emu->augment_index = eq->augment_index; + emu->dest_inst_id = eq->dest_inst_id; + emu->augment_action = eq->augment_action; + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_BlockedBuffs) { DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index f481d96dc..14c8f8182 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -2,6 +2,7 @@ //list of packets we need to encode on the way out: E(OP_Action) E(OP_Animation) +E(OP_AugmentInfo) E(OP_BeginCast) E(OP_BlockedBuffs) E(OP_Buff) @@ -52,6 +53,8 @@ E(OP_ZonePlayerToBind) E(OP_ZoneSpawns) //list of packets we need to decode on the way in: +D(OP_AugmentInfo) +D(OP_AugmentItem) D(OP_BlockedBuffs) D(OP_CastSpell) D(OP_ChannelMessage) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index d9ff005be..7c9870937 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -875,6 +875,29 @@ namespace Laurion { /*0152*/ uint16 unknown0152; /*0154*/ }; + + struct AugmentInfo_Struct + { + /*000*/ uint32 itemid; // id of the solvent needed + /*004*/ uint32 window; // window to display the information in + /*008*/ char augment_info[64]; // total packet length 80, all the rest were always 00 + /*072*/ uint32 unknown072; // seen 0, 56 + /*076*/ uint32 unknown076; // seen 8, 3, 11, always matches what client sends + /*080*/ + }; + + //seems to be unchanged from rof2? + //it's the same size at least + struct AugmentItem_Struct { + /*00*/ uint32 dest_inst_id; // The unique serial number for the item instance that is being augmented + /*04*/ uint32 container_index; // Seen 0 + /*08*/ InventorySlot_Struct container_slot; // Slot of the item being augmented + /*20*/ uint32 augment_index; // Seen 0 + /*24*/ InventorySlot_Struct augment_slot; // Slot of the distiller to use (if one applies) + /*36*/ int32 augment_action; // Guessed - 0 = augment, 1 = remove with distiller, 3 = delete aug + /*36*/ //int32 augment_slot; + /*40*/ + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 0b98ecc90..89aa1845c 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -321,7 +321,7 @@ OP_Translocate=0x2772 OP_Sacrifice=0x2cbf OP_PopupResponse=0x6be9 OP_OnLevelMessage=0x2a41 -OP_AugmentInfo=0x0000 +OP_AugmentInfo=0x2e11 OP_Petition=0x0000 OP_SomeItemPacketMaybe=0x0000 OP_PVPStats=0x0000 From 0241a905052932209e2ef9a35d6c66a3974b6728 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 29 Dec 2024 19:37:15 -0800 Subject: [PATCH 85/93] -ApplyPoison -Encode CastSpell (needs to be checked more) -Decode animation --- common/patches/laurion.cpp | 69 ++++++++++++++++++++++++++++++++ common/patches/laurion_ops.h | 4 ++ common/patches/laurion_structs.h | 6 +++ utils/patches/patch_Laurion.conf | 2 +- 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index c028db8ae..bfcb03ad8 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -70,6 +70,7 @@ namespace Laurion static inline uint32 LaurionToServerCorpseSlot(structs::InventorySlot_Struct laurion_corpse_slot); static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot); static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type); + static inline structs::InventorySlot_Struct LaurionCastingInventorySlotToInventorySlot(structs::CastSpellInventorySlot_Struct laurion_slot); // Item packet types static item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); @@ -193,6 +194,17 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToLaurionTypelessSlot(emu->inventorySlot, EQ::invtype::typePossessions); + OUT(success); + + FINISH_ENCODE(); + } + ENCODE(OP_AugmentInfo) { ENCODE_LENGTH_EXACT(AugmentInfo_Struct); @@ -323,6 +335,22 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_CastSpell) + { + ENCODE_LENGTH_EXACT(CastSpell_Struct); + SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct); + + eq->slot = static_cast(ServerToLaurionCastingSlot(static_cast(emu->slot))); + + OUT(spell_id); + //we should double check this cause it feels wrong + eq->inventory_slot = LaurionInventorySlotToCastingInventorySlot(ServerToLaurionSlot(emu->inventoryslot)); + //OUT(inventoryslot); + OUT(target_id); + + FINISH_ENCODE(); + } + ENCODE(OP_ChannelMessage) { EQApplicationPacket* in = *p; @@ -3308,6 +3336,29 @@ namespace Laurion } // DECODE methods + DECODE(OP_Animation) + { + DECODE_LENGTH_EXACT(structs::Animation_Struct); + SETUP_DIRECT_DECODE(Animation_Struct, structs::Animation_Struct); + + IN(spawnid); + IN(action); + IN(speed); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = LaurionToServerTypelessSlot(eq->inventorySlot, invtype::typePossessions); + IN(success); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_AugmentInfo) { DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); @@ -5050,6 +5101,24 @@ namespace Laurion return ServerSlot; } + static inline structs::InventorySlot_Struct LaurionCastingInventorySlotToInventorySlot(structs::CastSpellInventorySlot_Struct laurion_slot) { + structs::InventorySlot_Struct ret; + ret.Type = laurion_slot.type; + ret.Slot = laurion_slot.slot; + ret.SubIndex = laurion_slot.sub_index; + ret.AugIndex = laurion_slot.aug_index; + return ret; + } + + static inline structs::CastSpellInventorySlot_Struct LaurionInventorySlotToCastingInventorySlot(structs::InventorySlot_Struct laurion_slot) { + structs::CastSpellInventorySlot_Struct ret; + ret.type = laurion_slot.Type; + ret.slot = laurion_slot.Slot; + ret.sub_index = laurion_slot.SubIndex; + ret.aug_index = laurion_slot.AugIndex; + return ret; + } + static item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType server_type) { switch (server_type) { case ItemPacketType::ItemPacketMerchant: diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 14c8f8182..3e2485a84 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -2,11 +2,13 @@ //list of packets we need to encode on the way out: E(OP_Action) E(OP_Animation) +E(OP_ApplyPoison) E(OP_AugmentInfo) E(OP_BeginCast) E(OP_BlockedBuffs) E(OP_Buff) E(OP_BuffCreate) +E(OP_CastSpell) E(OP_ChannelMessage) E(OP_CharInventory) E(OP_ClientUpdate) @@ -53,6 +55,8 @@ E(OP_ZonePlayerToBind) E(OP_ZoneSpawns) //list of packets we need to decode on the way in: +D(OP_Animation) +D(OP_ApplyPoison) D(OP_AugmentInfo) D(OP_AugmentItem) D(OP_BlockedBuffs) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 7c9870937..9ed1829e4 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -898,6 +898,12 @@ namespace Laurion { /*36*/ //int32 augment_slot; /*40*/ }; + + struct ApplyPoison_Struct + { + TypelessInventorySlot_Struct inventorySlot; + uint32 success; + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 89aa1845c..40381fe43 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -284,7 +284,7 @@ OP_AugmentItem=0x3a1b OP_WeaponEquip1=0x0000 OP_PlayerStateAdd=0x2178 OP_PlayerStateRemove=0x178e -OP_ApplyPoison=0x0000 +OP_ApplyPoison=0x55b9 OP_Save=0x6da2 OP_TestBuff=0x0000 OP_CustomTitles=0x0000 From 24dbe6da0e5fae6367c8c4c11b9453254525b357 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 30 Dec 2024 19:10:56 -0800 Subject: [PATCH 86/93] Add tradeskill opcodes, some encodes and decodes to match rof2; i think they're all still the same but i could be wrong. --- common/patches/laurion.cpp | 49 ++++++++++++++++++++++++++++++++ common/patches/laurion_ops.h | 3 ++ common/patches/laurion_structs.h | 44 ++++++++++++++++++++++++++++ utils/patches/patch_Laurion.conf | 18 ++++++------ 4 files changed, 105 insertions(+), 9 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index bfcb03ad8..bfc0d3d25 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -434,6 +434,22 @@ namespace Laurion dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_ClickObjectAction) + { + ENCODE_LENGTH_EXACT(ClickObjectAction_Struct); + SETUP_DIRECT_ENCODE(ClickObjectAction_Struct, structs::ClickObjectAction_Struct); + + OUT(drop_id); + eq->unknown04 = -1; + eq->unknown08 = -1; + OUT(type); + OUT(icon); + eq->unknown16 = 0; + OUT_str(object_name); + + FINISH_ENCODE(); + } + ENCODE(OP_ClientUpdate) { ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); @@ -2187,6 +2203,28 @@ namespace Laurion delete in; } + ENCODE(OP_RecipeAutoCombine) + { + ENCODE_LENGTH_EXACT(RecipeAutoCombine_Struct); + SETUP_DIRECT_ENCODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); + + OUT(object_type); + OUT(some_id); + eq->container_slot = ServerToLaurionSlot(emu->unknown1); + structs::InventorySlot_Struct LaurionSlot; + LaurionSlot.Type = 8; // Observed + LaurionSlot.Padding1 = 0; + LaurionSlot.Slot = 0xffff; + LaurionSlot.SubIndex = 0xffff; + LaurionSlot.AugIndex = 0xffff; + LaurionSlot.Padding2 = 0; + eq->unknown_slot = LaurionSlot; + OUT(recipe_id); + OUT(reply_code); + + FINISH_ENCODE(); + } + ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } ENCODE(OP_RespondAA) @@ -3613,6 +3651,17 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = RoF2ToServerSlot(eq->container_slot); + emu->guildtribute_slot = RoF2ToServerSlot(eq->guildtribute_slot); // this should only return INVALID_INDEX until implemented + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_WearChange) { DECODE_LENGTH_EXACT(structs::WearChange_Struct); diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 3e2485a84..30ad3c6b1 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -11,6 +11,7 @@ E(OP_BuffCreate) E(OP_CastSpell) E(OP_ChannelMessage) E(OP_CharInventory) +E(OP_ClickObjectAction) E(OP_ClientUpdate) E(OP_Consider) E(OP_Damage) @@ -37,6 +38,7 @@ E(OP_PlayerProfile) E(OP_RemoveBlockedBuffs) E(OP_RespondAA) E(OP_RequestClientZoneChange) +E(OP_RecipeAutoCombine) E(OP_SendAATable) E(OP_SendCharInfo) E(OP_SendMaxCharacters) @@ -76,6 +78,7 @@ D(OP_RemoveBlockedBuffs) D(OP_SetServerFilter) D(OP_ShopRequest) D(OP_SpawnAppearance) +D(OP_TradeSkillCombine) D(OP_WearChange) D(OP_ZoneEntry) D(OP_ZoneChange) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 9ed1829e4..6d14ccdb3 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -904,6 +904,50 @@ namespace Laurion { TypelessInventorySlot_Struct inventorySlot; uint32 success; }; + + /* + ** Click Object Acknowledgement Struct + ** Response to client clicking on a World Container (ie, forge) + ** Seems to have not changed from RoF2 + */ + struct ClickObjectAction_Struct { + /*00*/ //uint32 player_id; // Appears to have been removed + /*00*/ uint32 drop_id; // Appears to use the object_count field now + /*04*/ int32 unknown04; // Seen -1 + /*08*/ int32 unknown08; // Seen -1 + /*08*/ //uint32 open; // 1=opening, 0=closing - Removed? + /*12*/ uint32 type; // See object.h, "Object Types" + /*16*/ uint32 unknown16; // + /*20*/ uint32 icon; // Icon to display for tradeskill containers + /*24*/ uint32 unknown24; // + /*28*/ char object_name[64]; // Object name to display + /*92*/ + }; + + //received and sent back as an ACK with different reply_code + struct RecipeAutoCombine_Struct { + /*00*/ uint32 object_type; + /*04*/ uint32 some_id; + /*08*/ InventorySlot_Struct container_slot; //echoed in reply - Was uint32 unknown1 + /*20*/ InventorySlot_Struct unknown_slot; //echoed in reply + /*32*/ uint32 recipe_id; + /*36*/ uint32 reply_code; + /*40*/ + }; + + /* + ** New Combine Struct + ** Client requesting to perform a tradeskill combine + ** Size: 24 bytes + ** Used In: OP_TradeSkillCombine + ** Last Updated: 01-05-2013 + */ + struct NewCombine_Struct + { + /*00*/ InventorySlot_Struct container_slot; + /*12*/ InventorySlot_Struct guildtribute_slot; // Slot type is 8? (MapGuildTribute = 8) + /*24*/ + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 40381fe43..7471131f1 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -475,15 +475,15 @@ OP_ShopRetrieveParcel=0x0000 OP_ShopParcelIcon=0x0000 # tradeskill stuff: -OP_ClickObject=0x0000 -OP_ClickObjectAction=0x0000 -OP_ClearObject=0x0000 -OP_RecipeDetails=0x0000 -OP_RecipesFavorite=0x0000 -OP_RecipesSearch=0x0000 -OP_RecipeReply=0x0000 -OP_RecipeAutoCombine=0x0000 -OP_TradeSkillCombine=0x0000 +OP_ClickObject=0x687e +OP_ClickObjectAction=0x110f +OP_ClearObject=0x6155 +OP_RecipeDetails=0x01e7 +OP_RecipesFavorite=0x0495 +OP_RecipesSearch=0x2f4e +OP_RecipeReply=0x2cd2 +OP_RecipeAutoCombine=0x5dba +OP_TradeSkillCombine=0x4ed8 # Tribute Packets: OP_OpenTributeMaster=0x0000 From 4d05e4b53e9a17c92e416230ec4cb30b7f366348 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 30 Dec 2024 19:16:49 -0800 Subject: [PATCH 87/93] Add encodes/decodes for delete charge and delete item --- common/patches/laurion.cpp | 35 +++++++++++++++++++++++++++++++++-- common/patches/laurion_ops.h | 3 +++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index bfc0d3d25..9fa50a63c 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -527,6 +527,25 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_DeleteCharge) + { + Log(Logs::Detail, Logs::Netcode, "Laurion::ENCODE(OP_DeleteCharge)"); + + ENCODE_FORWARD(OP_MoveItem); + } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToLaurionSlot(emu->from_slot); + eq->to_slot = ServerToLaurionSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + ENCODE(OP_DeleteSpawn) { ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); @@ -3546,6 +3565,18 @@ namespace Laurion DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = LaurionToServerSlot(eq->from_slot); + emu->to_slot = LaurionToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_EnterWorld) { DECODE_LENGTH_EXACT(structs::EnterWorld_Struct); @@ -3656,8 +3687,8 @@ namespace Laurion DECODE_LENGTH_EXACT(structs::NewCombine_Struct); SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - emu->container_slot = RoF2ToServerSlot(eq->container_slot); - emu->guildtribute_slot = RoF2ToServerSlot(eq->guildtribute_slot); // this should only return INVALID_INDEX until implemented + emu->container_slot = LaurionToServerSlot(eq->container_slot); + emu->guildtribute_slot = LaurionToServerSlot(eq->guildtribute_slot); // this should only return INVALID_INDEX until implemented FINISH_DIRECT_DECODE(); } diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 30ad3c6b1..c4d7be633 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -16,6 +16,8 @@ E(OP_ClientUpdate) E(OP_Consider) E(OP_Damage) E(OP_Death) +E(OP_DeleteCharge) +E(OP_DeleteItem) E(OP_DeleteSpawn) E(OP_ExpansionInfo) E(OP_ExpUpdate) @@ -68,6 +70,7 @@ D(OP_ClientUpdate) D(OP_ClickDoor) D(OP_Consider) D(OP_ConsiderCorpse) +D(OP_DeleteItem) D(OP_EnterWorld) D(OP_GMTraining) D(OP_GroupDisband) From 33164dc47ae052ad5dcc7253906b2c783e02e0a3 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 30 Dec 2024 19:39:26 -0800 Subject: [PATCH 88/93] Added rof2 style discipline opcodes. Might need work but looking at assembly looks like it might work too. --- common/patches/laurion.cpp | 10 ++++++++++ common/patches/laurion_ops.h | 1 + common/patches/laurion_structs.h | 4 ++++ utils/patches/patch_Laurion.conf | 4 ++-- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 9fa50a63c..feb38f6b3 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -557,6 +557,16 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_DisciplineUpdate) + { + ENCODE_LENGTH_EXACT(Disciplines_Struct); + SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); + + memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); + + FINISH_ENCODE(); + } + ENCODE(OP_ExpansionInfo) { ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index c4d7be633..d665f8e1e 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -19,6 +19,7 @@ E(OP_Death) E(OP_DeleteCharge) E(OP_DeleteItem) E(OP_DeleteSpawn) +E(OP_DisciplineUpdate) E(OP_ExpansionInfo) E(OP_ExpUpdate) E(OP_FormattedMessage) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 6d14ccdb3..ddd75a7b9 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -948,6 +948,10 @@ namespace Laurion { /*12*/ InventorySlot_Struct guildtribute_slot; // Slot type is 8? (MapGuildTribute = 8) /*24*/ }; + + struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 7471131f1..b89301765 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -339,8 +339,8 @@ OP_ItemViewUnknown=0x0000 OP_VetRewardsAvaliable=0x0000 OP_VetClaimRequest=0x0000 OP_VetClaimReply=0x0000 -OP_DisciplineUpdate=0x0000 -OP_DisciplineTimer=0x0000 +OP_DisciplineUpdate=0x6ce4 +OP_DisciplineTimer=0x7436 OP_BecomeCorpse=0x0000 # Unused? OP_Action2=0x0000 # Unused? OP_MobUpdate=0x0000 From 9a18b7772a228810e62fa2239137a46ced69d78b Mon Sep 17 00:00:00 2001 From: KimLS Date: Wed, 1 Jan 2025 13:38:02 -0800 Subject: [PATCH 89/93] Some more work on merchant system. --- common/patches/laurion.cpp | 52 ++++++++++++++++++++++++++++++++ common/patches/laurion_ops.h | 4 +++ common/patches/laurion_structs.h | 37 +++++++++++++++++++++++ utils/patches/patch_Laurion.conf | 14 ++++----- 4 files changed, 100 insertions(+), 7 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index feb38f6b3..e2b0911ea 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -2749,6 +2749,33 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_ShopPlayerBuy) + { + ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); + SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Response_Struct); + + OUT(npcid); + OUT(playerid); + OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Response_Struct); + + OUT(npcid); + eq->inventory_slot = ServerToLaurionTypelessSlot(emu->itemslot, EQ::invtype::typePossessions); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + ENCODE(OP_ShopRequest) { ENCODE_LENGTH_EXACT(MerchantClick_Struct); @@ -3671,6 +3698,31 @@ namespace Laurion FINISH_DIRECT_DECODE(); } + DECODE(OP_ShopPlayerBuy) + { + DECODE_LENGTH_EXACT(structs::Merchant_Sell_Request_Struct); + SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Request_Struct); + + IN(npcid); + IN(playerid); + IN(itemslot); + IN(quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Request_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Request_Struct); + + IN(npcid); + emu->itemslot = LaurionToServerTypelessSlot(eq->inventory_slot, invtype::typePossessions); + IN(quantity); + + FINISH_DIRECT_DECODE(); + } + DECODE(OP_ShopRequest) { DECODE_LENGTH_EXACT(structs::MerchantClickRequest_Struct); diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index d665f8e1e..0fc174a11 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -48,6 +48,8 @@ E(OP_SendMaxCharacters) E(OP_SendMembership) E(OP_SendMembershipDetails) E(OP_SendZonepoints) +E(OP_ShopPlayerBuy) +E(OP_ShopPlayerSell) E(OP_ShopRequest) E(OP_SkillUpdate) E(OP_SpecialMesg) @@ -80,6 +82,8 @@ D(OP_GroupInvite2) D(OP_MoveItem) D(OP_RemoveBlockedBuffs) D(OP_SetServerFilter) +D(OP_ShopPlayerBuy) +D(OP_ShopPlayerSell) D(OP_ShopRequest) D(OP_SpawnAppearance) D(OP_TradeSkillCombine) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index ddd75a7b9..637c6ff69 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -952,6 +952,43 @@ namespace Laurion { struct Disciplines_Struct { uint32 values[MAX_PP_DISCIPLINES]; }; + + struct Merchant_Sell_Request_Struct { + /*000*/ uint32 npcid; // Merchant NPC's entity id + /*004*/ uint32 playerid; // Player's entity id + /*008*/ uint32 itemslot; // Merchant Slot / Item Instance ID + /*012*/ uint32 unknown12; + /*016*/ uint32 quantity; // Already sold + /*020*/ + }; + + struct Merchant_Sell_Response_Struct { + /*000*/ uint32 npcid; // Merchant NPC's entity id + /*004*/ uint32 playerid; // Player's entity id + /*008*/ uint32 itemslot; // Merchant Slot / Item Instance ID + /*012*/ uint32 unknown12; + /*016*/ uint32 quantity; // Already sold + /*020*/ uint32 unknown20; + /*024*/ uint32 price; + /*028*/ uint32 unknown28; // Normally 0, but seen 84 c5 63 00 as well + /*032*/ + }; + + struct Merchant_Purchase_Request_Struct { + /*000*/ uint32 npcid; // Merchant NPC's entity id + /*004*/ TypelessInventorySlot_Struct inventory_slot; + /*012*/ uint32 quantity; + /*016*/ + }; + + struct Merchant_Purchase_Response_Struct { + /*000*/ uint32 npcid; // Merchant NPC's entity id + /*004*/ TypelessInventorySlot_Struct inventory_slot; + /*012*/ uint32 quantity; + /*016*/ uint32 price; + /*020*/ uint32 unknown020; + /*024*/ + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index b89301765..1e81cbd27 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -463,16 +463,16 @@ OP_ItemVerifyReply=0x0000 OP_ItemAdvancedLoreText=0x0000 # merchant stuff -OP_ShopPlayerSell=0x0000 +OP_ShopPlayerSell=0x6489 OP_ShopRequest=0x840 OP_ShopEnd=0x74bb OP_ShopEndConfirm=0x2ed1 -OP_ShopPlayerBuy=0x0000 -OP_ShopDelItem=0x0000 -OP_ShopSendParcel=0x0000 -OP_ShopDeleteParcel=0x0000 -OP_ShopRetrieveParcel=0x0000 -OP_ShopParcelIcon=0x0000 +OP_ShopPlayerBuy=0x625e +OP_ShopDelItem=0x4ce4 +OP_ShopSendParcel=0x0f16 +OP_ShopDeleteParcel=0x4e2a +OP_ShopRetrieveParcel=0x27d1 +OP_ShopParcelIcon=0x4f27 # tradeskill stuff: OP_ClickObject=0x687e From 8eb20efabbe4b9251c5ddd717d1cea369a949510 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 3 Jan 2025 22:19:15 -0800 Subject: [PATCH 90/93] -Stun -Player Trading -Item Recast -CancelSneakHide -Item Verification --- common/patches/laurion.cpp | 23 +++++++++++++++++ common/patches/laurion_ops.h | 2 ++ common/patches/laurion_structs.h | 22 +++++++++++++++++ utils/patches/patch_Laurion.conf | 42 ++++++++++++++++---------------- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index e2b0911ea..8c45ac303 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -335,6 +335,17 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_CancelTrade) + { + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); + + OUT(fromid); + OUT(action); + + FINISH_ENCODE(); + } + ENCODE(OP_CastSpell) { ENCODE_LENGTH_EXACT(CastSpell_Struct); @@ -2918,6 +2929,18 @@ namespace Laurion FINISH_ENCODE(); } + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + ENCODE(OP_WearChange) { ENCODE_LENGTH_EXACT(WearChange_Struct); diff --git a/common/patches/laurion_ops.h b/common/patches/laurion_ops.h index 0fc174a11..64e5de501 100644 --- a/common/patches/laurion_ops.h +++ b/common/patches/laurion_ops.h @@ -8,6 +8,7 @@ E(OP_BeginCast) E(OP_BlockedBuffs) E(OP_Buff) E(OP_BuffCreate) +E(OP_CancelTrade) E(OP_CastSpell) E(OP_ChannelMessage) E(OP_CharInventory) @@ -55,6 +56,7 @@ E(OP_SkillUpdate) E(OP_SpecialMesg) E(OP_SpawnAppearance) E(OP_SpawnDoor) +E(OP_Stun) E(OP_WearChange) E(OP_ZoneChange) E(OP_ZoneEntry) diff --git a/common/patches/laurion_structs.h b/common/patches/laurion_structs.h index 637c6ff69..e5eac187d 100644 --- a/common/patches/laurion_structs.h +++ b/common/patches/laurion_structs.h @@ -989,6 +989,28 @@ namespace Laurion { /*020*/ uint32 unknown020; /*024*/ }; + + /* + ** Cancel Trade struct + ** Sent when a player cancels a trade + ** Size: 8 bytes + ** Used In: OP_CancelTrade + ** + */ + struct CancelTrade_Struct { + /*00*/ uint32 fromid; + /*04*/ uint32 action; + /*08*/ + }; + + struct Stun_Struct { // 8 bytes total + /*000*/ uint32 duration; // Duration of stun + /*004*/ uint8 unknown004; // seen 0 + /*005*/ uint8 unknown005; // seen 163 + /*006*/ uint8 unknown006; // seen 67 + /*007*/ uint8 unknown007; // seen 0 + /*008*/ + }; #pragma pack() }; //end namespace structs diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 1e81cbd27..1b93b689c 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -78,11 +78,6 @@ OP_PreLogoutReply=0x0000 # Required to fully log in OP_SpawnAppearance=0x4eb0 OP_ChangeSize=0x2fdc -OP_TributeUpdate=0x0000 -OP_TributeTimer=0x0000 -OP_SendTributes=0x0000 -OP_RequestGuildTributes=0x0000 -OP_TributeInfo=0x0000 OP_Weather=0x6fe6 OP_ReqClientSpawn=0x6732 OP_SpawnDoor=0x4273 @@ -309,7 +304,7 @@ OP_ClearNPCMarks=0x0000 OP_DelegateAbility=0x0000 OP_SetGroupTarget=0x0000 OP_Charm=0x66bb -OP_Stun=0x0000 +OP_Stun=0x34be OP_SendFindableNPCs=0x0000 OP_FindPersonRequest=0x0000 OP_FindPersonReply=0x0000 @@ -364,11 +359,11 @@ OP_IncreaseStats=0x0000 OP_Weblink=0x0000 OP_OpenContainer=0x0000 OP_Marquee=0x0000 -OP_ItemRecastDelay=0x0000 +OP_ItemRecastDelay=0x547a #OP_OpenInventory=0x0000 # Likely does not exist in RoF -U OP_ResetAA=0x53c0 OP_Fling=0x3731 -OP_CancelSneakHide=0x0000 +OP_CancelSneakHide=0x7452 OP_AggroMeterLockTarget=0x0000 OP_AggroMeterTargetInfo=0x0000 OP_AggroMeterUpdate=0x0000 @@ -441,24 +436,24 @@ OP_Bazaar=0x0000 OP_TraderItemUpdate=0x0000 # pc/npc trading -OP_TradeRequest=0x0000 -OP_TradeAcceptClick=0x0000 -OP_TradeRequestAck=0x0000 -OP_TradeCoins=0x0000 -OP_FinishTrade=0x0000 -OP_CancelTrade=0x0000 -OP_TradeMoneyUpdate=0x0000 -OP_MoneyUpdate=0x0000 -OP_TradeBusy=0x0000 +OP_TradeRequest=0x7066 +OP_TradeAcceptClick=0x34ad +OP_TradeRequestAck=0x1c6b +OP_TradeCoins=0x44fe +OP_FinishTrade=0x0ec6 +OP_CancelTrade=0x5839 +OP_TradeMoneyUpdate=0x5fb3 +OP_MoneyUpdate=0x70bb +OP_TradeBusy=0x109f # Sent after canceling trade or after closing tradeskill object -OP_FinishWindow=0x0000 -OP_FinishWindow2=0x0000 +OP_FinishWindow=0x50d4 +OP_FinishWindow2=0x6b03 # Sent on Live for what seems to be item existance verification # Ex. Before Right Click Effect happens from items -OP_ItemVerifyRequest=0x0000 -OP_ItemVerifyReply=0x0000 +OP_ItemVerifyRequest=0x2003 +OP_ItemVerifyReply=0x43d0 OP_ItemAdvancedLoreText=0x0000 @@ -486,6 +481,11 @@ OP_RecipeAutoCombine=0x5dba OP_TradeSkillCombine=0x4ed8 # Tribute Packets: +OP_TributeUpdate=0x0000 +OP_TributeTimer=0x0000 +OP_SendTributes=0x0000 +OP_RequestGuildTributes=0x0000 +OP_TributeInfo=0x0000 OP_OpenTributeMaster=0x0000 OP_SelectTribute=0x0000 OP_TributeItem=0x0000 From c718ddbbf96227e38a9e3cca4fb36516d37bff46 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 5 Jan 2025 15:02:14 -0800 Subject: [PATCH 91/93] Fix compile issue with new casting slot conversion added the other day. --- common/patches/laurion.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/common/patches/laurion.cpp b/common/patches/laurion.cpp index 8c45ac303..6d29b014e 100644 --- a/common/patches/laurion.cpp +++ b/common/patches/laurion.cpp @@ -71,6 +71,7 @@ namespace Laurion static inline uint32 LaurionToServerCorpseMainSlot(uint32 laurion_corpse_slot); static inline uint32 LaurionToServerTypelessSlot(structs::TypelessInventorySlot_Struct laurion_slot, int16 laurion_type); static inline structs::InventorySlot_Struct LaurionCastingInventorySlotToInventorySlot(structs::CastSpellInventorySlot_Struct laurion_slot); + static inline structs::CastSpellInventorySlot_Struct LaurionInventorySlotToCastingInventorySlot(structs::InventorySlot_Struct laurion_slot); // Item packet types static item::ItemPacketType ServerToLaurionItemPacketType(ItemPacketType laurion_type); From ef8e1cf6e604df98711342cd82649edb08ef7abd Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 5 Jan 2025 18:17:50 -0800 Subject: [PATCH 92/93] Several opcodes --- utils/patches/patch_Laurion.conf | 48 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index 1b93b689c..fb0403190 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -170,7 +170,7 @@ OP_InspectAnswer=0x0000 OP_InspectMessageUpdate=0x0000 OP_BeginCast=0x31f9 OP_ColoredText=0x0f3c -OP_ConsentResponse=0x0000 +OP_ConsentResponse=0x3229 OP_MemorizeSpell=0x1d31 OP_LinkedReuse=0x7a8e OP_SwapSpell=0x63c7 @@ -181,19 +181,19 @@ OP_SimpleMessage=0x1943 OP_Buff=0x6ce5 OP_Illusion=0x5a3f OP_MoneyOnCorpse=0x39d3 -OP_RandomReply=0x0000 -OP_DenyResponse=0x0000 +OP_RandomReply=0x6603 +OP_DenyResponse=0x3f2c OP_SkillUpdate=0x6735 OP_GMTrainSkillConfirm=0x6fbc -OP_RandomReq=0x0000 +OP_RandomReq=0x528a OP_Death=0x429a OP_GMTraining=0x7c7a OP_GMEndTraining=0x3ec6 OP_GMTrainSkill=0x54e1 OP_Animation=0x79c7 OP_Begging=0x7ded -OP_Consent=0x0000 -OP_ConsentDeny=0x0000 +OP_Consent=0x44fc +OP_ConsentDeny=0x3df9 OP_AutoFire=0x5280 OP_PetCommands=0x0000 OP_PetCommandState=0x0000 @@ -203,15 +203,15 @@ OP_Surname=0x0000 OP_ClearSurname=0x0000 OP_FaceChange=0x0000 OP_SetFace=0x0000 -OP_SenseHeading=0x0000 +OP_SenseHeading=0x6fcf OP_Action=0x4c13 OP_ConsiderCorpse=0x6092 -OP_HideCorpse=0x0000 -OP_CorpseDrag=0x0000 -OP_CorpseDrop=0x0000 -OP_Bug=0x0000 +OP_HideCorpse=0x3f5c +OP_CorpseDrag=0x234d +OP_CorpseDrop=0x4342 +OP_Bug=0x770b OP_Feedback=0x0000 -OP_Report=0x0000 +OP_Report=0x5bcf OP_Damage=0x7d07 OP_ChannelMessage=0x6adc OP_Assist=0x51f1 @@ -219,12 +219,12 @@ OP_AssistGroup=0x3f23 OP_MoveCoin=0x4e6a OP_ZonePlayerToBind=0x5643 OP_KeyRing=0x0000 -OP_WhoAllRequest=0x0000 -OP_WhoAllResponse=0x0000 -OP_FriendsWho=0x0000 -OP_ConfirmDelete=0x0000 +OP_WhoAllRequest=0x2a09 +OP_WhoAllResponse=0x6404 +OP_FriendsWho=0x75a2 +OP_ConfirmDelete=0x4dd0 OP_Logout=0x771d -OP_Rewind=0x0000 +OP_Rewind=0x2b19 OP_TargetCommand=0x3b18 OP_Hide=0x1cdf OP_Jump=0x6fa0 @@ -257,18 +257,18 @@ OP_ItemLinkResponse=0x0000 OP_ItemLinkClick=0x0000 OP_ItemPreview=0x0000 OP_NewSpawn=0x3ea8 -OP_Track=0x0000 -OP_TrackTarget=0x0000 -OP_TrackUnknown=0x0000 +OP_Track=0x5351 +OP_TrackTarget=0x611a +OP_TrackUnknown=0x2c7a OP_ClickDoor=0x733c OP_MoveDoor=0x567c OP_RemoveAllDoors=0x73e8 -OP_EnvDamage=0x0000 +OP_EnvDamage=0x1ffd OP_BoardBoat=0x7015 OP_LeaveBoat=0x2486 OP_ControlBoat=0x166f -OP_Forage=0x0000 -OP_SafeFallSuccess=0x0000 +OP_Forage=0x4c52 +OP_SafeFallSuccess=0x6690 OP_RezzComplete=0x0000 OP_RezzRequest=0x0000 OP_RezzAnswer=0x0000 @@ -417,7 +417,7 @@ OP_MercenaryUnsuspendResponse=0x0000 # Looting OP_LootRequest=0x60e5 OP_EndLootRequest=0x35f6 -OP_LootItem=0x0000 +OP_LootItem=0x0856 OP_LootComplete=0x1f5e # bazaar trader stuff: From 4befe32c6109f29fb78059e7707332da96305373 Mon Sep 17 00:00:00 2001 From: KimLS Date: Tue, 7 Jan 2025 23:01:00 -0800 Subject: [PATCH 93/93] Handful of opcodes identified. --- utils/patches/patch_Laurion.conf | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/utils/patches/patch_Laurion.conf b/utils/patches/patch_Laurion.conf index fb0403190..c51d912fb 100644 --- a/utils/patches/patch_Laurion.conf +++ b/utils/patches/patch_Laurion.conf @@ -231,7 +231,7 @@ OP_Jump=0x6fa0 OP_Camp=0x326f OP_Emote=0x0000 OP_SetRunMode=0x1449 -OP_BankerChange=0x0000 +OP_BankerChange=0x2a33 OP_TargetMouse=0x5741 OP_MobHealth=0x5b77 OP_InitialMobHealth=0x0000 # Unused? @@ -291,12 +291,12 @@ OP_PotionBelt=0x0000 OP_DuelDecline=0x0000 OP_DuelAccept=0x0000 OP_SaveOnZoneReq=0x3bfe -OP_ReadBook=0x0000 +OP_ReadBook=0x51af OP_Dye=0x0000 OP_InterruptCast=0x1d71 OP_AAAction=0x71BB -OP_LeadershipExpToggle=0x0000 -OP_LeadershipExpUpdate=0x0000 +OP_LeadershipExpToggle=0x0000 #removed, these act as if all purchased now +OP_LeadershipExpUpdate=0x0000 #removed, these act as if all purchased now OP_PurchaseLeadershipAA=0x0000 #removed, these act as if all purchased now OP_UpdateLeadershipAA=0x0000 #removed, these act as if all purchased now OP_MarkNPC=0x0000 @@ -324,12 +324,12 @@ OP_PVPLeaderBoardRequest=0x0000 OP_PVPLeaderBoardReply=0x0000 OP_PVPLeaderBoardDetailsRequest=0x0000 OP_PVPLeaderBoardDetailsReply=0x0000 -OP_RestState=0x0000 +OP_RestState=0x0a92 OP_RespawnWindow=0x55ed OP_LDoNButton=0x0000 OP_SetStartCity=0x0000 -OP_VoiceMacroIn=0x0000 -OP_VoiceMacroOut=0x0000 +OP_VoiceMacroIn=0x703f +OP_VoiceMacroOut=0x72d1 OP_ItemViewUnknown=0x0000 OP_VetRewardsAvaliable=0x0000 OP_VetClaimRequest=0x0000 @@ -354,11 +354,11 @@ OP_AltCurrencyReclaim=0x0000 OP_CrystalCountUpdate=0x0000 OP_CrystalCreate=0x0000 OP_CrystalReclaim=0x0000 -OP_Untargetable=0x0000 -OP_IncreaseStats=0x0000 -OP_Weblink=0x0000 -OP_OpenContainer=0x0000 -OP_Marquee=0x0000 +OP_Untargetable=0x026f +OP_IncreaseStats=0x1005 +OP_Weblink=0x16a3 +OP_OpenContainer=0x6758 +OP_Marquee=0x6bca OP_ItemRecastDelay=0x547a #OP_OpenInventory=0x0000 # Likely does not exist in RoF -U OP_ResetAA=0x53c0 @@ -367,9 +367,9 @@ OP_CancelSneakHide=0x7452 OP_AggroMeterLockTarget=0x0000 OP_AggroMeterTargetInfo=0x0000 OP_AggroMeterUpdate=0x0000 -OP_UnderWorld=0x0000 # clients sends up when they detect an underworld issue, might be useful for cheat detection -OP_KickPlayers=0x0000 -OP_BookButton=0x0000 +OP_UnderWorld=0x4ca9 # clients sends up when they detect an underworld issue, might be useful for cheat detection +OP_KickPlayers=0x7154 +OP_BookButton=0x014d # Expeditions OP_DzQuit=0x0000 @@ -650,7 +650,7 @@ OP_LoginComplete=0x0000 # discovered opcodes not yet used: OP_PickLockSuccess=0x0000 -OP_PlayMP3=0x0000 +OP_PlayMP3=0x6451 OP_ReclaimCrystals=0x0000 OP_DynamicWall=0x0000 OP_OpenDiscordMerchant=0x0000