From 48fcebfe12b33adba684e248812b1ba78ce8662e Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Mon, 27 Apr 2020 14:36:00 +0200 Subject: [PATCH 01/21] fix: don't reenable updated plugins automatically --- Dalamud/Plugin/PluginRepository.cs | 36 +++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/Dalamud/Plugin/PluginRepository.cs b/Dalamud/Plugin/PluginRepository.cs index 97a80d567..4e68d64cc 100644 --- a/Dalamud/Plugin/PluginRepository.cs +++ b/Dalamud/Plugin/PluginRepository.cs @@ -58,14 +58,15 @@ namespace Dalamud.Plugin } } - public bool InstallPlugin(PluginDefinition definition) { + public bool InstallPlugin(PluginDefinition definition, bool enableAfterInstall = true) { try { var outputDir = new DirectoryInfo(Path.Combine(this.pluginDirectory, definition.InternalName, definition.AssemblyVersion)); var dllFile = new FileInfo(Path.Combine(outputDir.FullName, $"{definition.InternalName}.dll")); var disabledFile = new FileInfo(Path.Combine(outputDir.FullName, ".disabled")); + var wasDisabled = disabledFile.Exists; - if (dllFile.Exists) + if (dllFile.Exists && enableAfterInstall) { if (disabledFile.Exists) disabledFile.Delete(); @@ -73,9 +74,17 @@ namespace Dalamud.Plugin return this.manager.LoadPluginFromAssembly(dllFile, false); } - if (outputDir.Exists) - outputDir.Delete(true); - outputDir.Create(); + if (dllFile.Exists && !enableAfterInstall) { + return true; + } + + try { + if (outputDir.Exists) + outputDir.Delete(true); + outputDir.Create(); + } catch { + // ignored, since the plugin may be loaded already + } var path = Path.GetTempFileName(); Log.Information("Downloading plugin to {0}", path); @@ -86,6 +95,11 @@ namespace Dalamud.Plugin ZipFile.ExtractToDirectory(path, outputDir.FullName); + if (wasDisabled || !enableAfterInstall) { + disabledFile.Create(); + return true; + } + return this.manager.LoadPluginFromAssembly(dllFile, false); } catch (Exception e) @@ -145,6 +159,12 @@ namespace Dalamud.Plugin if (!dryRun) { + var wasEnabled = + this.manager.Plugins.Where(x => x.Definition != null).Any( + x => x.Definition.InternalName == info.InternalName); ; + + Log.Verbose("wasEnabled: {0}", wasEnabled); + // Try to disable plugin if it is loaded try { @@ -153,7 +173,7 @@ namespace Dalamud.Plugin catch (Exception ex) { Log.Error(ex, "Plugin disable failed"); - hasError = true; + //hasError = true; } try { @@ -165,10 +185,10 @@ namespace Dalamud.Plugin disabledFile.Create(); } } catch (Exception ex) { - Log.Error(ex, "Plugin disable failed"); + Log.Error(ex, "Plugin disable old versions failed"); } - var installSuccess = InstallPlugin(remoteInfo); + var installSuccess = InstallPlugin(remoteInfo, wasEnabled); if (installSuccess) { From 6e2c55a59cd19b784600dee0c4a46cb812a76f80 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Mon, 27 Apr 2020 14:37:11 +0200 Subject: [PATCH 02/21] build: v4.9.3.0 --- Dalamud.Injector/Dalamud.Injector.csproj | 6 +++--- Dalamud/Dalamud.csproj | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dalamud.Injector/Dalamud.Injector.csproj b/Dalamud.Injector/Dalamud.Injector.csproj index 1ebf766c1..2f5adf457 100644 --- a/Dalamud.Injector/Dalamud.Injector.csproj +++ b/Dalamud.Injector/Dalamud.Injector.csproj @@ -14,10 +14,10 @@ true - 4.9.2.0 - 4.9.2.0 + 4.9.3.0 + 4.9.3.0 XIVLauncher addon injection - 4.9.2.0 + 4.9.3.0 diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 503a7fbcc..ead306bc1 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -14,9 +14,9 @@ true - 4.9.2.0 - 4.9.2.0 - 4.9.2.0 + 4.9.3.0 + 4.9.3.0 + 4.9.3.0 From d4244566e81da3e382ce5c172ecd02ddc57e884b Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Mon, 27 Apr 2020 14:51:39 +0200 Subject: [PATCH 03/21] fix: remove asset "version check" --- Dalamud/Interface/AssetManager.cs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/Dalamud/Interface/AssetManager.cs b/Dalamud/Interface/AssetManager.cs index a6e19bec8..b2e2311d5 100644 --- a/Dalamud/Interface/AssetManager.cs +++ b/Dalamud/Interface/AssetManager.cs @@ -27,17 +27,6 @@ namespace Dalamud.Interface public static async Task EnsureAssets(string baseDir) { using var client = new HttpClient(); - var assetVerRemote = await client.GetStringAsync(AssetStoreUrl + "version"); - - var assetVerPath = Path.Combine(baseDir, "assetver"); - var assetVerLocal = "0"; - if (File.Exists(assetVerPath)) - assetVerLocal = File.ReadAllText(assetVerPath); - - var forceRedownload = assetVerLocal != assetVerRemote; - if (forceRedownload) - Log.Information("Assets need redownload"); - Log.Verbose("Starting asset download"); foreach (var entry in AssetDictionary) { @@ -45,7 +34,7 @@ namespace Dalamud.Interface Directory.CreateDirectory(Path.GetDirectoryName(filePath)); - if (!File.Exists(filePath) || forceRedownload) { + if (!File.Exists(filePath)) { Log.Verbose("Downloading {0} to {1}...", entry.Key, entry.Value); try { File.WriteAllBytes(filePath, await client.GetByteArrayAsync(entry.Key)); @@ -56,13 +45,6 @@ namespace Dalamud.Interface } } - - try { - File.WriteAllText(assetVerPath, assetVerRemote); - } catch (Exception ex) { - Log.Error(ex, "Could not write asset version."); - } } - } } From bd2e5cda2afff61f7e5625b17dc584a57b844c17 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Mon, 27 Apr 2020 15:17:13 +0200 Subject: [PATCH 04/21] fix: we shouldn't do xivapi requests for characters when the name is empty --- Dalamud/DiscordBot/DiscordBotManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/DiscordBot/DiscordBotManager.cs b/Dalamud/DiscordBot/DiscordBotManager.cs index 4dae26982..6fec653ed 100644 --- a/Dalamud/DiscordBot/DiscordBotManager.cs +++ b/Dalamud/DiscordBot/DiscordBotManager.cs @@ -216,7 +216,7 @@ namespace Dalamud.DiscordBot { var avatarUrl = string.Empty; var lodestoneId = string.Empty; - if (!this.config.DisableEmbeds) { + if (!this.config.DisableEmbeds && !string.IsNullOrEmpty(senderName)) { var searchResult = await GetCharacterInfo(senderName, senderWorld); lodestoneId = searchResult.LodestoneId; From 017c30e107b84314ff5346f4664f19554e2f58a4 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Tue, 28 Apr 2020 01:48:36 +0200 Subject: [PATCH 05/21] feat: add handler for client send packets --- Dalamud/Game/Internal/Network/GameNetwork.cs | 97 +++++++++++++++---- .../Network/GameNetworkAddressResolver.cs | 9 +- .../Network/NetworkMessageDirection.cs | 6 ++ Dalamud/Util.cs | 46 ++++----- 4 files changed, 109 insertions(+), 49 deletions(-) create mode 100644 Dalamud/Game/Internal/Network/NetworkMessageDirection.cs diff --git a/Dalamud/Game/Internal/Network/GameNetwork.cs b/Dalamud/Game/Internal/Network/GameNetwork.cs index eb59ce2a5..384d80315 100644 --- a/Dalamud/Game/Internal/Network/GameNetwork.cs +++ b/Dalamud/Game/Internal/Network/GameNetwork.cs @@ -4,55 +4,79 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Dalamud.Hooking; using Serilog; +using SharpDX.DXGI; namespace Dalamud.Game.Internal.Network { public sealed class GameNetwork : IDisposable { - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void ProcessZonePacketDelegate(IntPtr a, IntPtr b, IntPtr dataPtr); + #region Hooks - private readonly Hook processZonePacketHook; + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + private delegate void ProcessZonePacketDownDelegate(IntPtr a, IntPtr b, IntPtr dataPtr); + private readonly Hook processZonePacketDownHook; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4); + private readonly Hook processZonePacketUpHook; + + #endregion private GameNetworkAddressResolver Address { get; } private IntPtr baseAddress; public delegate void OnZonePacketDelegate(IntPtr dataPtr); + [Obsolete("Please use OnNetworkMessage instead. For modifications, it will take precedence.")] public OnZonePacketDelegate OnZonePacket; - private readonly Dalamud dalamud; + public delegate void OnNetworkMessageDelegate(IntPtr dataPtr, NetworkMessageDirection direction); + + /// + /// Event that is called when a network message is sent/received. + /// + public OnNetworkMessageDelegate OnNetworkMessage; + + private readonly Queue zoneInjectQueue = new Queue(); - public GameNetwork(Dalamud dalamud, SigScanner scanner) { - this.dalamud = dalamud; + public GameNetwork(SigScanner scanner) { Address = new GameNetworkAddressResolver(); Address.Setup(scanner); Log.Verbose("===== G A M E N E T W O R K ====="); - Log.Verbose("ProcessZonePacket address {ProcessZonePacket}", Address.ProcessZonePacket); + Log.Verbose("ProcessZonePacketDown address {ProcessZonePacketDown}", Address.ProcessZonePacketDown); + Log.Verbose("ProcessZonePacketUp address {ProcessZonePacketUp}", Address.ProcessZonePacketUp); - this.processZonePacketHook = - new Hook(Address.ProcessZonePacket, - new ProcessZonePacketDelegate(ProcessZonePacketDetour), + this.processZonePacketDownHook = + new Hook(Address.ProcessZonePacketDown, + new ProcessZonePacketDownDelegate(ProcessZonePacketDownDetour), this); + + this.processZonePacketUpHook = + new Hook(Address.ProcessZonePacketUp, + new ProcessZonePacketUpDelegate(ProcessZonePacketUpDetour), + this); } public void Enable() { - this.processZonePacketHook.Enable(); + this.processZonePacketDownHook.Enable(); + this.processZonePacketUpHook.Enable(); } public void Dispose() { - this.processZonePacketHook.Dispose(); + this.processZonePacketDownHook.Dispose(); + this.processZonePacketUpHook.Dispose(); } - private void ProcessZonePacketDetour(IntPtr a, IntPtr b, IntPtr dataPtr) { + private void ProcessZonePacketDownDetour(IntPtr a, IntPtr b, IntPtr dataPtr) { this.baseAddress = a; - // Call events - this.OnZonePacket?.Invoke(dataPtr); - try { - this.processZonePacketHook.Original(a, b, dataPtr); + // Call events + this.OnZonePacket?.Invoke(dataPtr); + this.OnNetworkMessage?.Invoke(dataPtr, NetworkMessageDirection.ZoneDown); + + this.processZonePacketDownHook.Original(a, b, dataPtr); } catch (Exception ex) { string header; try { @@ -63,12 +87,45 @@ namespace Dalamud.Game.Internal.Network { header = "failed"; } - Log.Error(ex, "Exception on ProcessZonePacket hook. Header: " + header); + Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header); - this.processZonePacketHook.Original(a, b, dataPtr); + this.processZonePacketDownHook.Original(a, b, dataPtr); } } + private byte ProcessZonePacketUpDetour(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4) { + + try + { + // Call events + this.OnNetworkMessage?.Invoke(dataPtr, NetworkMessageDirection.ZoneUp); + + var op = Marshal.ReadInt16(dataPtr); + var length = Marshal.ReadInt16(dataPtr, 8); + + Log.Verbose("[ZONEUP] op: {0} len: {1}", op.ToString("X"), length); + Util.DumpMemory(dataPtr + 0x20, length); + } + catch (Exception ex) + { + string header; + try + { + var data = new byte[32]; + Marshal.Copy(dataPtr, data, 0, 32); + header = BitConverter.ToString(data); + } + catch (Exception) + { + header = "failed"; + } + + Log.Error(ex, "Exception on ProcessZonePacketUp hook. Header: " + header); + } + + return this.processZonePacketUpHook.Original(a1, dataPtr, a3, a4); + } + #if DEBUG public void InjectZoneProtoPacket(byte[] data) { this.zoneInjectQueue.Enqueue(data); @@ -102,7 +159,7 @@ namespace Dalamud.Game.Internal.Network { Marshal.Copy(packetData, 0, unmanagedPacketData, packetData.Length); if (this.baseAddress != IntPtr.Zero) { - this.processZonePacketHook.Original(this.baseAddress, IntPtr.Zero, unmanagedPacketData); + this.processZonePacketDownHook.Original(this.baseAddress, IntPtr.Zero, unmanagedPacketData); } Marshal.FreeHGlobal(unmanagedPacketData); diff --git a/Dalamud/Game/Internal/Network/GameNetworkAddressResolver.cs b/Dalamud/Game/Internal/Network/GameNetworkAddressResolver.cs index 09ad99ebb..dd78ea2d4 100644 --- a/Dalamud/Game/Internal/Network/GameNetworkAddressResolver.cs +++ b/Dalamud/Game/Internal/Network/GameNetworkAddressResolver.cs @@ -2,12 +2,15 @@ using System; namespace Dalamud.Game.Internal.Network { public sealed class GameNetworkAddressResolver : BaseAddressResolver { - public IntPtr ProcessZonePacket { get; private set; } - + public IntPtr ProcessZonePacketDown { get; private set; } + public IntPtr ProcessZonePacketUp { get; private set; } + protected override void Setup64Bit(SigScanner sig) { //ProcessZonePacket = sig.ScanText("48 89 74 24 18 57 48 83 EC 50 8B F2 49 8B F8 41 0F B7 50 02 8B CE E8 ?? ?? 7A FF 0F B7 57 02 8D 42 89 3D 5F 02 00 00 0F 87 60 01 00 00 4C 8D 05"); //ProcessZonePacket = sig.ScanText("48 89 74 24 18 57 48 83 EC 50 8B F2 49 8B F8 41 0F B7 50 02 8B CE E8 ?? ?? 73 FF 0F B7 57 02 8D 42 ?? 3D ?? ?? 00 00 0F 87 60 01 00 00 4C 8D 05"); - ProcessZonePacket = sig.ScanText("48 89 74 24 ?? 57 48 83 EC 50 8B FA 49 8B F0"); + ProcessZonePacketDown = sig.ScanText("48 89 74 24 ?? 57 48 83 EC 50 8B FA 49 8B F0"); + ProcessZonePacketUp = + sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 70 8B 81 ?? ?? ?? ??"); } } } diff --git a/Dalamud/Game/Internal/Network/NetworkMessageDirection.cs b/Dalamud/Game/Internal/Network/NetworkMessageDirection.cs new file mode 100644 index 000000000..0074a24de --- /dev/null +++ b/Dalamud/Game/Internal/Network/NetworkMessageDirection.cs @@ -0,0 +1,6 @@ +namespace Dalamud.Game.Internal.Network { + public enum NetworkMessageDirection { + ZoneDown, + ZoneUp + } +} diff --git a/Dalamud/Util.cs b/Dalamud/Util.cs index 8c1642c0f..36fbd92b6 100644 --- a/Dalamud/Util.cs +++ b/Dalamud/Util.cs @@ -1,19 +1,21 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; +using Serilog; -namespace Dalamud -{ - static class Util - { - public static string ByteArrayToHex(byte[] bytes, int offset = 0, int bytesPerLine = 16) - { - if (bytes == null) - { - return String.Empty; - } +namespace Dalamud { + internal static class Util { + public static void DumpMemory(IntPtr offset, int len = 512) { + var data = new byte[len]; + Marshal.Copy(offset, data, 0, len); + Log.Information(ByteArrayToHex(data)); + } + + public static string ByteArrayToHex(byte[] bytes, int offset = 0, int bytesPerLine = 16) { + if (bytes == null) return string.Empty; var hexChars = "0123456789ABCDEF".ToCharArray(); @@ -26,8 +28,7 @@ namespace Dalamud var sb = new StringBuilder(numLines * lineLength); - for (var i = 0; i < bytes.Length; i += bytesPerLine) - { + for (var i = 0; i < bytes.Length; i += bytesPerLine) { var h = i + offset; line[0] = hexChars[(h >> 28) & 0xF]; @@ -42,25 +43,18 @@ namespace Dalamud var hexColumn = offsetBlock; var charColumn = byteBlock; - for (var j = 0; j < bytesPerLine; j++) - { - if (j > 0 && (j & 7) == 0) - { - hexColumn++; - } + for (var j = 0; j < bytesPerLine; j++) { + if (j > 0 && (j & 7) == 0) hexColumn++; - if (i + j >= bytes.Length) - { + if (i + j >= bytes.Length) { line[hexColumn] = ' '; line[hexColumn + 1] = ' '; line[charColumn] = ' '; - } - else - { + } else { var by = bytes[i + j]; - line[hexColumn] = hexChars[(@by >> 4) & 0xF]; - line[hexColumn + 1] = hexChars[@by & 0xF]; - line[charColumn] = @by < 32 ? '.' : (char)@by; + line[hexColumn] = hexChars[(by >> 4) & 0xF]; + line[hexColumn + 1] = hexChars[by & 0xF]; + line[charColumn] = by < 32 ? '.' : (char) by; } hexColumn += 3; From fd18ba6e8ecb137623cb54ce36cfe8c237589eea Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Tue, 28 Apr 2020 02:33:25 +0200 Subject: [PATCH 06/21] refactor: remove OnZonePacket API --- Dalamud/Game/Internal/Framework.cs | 2 +- Dalamud/Game/Internal/Network/GameNetwork.cs | 6 ------ Dalamud/Game/Network/NetworkHandlers.cs | 8 ++++++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Dalamud/Game/Internal/Framework.cs b/Dalamud/Game/Internal/Framework.cs index 78250a17b..e65ead3a0 100644 --- a/Dalamud/Game/Internal/Framework.cs +++ b/Dalamud/Game/Internal/Framework.cs @@ -65,7 +65,7 @@ namespace Dalamud.Game.Internal { Gui = new GameGui(Address.GuiManager, scanner, dalamud); - Network = new GameNetwork(dalamud, scanner); + Network = new GameNetwork(scanner); //Resource = new ResourceManager(dalamud, scanner); } diff --git a/Dalamud/Game/Internal/Network/GameNetwork.cs b/Dalamud/Game/Internal/Network/GameNetwork.cs index 384d80315..505d7ab73 100644 --- a/Dalamud/Game/Internal/Network/GameNetwork.cs +++ b/Dalamud/Game/Internal/Network/GameNetwork.cs @@ -23,11 +23,6 @@ namespace Dalamud.Game.Internal.Network { private GameNetworkAddressResolver Address { get; } private IntPtr baseAddress; - public delegate void OnZonePacketDelegate(IntPtr dataPtr); - - [Obsolete("Please use OnNetworkMessage instead. For modifications, it will take precedence.")] - public OnZonePacketDelegate OnZonePacket; - public delegate void OnNetworkMessageDelegate(IntPtr dataPtr, NetworkMessageDirection direction); /// @@ -73,7 +68,6 @@ namespace Dalamud.Game.Internal.Network { try { // Call events - this.OnZonePacket?.Invoke(dataPtr); this.OnNetworkMessage?.Invoke(dataPtr, NetworkMessageDirection.ZoneDown); this.processZonePacketDownHook.Original(a, b, dataPtr); diff --git a/Dalamud/Game/Network/NetworkHandlers.cs b/Dalamud/Game/Network/NetworkHandlers.cs index 7f33290c6..f22b8745f 100644 --- a/Dalamud/Game/Network/NetworkHandlers.cs +++ b/Dalamud/Game/Network/NetworkHandlers.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using Dalamud.Data.TransientSheet; +using Dalamud.Game.Internal.Network; using Dalamud.Game.Network.MarketBoardUploaders; using Dalamud.Game.Network.Structures; using Dalamud.Game.Network.Universalis.MarketBoardUploaders; @@ -32,11 +33,14 @@ namespace Dalamud.Game.Network { this.uploader = new UniversalisMarketBoardUploader(dalamud); - dalamud.Framework.Network.OnZonePacket += OnZonePacket; + dalamud.Framework.Network.OnNetworkMessage += OnNetworkMessage; } - private void OnZonePacket(IntPtr dataPtr) { + private void OnNetworkMessage(IntPtr dataPtr, NetworkMessageDirection direction) { + if (direction != NetworkMessageDirection.ZoneDown) + return; + if (!this.dalamud.Data.IsDataReady) return; From 832c5f758fdb16ee6d022204037c8340af4d2d26 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Tue, 28 Apr 2020 17:59:42 +0200 Subject: [PATCH 07/21] fix: only flash window if NOT in foreground --- Dalamud/Game/Network/NetworkHandlers.cs | 18 ++++++++++-------- Dalamud/NativeFunctions.cs | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Dalamud/Game/Network/NetworkHandlers.cs b/Dalamud/Game/Network/NetworkHandlers.cs index f22b8745f..ad723e3c4 100644 --- a/Dalamud/Game/Network/NetworkHandlers.cs +++ b/Dalamud/Game/Network/NetworkHandlers.cs @@ -69,14 +69,16 @@ namespace Dalamud.Game.Network { contentFinderCondition.Image = 112324; } - var flashInfo = new NativeFunctions.FLASHWINFO(); - flashInfo.cbSize = (uint) Marshal.SizeOf(); - flashInfo.uCount = uint.MaxValue; - flashInfo.dwTimeout = 0; - flashInfo.dwFlags = NativeFunctions.FlashWindow.FLASHW_TRAY | - NativeFunctions.FlashWindow.FLASHW_TIMERNOFG; - flashInfo.hwnd = Process.GetCurrentProcess().MainWindowHandle; - NativeFunctions.FlashWindowEx(ref flashInfo); + if (!NativeFunctions.ApplicationIsActivated()) { + var flashInfo = new NativeFunctions.FLASHWINFO(); + flashInfo.cbSize = (uint)Marshal.SizeOf(); + flashInfo.uCount = uint.MaxValue; + flashInfo.dwTimeout = 0; + flashInfo.dwFlags = NativeFunctions.FlashWindow.FLASHW_ALL | + NativeFunctions.FlashWindow.FLASHW_TIMERNOFG; + flashInfo.hwnd = Process.GetCurrentProcess().MainWindowHandle; + NativeFunctions.FlashWindowEx(ref flashInfo); + } Task.Run(async () => { this.dalamud.Framework.Gui.Chat.Print("Duty pop: " + contentFinderCondition.Name); diff --git a/Dalamud/NativeFunctions.cs b/Dalamud/NativeFunctions.cs index 1b5f8a5be..37988eb48 100644 --- a/Dalamud/NativeFunctions.cs +++ b/Dalamud/NativeFunctions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -58,6 +59,28 @@ namespace Dalamud #endregion + /// Returns true if the current application has focus, false otherwise + public static bool ApplicationIsActivated() + { + var activatedHandle = GetForegroundWindow(); + if (activatedHandle == IntPtr.Zero) + { + return false; // No window is currently activated + } + + var procId = Process.GetCurrentProcess().Id; + int activeProcId; + GetWindowThreadProcessId(activatedHandle, out activeProcId); + + return activeProcId == procId; + } + + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); + [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FlashWindowEx(ref FLASHWINFO pwfi); From 10d91031dd53bf207fa68ef62e714f459bdc171c Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Tue, 28 Apr 2020 18:03:37 +0200 Subject: [PATCH 08/21] feat: add ClientOpCodes dict to DataManager --- Dalamud/Data/DataManager.cs | 21 +++++++++++++++++---- Dalamud/Interface/AssetManager.cs | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index f90f5ea22..090a522c1 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -22,7 +22,14 @@ namespace Dalamud.Data /// This class provides data for Dalamud-internal features, but can also be used by plugins if needed. /// public class DataManager { + /// + /// OpCodes sent by the server to the client. + /// public ReadOnlyDictionary ServerOpCodes { get; private set; } + /// + /// OpCodes sent by the client to the server. + /// + public ReadOnlyDictionary ClientOpCodes { get; private set; } /// /// An object which gives access to any of the game's sheet data. @@ -55,13 +62,19 @@ namespace Dalamud.Data { try { - Log.Verbose("Starting data download..."); + Log.Verbose("Starting data load..."); - var opCodeDict = + var zoneOpCodeDict = JsonConvert.DeserializeObject>(File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json"))); - this.ServerOpCodes = new ReadOnlyDictionary(opCodeDict); + ServerOpCodes = new ReadOnlyDictionary(zoneOpCodeDict); - Log.Verbose("Loaded {0} ServerOpCodes.", opCodeDict.Count); + Log.Verbose("Loaded {0} ServerOpCodes.", zoneOpCodeDict.Count); + + var clientOpCodeDict = + JsonConvert.DeserializeObject>(File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json"))); + ClientOpCodes = new ReadOnlyDictionary(clientOpCodeDict); + + Log.Verbose("Loaded {0} ClientOpCodes.", clientOpCodeDict.Count); var luminaOptions = new LuminaOptions diff --git a/Dalamud/Interface/AssetManager.cs b/Dalamud/Interface/AssetManager.cs index b2e2311d5..2b1d61adf 100644 --- a/Dalamud/Interface/AssetManager.cs +++ b/Dalamud/Interface/AssetManager.cs @@ -14,6 +14,7 @@ namespace Dalamud.Interface private static readonly Dictionary AssetDictionary = new Dictionary { {AssetStoreUrl + "UIRes/serveropcode.json", "UIRes/serveropcode.json" }, + {AssetStoreUrl + "UIRes/clientopcode.json", "UIRes/clientopcode.json" }, {AssetStoreUrl + "UIRes/NotoSansCJKjp-Medium.otf", "UIRes/NotoSansCJKjp-Medium.otf" }, {AssetStoreUrl + "UIRes/logo.png", "UIRes/logo.png" }, {AssetStoreUrl + "UIRes/loc/dalamud/dalamud_de.json", "UIRes/loc/dalamud/dalamud_de.json" }, From 193dab20a4901204c316901f7cb40c47eecb4de5 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Tue, 28 Apr 2020 19:50:47 +0200 Subject: [PATCH 09/21] feat: rework network message API again --- Dalamud/Game/Internal/Network/GameNetwork.cs | 16 +++++++------- Dalamud/Game/Network/NetworkHandlers.cs | 22 +++++++++----------- Dalamud/Interface/AssetManager.cs | 1 - 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/Dalamud/Game/Internal/Network/GameNetwork.cs b/Dalamud/Game/Internal/Network/GameNetwork.cs index 505d7ab73..953a775c7 100644 --- a/Dalamud/Game/Internal/Network/GameNetwork.cs +++ b/Dalamud/Game/Internal/Network/GameNetwork.cs @@ -11,7 +11,7 @@ namespace Dalamud.Game.Internal.Network { #region Hooks [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void ProcessZonePacketDownDelegate(IntPtr a, IntPtr b, IntPtr dataPtr); + private delegate void ProcessZonePacketDownDelegate(IntPtr a, uint targetId, IntPtr dataPtr); private readonly Hook processZonePacketDownHook; [UnmanagedFunctionPointer(CallingConvention.ThisCall)] @@ -23,7 +23,7 @@ namespace Dalamud.Game.Internal.Network { private GameNetworkAddressResolver Address { get; } private IntPtr baseAddress; - public delegate void OnNetworkMessageDelegate(IntPtr dataPtr, NetworkMessageDirection direction); + public delegate void OnNetworkMessageDelegate(IntPtr dataPtr, ushort opCode, uint targetId, NetworkMessageDirection direction); /// /// Event that is called when a network message is sent/received. @@ -63,14 +63,14 @@ namespace Dalamud.Game.Internal.Network { this.processZonePacketUpHook.Dispose(); } - private void ProcessZonePacketDownDetour(IntPtr a, IntPtr b, IntPtr dataPtr) { + private void ProcessZonePacketDownDetour(IntPtr a, uint targetId, IntPtr dataPtr) { this.baseAddress = a; try { // Call events - this.OnNetworkMessage?.Invoke(dataPtr, NetworkMessageDirection.ZoneDown); + this.OnNetworkMessage?.Invoke(dataPtr + 0x10, (ushort) Marshal.ReadInt16(dataPtr, 2), targetId, NetworkMessageDirection.ZoneDown); - this.processZonePacketDownHook.Original(a, b, dataPtr); + this.processZonePacketDownHook.Original(a, targetId, dataPtr); } catch (Exception ex) { string header; try { @@ -83,7 +83,7 @@ namespace Dalamud.Game.Internal.Network { Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header); - this.processZonePacketDownHook.Original(a, b, dataPtr); + this.processZonePacketDownHook.Original(a, targetId, dataPtr); } } @@ -92,7 +92,7 @@ namespace Dalamud.Game.Internal.Network { try { // Call events - this.OnNetworkMessage?.Invoke(dataPtr, NetworkMessageDirection.ZoneUp); + this.OnNetworkMessage?.Invoke(dataPtr + 0x20, (ushort) Marshal.ReadInt16(dataPtr), 0x0, NetworkMessageDirection.ZoneUp); var op = Marshal.ReadInt16(dataPtr); var length = Marshal.ReadInt16(dataPtr, 8); @@ -153,7 +153,7 @@ namespace Dalamud.Game.Internal.Network { Marshal.Copy(packetData, 0, unmanagedPacketData, packetData.Length); if (this.baseAddress != IntPtr.Zero) { - this.processZonePacketDownHook.Original(this.baseAddress, IntPtr.Zero, unmanagedPacketData); + this.processZonePacketDownHook.Original(this.baseAddress, 0, unmanagedPacketData); } Marshal.FreeHGlobal(unmanagedPacketData); diff --git a/Dalamud/Game/Network/NetworkHandlers.cs b/Dalamud/Game/Network/NetworkHandlers.cs index ad723e3c4..f0cc9c237 100644 --- a/Dalamud/Game/Network/NetworkHandlers.cs +++ b/Dalamud/Game/Network/NetworkHandlers.cs @@ -37,21 +37,19 @@ namespace Dalamud.Game.Network { } - private void OnNetworkMessage(IntPtr dataPtr, NetworkMessageDirection direction) { + private void OnNetworkMessage(IntPtr dataPtr, ushort opCode, uint targetId, NetworkMessageDirection direction) { if (direction != NetworkMessageDirection.ZoneDown) return; if (!this.dalamud.Data.IsDataReady) return; - var opCode = (ushort) Marshal.ReadInt16(dataPtr, 2); - if (opCode == this.dalamud.Data.ServerOpCodes["CfNotifyPop"]) { var data = new byte[64]; Marshal.Copy(dataPtr, data, 0, 64); - var notifyType = data[16]; - var contentFinderConditionId = BitConverter.ToUInt16(data, 36); + var notifyType = data[0]; + var contentFinderConditionId = BitConverter.ToUInt16(data, 0x14); if (notifyType != 3) return; @@ -103,8 +101,8 @@ namespace Dalamud.Game.Network { Task.Run(async () => { for (var rouletteIndex = 1; rouletteIndex < 11; rouletteIndex++) { - var currentRoleKey = data[16 + rouletteIndex]; - var prevRoleKey = this.lastPreferredRole[16 + rouletteIndex]; + var currentRoleKey = data[rouletteIndex]; + var prevRoleKey = this.lastPreferredRole[rouletteIndex]; Log.Verbose("CfPreferredRole: {0} - {1} => {2}", rouletteIndex, prevRoleKey, currentRoleKey); @@ -145,8 +143,8 @@ namespace Dalamud.Game.Network { if (!this.optOutMbUploads) { if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardItemRequestStart"]) { - var catalogId = (uint) Marshal.ReadInt32(dataPtr + 0x10); - var amount = Marshal.ReadByte(dataPtr + 0x1B); + var catalogId = (uint) Marshal.ReadInt32(dataPtr); + var amount = Marshal.ReadByte(dataPtr + 0xB); this.marketBoardRequests.Add(new MarketBoardItemRequest { CatalogId = catalogId, @@ -160,7 +158,7 @@ namespace Dalamud.Game.Network { } if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardOfferings"]) { - var listing = MarketBoardCurrentOfferings.Read(dataPtr + 0x10); + var listing = MarketBoardCurrentOfferings.Read(dataPtr); var request = this.marketBoardRequests.LastOrDefault( @@ -215,7 +213,7 @@ namespace Dalamud.Game.Network { } if (opCode == this.dalamud.Data.ServerOpCodes["MarketBoardHistory"]) { - var listing = MarketBoardHistory.Read(dataPtr + 0x10); + var listing = MarketBoardHistory.Read(dataPtr); var request = this.marketBoardRequests.LastOrDefault(r => r.CatalogId == listing.CatalogId); @@ -238,7 +236,7 @@ namespace Dalamud.Game.Network { if (opCode == this.dalamud.Data.ServerOpCodes["MarketTaxRates"]) { - var taxes = MarketTaxRates.Read(dataPtr + 0x10); + var taxes = MarketTaxRates.Read(dataPtr); Log.Verbose("MarketTaxRates: limsa#{0} grid#{1} uldah#{2} ish#{3} kugane#{4} cr#{5}", taxes.LimsaLominsaTax, taxes.GridaniaTax, taxes.UldahTax, taxes.IshgardTax, taxes.KuganeTax, taxes.CrystariumTax); diff --git a/Dalamud/Interface/AssetManager.cs b/Dalamud/Interface/AssetManager.cs index 2b1d61adf..a9aa492e7 100644 --- a/Dalamud/Interface/AssetManager.cs +++ b/Dalamud/Interface/AssetManager.cs @@ -43,7 +43,6 @@ namespace Dalamud.Interface // If another game is running, we don't want to just fail in here Log.Error(ex, "Could not download asset."); } - } } } From 923dd8b11fd2d2881ffbd36735184200fc77d4f7 Mon Sep 17 00:00:00 2001 From: pmgr <26606291+pmgr@users.noreply.github.com> Date: Wed, 29 Apr 2020 06:42:11 +0100 Subject: [PATCH 10/21] Added WorldToScreen and a prototype for ScreenToWorld. --- Dalamud/Game/Internal/Gui/GameGui.cs | 48 +++++++++++++++++++ .../Internal/Gui/GameGuiAddressResolver.cs | 4 ++ 2 files changed, 52 insertions(+) diff --git a/Dalamud/Game/Internal/Gui/GameGui.cs b/Dalamud/Game/Internal/Gui/GameGui.cs index 89f64eb2e..764b49d39 100644 --- a/Dalamud/Game/Internal/Gui/GameGui.cs +++ b/Dalamud/Game/Internal/Gui/GameGui.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; using Dalamud.Game.Chat.SeStringHandling.Payloads; using Dalamud.Hooking; using Serilog; +using SharpDX; namespace Dalamud.Game.Internal.Gui { public sealed class GameGui : IDisposable { @@ -34,6 +35,14 @@ namespace Dalamud.Game.Internal.Gui { private delegate bool OpenMapWithFlagDelegate(IntPtr UIMapObject, string flag); private OpenMapWithFlagDelegate openMapWithFlag; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate IntPtr GetMatrixSingletonDelegate(); + internal readonly GetMatrixSingletonDelegate getMatrixSingleton; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + private unsafe delegate IntPtr ScreenToWorldNativeDelegate(float *camPosition, float *clipCoords, float rayDistance, float *worldCoords, float *unknown); + private readonly ScreenToWorldNativeDelegate screenToWorldNative; + /// /// The item ID that is currently hovered by the player. 0 when no item is hovered. /// If > 1.000.000, subtract 1.000.000 and treat it as HQ @@ -74,6 +83,12 @@ namespace Dalamud.Game.Internal.Gui { this); this.getUIObject = Marshal.GetDelegateForFunctionPointer(Address.GetUIObject); + + this.getMatrixSingleton = + Marshal.GetDelegateForFunctionPointer(Address.GetMatrixSingleton); + + this.screenToWorldNative = + Marshal.GetDelegateForFunctionPointer(Address.ScreenToWorld); } private IntPtr HandleSetGlobalBgmDetour(UInt16 bgmKey, byte a2, UInt32 a3, UInt32 a4, UInt32 a5, byte a6) { @@ -159,6 +174,39 @@ namespace Dalamud.Game.Internal.Gui { return this.openMapWithFlag(uiMapObjectPtr, mapLinkString); } + public Vector2 WorldToScreen(Vector3 worldCoords) + { + // Get base object with matrices + var matrixSingleton = this.getMatrixSingleton(); + + // Read current ViewProjectionMatrix plus game window size + var viewProjectionMatrix = new Matrix(); + float width, height; + unsafe { + var rawMatrix = (float*) (matrixSingleton + 0x1b4).ToPointer(); + + for (var i = 0; i < 16; i++, rawMatrix += 1) { + viewProjectionMatrix[i] = *rawMatrix; + } + + width = *rawMatrix; + height = *(rawMatrix + 1); + } + + Vector3.Transform(ref worldCoords, ref viewProjectionMatrix, out Vector3 pCoords); + + var normalProjCoords = new Vector2(pCoords.X / pCoords.Z, pCoords.Y / pCoords.Z); + + normalProjCoords.X = 0.5f * width * (normalProjCoords.X + 1f); + normalProjCoords.Y = 0.5f * height * (1f - normalProjCoords.Y); + + return normalProjCoords; + } + + public Vector3 ScreenToWorld(Vector2 screenCoords) { + return new Vector3(); + } + public void SetBgm(ushort bgmKey) => this.setGlobalBgmHook.Original(bgmKey, 0, 0, 0, 0, 0); public void Enable() { diff --git a/Dalamud/Game/Internal/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Internal/Gui/GameGuiAddressResolver.cs index 5142d0d41..0b092fdf6 100644 --- a/Dalamud/Game/Internal/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Internal/Gui/GameGuiAddressResolver.cs @@ -12,6 +12,8 @@ namespace Dalamud.Game.Internal.Gui { public IntPtr HandleItemHover { get; set; } public IntPtr HandleItemOut { get; set; } public IntPtr GetUIObject { get; private set; } + public IntPtr GetMatrixSingleton { get; private set; } + public IntPtr ScreenToWorld { get; private set; } public GameGuiAddressResolver(IntPtr baseAddress) { BaseAddress = baseAddress; @@ -31,6 +33,8 @@ namespace Dalamud.Game.Internal.Gui { HandleItemHover = sig.ScanText("E8 ?? ?? ?? ?? 48 8B 5C 24 ?? 48 89 AE ?? ?? ?? ??"); HandleItemOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 4D"); GetUIObject = sig.ScanText("E8 ?? ?? ?? ?? 48 8B C8 48 8B 10 FF 52 40 80 88 ?? ?? ?? ?? 01 E9"); + GetMatrixSingleton = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? 48 89 4c 24 ?? 4C 8D 4D ?? 4C 8D 44 24 ??"); + ScreenToWorld = sig.ScanText("48 83 EC 48 48 8B 05 ?? ?? ?? ?? 4D 8B D1"); } } } From 84d1494b63594e225351757a9da373431681a4be Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Wed, 29 Apr 2020 15:15:41 +0200 Subject: [PATCH 11/21] feat: add implicit conversions to common vector types to Position3 --- Dalamud/Game/ClientState/Actors/Position3.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Dalamud/Game/ClientState/Actors/Position3.cs b/Dalamud/Game/ClientState/Actors/Position3.cs index 381d1a8de..517bc651f 100644 --- a/Dalamud/Game/ClientState/Actors/Position3.cs +++ b/Dalamud/Game/ClientState/Actors/Position3.cs @@ -6,5 +6,17 @@ namespace Dalamud.Game.ClientState.Actors { public float X; public float Z; public float Y; + + /// + /// Convert this Position3 to a System.Numerics.Vector3 + /// + /// Position to convert. + public static implicit operator System.Numerics.Vector3(Position3 pos) => new System.Numerics.Vector3(pos.X, pos.Y, pos.Z); + + /// + /// Convert this Position3 to a SharpDX.Vector3 + /// + /// Position to convert. + public static implicit operator SharpDX.Vector3(Position3 pos) => new SharpDX.Vector3(pos.X, pos.Y, pos.Z); } } From 8a1c923b369ea6bc5b6e762a19de0f239b2ee2f6 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Wed, 29 Apr 2020 15:34:27 +0200 Subject: [PATCH 12/21] fix: swap X and Z axis for SharpDX vectors --- Dalamud/Game/ClientState/Actors/Position3.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/ClientState/Actors/Position3.cs b/Dalamud/Game/ClientState/Actors/Position3.cs index 517bc651f..df367a06b 100644 --- a/Dalamud/Game/ClientState/Actors/Position3.cs +++ b/Dalamud/Game/ClientState/Actors/Position3.cs @@ -17,6 +17,6 @@ namespace Dalamud.Game.ClientState.Actors { /// Convert this Position3 to a SharpDX.Vector3 /// /// Position to convert. - public static implicit operator SharpDX.Vector3(Position3 pos) => new SharpDX.Vector3(pos.X, pos.Y, pos.Z); + public static implicit operator SharpDX.Vector3(Position3 pos) => new SharpDX.Vector3(pos.X, pos.Z, pos.Y); } } From 274148361e5a7bc4cc43252d91b0d68adfd8f579 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Wed, 29 Apr 2020 15:34:41 +0200 Subject: [PATCH 13/21] feat: add actor debug overlay for Dalamud Data Window --- Dalamud/Interface/DalamudDataWindow.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Dalamud/Interface/DalamudDataWindow.cs b/Dalamud/Interface/DalamudDataWindow.cs index 5232ba3e8..1fac5f67e 100644 --- a/Dalamud/Interface/DalamudDataWindow.cs +++ b/Dalamud/Interface/DalamudDataWindow.cs @@ -17,6 +17,8 @@ namespace Dalamud.Interface private int currentKind; + private bool drawActors = false; + public DalamudDataWindow(Dalamud dalamud) { this.dalamud = dalamud; @@ -84,6 +86,8 @@ namespace Dalamud.Interface stateString += $"LastLinkedItem: {this.dalamud.Framework.Gui.Chat.LastLinkedItemId.ToString()}\n"; stateString += $"TerritoryType: {this.dalamud.ClientState.TerritoryType}\n\n"; + ImGui.Checkbox("Draw actors on screen", ref this.drawActors); + for (var i = 0; i < this.dalamud.ClientState.Actors.Length; i++) { var actor = this.dalamud.ClientState.Actors[i]; @@ -103,6 +107,22 @@ namespace Dalamud.Interface if (actor is PlayerCharacter pc) stateString += $" HomeWorld: {pc.HomeWorld.GameData.Name} CurrentWorld: {pc.CurrentWorld.GameData.Name} FC: {pc.CompanyTag}\n"; + + if (this.drawActors) { + var screenCoords = this.dalamud.Framework.Gui.WorldToScreen(actor.Position); + + ImGui.PushID("ActorWindow" + i); + ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y)); + ImGui.SetNextWindowBgAlpha(0.35f); + if (ImGui.Begin("Actor" + i, + ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.AlwaysAutoResize | + ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoMouseInputs | + ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNav)) { + ImGui.Text($"{actor.Address.ToInt64():X}:{actor.ActorId:X}[{i}] - {actor.ObjectKind} - {actor.Name}"); + ImGui.End(); + } + ImGui.PopID(); + } } } From 143b8a15fcc0710b479f49e004d04132aac3980d Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Wed, 29 Apr 2020 16:03:58 +0200 Subject: [PATCH 14/21] feat: add plugin authors to credits --- Dalamud/Dalamud.cs | 8 ++------ Dalamud/Interface/DalamudCreditsWindow.cs | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 26fd2b73b..cb35b6fc7 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -238,11 +238,7 @@ namespace Dalamud { this.isImguiDrawDataWindow = true; } if (ImGui.MenuItem("Open Credits window")) { - var logoGraphic = - this.InterfaceManager.LoadImage( - Path.Combine(this.StartInfo.WorkingDirectory, "UIRes", "logo.png")); - this.creditsWindow = new DalamudCreditsWindow(logoGraphic, this.Framework); - this.isImguiDrawCreditsWindow = true; + OnOpenCreditsCommand(null, null); } ImGui.MenuItem("Draw ImGui demo", "", ref this.isImguiDrawDemoWindow); if (ImGui.MenuItem("Dump ImGui info")) @@ -671,7 +667,7 @@ namespace Dalamud { var logoGraphic = this.InterfaceManager.LoadImage( Path.Combine(this.StartInfo.WorkingDirectory, "UIRes", "logo.png")); - this.creditsWindow = new DalamudCreditsWindow(logoGraphic, this.Framework); + this.creditsWindow = new DalamudCreditsWindow(this, logoGraphic, this.Framework); this.isImguiDrawCreditsWindow = true; } diff --git a/Dalamud/Interface/DalamudCreditsWindow.cs b/Dalamud/Interface/DalamudCreditsWindow.cs index b2a3fbd2a..153f6fcdb 100644 --- a/Dalamud/Interface/DalamudCreditsWindow.cs +++ b/Dalamud/Interface/DalamudCreditsWindow.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Numerics; using Dalamud.Game.Internal; using ImGuiNET; @@ -7,10 +8,10 @@ using ImGuiScene; namespace Dalamud.Interface { class DalamudCreditsWindow : IDisposable { - private string creditsText = @$" + private const string CreditsTextTempl = @" Dalamud A FFXIV Hooking Framework -Version {typeof(Dalamud).Assembly.GetName().Version} +Version {0} created by: @@ -44,6 +45,11 @@ gucciBane +Your plugins were made by: + +{1} + + Special thanks: Adam @@ -64,14 +70,23 @@ Contribute at: https://github.com/goaaats/Dalamud Thank you for using XIVLauncher! "; + private readonly Dalamud dalamud; private TextureWrap logoTexture; private Framework framework; - public DalamudCreditsWindow(TextureWrap logoTexture, Framework framework) { + private string creditsText; + + public DalamudCreditsWindow(Dalamud dalamud, TextureWrap logoTexture, Framework framework) { + this.dalamud = dalamud; this.logoTexture = logoTexture; this.framework = framework; framework.Gui.SetBgm(132); + + var pluginCredits = dalamud.PluginManager.Plugins.Where(x => x.Definition != null).Aggregate(string.Empty, (current, plugin) => current + $"{plugin.Definition.Name} by {plugin.Definition.Author}\n"); + + this.creditsText = + string.Format(CreditsTextTempl, typeof(Dalamud).Assembly.GetName().Version, pluginCredits); } public void Dispose() { From 87bf4980979cc6bd1376415d999e9201840c5744 Mon Sep 17 00:00:00 2001 From: pmgr <26606291+pmgr@users.noreply.github.com> Date: Wed, 29 Apr 2020 15:39:26 +0100 Subject: [PATCH 15/21] Adding check to WorldToScreen for coordinates being in front, plus documentation --- Dalamud/Game/Internal/Gui/GameGui.cs | 23 +++++++++++++++++------ Dalamud/Interface/DalamudDataWindow.cs | 4 +--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Dalamud/Game/Internal/Gui/GameGui.cs b/Dalamud/Game/Internal/Gui/GameGui.cs index 764b49d39..aaa800460 100644 --- a/Dalamud/Game/Internal/Gui/GameGui.cs +++ b/Dalamud/Game/Internal/Gui/GameGui.cs @@ -141,6 +141,11 @@ namespace Dalamud.Game.Internal.Gui { return retVal; } + /// + /// Opens the in-game map with a flag on the location of the parameter + /// + /// Link to the map to be opened + /// True if there were no errors and it could open the map public bool OpenMapWithMapLink(MapLinkPayload mapLink) { var uiObjectPtr = getUIObject(); @@ -174,7 +179,13 @@ namespace Dalamud.Game.Internal.Gui { return this.openMapWithFlag(uiMapObjectPtr, mapLinkString); } - public Vector2 WorldToScreen(Vector3 worldCoords) + /// + /// Converts in-world coordinates to screen coordinates (upper left corner origin). + /// + /// Coordinates in the world + /// Converted coordinates + /// True if worldPos corresponds to a position in front of the camera + public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos) { // Get base object with matrices var matrixSingleton = this.getMatrixSingleton(); @@ -193,14 +204,14 @@ namespace Dalamud.Game.Internal.Gui { height = *(rawMatrix + 1); } - Vector3.Transform(ref worldCoords, ref viewProjectionMatrix, out Vector3 pCoords); + Vector3.Transform( ref worldPos, ref viewProjectionMatrix, out Vector3 pCoords); - var normalProjCoords = new Vector2(pCoords.X / pCoords.Z, pCoords.Y / pCoords.Z); + screenPos = new Vector2(pCoords.X / pCoords.Z, pCoords.Y / pCoords.Z); - normalProjCoords.X = 0.5f * width * (normalProjCoords.X + 1f); - normalProjCoords.Y = 0.5f * height * (1f - normalProjCoords.Y); + screenPos.X = 0.5f * width * (screenPos.X + 1f); + screenPos.Y = 0.5f * height * (1f - screenPos.Y); - return normalProjCoords; + return pCoords.Z > 0; } public Vector3 ScreenToWorld(Vector2 screenCoords) { diff --git a/Dalamud/Interface/DalamudDataWindow.cs b/Dalamud/Interface/DalamudDataWindow.cs index 1fac5f67e..d55476f2b 100644 --- a/Dalamud/Interface/DalamudDataWindow.cs +++ b/Dalamud/Interface/DalamudDataWindow.cs @@ -108,9 +108,7 @@ namespace Dalamud.Interface stateString += $" HomeWorld: {pc.HomeWorld.GameData.Name} CurrentWorld: {pc.CurrentWorld.GameData.Name} FC: {pc.CompanyTag}\n"; - if (this.drawActors) { - var screenCoords = this.dalamud.Framework.Gui.WorldToScreen(actor.Position); - + if (this.drawActors && this.dalamud.Framework.Gui.WorldToScreen(actor.Position, out var screenCoords)) { ImGui.PushID("ActorWindow" + i); ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y)); ImGui.SetNextWindowBgAlpha(0.35f); From 0800cc4e916c9b7c92f48cd9793ec0bcb5843579 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Wed, 29 Apr 2020 18:56:21 +0200 Subject: [PATCH 16/21] feat: add draw distance to actor view --- Dalamud/Game/ClientState/Actors/Types/Actor.cs | 10 ++++++++++ Dalamud/Game/ClientState/Structs/Actor.cs | 4 ++-- Dalamud/Interface/DalamudDataWindow.cs | 11 +++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Dalamud/Game/ClientState/Actors/Types/Actor.cs b/Dalamud/Game/ClientState/Actors/Types/Actor.cs index 275a5cd39..2289a8b30 100644 --- a/Dalamud/Game/ClientState/Actors/Types/Actor.cs +++ b/Dalamud/Game/ClientState/Actors/Types/Actor.cs @@ -49,5 +49,15 @@ namespace Dalamud.Game.ClientState.Actors.Types { /// possible values. /// public ObjectKind ObjectKind => this.actorStruct.ObjectKind; + + /// + /// The X distance from the local player in yalms. + /// + public byte YalmDistanceX => this.actorStruct.YalmDistanceFromPlayerX; + + /// + /// The Y distance from the local player in yalms. + /// + public byte YalmDistanceY => this.actorStruct.YalmDistanceFromPlayerY; } } diff --git a/Dalamud/Game/ClientState/Structs/Actor.cs b/Dalamud/Game/ClientState/Structs/Actor.cs index 68090891b..934ba630f 100644 --- a/Dalamud/Game/ClientState/Structs/Actor.cs +++ b/Dalamud/Game/ClientState/Structs/Actor.cs @@ -20,9 +20,9 @@ namespace Dalamud.Game.ClientState.Structs [FieldOffset(140)] public ObjectKind ObjectKind; [FieldOffset(141)] public byte SubKind; [FieldOffset(142)] public bool IsFriendly; - [FieldOffset(144)] public byte YalmDistanceFromPlayer1; // Demo says one of these is x distance + [FieldOffset(144)] public byte YalmDistanceFromPlayerX; // Demo says one of these is x distance [FieldOffset(145)] public byte PlayerTargetStatus; // This is some kind of enum - [FieldOffset(146)] public byte YalmDistanceFromPlayer2; // and the other is z distance + [FieldOffset(146)] public byte YalmDistanceFromPlayerY; // and the other is z distance [FieldOffset(160)] public Position3 Position; [FieldOffset(0x17F8)] public int TargetActorId; // This field can't be correctly aligned, so we have to cut it manually. diff --git a/Dalamud/Interface/DalamudDataWindow.cs b/Dalamud/Interface/DalamudDataWindow.cs index d55476f2b..59284f113 100644 --- a/Dalamud/Interface/DalamudDataWindow.cs +++ b/Dalamud/Interface/DalamudDataWindow.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Numerics; using Dalamud.Game.Chat; @@ -18,6 +19,7 @@ namespace Dalamud.Interface private int currentKind; private bool drawActors = false; + private float maxActorDrawDistance = 20; public DalamudDataWindow(Dalamud dalamud) { this.dalamud = dalamud; @@ -87,6 +89,7 @@ namespace Dalamud.Interface stateString += $"TerritoryType: {this.dalamud.ClientState.TerritoryType}\n\n"; ImGui.Checkbox("Draw actors on screen", ref this.drawActors); + ImGui.SliderFloat("Draw Distance", ref this.maxActorDrawDistance, 2f, 40f); for (var i = 0; i < this.dalamud.ClientState.Actors.Length; i++) { var actor = this.dalamud.ClientState.Actors[i]; @@ -95,7 +98,7 @@ namespace Dalamud.Interface continue; stateString += - $"{actor.Address.ToInt64():X}:{actor.ActorId:X}[{i}] - {actor.ObjectKind} - {actor.Name} - {actor.Position.X} {actor.Position.Y} {actor.Position.Z}\n"; + $"{actor.Address.ToInt64():X}:{actor.ActorId:X}[{i}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX}\n"; if (actor is Npc npc) stateString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n"; @@ -111,7 +114,11 @@ namespace Dalamud.Interface if (this.drawActors && this.dalamud.Framework.Gui.WorldToScreen(actor.Position, out var screenCoords)) { ImGui.PushID("ActorWindow" + i); ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y)); - ImGui.SetNextWindowBgAlpha(0.35f); + + if (actor.YalmDistanceX > this.maxActorDrawDistance) + continue; + + ImGui.SetNextWindowBgAlpha(Math.Max(1f - (actor.YalmDistanceX / this.maxActorDrawDistance), 0.2f)); if (ImGui.Begin("Actor" + i, ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoMouseInputs | From fcd98ee2bb557c8048c91749e03b8defed593b32 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 29 Apr 2020 17:13:05 -0700 Subject: [PATCH 17/21] Add ability for plugins to add ImGui fonts at runtime, though we should discourage this as much as possible. Also sneak in an api-matching update to part of ImGuiScene, to make more things useable in SamplePlugin --- Dalamud/Interface/InterfaceManager.cs | 85 +++++++++++++++++++-------- Dalamud/Interface/UiBuilder.cs | 20 +++++++ lib/ImGuiScene | 2 +- 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/Dalamud/Interface/InterfaceManager.cs b/Dalamud/Interface/InterfaceManager.cs index 3d66d24d8..5fde5a051 100644 --- a/Dalamud/Interface/InterfaceManager.cs +++ b/Dalamud/Interface/InterfaceManager.cs @@ -54,11 +54,15 @@ namespace Dalamud.Interface public ImGuiIOPtr LastImGuiIoPtr; + public Action OnBuildFonts; + private bool isRebuildingFonts = false; + /// /// This event gets called by a plugin UiBuilder when read /// public event RawDX11Scene.BuildUIDelegate OnDraw; + public InterfaceManager(Dalamud dalamud, SigScanner scanner) { this.dalamud = dalamud; @@ -200,7 +204,18 @@ namespace Dalamud.Interface return null; } - private unsafe IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags) + // Sets up a deferred invocation of font rebuilding, before the next render frame + public void RebuildFonts() + { + // don't invoke this multiple times per frame, in case multiple plugins call it + if (!this.isRebuildingFonts) + { + this.isRebuildingFonts = true; + this.scene.OnNewRenderFrame += RebuildFontsInternal; + } + } + + private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags) { if (this.scene == null) { @@ -209,30 +224,7 @@ namespace Dalamud.Interface this.scene.OnBuildUI += Display; this.scene.OnNewInputFrame += OnNewInputFrame; - ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig(); - fontConfig.MergeMode = true; - fontConfig.PixelSnapH = true; - - var fontPathJp = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "UIRes", "NotoSansCJKjp-Medium.otf"); - ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathJp, 17.0f, null, ImGui.GetIO().Fonts.GetGlyphRangesJapanese()); - - var fontPathGame = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "UIRes", "gamesym.ttf"); - Log.Verbose(fontPathGame); - - var rangeHandle = GCHandle.Alloc(new ushort[] - { - 0xE020, - 0xE0DB, - 0 - }, GCHandleType.Pinned); - - - ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, rangeHandle.AddrOfPinnedObject()); - - ImGui.GetIO().Fonts.Build(); - - fontConfig.Destroy(); - rangeHandle.Free(); + SetupFonts(); ImGui.GetStyle().GrabRounding = 3f; ImGui.GetStyle().FrameRounding = 4f; @@ -268,6 +260,49 @@ namespace Dalamud.Interface return this.presentHook.Original(swapChain, syncInterval, presentFlags); } + private unsafe void SetupFonts() + { + ImGui.GetIO().Fonts.Clear(); + + ImFontConfigPtr fontConfig = ImGuiNative.ImFontConfig_ImFontConfig(); + fontConfig.MergeMode = true; + fontConfig.PixelSnapH = true; + + var fontPathJp = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "UIRes", "NotoSansCJKjp-Medium.otf"); + ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathJp, 17.0f, null, ImGui.GetIO().Fonts.GetGlyphRangesJapanese()); + + var fontPathGame = Path.Combine(this.dalamud.StartInfo.WorkingDirectory, "UIRes", "gamesym.ttf"); + Log.Verbose(fontPathGame); + + var rangeHandle = GCHandle.Alloc(new ushort[] + { + 0xE020, + 0xE0DB, + 0 + }, GCHandleType.Pinned); + + + ImGui.GetIO().Fonts.AddFontFromFileTTF(fontPathGame, 17.0f, fontConfig, rangeHandle.AddrOfPinnedObject()); + + OnBuildFonts?.Invoke(); + + ImGui.GetIO().Fonts.Build(); + + fontConfig.Destroy(); + rangeHandle.Free(); + } + + // This is intended to only be called as a handler attached to scene.OnNewRenderFrame + private void RebuildFontsInternal() + { + SetupFonts(); + + this.scene.OnNewRenderFrame -= RebuildFontsInternal; + this.scene.InvalidateFonts(); + + this.isRebuildingFonts = false; + } + private IntPtr ResizeBuffersDetour(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags) { Log.Verbose($"Calling resizebuffers {bufferCount} {width} {height} {newFormat} {swapChainFlags}"); diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index 31468e302..8b2d3f30a 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -70,6 +70,26 @@ namespace Dalamud.Interface public TextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels) => this.interfaceManager.LoadImageRaw(imageData, width, height, numChannels); + /// + /// An event that is called any time ImGui fonts need to be rebuilt.
+ /// Any ImFontPtr objects that you store can be invalidated when fonts are rebuilt + /// (at any time), so you should both reload your custom fonts and restore those + /// pointers inside this handler. + ///
+ public Action OnBuildFonts + { + get { return this.interfaceManager.OnBuildFonts; } + set { this.interfaceManager.OnBuildFonts = value; } + } + + /// + /// Call this to queue a rebuild of the font atlas.
+ /// This will invoke any handlers and ensure that any loaded fonts are + /// ready to be used on the next UI frame. + ///
+ public void RebuildFonts() => + this.interfaceManager.RebuildFonts(); + /// /// Event that is fired when the plugin should open its configuration interface. /// diff --git a/lib/ImGuiScene b/lib/ImGuiScene index aaa037938..d5b9345dc 160000 --- a/lib/ImGuiScene +++ b/lib/ImGuiScene @@ -1 +1 @@ -Subproject commit aaa037938d6fe835a15542a3451d12108e3f83b6 +Subproject commit d5b9345dc1463d746b832843bd7c81b753d4e5b0 From de3b771d34b6133ceeaf17a100ccb6709fad6758 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Wed, 29 Apr 2020 17:17:22 -0700 Subject: [PATCH 18/21] whine at people in api comments --- Dalamud/Interface/UiBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index 8b2d3f30a..9728d1270 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -74,7 +74,8 @@ namespace Dalamud.Interface /// An event that is called any time ImGui fonts need to be rebuilt.
/// Any ImFontPtr objects that you store can be invalidated when fonts are rebuilt /// (at any time), so you should both reload your custom fonts and restore those - /// pointers inside this handler. + /// pointers inside this handler.
+ /// PLEASE remove this handler inside Dipose, or when you no longer need your fonts! ///
public Action OnBuildFonts { From 520cd4bddc5cd1d334f31941be2d35d9b007eb7e Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Thu, 30 Apr 2020 14:10:02 +0200 Subject: [PATCH 19/21] feat: add Customize to Chara --- Dalamud.Injector/Dalamud.Injector.csproj | 83 +++++----- Dalamud/Dalamud.csproj | 148 +++++++++--------- .../Game/ClientState/Actors/CustomizeIndex.cs | 41 +++++ .../Game/ClientState/Actors/Types/Chara.cs | 5 + Dalamud/Game/ClientState/Structs/Actor.cs | 12 +- Dalamud/Interface/DalamudDataWindow.cs | 2 +- 6 files changed, 172 insertions(+), 119 deletions(-) create mode 100644 Dalamud/Game/ClientState/Actors/CustomizeIndex.cs diff --git a/Dalamud.Injector/Dalamud.Injector.csproj b/Dalamud.Injector/Dalamud.Injector.csproj index 2f5adf457..cf79dd174 100644 --- a/Dalamud.Injector/Dalamud.Injector.csproj +++ b/Dalamud.Injector/Dalamud.Injector.csproj @@ -1,41 +1,46 @@ - - AnyCPU - net48 - 8.0 - AnyCPU;x64 - - - Exe - $(SolutionDir)/bin - false - true - Portable - - - true - 4.9.3.0 - 4.9.3.0 - XIVLauncher addon injection - 4.9.3.0 - - - - - - false - - - dalamud.ico - - - - - - - - - - - + + AnyCPU + net48 + 8.0 + AnyCPU;x64 + + + Exe + $(SolutionDir)/bin + false + true + Portable + + + true + 4.9.3.0 + 4.9.3.0 + XIVLauncher addon injection + 4.9.3.0 + + + + + + $(MSBuildProjectDirectory)\ + $(AppOutputBase)=C:\goatsoft\companysecrets\injector\ + true + + + false + + + dalamud.ico + + + + + + + + + + + diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index ead306bc1..e8fd28ddf 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -1,79 +1,73 @@ - - AnyCPU - net472 - 8.0 - AnyCPU;x64 - - - Library - - false - true - Portable - - - true - 4.9.3.0 - 4.9.3.0 - 4.9.3.0 - - - - - - $(SolutionDir)\bin\Dalamud.xml - - - $(SolutionDir)\bin\Dalamud.xml - - - $(SolutionDir)\bin\Dalamud.xml - - - $(SolutionDir)bin\Dalamud.xml - - - false - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - - - - + + AnyCPU + net472 + 8.0 + AnyCPU;x64 + + + Library + + false + true + Portable + $(SolutionDir)\bin\Dalamud.xml + + + true + 4.9.3.0 + 4.9.3.0 + 4.9.3.0 + + + + + + $(MSBuildProjectDirectory)\ + $(AppOutputBase)=C:\goatsoft\companysecrets\dalamud\ + true + + + false + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + + diff --git a/Dalamud/Game/ClientState/Actors/CustomizeIndex.cs b/Dalamud/Game/ClientState/Actors/CustomizeIndex.cs new file mode 100644 index 000000000..93553f0b7 --- /dev/null +++ b/Dalamud/Game/ClientState/Actors/CustomizeIndex.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dalamud.Game.ClientState.Actors +{ + /// + /// This enum describes the indices of the Customize array. + /// + // TODO: This may need some rework since it may not be entirely accurate (stolen from Sapphire) + public enum CustomizeIndex { + Race = 0x00, + Gender = 0x01, + Tribe = 0x04, + Height = 0x03, + ModelType = 0x02, // Au Ra: changes horns/tails, everything else: seems to drastically change appearance (flip between two sets, odd/even numbers). sometimes retains hairstyle and other features + FaceType = 0x05, + HairStyle = 0x06, + HasHighlights = 0x07, // negative to enable, positive to disable + SkinColor = 0x08, + EyeColor = 0x09, // color of character's right eye + HairColor = 0x0A, // main color + HairColor2 = 0x0B, // highlights color + FaceFeatures = 0x0C, // seems to be a toggle, (-odd and +even for large face covering), opposite for small + FaceFeaturesColor = 0x0D, + Eyebrows = 0x0E, + EyeColor2 = 0x0F, // color of character's left eye + EyeShape = 0x10, + NoseShape = 0x11, + JawShape = 0x12, + LipStyle = 0x13, // lip colour depth and shape (negative values around -120 darker/more noticeable, positive no colour) + LipColor = 0x14, + RaceFeatureSize = 0x15, + RaceFeatureType = 0x16, // negative or out of range tail shapes for race result in no tail (e.g. Au Ra has max of 4 tail shapes), incorrect value can crash client + BustSize = 0x17, // char creator allows up to max of 100, i set to 127 cause who wouldnt but no visible difference + Facepaint = 0x18, + FacepaintColor = 0x19, + } +} diff --git a/Dalamud/Game/ClientState/Actors/Types/Chara.cs b/Dalamud/Game/ClientState/Actors/Types/Chara.cs index 45471e719..06b0657f9 100644 --- a/Dalamud/Game/ClientState/Actors/Types/Chara.cs +++ b/Dalamud/Game/ClientState/Actors/Types/Chara.cs @@ -43,5 +43,10 @@ namespace Dalamud.Game.ClientState.Actors.Types { /// The maximum MP of this Chara. /// public int MaxMp => this.actorStruct.MaxMp; + + /// + /// Byte array describing the visual appearance of this Chara. Indexed by . + /// + public byte[] Customize => this.actorStruct.Customize; } } diff --git a/Dalamud/Game/ClientState/Structs/Actor.cs b/Dalamud/Game/ClientState/Structs/Actor.cs index 934ba630f..89308a2f2 100644 --- a/Dalamud/Game/ClientState/Structs/Actor.cs +++ b/Dalamud/Game/ClientState/Structs/Actor.cs @@ -13,7 +13,9 @@ namespace Dalamud.Game.ClientState.Structs /// [StructLayout(LayoutKind.Explicit)] public struct Actor { - [FieldOffset(0x30)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)] public string Name; + [FieldOffset(0x30)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)] + public string Name; + [FieldOffset(116)] public int ActorId; [FieldOffset(128)] public int DataId; [FieldOffset(132)] public int OwnerId; @@ -24,9 +26,15 @@ namespace Dalamud.Game.ClientState.Structs [FieldOffset(145)] public byte PlayerTargetStatus; // This is some kind of enum [FieldOffset(146)] public byte YalmDistanceFromPlayerY; // and the other is z distance [FieldOffset(160)] public Position3 Position; + + [FieldOffset(0x17B8)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] public byte[] Customize; + [FieldOffset(0x17F8)] public int TargetActorId; + // This field can't be correctly aligned, so we have to cut it manually. - [FieldOffset(0x17d0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] public byte[] CompanyTag; + [FieldOffset(0x17d0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] CompanyTag; + [FieldOffset(0x1868)] public int NameId; [FieldOffset(0x1884)] public byte CurrentWorld; [FieldOffset(0x1886)] public byte HomeWorld; diff --git a/Dalamud/Interface/DalamudDataWindow.cs b/Dalamud/Interface/DalamudDataWindow.cs index 59284f113..8b8828676 100644 --- a/Dalamud/Interface/DalamudDataWindow.cs +++ b/Dalamud/Interface/DalamudDataWindow.cs @@ -105,7 +105,7 @@ namespace Dalamud.Interface if (actor is Chara chara) stateString += - $" Level: {chara.Level} ClassJob: {chara.ClassJob.GameData.Name} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n"; + $" Level: {chara.Level} ClassJob: {chara.ClassJob.GameData.Name} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n Customize: {BitConverter.ToString(chara.Customize).Replace("-", " ")}\n"; if (actor is PlayerCharacter pc) stateString += From f1b51708b406ad46b2a6262be135c16266ac5d28 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Thu, 30 Apr 2020 16:50:34 -0700 Subject: [PATCH 20/21] Update ActorTable offsets for hp/mp, add ui (display-only.. probably) status effects --- Dalamud/Game/ClientState/Actors/Types/Chara.cs | 2 +- Dalamud/Game/ClientState/Structs/Actor.cs | 12 ++++++++---- .../Game/ClientState/Structs/StatusEffect.cs | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 Dalamud/Game/ClientState/Structs/StatusEffect.cs diff --git a/Dalamud/Game/ClientState/Actors/Types/Chara.cs b/Dalamud/Game/ClientState/Actors/Types/Chara.cs index 06b0657f9..467ad087a 100644 --- a/Dalamud/Game/ClientState/Actors/Types/Chara.cs +++ b/Dalamud/Game/ClientState/Actors/Types/Chara.cs @@ -42,7 +42,7 @@ namespace Dalamud.Game.ClientState.Actors.Types { /// /// The maximum MP of this Chara. /// - public int MaxMp => this.actorStruct.MaxMp; + public int MaxMp => 10000; // Currently hardcoded because the value in actorStruct is very questionable. /// /// Byte array describing the visual appearance of this Chara. Indexed by . diff --git a/Dalamud/Game/ClientState/Structs/Actor.cs b/Dalamud/Game/ClientState/Structs/Actor.cs index 89308a2f2..745eb7e76 100644 --- a/Dalamud/Game/ClientState/Structs/Actor.cs +++ b/Dalamud/Game/ClientState/Structs/Actor.cs @@ -38,12 +38,16 @@ namespace Dalamud.Game.ClientState.Structs [FieldOffset(0x1868)] public int NameId; [FieldOffset(0x1884)] public byte CurrentWorld; [FieldOffset(0x1886)] public byte HomeWorld; - [FieldOffset(6328)] public int CurrentHp; - [FieldOffset(6332)] public int MaxHp; - [FieldOffset(6336)] public int CurrentMp; - [FieldOffset(6340)] public int MaxMp; + [FieldOffset(0x1898)] public int CurrentHp; + [FieldOffset(0x189C)] public int MaxHp; + [FieldOffset(0x18A0)] public int CurrentMp; + // This value is weird. It seems to change semi-randomly between 0 and 10k, definitely + // in response to mp-using events, but it doesn't often have a value and the changing seems + // somewhat arbitrary. + [FieldOffset(0x18AA)] public int MaxMp; [FieldOffset(6358)] public byte ClassJob; [FieldOffset(6360)] public byte Level; + [FieldOffset(0x1958)][MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public StatusEffect[] UIStatusEffects; } } diff --git a/Dalamud/Game/ClientState/Structs/StatusEffect.cs b/Dalamud/Game/ClientState/Structs/StatusEffect.cs new file mode 100644 index 000000000..584c5c48d --- /dev/null +++ b/Dalamud/Game/ClientState/Structs/StatusEffect.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.InteropServices; + +namespace Dalamud.Game.ClientState.Structs +{ + /// + /// Native memory representation of a FFXIV status effect. + /// + [StructLayout(LayoutKind.Sequential)] + public struct StatusEffect + { + public short EffectId; + public byte StackCount; + public byte Param; + public float Duration; + public int OwnerId; + } +} From 2e39dd07e334f0bba71c4194bbb88d9b394bf183 Mon Sep 17 00:00:00 2001 From: meli <57847713+ff-meli@users.noreply.github.com> Date: Thu, 30 Apr 2020 18:34:42 -0700 Subject: [PATCH 21/21] Add actor rotation --- Dalamud/Game/ClientState/Actors/Types/Actor.cs | 6 ++++++ Dalamud/Game/ClientState/Structs/Actor.cs | 1 + Dalamud/Interface/DalamudDataWindow.cs | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Dalamud/Game/ClientState/Actors/Types/Actor.cs b/Dalamud/Game/ClientState/Actors/Types/Actor.cs index 2289a8b30..caed4962d 100644 --- a/Dalamud/Game/ClientState/Actors/Types/Actor.cs +++ b/Dalamud/Game/ClientState/Actors/Types/Actor.cs @@ -34,6 +34,12 @@ namespace Dalamud.Game.ClientState.Actors.Types { /// public Position3 Position => this.actorStruct.Position; + /// + /// Rotation of this .
+ /// This ranges from -pi to pi radians. + ///
+ public float Rotation => this.actorStruct.Rotation; + /// /// Displayname of this Actor. /// diff --git a/Dalamud/Game/ClientState/Structs/Actor.cs b/Dalamud/Game/ClientState/Structs/Actor.cs index 745eb7e76..b8cf25680 100644 --- a/Dalamud/Game/ClientState/Structs/Actor.cs +++ b/Dalamud/Game/ClientState/Structs/Actor.cs @@ -26,6 +26,7 @@ namespace Dalamud.Game.ClientState.Structs [FieldOffset(145)] public byte PlayerTargetStatus; // This is some kind of enum [FieldOffset(146)] public byte YalmDistanceFromPlayerY; // and the other is z distance [FieldOffset(160)] public Position3 Position; + [FieldOffset(176)] public float Rotation; // Rotation around the vertical axis (yaw), from -pi to pi radians [FieldOffset(0x17B8)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] public byte[] Customize; diff --git a/Dalamud/Interface/DalamudDataWindow.cs b/Dalamud/Interface/DalamudDataWindow.cs index 8b8828676..aefbe4e0b 100644 --- a/Dalamud/Interface/DalamudDataWindow.cs +++ b/Dalamud/Interface/DalamudDataWindow.cs @@ -98,7 +98,7 @@ namespace Dalamud.Interface continue; stateString += - $"{actor.Address.ToInt64():X}:{actor.ActorId:X}[{i}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX}\n"; + $"{actor.Address.ToInt64():X}:{actor.ActorId:X}[{i}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation}\n"; if (actor is Npc npc) stateString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n";