mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
Merge branch 'api13' into StyleEvents
This commit is contained in:
commit
7ba3bc6f64
1084 changed files with 1129987 additions and 6380 deletions
8
.gitattributes
vendored
8
.gitattributes
vendored
|
|
@ -17,7 +17,7 @@
|
|||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
|
|
@ -46,9 +46,9 @@
|
|||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
|
|
@ -61,3 +61,5 @@
|
|||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
||||
|
||||
imgui/**/Generated/**/*.cs linguist-generated=true
|
||||
|
|
|
|||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -327,4 +327,7 @@ ASALocalRun/
|
|||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
.mfractor/
|
||||
|
||||
# HexaGen generated files
|
||||
#imgui/**/Generated/**/*
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
|
|
@ -1,6 +1,3 @@
|
|||
[submodule "lib/ImGuiScene"]
|
||||
path = lib/ImGuiScene
|
||||
url = https://github.com/goatcorp/ImGuiScene
|
||||
[submodule "lib/FFXIVClientStructs"]
|
||||
path = lib/FFXIVClientStructs
|
||||
url = https://github.com/aers/FFXIVClientStructs
|
||||
|
|
@ -19,3 +16,6 @@
|
|||
[submodule "lib/cimguizmo"]
|
||||
path = lib/cimguizmo
|
||||
url = https://github.com/goatcorp/gc-cimguizmo
|
||||
[submodule "lib/Hexa.NET.ImGui"]
|
||||
path = lib/Hexa.NET.ImGui
|
||||
url = https://github.com/goatcorp/Hexa.NET.ImGui.git
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@
|
|||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<PreprocessorDefinitions>CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
|
@ -66,6 +65,7 @@
|
|||
<ClCompile>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
|
||||
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26812</DisableSpecificWarnings>
|
||||
|
|
@ -80,6 +80,7 @@
|
|||
<ClCompile>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
|
||||
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26812</DisableSpecificWarnings>
|
||||
|
|
@ -203,7 +204,6 @@
|
|||
<Target Name="CopyOutputDlls" AfterTargets="PostBuildEvent">
|
||||
<Copy SourceFiles="$(OutDir)$(TargetName).dll" DestinationFolder="..\bin\$(Configuration)\" />
|
||||
<Copy SourceFiles="$(OutDir)$(TargetName).pdb" DestinationFolder="..\bin\$(Configuration)\" />
|
||||
|
||||
<Copy SourceFiles="$(OutDir)nethost.dll" DestinationFolder="..\bin\$(Configuration)\" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ void from_json(const nlohmann::json& json, DalamudStartInfo& config) {
|
|||
config.NoLoadThirdPartyPlugins = json.value("NoLoadThirdPartyPlugins", config.NoLoadThirdPartyPlugins);
|
||||
|
||||
config.BootLogPath = json.value("BootLogPath", config.BootLogPath);
|
||||
config.BootDebugDirectX = json.value("BootDebugDirectX", config.BootDebugDirectX);
|
||||
config.BootShowConsole = json.value("BootShowConsole", config.BootShowConsole);
|
||||
config.BootDisableFallbackConsole = json.value("BootDisableFallbackConsole", config.BootDisableFallbackConsole);
|
||||
config.BootWaitMessageBox = json.value("BootWaitMessageBox", config.BootWaitMessageBox);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ struct DalamudStartInfo {
|
|||
bool NoLoadThirdPartyPlugins;
|
||||
|
||||
std::string BootLogPath;
|
||||
bool BootDebugDirectX = false;
|
||||
bool BootShowConsole = false;
|
||||
bool BootDisableFallbackConsole = false;
|
||||
WaitMessageboxFlags BootWaitMessageBox = WaitMessageboxFlags::None;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
#include "pch.h"
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
#include "DalamudStartInfo.h"
|
||||
#include "hooks.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
#include "veh.h"
|
||||
|
|
@ -90,6 +94,69 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
|||
if ((g_startInfo.BootWaitMessageBox & DalamudStartInfo::WaitMessageboxFlags::BeforeInitialize) != DalamudStartInfo::WaitMessageboxFlags::None)
|
||||
MessageBoxW(nullptr, L"Press OK to continue (BeforeInitialize)", L"Dalamud Boot", MB_OK);
|
||||
|
||||
if (g_startInfo.BootDebugDirectX) {
|
||||
logging::I("Enabling DirectX Debugging.");
|
||||
|
||||
const auto hD3D11 = GetModuleHandleW(L"d3d11.dll");
|
||||
const auto hDXGI = GetModuleHandleW(L"dxgi.dll");
|
||||
const auto pfnD3D11CreateDevice = static_cast<decltype(&D3D11CreateDevice)>(
|
||||
hD3D11 ? static_cast<void*>(GetProcAddress(hD3D11, "D3D11CreateDevice")) : nullptr);
|
||||
if (pfnD3D11CreateDevice) {
|
||||
static hooks::direct_hook<decltype(D3D11CreateDevice)> s_hookD3D11CreateDevice(
|
||||
"d3d11.dll!D3D11CreateDevice",
|
||||
pfnD3D11CreateDevice);
|
||||
s_hookD3D11CreateDevice.set_detour([](
|
||||
IDXGIAdapter* pAdapter,
|
||||
D3D_DRIVER_TYPE DriverType,
|
||||
HMODULE Software,
|
||||
UINT Flags,
|
||||
const D3D_FEATURE_LEVEL* pFeatureLevels,
|
||||
UINT FeatureLevels,
|
||||
UINT SDKVersion,
|
||||
ID3D11Device** ppDevice,
|
||||
D3D_FEATURE_LEVEL* pFeatureLevel,
|
||||
ID3D11DeviceContext** ppImmediateContext
|
||||
) -> HRESULT {
|
||||
return s_hookD3D11CreateDevice.call_original(
|
||||
pAdapter,
|
||||
DriverType,
|
||||
Software,
|
||||
(Flags & ~D3D11_CREATE_DEVICE_PREVENT_ALTERING_LAYER_SETTINGS_FROM_REGISTRY) | D3D11_CREATE_DEVICE_DEBUG,
|
||||
pFeatureLevels,
|
||||
FeatureLevels,
|
||||
SDKVersion,
|
||||
ppDevice,
|
||||
pFeatureLevel,
|
||||
ppImmediateContext);
|
||||
});
|
||||
} else {
|
||||
logging::W("Could not find d3d11!D3D11CreateDevice.");
|
||||
}
|
||||
|
||||
const auto pfnCreateDXGIFactory = static_cast<decltype(&CreateDXGIFactory)>(
|
||||
hDXGI ? static_cast<void*>(GetProcAddress(hDXGI, "CreateDXGIFactory")) : nullptr);
|
||||
const auto pfnCreateDXGIFactory1 = static_cast<decltype(&CreateDXGIFactory1)>(
|
||||
hDXGI ? static_cast<void*>(GetProcAddress(hDXGI, "CreateDXGIFactory1")) : nullptr);
|
||||
static const auto pfnCreateDXGIFactory2 = static_cast<decltype(&CreateDXGIFactory2)>(
|
||||
hDXGI ? static_cast<void*>(GetProcAddress(hDXGI, "CreateDXGIFactory2")) : nullptr);
|
||||
if (pfnCreateDXGIFactory2) {
|
||||
static hooks::direct_hook<decltype(CreateDXGIFactory)> s_hookCreateDXGIFactory(
|
||||
"dxgi.dll!CreateDXGIFactory",
|
||||
pfnCreateDXGIFactory);
|
||||
static hooks::direct_hook<decltype(CreateDXGIFactory1)> s_hookCreateDXGIFactory1(
|
||||
"dxgi.dll!CreateDXGIFactory1",
|
||||
pfnCreateDXGIFactory1);
|
||||
s_hookCreateDXGIFactory.set_detour([](REFIID riid, _COM_Outptr_ void **ppFactory) -> HRESULT {
|
||||
return pfnCreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, riid, ppFactory);
|
||||
});
|
||||
s_hookCreateDXGIFactory1.set_detour([](REFIID riid, _COM_Outptr_ void **ppFactory) -> HRESULT {
|
||||
return pfnCreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, riid, ppFactory);
|
||||
});
|
||||
} else {
|
||||
logging::W("Could not find dxgi!CreateDXGIFactory2.");
|
||||
}
|
||||
}
|
||||
|
||||
if (minHookLoaded) {
|
||||
logging::I("Applying fixes...");
|
||||
xivfixes::apply_all(true);
|
||||
|
|
|
|||
|
|
@ -101,6 +101,11 @@ public record DalamudStartInfo
|
|||
/// </summary>
|
||||
public bool BootShowConsole { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to enable D3D11 and DXGI debugging if possible.
|
||||
/// </summary>
|
||||
public bool BootDebugDirectX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the fallback console should be shown, if needed.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -38,15 +38,6 @@
|
|||
<ProjectReference Include="..\Dalamud\Dalamud.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ namespace Dalamud.CorePlugin
|
|||
#else
|
||||
|
||||
private readonly WindowSystem windowSystem = new("Dalamud.CorePlugin");
|
||||
private readonly PluginWindow window;
|
||||
|
||||
private Localization localization;
|
||||
|
||||
private IPluginLog pluginLog;
|
||||
|
|
@ -63,7 +65,8 @@ namespace Dalamud.CorePlugin
|
|||
this.Interface = pluginInterface;
|
||||
this.pluginLog = log;
|
||||
|
||||
this.windowSystem.AddWindow(new PluginWindow());
|
||||
this.window = new PluginWindow();
|
||||
this.windowSystem.AddWindow(this.window);
|
||||
|
||||
this.Interface.UiBuilder.Draw += this.OnDraw;
|
||||
this.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi;
|
||||
|
|
@ -136,12 +139,12 @@ namespace Dalamud.CorePlugin
|
|||
{
|
||||
this.pluginLog.Information("Command called!");
|
||||
|
||||
// this.window.IsOpen = true;
|
||||
this.window.IsOpen ^= true;
|
||||
}
|
||||
|
||||
private void OnOpenConfigUi()
|
||||
{
|
||||
// this.window.IsOpen = true;
|
||||
this.window.IsOpen = true;
|
||||
}
|
||||
|
||||
private void OnOpenMainUi()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.CorePlugin
|
||||
{
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@
|
|||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<PreprocessorDefinitions>CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
|
@ -56,6 +55,7 @@
|
|||
<ClCompile>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
|
@ -67,6 +67,7 @@
|
|||
<ClCompile>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
|
@ -107,4 +108,4 @@
|
|||
<Delete Files="$(OutDir)$(TargetName).lib" />
|
||||
<Delete Files="$(OutDir)$(TargetName).exp" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ namespace Dalamud.Injector
|
|||
|
||||
startInfo = ExtractAndInitializeStartInfoFromArguments(startInfo, args);
|
||||
// Remove already handled arguments
|
||||
args.Remove("--debug-directx");
|
||||
args.Remove("--console");
|
||||
args.Remove("--msgbox1");
|
||||
args.Remove("--msgbox2");
|
||||
|
|
@ -464,6 +465,7 @@ namespace Dalamud.Injector
|
|||
startInfo.LogName ??= string.Empty;
|
||||
|
||||
// Set boot defaults
|
||||
startInfo.BootDebugDirectX = args.Contains("--debug-directx");
|
||||
startInfo.BootShowConsole = args.Contains("--console");
|
||||
startInfo.BootEnableEtw = args.Contains("--etw");
|
||||
startInfo.BootLogPath = GetLogPath(startInfo.LogPath, "dalamud.boot", startInfo.LogName);
|
||||
|
|
|
|||
58
Dalamud.sln
58
Dalamud.sln
|
|
@ -32,12 +32,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Test", "Dalamud.Tes
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{E15BDA6D-E881-4482-94BA-BE5527E917FF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGui.NET-472", "lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj", "{0483026E-C6CE-4B1A-AA68-46544C08140B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGuiScene", "lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj", "{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SDL2-CS", "lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj", "{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.CorePlugin", "Dalamud.CorePlugin\Dalamud.CorePlugin.csproj", "{4AFDB34A-7467-4D41-B067-53BC4101D9D0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFXIVClientStructs", "lib\FFXIVClientStructs\FFXIVClientStructs\FFXIVClientStructs.csproj", "{C9B87BD7-AF49-41C3-91F1-D550ADEB7833}"
|
||||
|
|
@ -68,6 +62,18 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cimplot", "external\cimplot
|
|||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cimguizmo", "external\cimguizmo\cimguizmo.vcxproj", "{F258347D-31BE-4605-98CE-40E43BDF6F9D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Bindings.ImGui", "imgui\Dalamud.Bindings.ImGui\Dalamud.Bindings.ImGui.csproj", "{B0AA8737-33A3-4766-8CBE-A48F2EF283BA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Bindings.ImGuizmo", "imgui\Dalamud.Bindings.ImGuizmo\Dalamud.Bindings.ImGuizmo.csproj", "{5E6EDD75-AE95-43A6-9D67-95B840EB4B71}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Bindings.ImPlot", "imgui\Dalamud.Bindings.ImPlot\Dalamud.Bindings.ImPlot.csproj", "{9C70BD06-D52C-425E-9C14-5D66BC6046EF}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bindings", "Bindings", "{A217B3DF-607A-4EFB-B107-3C4809348043}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandaloneImGuiTestbed", "imgui\StandaloneImGuiTestbed\StandaloneImGuiTestbed.csproj", "{4702A911-2513-478C-A434-2776393FDE77}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImGuiScene", "imgui\ImGuiScene\ImGuiScene.csproj", "{66753AC7-0029-4373-9CC4-7760B1F46141}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -98,18 +104,6 @@ Global
|
|||
{C8004563-1806-4329-844F-0EF6274291FC}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{C8004563-1806-4329-844F-0EF6274291FC}.Release|Any CPU.Build.0 = Release|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.Build.0 = Release|x64
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
|
|
@ -154,6 +148,26 @@ Global
|
|||
{F258347D-31BE-4605-98CE-40E43BDF6F9D}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{F258347D-31BE-4605-98CE-40E43BDF6F9D}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{F258347D-31BE-4605-98CE-40E43BDF6F9D}.Release|Any CPU.Build.0 = Release|x64
|
||||
{B0AA8737-33A3-4766-8CBE-A48F2EF283BA}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{B0AA8737-33A3-4766-8CBE-A48F2EF283BA}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{B0AA8737-33A3-4766-8CBE-A48F2EF283BA}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{B0AA8737-33A3-4766-8CBE-A48F2EF283BA}.Release|Any CPU.Build.0 = Release|x64
|
||||
{5E6EDD75-AE95-43A6-9D67-95B840EB4B71}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{5E6EDD75-AE95-43A6-9D67-95B840EB4B71}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{5E6EDD75-AE95-43A6-9D67-95B840EB4B71}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{5E6EDD75-AE95-43A6-9D67-95B840EB4B71}.Release|Any CPU.Build.0 = Release|x64
|
||||
{9C70BD06-D52C-425E-9C14-5D66BC6046EF}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{9C70BD06-D52C-425E-9C14-5D66BC6046EF}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{9C70BD06-D52C-425E-9C14-5D66BC6046EF}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{9C70BD06-D52C-425E-9C14-5D66BC6046EF}.Release|Any CPU.Build.0 = Release|x64
|
||||
{4702A911-2513-478C-A434-2776393FDE77}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{4702A911-2513-478C-A434-2776393FDE77}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{4702A911-2513-478C-A434-2776393FDE77}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{4702A911-2513-478C-A434-2776393FDE77}.Release|Any CPU.Build.0 = Release|x64
|
||||
{66753AC7-0029-4373-9CC4-7760B1F46141}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{66753AC7-0029-4373-9CC4-7760B1F46141}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{66753AC7-0029-4373-9CC4-7760B1F46141}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{66753AC7-0029-4373-9CC4-7760B1F46141}.Release|Any CPU.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -161,9 +175,6 @@ Global
|
|||
GlobalSection(NestedProjects) = preSolution
|
||||
{5B832F73-5F54-4ADC-870F-D0095EF72C9A} = {19775C83-7117-4A5F-AA00-18889F46A490}
|
||||
{8874326B-E755-4D13-90B4-59AB263A3E6B} = {19775C83-7117-4A5F-AA00-18889F46A490}
|
||||
{0483026E-C6CE-4B1A-AA68-46544C08140B} = {DBE5345E-6594-4A59-B183-1C3D5592269D}
|
||||
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A} = {DBE5345E-6594-4A59-B183-1C3D5592269D}
|
||||
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8} = {DBE5345E-6594-4A59-B183-1C3D5592269D}
|
||||
{4AFDB34A-7467-4D41-B067-53BC4101D9D0} = {8F079208-C227-4D96-9427-2BEBE0003944}
|
||||
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833} = {8BBACF2D-7AB8-4610-A115-0E363D35C291}
|
||||
{E0D51896-604F-4B40-8CFE-51941607B3A1} = {8BBACF2D-7AB8-4610-A115-0E363D35C291}
|
||||
|
|
@ -175,6 +186,11 @@ Global
|
|||
{8BBACF2D-7AB8-4610-A115-0E363D35C291} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
|
||||
{76CAA246-C405-4A8C-B0AE-F4A0EF3D4E16} = {DBE5345E-6594-4A59-B183-1C3D5592269D}
|
||||
{F258347D-31BE-4605-98CE-40E43BDF6F9D} = {DBE5345E-6594-4A59-B183-1C3D5592269D}
|
||||
{B0AA8737-33A3-4766-8CBE-A48F2EF283BA} = {A217B3DF-607A-4EFB-B107-3C4809348043}
|
||||
{5E6EDD75-AE95-43A6-9D67-95B840EB4B71} = {A217B3DF-607A-4EFB-B107-3C4809348043}
|
||||
{9C70BD06-D52C-425E-9C14-5D66BC6046EF} = {A217B3DF-607A-4EFB-B107-3C4809348043}
|
||||
{4702A911-2513-478C-A434-2776393FDE77} = {A217B3DF-607A-4EFB-B107-3C4809348043}
|
||||
{66753AC7-0029-4373-9CC4-7760B1F46141} = {A217B3DF-607A-4EFB-B107-3C4809348043}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ using Dalamud.Utility;
|
|||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Dalamud.Configuration.Internal;
|
||||
|
||||
|
|
@ -152,23 +153,11 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
|
|||
/// </summary>
|
||||
public float GlobalUiScale { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use AXIS fonts from the game.
|
||||
/// </summary>
|
||||
[Obsolete($"See {nameof(DefaultFontSpec)}")]
|
||||
public bool UseAxisFontsFromGame { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default font spec.
|
||||
/// </summary>
|
||||
public IFontSpec? DefaultFontSpec { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gamma value to apply for Dalamud fonts. Do not use.
|
||||
/// </summary>
|
||||
[Obsolete("It happens that nobody touched this setting", true)]
|
||||
public float FontGammaLevel { get; set; } = 1.4f;
|
||||
|
||||
/// <summary>Gets or sets the opacity of the IME state indicator.</summary>
|
||||
/// <value>0 will hide the state indicator. 1 will make the state indicator fully visible. Values outside the
|
||||
/// range will be clamped to [0, 1].</value>
|
||||
|
|
@ -604,11 +593,15 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
|
|||
{
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/animation/animation_win.cc;l=29?q=ReducedMotion&ss=chromium
|
||||
var winAnimEnabled = 0;
|
||||
var success = NativeFunctions.SystemParametersInfo(
|
||||
(uint)NativeFunctions.AccessibilityParameter.SPI_GETCLIENTAREAANIMATION,
|
||||
0,
|
||||
ref winAnimEnabled,
|
||||
0);
|
||||
var success = false;
|
||||
unsafe
|
||||
{
|
||||
success = Windows.Win32.PInvoke.SystemParametersInfo(
|
||||
SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETCLIENTAREAANIMATION,
|
||||
0,
|
||||
&winAnimEnabled,
|
||||
0);
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
|
|
@ -650,6 +643,16 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
|
|||
}
|
||||
});
|
||||
|
||||
this.DalamudConfigurationSaved?.Invoke(this);
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.DalamudConfigurationSaved))
|
||||
{
|
||||
try
|
||||
{
|
||||
action(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,8 +184,18 @@ internal partial class ConsoleManager : IServiceType
|
|||
/// <returns>Whether the command was successfully processed.</returns>
|
||||
public bool ProcessCommand(string command)
|
||||
{
|
||||
if (this.Invoke?.Invoke(command) == true)
|
||||
return true;
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.Invoke))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (action(command))
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
}
|
||||
|
||||
var matches = GetCommandParsingRegex().Matches(command);
|
||||
if (matches.Count == 0)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ using Dalamud.Plugin.Internal;
|
|||
using Dalamud.Storage;
|
||||
using Dalamud.Utility;
|
||||
using Dalamud.Utility.Timing;
|
||||
using PInvoke;
|
||||
using Serilog;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Security;
|
||||
|
||||
#if DEBUG
|
||||
[assembly: InternalsVisibleTo("Dalamud.CorePlugin")]
|
||||
|
|
@ -29,7 +30,7 @@ namespace Dalamud;
|
|||
/// The main Dalamud class containing all subsystems.
|
||||
/// </summary>
|
||||
[ServiceManager.ProvidedService]
|
||||
internal sealed class Dalamud : IServiceType
|
||||
internal sealed unsafe class Dalamud : IServiceType
|
||||
{
|
||||
#region Internals
|
||||
|
||||
|
|
@ -79,7 +80,7 @@ internal sealed class Dalamud : IServiceType
|
|||
{
|
||||
Log.Verbose("=============== GAME THREAD KICKOFF ===============");
|
||||
Timings.Event("Game thread kickoff");
|
||||
NativeFunctions.SetEvent(mainThreadContinueEvent);
|
||||
Windows.Win32.PInvoke.SetEvent(new HANDLE(mainThreadContinueEvent));
|
||||
}
|
||||
|
||||
void HandleServiceInitFailure(Task t)
|
||||
|
|
@ -116,9 +117,9 @@ internal sealed class Dalamud : IServiceType
|
|||
HandleServiceInitFailure(t);
|
||||
});
|
||||
|
||||
this.DefaultExceptionFilter = NativeFunctions.SetUnhandledExceptionFilter(nint.Zero);
|
||||
NativeFunctions.SetUnhandledExceptionFilter(this.DefaultExceptionFilter);
|
||||
Log.Debug($"SE default exception filter at {this.DefaultExceptionFilter.ToInt64():X}");
|
||||
this.DefaultExceptionFilter = SetExceptionHandler(nint.Zero);
|
||||
SetExceptionHandler(this.DefaultExceptionFilter);
|
||||
Log.Debug($"SE default exception filter at {new IntPtr(this.DefaultExceptionFilter):X}");
|
||||
|
||||
var debugSig = "40 55 53 57 48 8D AC 24 70 AD FF FF";
|
||||
this.DebugExceptionFilter = Service<TargetSigScanner>.Get().ScanText(debugSig);
|
||||
|
|
@ -170,8 +171,9 @@ internal sealed class Dalamud : IServiceType
|
|||
if (!reportCrashesSetting && !pmHasDevPlugins)
|
||||
{
|
||||
// Leaking on purpose for now
|
||||
var attribs = Kernel32.SECURITY_ATTRIBUTES.Create();
|
||||
Kernel32.CreateMutex(attribs, false, "DALAMUD_CRASHES_NO_MORE");
|
||||
var attribs = default(SECURITY_ATTRIBUTES);
|
||||
attribs.nLength = (uint)Unsafe.SizeOf<SECURITY_ATTRIBUTES>();
|
||||
Windows.Win32.PInvoke.CreateMutex(attribs, false, "DALAMUD_CRASHES_NO_MORE");
|
||||
}
|
||||
|
||||
this.unloadSignal.Set();
|
||||
|
|
@ -189,27 +191,29 @@ internal sealed class Dalamud : IServiceType
|
|||
/// Replace the current exception handler with the default one.
|
||||
/// </summary>
|
||||
internal void UseDefaultExceptionHandler() =>
|
||||
this.SetExceptionHandler(this.DefaultExceptionFilter);
|
||||
SetExceptionHandler(this.DefaultExceptionFilter);
|
||||
|
||||
/// <summary>
|
||||
/// Replace the current exception handler with a debug one.
|
||||
/// </summary>
|
||||
internal void UseDebugExceptionHandler() =>
|
||||
this.SetExceptionHandler(this.DebugExceptionFilter);
|
||||
SetExceptionHandler(this.DebugExceptionFilter);
|
||||
|
||||
/// <summary>
|
||||
/// Disable the current exception handler.
|
||||
/// </summary>
|
||||
internal void UseNoExceptionHandler() =>
|
||||
this.SetExceptionHandler(nint.Zero);
|
||||
SetExceptionHandler(nint.Zero);
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to set the exception handler.
|
||||
/// </summary>
|
||||
private void SetExceptionHandler(nint newFilter)
|
||||
private static nint SetExceptionHandler(nint newFilter)
|
||||
{
|
||||
var oldFilter = NativeFunctions.SetUnhandledExceptionFilter(newFilter);
|
||||
Log.Debug("Set ExceptionFilter to {0}, old: {1}", newFilter, oldFilter);
|
||||
var oldFilter =
|
||||
Windows.Win32.PInvoke.SetUnhandledExceptionFilter((delegate* unmanaged[Stdcall]<global::Windows.Win32.System.Diagnostics.Debug.EXCEPTION_POINTERS*, int>)newFilter);
|
||||
Log.Debug("Set ExceptionFilter to {0}, old: {1}", newFilter, (nint)oldFilter);
|
||||
return (nint)oldFilter;
|
||||
}
|
||||
|
||||
private void SetupClientStructsResolver(DirectoryInfo cacheDir)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<PropertyGroup Label="Feature">
|
||||
<Description>XIV Launcher addon framework</Description>
|
||||
<DalamudVersion>12.0.0.15</DalamudVersion>
|
||||
<DalamudVersion>13.0.0.0</DalamudVersion>
|
||||
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
||||
<Version>$(DalamudVersion)</Version>
|
||||
<FileVersion>$(DalamudVersion)</FileVersion>
|
||||
|
|
@ -35,6 +35,10 @@
|
|||
<Deterministic>true</Deterministic>
|
||||
<Nullable>annotations</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<!-- Enable when cswin32 properly supports implementing COM interfaces and we can
|
||||
make IDropTarget work -->
|
||||
<!-- <DisableRuntimeMarshalling>true</DisableRuntimeMarshalling> -->
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Configuration">
|
||||
|
|
@ -66,10 +70,12 @@
|
|||
<PackageReference Include="Lumina" Version="$(LuminaVersion)" />
|
||||
<PackageReference Include="Lumina.Excel" Version="$(LuminaExcelVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="9.0.0-preview.1.24081.5" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.46-beta">
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.183">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MinSharp" Version="1.0.4" />
|
||||
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
|
||||
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
|
||||
<PackageReference Include="Serilog" Version="4.0.2" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
|
||||
|
|
@ -87,13 +93,22 @@
|
|||
<PackageReference Include="System.Resources.Extensions" Version="8.0.0" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.22621.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Interface\ImGuiBackend\Renderers\imgui-frag.hlsl.bytes">
|
||||
<LogicalName>imgui-frag.hlsl.bytes</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Interface\ImGuiBackend\Renderers\imgui-vertex.hlsl.bytes">
|
||||
<LogicalName>imgui-vertex.hlsl.bytes</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dalamud.Common\Dalamud.Common.csproj" />
|
||||
<ProjectReference Include="..\imgui\Dalamud.Bindings.ImGuizmo\Dalamud.Bindings.ImGuizmo.csproj" />
|
||||
<ProjectReference Include="..\imgui\Dalamud.Bindings.ImGui\Dalamud.Bindings.ImGui.csproj" />
|
||||
<ProjectReference Include="..\imgui\Dalamud.Bindings.ImPlot\Dalamud.Bindings.ImPlot.csproj" />
|
||||
<ProjectReference Include="..\imgui\ImGuiScene\ImGuiScene.csproj" />
|
||||
<ProjectReference Include="..\lib\FFXIVClientStructs\FFXIVClientStructs\FFXIVClientStructs.csproj" />
|
||||
<ProjectReference Include="..\lib\FFXIVClientStructs\InteropGenerator.Runtime\InteropGenerator.Runtime.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj" />
|
||||
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=interface_005Cfontawesome/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Int64 x:Key="/Default/PerformanceThreshold/AnalysisFileSizeThreshold/=CSHARP/@EntryIndexedValue">300000</s:Int64>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=interface_005Cfontawesome/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=interface_005Ctextures_005Ctexturewraps_005Cinternal_005Cdrawlisttexturewrap/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -15,12 +17,11 @@ using Dalamud.Storage;
|
|||
using Dalamud.Support;
|
||||
using Dalamud.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using PInvoke;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
using static Dalamud.NativeFunctions;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Dalamud;
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ public sealed class EntryPoint
|
|||
var info = JsonConvert.DeserializeObject<DalamudStartInfo>(infoStr)!;
|
||||
|
||||
if ((info.BootWaitMessageBox & 4) != 0)
|
||||
MessageBoxW(IntPtr.Zero, "Press OK to continue (BeforeDalamudConstruct)", "Dalamud Boot", MessageBoxType.Ok);
|
||||
Windows.Win32.PInvoke.MessageBox(HWND.Null, "Press OK to continue (BeforeDalamudConstruct)", "Dalamud Boot", MESSAGEBOX_STYLE.MB_OK);
|
||||
|
||||
new Thread(() => RunThread(info, mainThreadContinueEvent)).Start();
|
||||
}
|
||||
|
|
@ -135,6 +136,8 @@ public sealed class EntryPoint
|
|||
/// <param name="mainThreadContinueEvent">Event used to signal the main thread to continue.</param>
|
||||
private static void RunThread(DalamudStartInfo info, IntPtr mainThreadContinueEvent)
|
||||
{
|
||||
NativeLibrary.Load(Path.Combine(info.WorkingDirectory!, "cimgui.dll"));
|
||||
|
||||
// Setup logger
|
||||
InitLogging(info.LogPath!, info.BootShowConsole, true, info.LogName);
|
||||
SerilogEventSink.Instance.LogLine += SerilogOnLogLine;
|
||||
|
|
@ -259,10 +262,12 @@ public sealed class EntryPoint
|
|||
var symbolPath = Path.Combine(info.AssetDirectory, "UIRes", "pdb");
|
||||
var searchPath = $".;{symbolPath}";
|
||||
|
||||
// Remove any existing Symbol Handler and Init a new one with our search path added
|
||||
SymCleanup(GetCurrentProcess());
|
||||
var currentProcess = Windows.Win32.PInvoke.GetCurrentProcess_SafeHandle();
|
||||
|
||||
if (!SymInitialize(GetCurrentProcess(), searchPath, true))
|
||||
// Remove any existing Symbol Handler and Init a new one with our search path added
|
||||
Windows.Win32.PInvoke.SymCleanup(currentProcess);
|
||||
|
||||
if (!Windows.Win32.PInvoke.SymInitialize(currentProcess, searchPath, true))
|
||||
throw new Win32Exception();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -304,14 +309,14 @@ public sealed class EntryPoint
|
|||
// ignored
|
||||
}
|
||||
|
||||
const MessageBoxType flags = NativeFunctions.MessageBoxType.YesNo | NativeFunctions.MessageBoxType.IconError | NativeFunctions.MessageBoxType.SystemModal;
|
||||
var result = MessageBoxW(
|
||||
Process.GetCurrentProcess().MainWindowHandle,
|
||||
const MESSAGEBOX_STYLE flags = MESSAGEBOX_STYLE.MB_YESNO | MESSAGEBOX_STYLE.MB_ICONERROR | MESSAGEBOX_STYLE.MB_SYSTEMMODAL;
|
||||
var result = Windows.Win32.PInvoke.MessageBox(
|
||||
new HWND(Process.GetCurrentProcess().MainWindowHandle),
|
||||
$"An internal error in a Dalamud plugin occurred.\nThe game must close.\n\n{ex.GetType().Name}\n{info}\n\n{pluginInfo}More information has been recorded separately{supportText}.\n\nDo you want to disable all plugins the next time you start the game?",
|
||||
"Dalamud",
|
||||
flags);
|
||||
|
||||
if (result == (int)User32.MessageBoxResult.IDYES)
|
||||
if (result == MESSAGEBOX_RESULT.IDYES)
|
||||
{
|
||||
Log.Information("User chose to disable plugins on next launch...");
|
||||
var config = Service<DalamudConfiguration>.Get();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Dalamud.Memory;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace Dalamud.Game.Addon.Events;
|
||||
|
|
@ -14,9 +15,9 @@ internal unsafe class AddonEventEntry
|
|||
/// Name of an invalid addon.
|
||||
/// </summary>
|
||||
public const string InvalidAddonName = "NullAddon";
|
||||
|
||||
|
||||
private string? addonName;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the addons AtkUnitBase.
|
||||
/// </summary>
|
||||
|
|
@ -33,20 +34,20 @@ internal unsafe class AddonEventEntry
|
|||
public required nint Node { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the handler that gets called when this event is triggered.
|
||||
/// Gets the delegate that gets called when this event is triggered.
|
||||
/// </summary>
|
||||
public required IAddonEventManager.AddonEventHandler Handler { get; init; }
|
||||
|
||||
public required IAddonEventManager.AddonEventDelegate Delegate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique id for this event.
|
||||
/// </summary>
|
||||
public required uint ParamKey { get; init; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the event type for this event.
|
||||
/// </summary>
|
||||
public required AddonEventType EventType { get; init; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the event handle for this event.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -24,21 +24,21 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
/// PluginName for Dalamud Internal use.
|
||||
/// </summary>
|
||||
public static readonly Guid DalamudInternalKey = Guid.NewGuid();
|
||||
|
||||
|
||||
private static readonly ModuleLog Log = new("AddonEventManager");
|
||||
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly AddonLifecycle addonLifecycle = Service<AddonLifecycle>.Get();
|
||||
|
||||
private readonly AddonLifecycleEventListener finalizeEventListener;
|
||||
|
||||
|
||||
private readonly AddonEventManagerAddressResolver address;
|
||||
private readonly Hook<UpdateCursorDelegate> onUpdateCursor;
|
||||
|
||||
private readonly ConcurrentDictionary<Guid, PluginEventController> pluginEventControllers;
|
||||
|
||||
|
||||
private AddonCursorType? cursorOverride;
|
||||
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private AddonEventManager(TargetSigScanner sigScanner)
|
||||
{
|
||||
|
|
@ -47,7 +47,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
|
||||
this.pluginEventControllers = new ConcurrentDictionary<Guid, PluginEventController>();
|
||||
this.pluginEventControllers.TryAdd(DalamudInternalKey, new PluginEventController());
|
||||
|
||||
|
||||
this.cursorOverride = null;
|
||||
|
||||
this.onUpdateCursor = Hook<UpdateCursorDelegate>.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour);
|
||||
|
|
@ -69,7 +69,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
{
|
||||
pluginEventController.Dispose();
|
||||
}
|
||||
|
||||
|
||||
this.addonLifecycle.UnregisterListener(this.finalizeEventListener);
|
||||
}
|
||||
|
||||
|
|
@ -80,19 +80,19 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
/// <param name="atkUnitBase">The parent addon for this event.</param>
|
||||
/// <param name="atkResNode">The node that will trigger this event.</param>
|
||||
/// <param name="eventType">The event type for this event.</param>
|
||||
/// <param name="eventHandler">The handler to call when event is triggered.</param>
|
||||
/// <param name="eventDelegate">The delegate to call when event is triggered.</param>
|
||||
/// <returns>IAddonEventHandle used to remove the event.</returns>
|
||||
internal IAddonEventHandle? AddEvent(Guid pluginId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
||||
internal IAddonEventHandle? AddEvent(Guid pluginId, nint atkUnitBase, nint atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventDelegate eventDelegate)
|
||||
{
|
||||
if (this.pluginEventControllers.TryGetValue(pluginId, out var controller))
|
||||
{
|
||||
return controller.AddEvent(atkUnitBase, atkResNode, eventType, eventHandler);
|
||||
return controller.AddEvent(atkUnitBase, atkResNode, eventType, eventDelegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Verbose($"Unable to locate controller for {pluginId}. No event was added.");
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
Log.Verbose($"Unable to locate controller for {pluginId}. No event was removed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Force the game cursor to be the specified cursor.
|
||||
/// </summary>
|
||||
|
|
@ -167,21 +167,21 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
pluginList.Value.RemoveForAddon(addonInfo.AddonName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private nint UpdateCursorDetour(RaptureAtkModule* module)
|
||||
{
|
||||
try
|
||||
{
|
||||
var atkStage = AtkStage.Instance();
|
||||
|
||||
|
||||
if (this.cursorOverride is not null && atkStage is not null)
|
||||
{
|
||||
var cursor = (AddonCursorType)atkStage->AtkCursor.Type;
|
||||
if (cursor != this.cursorOverride)
|
||||
if (cursor != this.cursorOverride)
|
||||
{
|
||||
AtkStage.Instance()->AtkCursor.SetCursorType((AtkCursor.CursorType)this.cursorOverride, 1);
|
||||
}
|
||||
|
||||
|
||||
return nint.Zero;
|
||||
}
|
||||
}
|
||||
|
|
@ -218,7 +218,7 @@ internal class AddonEventManagerPluginScoped : IInternalDisposableService, IAddo
|
|||
public AddonEventManagerPluginScoped(LocalPlugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
|
||||
|
||||
this.eventManagerService.AddPluginEventController(plugin.EffectiveWorkingPluginId);
|
||||
}
|
||||
|
||||
|
|
@ -236,28 +236,28 @@ internal class AddonEventManagerPluginScoped : IInternalDisposableService, IAddo
|
|||
this.eventManagerService.RemovePluginEventController(this.plugin.EffectiveWorkingPluginId);
|
||||
}).Wait();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IAddonEventHandle? AddEvent(IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
||||
=> this.eventManagerService.AddEvent(this.plugin.EffectiveWorkingPluginId, atkUnitBase, atkResNode, eventType, eventHandler);
|
||||
public IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventDelegate eventDelegate)
|
||||
=> this.eventManagerService.AddEvent(this.plugin.EffectiveWorkingPluginId, atkUnitBase, atkResNode, eventType, eventDelegate);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveEvent(IAddonEventHandle eventHandle)
|
||||
=> this.eventManagerService.RemoveEvent(this.plugin.EffectiveWorkingPluginId, eventHandle);
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetCursor(AddonCursorType cursor)
|
||||
{
|
||||
this.isForcingCursor = true;
|
||||
|
||||
|
||||
this.eventManagerService.SetCursor(cursor);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ResetCursor()
|
||||
{
|
||||
this.isForcingCursor = false;
|
||||
|
||||
|
||||
this.eventManagerService.ResetCursor();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Dalamud.Game.Addon.Events;
|
||||
namespace Dalamud.Game.Addon.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Reimplementation of AtkEventType.
|
||||
|
|
@ -50,6 +50,17 @@ public enum AddonEventType : byte
|
|||
/// </summary>
|
||||
InputReceived = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Input Navigation (LEFT, RIGHT, UP, DOWN, TAB_NEXT, TAB_PREV, TAB_BOTH_NEXT, TAB_BOTH_PREV, PAGEUP, PAGEDOWN).
|
||||
/// </summary>
|
||||
InputNavigation = 13,
|
||||
|
||||
/// <summary>
|
||||
/// InputBase Input Received (AtkComponentTextInput and AtkComponentNumericInput).<br/>
|
||||
/// For example, this is fired for moving the text cursor, deletion of a character and inserting a new line.
|
||||
/// </summary>
|
||||
InputBaseInputReceived = 15,
|
||||
|
||||
/// <summary>
|
||||
/// Focus Start.
|
||||
/// </summary>
|
||||
|
|
@ -110,12 +121,6 @@ public enum AddonEventType : byte
|
|||
/// </summary>
|
||||
ListItemClick = 35,
|
||||
|
||||
/// <summary>
|
||||
/// AtkComponentList Toggle.
|
||||
/// </summary>
|
||||
[Obsolete("Use ListItemClick")]
|
||||
ListItemToggle = 35,
|
||||
|
||||
/// <summary>
|
||||
/// AtkComponentList Double Click.
|
||||
/// </summary>
|
||||
|
|
|
|||
68
Dalamud/Game/Addon/Events/EventDataTypes/AddonEventData.cs
Normal file
68
Dalamud/Game/Addon/Events/EventDataTypes/AddonEventData.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
namespace Dalamud.Game.Addon.Events.EventDataTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Object representing data that is relevant in handling native events.
|
||||
/// </summary>
|
||||
public class AddonEventData
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddonEventData"/> class.
|
||||
/// </summary>
|
||||
internal AddonEventData()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddonEventData"/> class.
|
||||
/// </summary>
|
||||
/// <param name="eventData">Other event data to copy.</param>
|
||||
internal AddonEventData(AddonEventData eventData)
|
||||
{
|
||||
this.AtkEventType = eventData.AtkEventType;
|
||||
this.Param = eventData.Param;
|
||||
this.AtkEventPointer = eventData.AtkEventPointer;
|
||||
this.AtkEventDataPointer = eventData.AtkEventDataPointer;
|
||||
this.AddonPointer = eventData.AddonPointer;
|
||||
this.NodeTargetPointer = eventData.NodeTargetPointer;
|
||||
this.AtkEventListener = eventData.AtkEventListener;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AtkEventType for this event.
|
||||
/// </summary>
|
||||
public AddonEventType AtkEventType { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the param field for this event.
|
||||
/// </summary>
|
||||
public uint Param { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the AtkEvent object for this event.
|
||||
/// </summary>
|
||||
/// <remarks>Note: This is not a pointer to the AtkEventData object.<br/><br/>
|
||||
/// Warning: AtkEvent->Node has been modified to be the AtkUnitBase*, and AtkEvent->Target has been modified to be the AtkResNode* that triggered this event.</remarks>
|
||||
public nint AtkEventPointer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the AtkEventData object for this event.
|
||||
/// </summary>
|
||||
/// <remarks>This field will contain relevant data such as left vs right click, scroll up vs scroll down.</remarks>
|
||||
public nint AtkEventDataPointer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the AtkUnitBase that is handling this event.
|
||||
/// </summary>
|
||||
public nint AddonPointer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the AtkResNode that triggered this event.
|
||||
/// </summary>
|
||||
public nint NodeTargetPointer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a pointer to the AtkEventListener responsible for handling this event.
|
||||
/// Note: As the event listener is dalamud allocated, there's no reason to expose this field.
|
||||
/// </summary>
|
||||
internal nint AtkEventListener { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
using System.Numerics;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
using AtkMouseData = FFXIVClientStructs.FFXIV.Component.GUI.AtkEventData.AtkMouseData;
|
||||
using ModifierFlag = FFXIVClientStructs.FFXIV.Component.GUI.AtkEventData.AtkMouseData.ModifierFlag;
|
||||
|
||||
namespace Dalamud.Game.Addon.Events.EventDataTypes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public unsafe class AddonMouseEventData : AddonEventData
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddonMouseEventData"/> class.
|
||||
/// </summary>
|
||||
internal AddonMouseEventData()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddonMouseEventData"/> class.
|
||||
/// </summary>
|
||||
/// <param name="eventData">Other event data to copy.</param>
|
||||
internal AddonMouseEventData(AddonEventData eventData)
|
||||
: base(eventData)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the event was a Left Mouse Click.
|
||||
/// </summary>
|
||||
public bool IsLeftClick => this.MouseData.ButtonId is 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the event was a Right Mouse Click.
|
||||
/// </summary>
|
||||
public bool IsRightClick => this.MouseData.ButtonId is 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there are any modifiers set such as alt, control, shift, or dragging.
|
||||
/// </summary>
|
||||
public bool IsNoModifier => this.MouseData.Modifier is 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether alt was being held when this event triggered.
|
||||
/// </summary>
|
||||
public bool IsAltHeld => this.MouseData.Modifier.HasFlag(ModifierFlag.Alt);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether control was being held when this event triggered.
|
||||
/// </summary>
|
||||
public bool IsControlHeld => this.MouseData.Modifier.HasFlag(ModifierFlag.Ctrl);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether shift was being held when this event triggered.
|
||||
/// </summary>
|
||||
public bool IsShiftHeld => this.MouseData.Modifier.HasFlag(ModifierFlag.Shift);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this event is a mouse drag or not.
|
||||
/// </summary>
|
||||
public bool IsDragging => this.MouseData.Modifier.HasFlag(ModifierFlag.Dragging);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the event was a scroll up.
|
||||
/// </summary>
|
||||
public bool IsScrollUp => this.MouseData.WheelDirection is 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the event was a scroll down.
|
||||
/// </summary>
|
||||
public bool IsScrollDown => this.MouseData.WheelDirection is -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position of the mouse when this event was triggered.
|
||||
/// </summary>
|
||||
public Vector2 Position => new(this.MouseData.PosX, this.MouseData.PosY);
|
||||
|
||||
private AtkEventData* AtkEventData => (AtkEventData*)this.AtkEventDataPointer;
|
||||
|
||||
private AtkMouseData MouseData => this.AtkEventData->MouseData;
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Game.Addon.Events.EventDataTypes;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
|
|
@ -25,7 +27,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
}
|
||||
|
||||
private AddonEventListener EventListener { get; init; }
|
||||
|
||||
|
||||
private List<AddonEventEntry> Events { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -34,16 +36,16 @@ internal unsafe class PluginEventController : IDisposable
|
|||
/// <param name="atkUnitBase">The Parent addon for the event.</param>
|
||||
/// <param name="atkResNode">The Node for the event.</param>
|
||||
/// <param name="atkEventType">The Event Type.</param>
|
||||
/// <param name="handler">The delegate to call when invoking this event.</param>
|
||||
/// <param name="eventDelegate">The delegate to call when invoking this event.</param>
|
||||
/// <returns>IAddonEventHandle used to remove the event.</returns>
|
||||
public IAddonEventHandle AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType atkEventType, IAddonEventManager.AddonEventHandler handler)
|
||||
public IAddonEventHandle AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType atkEventType, IAddonEventManager.AddonEventDelegate eventDelegate)
|
||||
{
|
||||
var node = (AtkResNode*)atkResNode;
|
||||
var addon = (AtkUnitBase*)atkUnitBase;
|
||||
var eventType = (AtkEventType)atkEventType;
|
||||
var eventId = this.GetNextParamKey();
|
||||
var eventGuid = Guid.NewGuid();
|
||||
|
||||
|
||||
var eventHandle = new AddonEventHandle
|
||||
{
|
||||
AddonName = addon->NameString,
|
||||
|
|
@ -51,11 +53,11 @@ internal unsafe class PluginEventController : IDisposable
|
|||
EventType = atkEventType,
|
||||
EventGuid = eventGuid,
|
||||
};
|
||||
|
||||
|
||||
var eventEntry = new AddonEventEntry
|
||||
{
|
||||
Addon = atkUnitBase,
|
||||
Handler = handler,
|
||||
Delegate = eventDelegate,
|
||||
Node = atkResNode,
|
||||
EventType = atkEventType,
|
||||
ParamKey = eventId,
|
||||
|
|
@ -91,14 +93,14 @@ internal unsafe class PluginEventController : IDisposable
|
|||
if (this.Events.Where(entry => entry.AddonName == addonName).ToList() is { Count: not 0 } events)
|
||||
{
|
||||
Log.Verbose($"Addon: {addonName} is Finalizing, removing {events.Count} events.");
|
||||
|
||||
|
||||
foreach (var registeredEvent in events)
|
||||
{
|
||||
this.RemoveEvent(registeredEvent.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
@ -106,7 +108,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
{
|
||||
this.RemoveEvent(registeredEvent.Handle);
|
||||
}
|
||||
|
||||
|
||||
this.EventListener.Dispose();
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +121,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
|
||||
throw new OverflowException($"uint.MaxValue number of ParamKeys used for this event controller.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove a tracked event from native UI.
|
||||
/// This method performs several safety checks to only remove events from a still active addon.
|
||||
|
|
@ -139,7 +141,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
if (currentAddonPointer != eventEntry.Addon) return;
|
||||
|
||||
// Make sure the addon is not unloaded
|
||||
var atkUnitBase = (AtkUnitBase*)currentAddonPointer;
|
||||
var atkUnitBase = currentAddonPointer.Struct;
|
||||
if (atkUnitBase->UldManager.LoadedState == AtkLoadState.Unloaded) return;
|
||||
|
||||
// Does this addon contain the node this event is for? (by address)
|
||||
|
|
@ -153,7 +155,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we didn't find the node, we can't remove the event.
|
||||
if (!nodeFound) return;
|
||||
|
||||
|
|
@ -167,33 +169,41 @@ internal unsafe class PluginEventController : IDisposable
|
|||
var paramKeyMatches = currentEvent->Param == eventEntry.ParamKey;
|
||||
var eventListenerAddressMatches = (nint)currentEvent->Listener == this.EventListener.Address;
|
||||
var eventTypeMatches = currentEvent->State.EventType == eventType;
|
||||
|
||||
|
||||
if (paramKeyMatches && eventListenerAddressMatches && eventTypeMatches)
|
||||
{
|
||||
eventFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Move to the next event.
|
||||
currentEvent = currentEvent->NextEvent;
|
||||
}
|
||||
|
||||
|
||||
// If we didn't find the event, we can't remove the event.
|
||||
if (!eventFound) return;
|
||||
|
||||
// We have a valid addon, valid node, valid event, and valid key.
|
||||
this.EventListener.UnregisterEvent(atkResNode, eventType, eventEntry.ParamKey);
|
||||
}
|
||||
|
||||
|
||||
private void PluginEventListHandler(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventPtr, AtkEventData* eventDataPtr)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (eventPtr is null) return;
|
||||
if (this.Events.FirstOrDefault(handler => handler.ParamKey == eventParam) is not { } eventInfo) return;
|
||||
|
||||
// We stored the AtkUnitBase* in EventData->Node, and EventData->Target contains the node that triggered the event.
|
||||
eventInfo.Handler.Invoke((AddonEventType)eventType, (nint)eventPtr->Node, (nint)eventPtr->Target);
|
||||
|
||||
eventInfo.Delegate.Invoke((AddonEventType)eventType, new AddonEventData
|
||||
{
|
||||
AddonPointer = (nint)eventPtr->Node,
|
||||
NodeTargetPointer = (nint)eventPtr->Target,
|
||||
AtkEventDataPointer = (nint)eventDataPtr,
|
||||
AtkEventListener = (nint)self,
|
||||
AtkEventType = (AddonEventType)eventType,
|
||||
Param = eventParam,
|
||||
AtkEventPointer = (nint)eventPtr,
|
||||
});
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Dalamud.Memory;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using Dalamud.Game.NativeWrapper;
|
||||
|
||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
|
||||
|
|
@ -17,7 +13,6 @@ public abstract unsafe class AddonArgs
|
|||
public const string InvalidAddon = "NullAddon";
|
||||
|
||||
private string? addonName;
|
||||
private IntPtr addon;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the addon this args referrers to.
|
||||
|
|
@ -27,10 +22,10 @@ public abstract unsafe class AddonArgs
|
|||
/// <summary>
|
||||
/// Gets the pointer to the addons AtkUnitBase.
|
||||
/// </summary>
|
||||
public nint Addon
|
||||
public AtkUnitBasePtr Addon
|
||||
{
|
||||
get => this.AddonInternal;
|
||||
init => this.AddonInternal = value;
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -38,22 +33,6 @@ public abstract unsafe class AddonArgs
|
|||
/// </summary>
|
||||
public abstract AddonArgsType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pointer to the addons AtkUnitBase.
|
||||
/// </summary>
|
||||
internal nint AddonInternal
|
||||
{
|
||||
get => this.addon;
|
||||
set
|
||||
{
|
||||
this.addon = value;
|
||||
|
||||
// Note: always clear addonName on updating the addon being pointed.
|
||||
// Same address may point to a different addon.
|
||||
this.addonName = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if addon name matches the given span of char.
|
||||
/// </summary>
|
||||
|
|
@ -61,19 +40,27 @@ public abstract unsafe class AddonArgs
|
|||
/// <returns>Whether it is the case.</returns>
|
||||
internal bool IsAddon(ReadOnlySpan<char> name)
|
||||
{
|
||||
if (this.Addon == nint.Zero) return false;
|
||||
if (name.Length is 0 or > 0x20)
|
||||
if (this.Addon.IsNull)
|
||||
return false;
|
||||
|
||||
var addonPointer = (AtkUnitBase*)this.Addon;
|
||||
if (addonPointer->Name[0] == 0) return false;
|
||||
if (name.Length is 0 or > 32)
|
||||
return false;
|
||||
|
||||
// note: might want to rewrite this to just compare to NameString
|
||||
return MemoryHelper.EqualsZeroTerminatedString(
|
||||
name,
|
||||
(nint)Unsafe.AsPointer(ref addonPointer->Name[0]),
|
||||
null,
|
||||
0x20);
|
||||
var addonName = this.Addon.Name;
|
||||
|
||||
if (string.IsNullOrEmpty(addonName))
|
||||
return false;
|
||||
|
||||
return name == addonName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears this AddonArgs values.
|
||||
/// </summary>
|
||||
internal virtual void Clear()
|
||||
{
|
||||
this.addonName = null;
|
||||
this.Addon = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -82,11 +69,13 @@ public abstract unsafe class AddonArgs
|
|||
/// <returns>The name of the addon for this object. <see cref="InvalidAddon"/> when invalid.</returns>
|
||||
private string GetAddonName()
|
||||
{
|
||||
if (this.Addon == nint.Zero) return InvalidAddon;
|
||||
if (this.Addon.IsNull) return InvalidAddon;
|
||||
|
||||
var addonPointer = (AtkUnitBase*)this.Addon;
|
||||
if (addonPointer->Name[0] == 0) return InvalidAddon;
|
||||
var name = this.Addon.Name;
|
||||
|
||||
return this.addonName ??= addonPointer->NameString;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return InvalidAddon;
|
||||
|
||||
return this.addonName ??= name;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,4 +41,14 @@ public class AddonReceiveEventArgs : AddonArgs, ICloneable
|
|||
|
||||
/// <inheritdoc cref="Clone"/>
|
||||
object ICloneable.Clone() => this.Clone();
|
||||
|
||||
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||
internal override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
this.AtkEventType = default;
|
||||
this.EventParam = default;
|
||||
this.AtkEvent = default;
|
||||
this.Data = default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,4 +38,12 @@ public class AddonRefreshArgs : AddonArgs, ICloneable
|
|||
|
||||
/// <inheritdoc cref="Clone"/>
|
||||
object ICloneable.Clone() => this.Clone();
|
||||
|
||||
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||
internal override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
this.AtkValueCount = default;
|
||||
this.AtkValues = default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Addon argument data for OnRequestedUpdate events.
|
||||
|
|
@ -31,4 +31,12 @@ public class AddonRequestedUpdateArgs : AddonArgs, ICloneable
|
|||
|
||||
/// <inheritdoc cref="Clone"/>
|
||||
object ICloneable.Clone() => this.Clone();
|
||||
|
||||
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||
internal override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
this.NumberArrayData = default;
|
||||
this.StringArrayData = default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
|
||||
|
|
@ -38,4 +38,12 @@ public class AddonSetupArgs : AddonArgs, ICloneable
|
|||
|
||||
/// <inheritdoc cref="Clone"/>
|
||||
object ICloneable.Clone() => this.Clone();
|
||||
|
||||
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||
internal override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
this.AtkValueCount = default;
|
||||
this.AtkValues = default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,4 +35,11 @@ public class AddonUpdateArgs : AddonArgs, ICloneable
|
|||
|
||||
/// <inheritdoc cref="Clone"/>
|
||||
object ICloneable.Clone() => this.Clone();
|
||||
|
||||
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||
internal override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
this.TimeDeltaInternal = default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,7 +238,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
|||
}
|
||||
|
||||
using var returner = this.argsPool.Rent(out AddonSetupArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.Clear();
|
||||
arg.Addon = (nint)addon;
|
||||
arg.AtkValueCount = valueCount;
|
||||
arg.AtkValues = (nint)values;
|
||||
this.InvokeListenersSafely(AddonEvent.PreSetup, arg);
|
||||
|
|
@ -270,7 +271,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
|||
}
|
||||
|
||||
using var returner = this.argsPool.Rent(out AddonFinalizeArgs arg);
|
||||
arg.AddonInternal = (nint)atkUnitBase[0];
|
||||
arg.Clear();
|
||||
arg.Addon = (nint)atkUnitBase[0];
|
||||
this.InvokeListenersSafely(AddonEvent.PreFinalize, arg);
|
||||
|
||||
try
|
||||
|
|
@ -286,7 +288,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
|||
private void OnAddonDraw(AtkUnitBase* addon)
|
||||
{
|
||||
using var returner = this.argsPool.Rent(out AddonDrawArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.Clear();
|
||||
arg.Addon = (nint)addon;
|
||||
this.InvokeListenersSafely(AddonEvent.PreDraw, arg);
|
||||
|
||||
try
|
||||
|
|
@ -304,7 +307,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
|||
private void OnAddonUpdate(AtkUnitBase* addon, float delta)
|
||||
{
|
||||
using var returner = this.argsPool.Rent(out AddonUpdateArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.Clear();
|
||||
arg.Addon = (nint)addon;
|
||||
arg.TimeDeltaInternal = delta;
|
||||
this.InvokeListenersSafely(AddonEvent.PreUpdate, arg);
|
||||
|
||||
|
|
@ -325,7 +329,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
|||
var result = false;
|
||||
|
||||
using var returner = this.argsPool.Rent(out AddonRefreshArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.Clear();
|
||||
arg.Addon = (nint)addon;
|
||||
arg.AtkValueCount = valueCount;
|
||||
arg.AtkValues = (nint)values;
|
||||
this.InvokeListenersSafely(AddonEvent.PreRefresh, arg);
|
||||
|
|
@ -348,7 +353,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
|||
private void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData)
|
||||
{
|
||||
using var returner = this.argsPool.Rent(out AddonRequestedUpdateArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.Clear();
|
||||
arg.Addon = (nint)addon;
|
||||
arg.NumberArrayData = (nint)numberArrayData;
|
||||
arg.StringArrayData = (nint)stringArrayData;
|
||||
this.InvokeListenersSafely(AddonEvent.PreRequestedUpdate, arg);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Hooking;
|
||||
|
|
@ -86,7 +86,8 @@ internal unsafe class AddonLifecycleReceiveEventListener : IDisposable
|
|||
}
|
||||
|
||||
using var returner = this.argsPool.Rent(out AddonReceiveEventArgs arg);
|
||||
arg.AddonInternal = (nint)addon;
|
||||
arg.Clear();
|
||||
arg.Addon = (nint)addon;
|
||||
arg.AtkEventType = (byte)eventType;
|
||||
arg.EventParam = eventParam;
|
||||
arg.AtkEvent = (IntPtr)atkEvent;
|
||||
|
|
|
|||
|
|
@ -119,8 +119,6 @@ internal partial class ChatHandlers : IServiceType
|
|||
if (string.IsNullOrEmpty(this.configuration.LastVersion) || !Util.AssemblyVersion.StartsWith(this.configuration.LastVersion))
|
||||
{
|
||||
var linkPayload = chatGui.AddChatLinkHandler(
|
||||
"dalamud",
|
||||
8459324,
|
||||
(_, _) => dalamudInterface.OpenPluginInstallerTo(PluginInstallerOpenKind.Changelogs));
|
||||
|
||||
var updateMessage = new SeStringBuilder()
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
|
|||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly Framework framework = Service<Framework>.Get();
|
||||
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly NetworkHandlers networkHandlers = Service<NetworkHandlers>.Get();
|
||||
|
||||
|
||||
private bool lastConditionNone = true;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
|
|
@ -176,7 +176,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
|
|||
this.uiModuleHandlePacketHook.Dispose();
|
||||
this.onLogoutHook.Dispose();
|
||||
|
||||
this.framework.Update -= this.FrameworkOnOnUpdateEvent;
|
||||
this.framework.Update -= this.FrameworkOnOnUpdateEvent;
|
||||
this.networkHandlers.CfPop -= this.NetworkHandlersOnCfPop;
|
||||
}
|
||||
|
||||
|
|
@ -211,50 +211,51 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
|
|||
this.setupTerritoryTypeHook.Original(eventFramework, territoryType);
|
||||
}
|
||||
|
||||
private unsafe void UIModuleHandlePacketDetour(UIModule* thisPtr, UIModulePacketType type, uint uintParam, void* packet)
|
||||
private unsafe void UIModuleHandlePacketDetour(
|
||||
UIModule* thisPtr, UIModulePacketType type, uint uintParam, void* packet)
|
||||
{
|
||||
this.uiModuleHandlePacketHook.Original(thisPtr, type, uintParam, packet);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case UIModulePacketType.ClassJobChange when this.ClassJobChanged is { } callback:
|
||||
case UIModulePacketType.ClassJobChange:
|
||||
{
|
||||
var classJobId = uintParam;
|
||||
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.ClassJobChanged))
|
||||
{
|
||||
var classJobId = uintParam;
|
||||
|
||||
foreach (var action in callback.GetInvocationList().Cast<IClientState.ClassJobChangeDelegate>())
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
action(classJobId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
action(classJobId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UIModulePacketType.LevelChange when this.LevelChanged is { } callback:
|
||||
break;
|
||||
}
|
||||
|
||||
case UIModulePacketType.LevelChange:
|
||||
{
|
||||
var classJobId = *(uint*)packet;
|
||||
var level = *(ushort*)((nint)packet + 4);
|
||||
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.LevelChanged))
|
||||
{
|
||||
var classJobId = *(uint*)packet;
|
||||
var level = *(ushort*)((nint)packet + 4);
|
||||
|
||||
foreach (var action in callback.GetInvocationList().Cast<IClientState.LevelChangeDelegate>())
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
action(classJobId, level);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
action(classJobId, level);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,18 +292,15 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
|
|||
|
||||
Log.Debug("Logout: Type {type}, Code {code}", type, code);
|
||||
|
||||
if (this.Logout is { } callback)
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.Logout))
|
||||
{
|
||||
foreach (var action in callback.GetInvocationList().Cast<IClientState.LogoutDelegate>())
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
action(type, code);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
action(type, code);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", action.Method);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition
|
|||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly Framework framework = Service<Framework>.Get();
|
||||
|
||||
|
||||
private readonly bool[] cache = new bool[MaxConditionEntries];
|
||||
|
||||
private bool isDisposed;
|
||||
|
|
@ -38,7 +38,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition
|
|||
|
||||
this.framework.Update += this.FrameworkUpdate;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Finalizes an instance of the <see cref="Condition" /> class.</summary>
|
||||
~Condition() => this.Dispose(false);
|
||||
|
||||
|
|
@ -69,12 +69,12 @@ internal sealed class Condition : IInternalDisposableService, ICondition
|
|||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService() => this.Dispose(true);
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlySet<ConditionFlag> AsReadOnlySet()
|
||||
{
|
||||
var result = new HashSet<ConditionFlag>();
|
||||
|
||||
|
||||
for (var i = 0; i < MaxConditionEntries; i++)
|
||||
{
|
||||
if (this[i])
|
||||
|
|
@ -99,14 +99,14 @@ internal sealed class Condition : IInternalDisposableService, ICondition
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Any(params ConditionFlag[] flags)
|
||||
{
|
||||
foreach (var flag in flags)
|
||||
{
|
||||
// this[i] performs range checking, so no need to check here
|
||||
if (this[flag])
|
||||
if (this[flag])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -114,7 +114,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool AnyExcept(params ConditionFlag[] excluded)
|
||||
{
|
||||
|
|
@ -157,13 +157,16 @@ internal sealed class Condition : IInternalDisposableService, ICondition
|
|||
{
|
||||
this.cache[i] = value;
|
||||
|
||||
try
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.ConditionChange))
|
||||
{
|
||||
this.ConditionChange?.Invoke((ConditionFlag)i, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"While invoking {nameof(this.ConditionChange)}, an exception was thrown.");
|
||||
try
|
||||
{
|
||||
d((ConditionFlag)i, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"While invoking {d.Method.Name}, an exception was thrown.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -190,7 +193,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
|
|||
{
|
||||
this.conditionService.ConditionChange += this.ConditionChangedForward;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event ICondition.ConditionChangeDelegate? ConditionChange;
|
||||
|
||||
|
|
@ -202,7 +205,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
|
|||
|
||||
/// <inheritdoc/>
|
||||
public bool this[int flag] => this.conditionService[flag];
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
|
|
@ -210,7 +213,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
|
|||
|
||||
this.ConditionChange = null;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlySet<ConditionFlag> AsReadOnlySet() => this.conditionService.AsReadOnlySet();
|
||||
|
||||
|
|
@ -222,10 +225,10 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
|
|||
|
||||
/// <inheritdoc/>
|
||||
public bool AnyExcept(params ConditionFlag[] except) => this.conditionService.AnyExcept(except);
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool OnlyAny(params ConditionFlag[] other) => this.conditionService.OnlyAny(other);
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool EqualTo(params ConditionFlag[] other) => this.conditionService.EqualTo(other);
|
||||
|
||||
|
|
|
|||
|
|
@ -188,12 +188,6 @@ public enum ConditionFlag
|
|||
/// </summary>
|
||||
ExecutingCraftingAction = 40,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while crafting.
|
||||
/// </summary>
|
||||
[Obsolete("Renamed to ExecutingCraftingAction.")]
|
||||
Crafting40 = 40,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while preparing to craft.
|
||||
/// </summary>
|
||||
|
|
@ -205,12 +199,6 @@ public enum ConditionFlag
|
|||
/// <remarks> Includes fishing. </remarks>
|
||||
ExecutingGatheringAction = 42,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while gathering.
|
||||
/// </summary>
|
||||
[Obsolete("Renamed to ExecutingGatheringAction.")]
|
||||
Gathering42 = 42,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while fishing.
|
||||
/// </summary>
|
||||
|
|
@ -235,12 +223,6 @@ public enum ConditionFlag
|
|||
/// </summary>
|
||||
Jumping = 48,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while auto-run is active.
|
||||
/// </summary>
|
||||
[Obsolete("To avoid confusion, renamed to UsingChocoboTaxi.")]
|
||||
AutorunActive = 49,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while auto-run is active.
|
||||
/// </summary>
|
||||
|
|
@ -282,12 +264,6 @@ public enum ConditionFlag
|
|||
/// </summary>
|
||||
BoundByDuty56 = 56,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command at this time.
|
||||
/// </summary>
|
||||
[Obsolete("Renamed to MountOrOrnamentTransition.")]
|
||||
Unknown57 = 57,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command at this time.
|
||||
/// </summary>
|
||||
|
|
@ -461,12 +437,6 @@ public enum ConditionFlag
|
|||
/// </summary>
|
||||
RolePlaying = 90,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while bound by duty.
|
||||
/// </summary>
|
||||
[Obsolete("Use InDutyQueue")]
|
||||
BoundToDuty97 = 91,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while bound by duty.
|
||||
/// Specifically triggered when you are in a queue for a duty but not inside a duty.
|
||||
|
|
@ -483,12 +453,6 @@ public enum ConditionFlag
|
|||
/// </summary>
|
||||
WaitingToVisitOtherWorld = 93,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while using a parasol.
|
||||
/// </summary>
|
||||
[Obsolete("Renamed to UsingFashionAccessory.")]
|
||||
UsingParasol = 94,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while using a fashion accessory.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using System.Numerics;
|
|||
using Dalamud.Data;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Memory;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using Lumina.Excel;
|
||||
|
||||
|
|
@ -69,12 +68,6 @@ public interface IFate : IEquatable<IFate>
|
|||
/// </summary>
|
||||
byte Progress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="Fate"/> has a EXP bonus.
|
||||
/// </summary>
|
||||
[Obsolete($"Use {nameof(HasBonus)} instead")]
|
||||
bool HasExpBonus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="Fate"/> has a bonus.
|
||||
/// </summary>
|
||||
|
|
@ -222,10 +215,6 @@ internal unsafe partial class Fate : IFate
|
|||
/// <inheritdoc/>
|
||||
public byte Progress => this.Struct->Progress;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Api13ToDo("Remove")]
|
||||
public bool HasExpBonus => this.HasBonus;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool HasBonus => this.Struct->IsBonus;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Input;
|
||||
|
||||
using ImGuiNET;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game.ClientState.GamePad;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,24 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable
|
|||
/// <inheritdoc/>
|
||||
public int Length => objectTableLength;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IBattleChara> PlayerObjects => this.GetPlayerObjects();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IGameObject> CharacterManagerObjects => this.GetObjectsInRange(..199);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IGameObject> ClientObjects => this.GetObjectsInRange(200..448);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IGameObject> EventObjects => this.GetObjectsInRange(449..488);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IGameObject> StandObjects => this.GetObjectsInRange(489..628);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IGameObject> ReactionEventObjects => this.GetObjectsInRange(629..728);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IGameObject? this[int index]
|
||||
{
|
||||
|
|
@ -146,6 +164,28 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable
|
|||
};
|
||||
}
|
||||
|
||||
private IEnumerable<IBattleChara> GetPlayerObjects()
|
||||
{
|
||||
for (var index = 0; index < 200; index += 2)
|
||||
{
|
||||
if (this[index] is IBattleChara { ObjectKind: ObjectKind.Player } gameObject)
|
||||
{
|
||||
yield return gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IGameObject> GetObjectsInRange(Range range)
|
||||
{
|
||||
for (var index = range.Start.Value; index <= range.End.Value; index++)
|
||||
{
|
||||
if (this[index] is { } gameObject)
|
||||
{
|
||||
yield return gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Stores an object table entry, with preallocated concrete types.</summary>
|
||||
/// <remarks>Initializes a new instance of the <see cref="CachedEntry"/> struct.</remarks>
|
||||
/// <param name="gameObjectPtr">A pointer to the object table entry this entry should be pointing to.</param>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public unsafe class Status
|
|||
/// <summary>
|
||||
/// Gets the source ID of this status.
|
||||
/// </summary>
|
||||
public uint SourceId => this.Struct->SourceId;
|
||||
public uint SourceId => this.Struct->SourceObject.ObjectId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source actor associated with this status.
|
||||
|
|
|
|||
|
|
@ -45,6 +45,16 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma
|
|||
this.console.Invoke += this.ConsoleOnInvoke;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Published whenever a command is registered
|
||||
/// </summary>
|
||||
public event EventHandler<CommandEventArgs>? CommandAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Published whenever a command is unregistered
|
||||
/// </summary>
|
||||
public event EventHandler<CommandEventArgs>? CommandRemoved;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ReadOnlyDictionary<string, IReadOnlyCommandInfo> Commands => new(this.commandMap);
|
||||
|
||||
|
|
@ -122,6 +132,12 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma
|
|||
return false;
|
||||
}
|
||||
|
||||
this.CommandAdded?.InvokeSafely(this, new CommandEventArgs
|
||||
{
|
||||
Command = command,
|
||||
CommandInfo = info,
|
||||
});
|
||||
|
||||
if (!this.commandAssemblyNameMap.TryAdd((command, info), loaderAssemblyName))
|
||||
{
|
||||
this.commandMap.Remove(command, out _);
|
||||
|
|
@ -144,6 +160,12 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma
|
|||
return false;
|
||||
}
|
||||
|
||||
this.CommandAdded?.InvokeSafely(this, new CommandEventArgs
|
||||
{
|
||||
Command = command,
|
||||
CommandInfo = info,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -155,7 +177,17 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma
|
|||
this.commandAssemblyNameMap.TryRemove(assemblyKeyValuePair.Key, out _);
|
||||
}
|
||||
|
||||
return this.commandMap.Remove(command, out _);
|
||||
var removed = this.commandMap.Remove(command, out var info);
|
||||
if (removed)
|
||||
{
|
||||
this.CommandRemoved?.InvokeSafely(this, new CommandEventArgs
|
||||
{
|
||||
Command = command,
|
||||
CommandInfo = info,
|
||||
});
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -204,6 +236,20 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma
|
|||
|
||||
return this.ProcessCommand(command->ToString()) ? 0 : result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public class CommandEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the command string.
|
||||
/// </summary>
|
||||
public string Command { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command info.
|
||||
/// </summary>
|
||||
public IReadOnlyCommandInfo CommandInfo { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -268,7 +314,7 @@ internal class CommandManagerPluginScoped : IInternalDisposableService, ICommand
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"Command {command} is already registered.");
|
||||
Log.Error("Command {Command} is already registered.", command);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -287,7 +333,7 @@ internal class CommandManagerPluginScoped : IInternalDisposableService, ICommand
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"Command {command} not found.");
|
||||
Log.Error("Command {Command} not found.", command);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -350,17 +350,13 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
/// <param name="frameworkInstance">The Framework Instance to pass to delegate.</param>
|
||||
internal void ProfileAndInvoke(IFramework.OnUpdateDelegate? eventDelegate, IFramework frameworkInstance)
|
||||
{
|
||||
if (eventDelegate is null) return;
|
||||
|
||||
var invokeList = eventDelegate.GetInvocationList();
|
||||
|
||||
// Individually invoke OnUpdate handlers and time them.
|
||||
foreach (var d in invokeList)
|
||||
foreach (var d in Delegate.EnumerateInvocationList(eventDelegate))
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
d.Method.Invoke(d.Target, new object[] { frameworkInstance });
|
||||
d(frameworkInstance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -370,8 +366,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework
|
|||
stopwatch.Stop();
|
||||
|
||||
var key = $"{d.Target}::{d.Method.Name}";
|
||||
if (this.NonUpdatedSubDelegates.Contains(key))
|
||||
this.NonUpdatedSubDelegates.Remove(key);
|
||||
this.NonUpdatedSubDelegates.Remove(key);
|
||||
|
||||
AddToStats(key, stopwatch.Elapsed.TotalMilliseconds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Dalamud.IoC;
|
|||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Memory;
|
||||
using Dalamud.Plugin.Internal.Types;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
private static readonly ModuleLog Log = new("ChatGui");
|
||||
|
||||
private readonly Queue<XivChatEntry> chatQueue = new();
|
||||
private readonly Dictionary<(string PluginName, uint CommandId), Action<uint, SeString>> dalamudLinkHandlers = new();
|
||||
private readonly Dictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>> dalamudLinkHandlers = new();
|
||||
|
||||
private readonly Hook<PrintMessageDelegate> printMessageHook;
|
||||
private readonly Hook<InventoryItem.Delegates.Copy> inventoryItemCopyHook;
|
||||
|
|
@ -49,7 +50,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
[ServiceManager.ServiceDependency]
|
||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||
|
||||
private ImmutableDictionary<(string PluginName, uint CommandId), Action<uint, SeString>>? dalamudLinkHandlersCopy;
|
||||
private ImmutableDictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>>? dalamudLinkHandlersCopy;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private ChatGui()
|
||||
|
|
@ -85,7 +86,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
public byte LastLinkedItemFlags { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyDictionary<(string PluginName, uint CommandId), Action<uint, SeString>> RegisteredLinkHandlers
|
||||
public IReadOnlyDictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>> RegisteredLinkHandlers
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -161,6 +162,28 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
|
||||
#endregion
|
||||
|
||||
#region Chat Links
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DalamudLinkPayload AddChatLinkHandler(Action<Guid, SeString> commandAction)
|
||||
{
|
||||
return this.AddChatLinkHandler("Dalamud", commandAction);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveChatLinkHandler(Guid commandId)
|
||||
{
|
||||
this.RemoveChatLinkHandler("Dalamud", commandId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveChatLinkHandler()
|
||||
{
|
||||
this.RemoveChatLinkHandler("Dalamud");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Process a chat queue.
|
||||
/// </summary>
|
||||
|
|
@ -217,12 +240,11 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
/// Create a link handler.
|
||||
/// </summary>
|
||||
/// <param name="pluginName">The name of the plugin handling the link.</param>
|
||||
/// <param name="commandId">The ID of the command to run.</param>
|
||||
/// <param name="commandAction">The command action itself.</param>
|
||||
/// <returns>A payload for handling.</returns>
|
||||
[Api13ToDo("Plugins should not specify their own command IDs here. We should assign them ourselves.")]
|
||||
internal DalamudLinkPayload AddChatLinkHandler(string pluginName, uint commandId, Action<uint, SeString> commandAction)
|
||||
internal DalamudLinkPayload AddChatLinkHandler(string pluginName, Action<Guid, SeString> commandAction)
|
||||
{
|
||||
var commandId = Guid.CreateVersion7();
|
||||
var payload = new DalamudLinkPayload { Plugin = pluginName, CommandId = commandId };
|
||||
lock (this.dalamudLinkHandlers)
|
||||
{
|
||||
|
|
@ -255,7 +277,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
/// </summary>
|
||||
/// <param name="pluginName">The name of the plugin handling the link.</param>
|
||||
/// <param name="commandId">The ID of the command to be removed.</param>
|
||||
internal void RemoveChatLinkHandler(string pluginName, uint commandId)
|
||||
internal void RemoveChatLinkHandler(string pluginName, Guid commandId)
|
||||
{
|
||||
lock (this.dalamudLinkHandlers)
|
||||
{
|
||||
|
|
@ -346,24 +368,21 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
// Call events
|
||||
var isHandled = false;
|
||||
|
||||
if (this.CheckMessageHandled is { } handledCallback)
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.CheckMessageHandled))
|
||||
{
|
||||
foreach (var action in handledCallback.GetInvocationList().Cast<IChatGui.OnCheckMessageHandledDelegate>())
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
action(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Could not invoke registered OnCheckMessageHandledDelegate for {Name}", action.Method);
|
||||
}
|
||||
action(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Could not invoke registered OnCheckMessageHandledDelegate for {Name}", action.Method);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isHandled && this.ChatMessage is { } callback)
|
||||
if (!isHandled)
|
||||
{
|
||||
foreach (var action in callback.GetInvocationList().Cast<IChatGui.OnMessageDelegate>())
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.ChatMessage))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -394,12 +413,14 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
// Print the original chat if it's handled.
|
||||
if (isHandled)
|
||||
{
|
||||
this.ChatMessageHandled?.Invoke(chatType, timestamp, parsedSender, parsedMessage);
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.ChatMessageHandled))
|
||||
d(chatType, timestamp, parsedSender, parsedMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageId = this.printMessageHook.Original(manager, chatType, sender, message, timestamp, silent);
|
||||
this.ChatMessageUnhandled?.Invoke(chatType, timestamp, parsedSender, parsedMessage);
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.ChatMessageUnhandled))
|
||||
d(chatType, timestamp, parsedSender, parsedMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -479,11 +500,15 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui
|
|||
[ServiceManager.ServiceDependency]
|
||||
private readonly ChatGui chatGuiService = Service<ChatGui>.Get();
|
||||
|
||||
private readonly LocalPlugin plugin;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChatGuiPluginScoped"/> class.
|
||||
/// </summary>
|
||||
internal ChatGuiPluginScoped()
|
||||
/// <param name="plugin">The plugin.</param>
|
||||
internal ChatGuiPluginScoped(LocalPlugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.chatGuiService.ChatMessage += this.OnMessageForward;
|
||||
this.chatGuiService.CheckMessageHandled += this.OnCheckMessageForward;
|
||||
this.chatGuiService.ChatMessageHandled += this.OnMessageHandledForward;
|
||||
|
|
@ -509,7 +534,7 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui
|
|||
public byte LastLinkedItemFlags => this.chatGuiService.LastLinkedItemFlags;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReadOnlyDictionary<(string PluginName, uint CommandId), Action<uint, SeString>> RegisteredLinkHandlers => this.chatGuiService.RegisteredLinkHandlers;
|
||||
public IReadOnlyDictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>> RegisteredLinkHandlers => this.chatGuiService.RegisteredLinkHandlers;
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
|
|
@ -525,6 +550,18 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui
|
|||
this.ChatMessageUnhandled = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DalamudLinkPayload AddChatLinkHandler(Action<Guid, SeString> commandAction)
|
||||
=> this.chatGuiService.AddChatLinkHandler(this.plugin.InternalName, commandAction);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveChatLinkHandler(Guid commandId)
|
||||
=> this.chatGuiService.RemoveChatLinkHandler(this.plugin.InternalName, commandId);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveChatLinkHandler()
|
||||
=> this.chatGuiService.RemoveChatLinkHandler(this.plugin.InternalName);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Print(XivChatEntry chat)
|
||||
=> this.chatGuiService.Print(chat);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Threading;
|
|||
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game.Addon.Events;
|
||||
using Dalamud.Game.Addon.Events.EventDataTypes;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
|
@ -330,7 +331,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
|||
this.entriesReadOnlyCopy = null;
|
||||
}
|
||||
|
||||
private AtkUnitBase* GetDtr() => (AtkUnitBase*)this.gameGui.GetAddonByName("_DTR").ToPointer();
|
||||
private AtkUnitBase* GetDtr() => this.gameGui.GetAddonByName("_DTR").Struct;
|
||||
|
||||
private void Update(IFramework unused)
|
||||
{
|
||||
|
|
@ -427,7 +428,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
|||
|
||||
private void FixCollision(AddonEvent eventType, AddonArgs addonInfo)
|
||||
{
|
||||
var addon = (AtkUnitBase*)addonInfo.Addon;
|
||||
var addon = addonInfo.Addon.Struct;
|
||||
if (addon->RootNode is null || addon->UldManager.NodeList is null) return;
|
||||
|
||||
float minX = addon->RootNode->Width;
|
||||
|
|
@ -596,24 +597,13 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
|||
newTextNode->TextColor = new ByteColor { R = 255, G = 255, B = 255, A = 255 };
|
||||
newTextNode->EdgeColor = new ByteColor { R = 142, G = 106, B = 12, A = 255 };
|
||||
|
||||
// ICreatable was restored, this may be necessary if AtkUldManager.CreateAtkTextNode(); is used instead of Create<T>
|
||||
// // Memory is filled with random data after being created, zero out some things to avoid issues.
|
||||
// newTextNode->UnkPtr_1 = null;
|
||||
// newTextNode->SelectStart = 0;
|
||||
// newTextNode->SelectEnd = 0;
|
||||
// newTextNode->FontCacheHandle = 0;
|
||||
// newTextNode->CharSpacing = 0;
|
||||
// newTextNode->BackgroundColor = new ByteColor { R = 0, G = 0, B = 0, A = 0 };
|
||||
// newTextNode->TextId = 0;
|
||||
// newTextNode->SheetType = 0;
|
||||
|
||||
return newTextNode;
|
||||
}
|
||||
|
||||
private void DtrEventHandler(AddonEventType atkEventType, IntPtr atkUnitBase, IntPtr atkResNode)
|
||||
private void DtrEventHandler(AddonEventType atkEventType, AddonEventData eventData)
|
||||
{
|
||||
var addon = (AtkUnitBase*)atkUnitBase;
|
||||
var node = (AtkResNode*)atkResNode;
|
||||
var addon = (AtkUnitBase*)eventData.AddonPointer;
|
||||
var node = (AtkResNode*)eventData.NodeTargetPointer;
|
||||
|
||||
DtrBarEntry? dtrBarEntry = null;
|
||||
this.entriesLock.EnterReadLock();
|
||||
|
|
@ -652,7 +642,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
|||
break;
|
||||
|
||||
case AddonEventType.MouseClick:
|
||||
dtrBarEntry.OnClick.Invoke();
|
||||
dtrBarEntry.OnClick?.Invoke(new AddonMouseEventData(eventData));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game.Addon.Events.EventDataTypes;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Plugin.Internal.Types;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -42,12 +43,6 @@ public interface IReadOnlyDtrBarEntry
|
|||
/// Gets a value indicating whether the user has hidden this entry from view through the Dalamud settings.
|
||||
/// </summary>
|
||||
public bool UserHidden { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the click action of this entry.
|
||||
/// </summary>
|
||||
/// <returns>True, if a click action was registered and executed.</returns>
|
||||
public bool TriggerClickAction();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -71,9 +66,9 @@ public interface IDtrBarEntry : IReadOnlyDtrBarEntry
|
|||
public new bool Shown { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a action to be invoked when the user clicks on the dtr entry.
|
||||
/// Gets or sets an action to be invoked when the user clicks on the dtr entry.
|
||||
/// </summary>
|
||||
public Action? OnClick { get; set; }
|
||||
public Action<AddonMouseEventData>? OnClick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Remove this entry from the bar.
|
||||
|
|
@ -122,10 +117,8 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry
|
|||
/// <inheritdoc cref="IDtrBarEntry.Tooltip" />
|
||||
public SeString? Tooltip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a action to be invoked when the user clicks on the dtr entry.
|
||||
/// </summary>
|
||||
public Action? OnClick { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public Action<AddonMouseEventData>? OnClick { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool HasClickAction => this.OnClick != null;
|
||||
|
|
@ -145,7 +138,7 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Api13ToDo("Maybe make this config scoped to internalname?")]
|
||||
[Api13ToDo("Maybe make this config scoped to internal name?")]
|
||||
public bool UserHidden => this.configuration.DtrIgnore?.Contains(this.Title) ?? false;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -178,16 +171,6 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry
|
|||
/// </summary>
|
||||
internal LocalPlugin? OwnerPlugin { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TriggerClickAction()
|
||||
{
|
||||
if (this.OnClick == null)
|
||||
return false;
|
||||
|
||||
this.OnClick.Invoke();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove this entry from the bar.
|
||||
/// You will need to re-acquire it from DtrBar to reuse it.
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui
|
|||
|
||||
strArray->SetValue((int)strOffset + 0, text1.EncodeWithNullTerminator(), false, true, false);
|
||||
strArray->SetValue((int)strOffset + 1, text2.EncodeWithNullTerminator(), false, true, false);
|
||||
|
||||
|
||||
flytext->AddFlyText(actorIndex, 1, numArray, numOffset, 10, strArray, strOffset, 2, 0);
|
||||
}
|
||||
|
||||
|
|
@ -116,17 +116,27 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui
|
|||
$"text1({(nint)text1:X}, \"{tmpText1}\") text2({(nint)text2:X}, \"{tmpText2}\") " +
|
||||
$"color({color:X}) icon({icon}) yOffset({yOffset})");
|
||||
Log.Verbose("[FlyText] Calling flytext events!");
|
||||
this.FlyTextCreated?.Invoke(
|
||||
ref tmpKind,
|
||||
ref tmpVal1,
|
||||
ref tmpVal2,
|
||||
ref tmpText1,
|
||||
ref tmpText2,
|
||||
ref tmpColor,
|
||||
ref tmpIcon,
|
||||
ref tmpDamageTypeIcon,
|
||||
ref tmpYOffset,
|
||||
ref handled);
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.FlyTextCreated))
|
||||
{
|
||||
try
|
||||
{
|
||||
d(
|
||||
ref tmpKind,
|
||||
ref tmpVal1,
|
||||
ref tmpVal2,
|
||||
ref tmpText1,
|
||||
ref tmpText2,
|
||||
ref tmpColor,
|
||||
ref tmpIcon,
|
||||
ref tmpDamageTypeIcon,
|
||||
ref tmpYOffset,
|
||||
ref handled);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", d.Method);
|
||||
}
|
||||
}
|
||||
|
||||
// If handled, ignore the original call
|
||||
if (handled)
|
||||
|
|
|
|||
|
|
@ -105,16 +105,26 @@ public enum FlyTextKind : int
|
|||
|
||||
/// <summary>
|
||||
/// Val1 in serif font, Text2 in sans-serif as subtitle.
|
||||
/// Added in 7.2, usage currently unknown.
|
||||
/// </summary>
|
||||
[Obsolete("Use Knowledge instead", true)]
|
||||
Unknown17 = 17,
|
||||
|
||||
/// <summary>
|
||||
/// Val1 in serif font, Text2 in sans-serif as subtitle.
|
||||
/// Added in 7.2, usage currently unknown.
|
||||
/// </summary>
|
||||
Knowledge = 17,
|
||||
|
||||
/// <summary>
|
||||
/// Val1 in serif font, Text2 in sans-serif as subtitle.
|
||||
/// </summary>
|
||||
[Obsolete("Use PhantomExp instead", true)]
|
||||
Unknown18 = 18,
|
||||
|
||||
/// <summary>
|
||||
/// Val1 in serif font, Text2 in sans-serif as subtitle.
|
||||
/// </summary>
|
||||
PhantomExp = 18,
|
||||
|
||||
/// <summary>
|
||||
/// Sans-serif Text1 next to serif Val1 with all caps condensed font MP with Text2 in sans-serif as subtitle.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.NativeWrapper;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Interface.Utility;
|
||||
|
|
@ -18,8 +20,6 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|||
using FFXIVClientStructs.FFXIV.Common.Component.BGCollision;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
using Vector3 = System.Numerics.Vector3;
|
||||
using Vector4 = System.Numerics.Vector4;
|
||||
|
|
@ -167,79 +167,59 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr GetUIModule()
|
||||
public UIModulePtr GetUIModule()
|
||||
{
|
||||
var framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance();
|
||||
if (framework == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
var uiModule = framework->GetUIModule();
|
||||
if (uiModule == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
return (IntPtr)uiModule;
|
||||
return (nint)UIModule.Instance();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr GetAddonByName(string name, int index = 1)
|
||||
public AtkUnitBasePtr GetAddonByName(string name, int index = 1)
|
||||
{
|
||||
var atkStage = AtkStage.Instance();
|
||||
if (atkStage == null)
|
||||
return IntPtr.Zero;
|
||||
var unitManager = RaptureAtkUnitManager.Instance();
|
||||
if (unitManager == null)
|
||||
return 0;
|
||||
|
||||
var unitMgr = atkStage->RaptureAtkUnitManager;
|
||||
if (unitMgr == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
var addon = unitMgr->GetAddonByName(name, index);
|
||||
if (addon == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
return (IntPtr)addon;
|
||||
return (nint)unitManager->GetAddonByName(name, index);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr FindAgentInterface(string addonName)
|
||||
public AgentInterfacePtr GetAgentById(int id)
|
||||
{
|
||||
var agentModule = AgentModule.Instance();
|
||||
if (agentModule == null || id < 0 || id >= agentModule->Agents.Length)
|
||||
return 0;
|
||||
|
||||
return (nint)agentModule->Agents[id].Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AgentInterfacePtr FindAgentInterface(string addonName)
|
||||
{
|
||||
var addon = this.GetAddonByName(addonName);
|
||||
return this.FindAgentInterface(addon);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr FindAgentInterface(void* addon) => this.FindAgentInterface((IntPtr)addon);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr FindAgentInterface(IntPtr addonPtr)
|
||||
public AgentInterfacePtr FindAgentInterface(AtkUnitBasePtr addon)
|
||||
{
|
||||
if (addonPtr == IntPtr.Zero)
|
||||
return IntPtr.Zero;
|
||||
if (addon.IsNull)
|
||||
return 0;
|
||||
|
||||
var uiModule = (UIModule*)this.GetUIModule();
|
||||
if (uiModule == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
var agentModule = uiModule->GetAgentModule();
|
||||
var agentModule = AgentModule.Instance();
|
||||
if (agentModule == null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
var addon = (AtkUnitBase*)addonPtr;
|
||||
var addonId = addon->ParentId == 0 ? addon->Id : addon->ParentId;
|
||||
return 0;
|
||||
|
||||
var addonId = addon.ParentId == 0 ? addon.Id : addon.ParentId;
|
||||
if (addonId == 0)
|
||||
return IntPtr.Zero;
|
||||
return 0;
|
||||
|
||||
var index = 0;
|
||||
while (true)
|
||||
foreach (AgentInterface* agent in agentModule->Agents)
|
||||
{
|
||||
var agent = agentModule->GetAgentByInternalId((AgentId)index++);
|
||||
if (agent == uiModule || agent == null)
|
||||
break;
|
||||
|
||||
if (agent->AddonId == addonId)
|
||||
return new IntPtr(agent);
|
||||
if (agent != null && agent->AddonId == addonId)
|
||||
return (nint)agent;
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -454,25 +434,25 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui
|
|||
=> this.gameGuiService.ScreenToWorld(screenPos, out worldPos, rayDistance);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr GetUIModule()
|
||||
public UIModulePtr GetUIModule()
|
||||
=> this.gameGuiService.GetUIModule();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr GetAddonByName(string name, int index = 1)
|
||||
public AtkUnitBasePtr GetAddonByName(string name, int index = 1)
|
||||
=> this.gameGuiService.GetAddonByName(name, index);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr FindAgentInterface(string addonName)
|
||||
public AgentInterfacePtr GetAgentById(int id)
|
||||
=> this.gameGuiService.GetAgentById(id);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AgentInterfacePtr FindAgentInterface(string addonName)
|
||||
=> this.gameGuiService.FindAgentInterface(addonName);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public unsafe IntPtr FindAgentInterface(void* addon)
|
||||
public AgentInterfacePtr FindAgentInterface(AtkUnitBasePtr addon)
|
||||
=> this.gameGuiService.FindAgentInterface(addon);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr FindAgentInterface(IntPtr addonPtr)
|
||||
=> this.gameGuiService.FindAgentInterface(addonPtr);
|
||||
|
||||
private void UiHideToggledForward(object sender, bool toggled) => this.UiHideToggled?.Invoke(sender, toggled);
|
||||
|
||||
private void HoveredItemForward(object sender, ulong itemId) => this.HoveredItemChanged?.Invoke(sender, itemId);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Dalamud.Hooking;
|
|||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
|
@ -71,7 +72,7 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
|
|||
/// <inheritdoc/>
|
||||
public unsafe void RequestRedraw()
|
||||
{
|
||||
var addon = (AddonNamePlate*)this.gameGui.GetAddonByName("NamePlate");
|
||||
var addon = (AddonNamePlate*)(nint)this.gameGui.GetAddonByName("NamePlate");
|
||||
if (addon != null)
|
||||
{
|
||||
addon->DoFullUpdate = 1;
|
||||
|
|
@ -169,8 +170,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
|
|||
handler.ResetState();
|
||||
}
|
||||
|
||||
this.OnDataUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnNamePlateUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnDataUpdate?.InvokeSafely(this.context, activeHandlers);
|
||||
this.OnNamePlateUpdate?.InvokeSafely(this.context, activeHandlers);
|
||||
|
||||
if (this.context.HasParts)
|
||||
this.ApplyBuilders(activeHandlers);
|
||||
|
|
@ -185,8 +186,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
|
|||
Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate.");
|
||||
}
|
||||
|
||||
this.OnPostNamePlateUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnPostDataUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnPostNamePlateUpdate?.InvokeSafely(this.context, activeHandlers);
|
||||
this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -200,8 +201,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
|
|||
|
||||
if (this.OnDataUpdate is not null)
|
||||
{
|
||||
this.OnDataUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnNamePlateUpdate?.Invoke(this.context, updatedHandlers);
|
||||
this.OnDataUpdate?.InvokeSafely(this.context, activeHandlers);
|
||||
this.OnNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers);
|
||||
|
||||
if (this.context.HasParts)
|
||||
this.ApplyBuilders(activeHandlers);
|
||||
|
|
@ -216,12 +217,12 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
|
|||
Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate.");
|
||||
}
|
||||
|
||||
this.OnPostNamePlateUpdate?.Invoke(this.context, updatedHandlers);
|
||||
this.OnPostDataUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnPostNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers);
|
||||
this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers);
|
||||
}
|
||||
else if (updatedHandlers.Count != 0)
|
||||
{
|
||||
this.OnNamePlateUpdate?.Invoke(this.context, updatedHandlers);
|
||||
this.OnNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers);
|
||||
|
||||
if (this.context.HasParts)
|
||||
this.ApplyBuilders(updatedHandlers);
|
||||
|
|
@ -236,8 +237,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
|
|||
Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate.");
|
||||
}
|
||||
|
||||
this.OnPostNamePlateUpdate?.Invoke(this.context, updatedHandlers);
|
||||
this.OnPostDataUpdate?.Invoke(this.context, activeHandlers);
|
||||
this.OnPostNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers);
|
||||
this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,17 @@ internal sealed unsafe class PartyFinderGui : IInternalDisposableService, IParty
|
|||
|
||||
var listing = new PartyFinderListing(packet.Listings[i]);
|
||||
var args = new PartyFinderListingEventArgs(packet.BatchNumber);
|
||||
this.ReceiveListing?.Invoke(listing, args);
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.ReceiveListing))
|
||||
{
|
||||
try
|
||||
{
|
||||
d(listing, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", d.Method);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.Visible)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public enum JobFlags : ulong
|
|||
RedMage = 1ul << 24,
|
||||
|
||||
/// <summary>
|
||||
/// Blue mage (BLM).
|
||||
/// Blue mage (BLU).
|
||||
/// </summary>
|
||||
BlueMage = 1ul << 25,
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ using Dalamud.Plugin.Services;
|
|||
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game.Gui.Toast;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -112,7 +114,7 @@ internal sealed partial class ToastGui
|
|||
options ??= new ToastOptions();
|
||||
this.normalQueue.Enqueue((Encoding.UTF8.GetBytes(message), options));
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ShowNormal(SeString message, ToastOptions? options = null)
|
||||
{
|
||||
|
|
@ -150,7 +152,17 @@ internal sealed partial class ToastGui
|
|||
Speed = (ToastSpeed)isFast,
|
||||
};
|
||||
|
||||
this.Toast?.Invoke(ref str, ref options, ref isHandled);
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.Toast))
|
||||
{
|
||||
try
|
||||
{
|
||||
d.Invoke(ref str, ref options, ref isHandled);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", d.Method);
|
||||
}
|
||||
}
|
||||
|
||||
// do nothing if handled
|
||||
if (isHandled)
|
||||
|
|
@ -180,7 +192,7 @@ internal sealed partial class ToastGui
|
|||
options ??= new QuestToastOptions();
|
||||
this.questQueue.Enqueue((Encoding.UTF8.GetBytes(message), options));
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ShowQuest(SeString message, QuestToastOptions? options = null)
|
||||
{
|
||||
|
|
@ -223,7 +235,17 @@ internal sealed partial class ToastGui
|
|||
PlaySound = playSound == 1,
|
||||
};
|
||||
|
||||
this.QuestToast?.Invoke(ref str, ref options, ref isHandled);
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.QuestToast))
|
||||
{
|
||||
try
|
||||
{
|
||||
d.Invoke(ref str, ref options, ref isHandled);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", d.Method);
|
||||
}
|
||||
}
|
||||
|
||||
// do nothing if handled
|
||||
if (isHandled)
|
||||
|
|
@ -286,7 +308,17 @@ internal sealed partial class ToastGui
|
|||
var isHandled = false;
|
||||
var str = SeString.Parse(text);
|
||||
|
||||
this.ErrorToast?.Invoke(ref str, ref isHandled);
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.ErrorToast))
|
||||
{
|
||||
try
|
||||
{
|
||||
d.Invoke(ref str, ref isHandled);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", d.Method);
|
||||
}
|
||||
}
|
||||
|
||||
// do nothing if handled
|
||||
if (isHandled)
|
||||
|
|
@ -321,16 +353,16 @@ internal class ToastGuiPluginScoped : IInternalDisposableService, IToastGui
|
|||
this.toastGuiService.QuestToast += this.QuestToastForward;
|
||||
this.toastGuiService.ErrorToast += this.ErrorToastForward;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IToastGui.OnNormalToastDelegate? Toast;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IToastGui.OnQuestToastDelegate? QuestToast;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IToastGui.OnErrorToastDelegate? ErrorToast;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
|
|
@ -342,7 +374,7 @@ internal class ToastGuiPluginScoped : IInternalDisposableService, IToastGui
|
|||
this.QuestToast = null;
|
||||
this.ErrorToast = null;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ShowNormal(string message, ToastOptions? options = null) => this.toastGuiService.ShowNormal(message, options);
|
||||
|
||||
|
|
|
|||
279
Dalamud/Game/Internal/DalamudCompletion.cs
Normal file
279
Dalamud/Game/Internal/DalamudCompletion.cs
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.Completion;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
using Lumina.Text;
|
||||
|
||||
namespace Dalamud.Game.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// This class adds Dalamud and plugin commands to the chat box's autocompletion.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal sealed unsafe class DalamudCompletion : IInternalDisposableService
|
||||
{
|
||||
// 0xFF is a magic group number that causes CompletionModule's internals to treat entries
|
||||
// as raw strings instead of as lookups into an EXD sheet
|
||||
private const int GroupNumber = 0xFF;
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly CommandManager commandManager = Service<CommandManager>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly Framework framework = Service<Framework>.Get();
|
||||
|
||||
private readonly Dictionary<string, EntryStrings> cachedCommands = [];
|
||||
|
||||
private EntryStrings? dalamudCategory;
|
||||
|
||||
private Hook<AtkTextInput.Delegates.OpenCompletion> openSuggestionsHook;
|
||||
private Hook<CompletionModule.Delegates.GetSelection>? getSelectionHook;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DalamudCompletion"/> class.
|
||||
/// </summary>
|
||||
[ServiceManager.ServiceConstructor]
|
||||
internal DalamudCompletion()
|
||||
{
|
||||
this.framework.RunOnTick(this.Setup);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
this.openSuggestionsHook?.Disable();
|
||||
this.openSuggestionsHook?.Dispose();
|
||||
|
||||
this.getSelectionHook?.Disable();
|
||||
this.getSelectionHook?.Dispose();
|
||||
|
||||
this.dalamudCategory?.Dispose();
|
||||
|
||||
this.ClearCachedCommands();
|
||||
}
|
||||
|
||||
private void Setup()
|
||||
{
|
||||
var uiModule = UIModule.Instance();
|
||||
if (uiModule == null || uiModule->FrameCount == 0)
|
||||
{
|
||||
this.framework.RunOnTick(this.Setup);
|
||||
return;
|
||||
}
|
||||
|
||||
this.dalamudCategory = new EntryStrings("【Dalamud】");
|
||||
|
||||
this.openSuggestionsHook = Hook<AtkTextInput.Delegates.OpenCompletion>.FromAddress(
|
||||
(nint)AtkTextInput.MemberFunctionPointers.OpenCompletion,
|
||||
this.OpenSuggestionsDetour);
|
||||
|
||||
this.getSelectionHook = Hook<CompletionModule.Delegates.GetSelection>.FromAddress(
|
||||
(nint)uiModule->CompletionModule.VirtualTable->GetSelection,
|
||||
this.GetSelectionDetour);
|
||||
|
||||
this.openSuggestionsHook.Enable();
|
||||
this.getSelectionHook.Enable();
|
||||
}
|
||||
|
||||
private void OpenSuggestionsDetour(AtkTextInput* thisPtr)
|
||||
{
|
||||
this.UpdateCompletionData();
|
||||
this.openSuggestionsHook!.Original(thisPtr);
|
||||
}
|
||||
|
||||
private int GetSelectionDetour(CompletionModule* thisPtr, CategoryData.CompletionDataStruct* dataStructs, int index, Utf8String* outputString, Utf8String* outputDisplayString)
|
||||
{
|
||||
var ret = this.getSelectionHook!.Original.Invoke(thisPtr, dataStructs, index, outputString, outputDisplayString);
|
||||
this.HandleInsert(ret, outputString, outputDisplayString);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void UpdateCompletionData()
|
||||
{
|
||||
if (!this.TryGetActiveTextInput(out var component, out var addon))
|
||||
{
|
||||
if (this.HasDalamudCategory())
|
||||
this.ResetCompletionData();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var uiModule = UIModule.Instance();
|
||||
if (uiModule == null)
|
||||
return;
|
||||
|
||||
this.ResetCompletionData();
|
||||
this.ClearCachedCommands();
|
||||
|
||||
var currentText = component->UnkText1.StringPtr.ExtractText();
|
||||
|
||||
var commands = this.commandManager.Commands
|
||||
.Where(kv => kv.Value.ShowInHelp && (currentText.Length == 0 || kv.Key.StartsWith(currentText)))
|
||||
.OrderBy(kv => kv.Key);
|
||||
|
||||
if (!commands.Any())
|
||||
return;
|
||||
|
||||
var categoryData = (CategoryData*)IMemorySpace.GetDefaultSpace()->Malloc((ulong)sizeof(CategoryData), 0x08);
|
||||
categoryData->Ctor(GroupNumber, 0xFF);
|
||||
|
||||
uiModule->CompletionModule.AddCategoryData(
|
||||
GroupNumber,
|
||||
this.dalamudCategory!.Display->StringPtr,
|
||||
this.dalamudCategory.Match->StringPtr, categoryData);
|
||||
|
||||
foreach (var (cmd, info) in commands)
|
||||
{
|
||||
if (!this.cachedCommands.TryGetValue(cmd, out var entryString))
|
||||
this.cachedCommands.Add(cmd, entryString = new EntryStrings(cmd));
|
||||
|
||||
uiModule->CompletionModule.AddCompletionEntry(
|
||||
GroupNumber,
|
||||
0xFF,
|
||||
entryString.Display->StringPtr,
|
||||
entryString.Match->StringPtr,
|
||||
0xFF);
|
||||
}
|
||||
|
||||
categoryData->SortEntries();
|
||||
}
|
||||
|
||||
private void HandleInsert(int ret, Utf8String* outputString, Utf8String* outputDisplayString)
|
||||
{
|
||||
// -2 means it was a plain text final selection, so it might be ours.
|
||||
if (ret != -2 || outputString == null)
|
||||
return;
|
||||
|
||||
// Strip out color payloads that we added to the string.
|
||||
var txt = outputString->StringPtr.ExtractText();
|
||||
if (!this.cachedCommands.ContainsKey(txt))
|
||||
return;
|
||||
|
||||
if (!this.TryGetActiveTextInput(out _, out _))
|
||||
{
|
||||
outputString->Clear();
|
||||
|
||||
if (outputDisplayString != null)
|
||||
outputDisplayString->Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
outputString->SetString(txt + ' ');
|
||||
}
|
||||
|
||||
private bool TryGetActiveTextInput(out AtkComponentTextInput* component, out AtkUnitBase* addon)
|
||||
{
|
||||
component = null;
|
||||
addon = null;
|
||||
|
||||
var raptureAtkModule = RaptureAtkModule.Instance();
|
||||
if (raptureAtkModule == null)
|
||||
return false;
|
||||
|
||||
var textInputEventInterface = raptureAtkModule->TextInput.TargetTextInputEventInterface;
|
||||
if (textInputEventInterface == null)
|
||||
return false;
|
||||
|
||||
var ownerNode = textInputEventInterface->GetOwnerNode();
|
||||
if (ownerNode == null || ownerNode->GetNodeType() != NodeType.Component)
|
||||
return false;
|
||||
|
||||
var componentNode = (AtkComponentNode*)ownerNode;
|
||||
var componentBase = componentNode->Component;
|
||||
if (componentBase == null || componentBase->GetComponentType() != ComponentType.TextInput)
|
||||
return false;
|
||||
|
||||
component = (AtkComponentTextInput*)componentBase;
|
||||
|
||||
addon = component->ContainingAddon;
|
||||
|
||||
if (addon == null)
|
||||
addon = component->ContainingAddon2;
|
||||
|
||||
if (addon == null)
|
||||
addon = RaptureAtkUnitManager.Instance()->GetAddonByNode((AtkResNode*)component->OwnerNode);
|
||||
|
||||
return addon != null && addon->NameString == "ChatLog";
|
||||
}
|
||||
|
||||
private bool HasDalamudCategory()
|
||||
{
|
||||
var uiModule = UIModule.Instance();
|
||||
if (uiModule == null)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < uiModule->CompletionModule.CategoryNames.Count; i++)
|
||||
{
|
||||
if (uiModule->CompletionModule.CategoryNames[i].AsReadOnlySeStringSpan().ContainsText("【Dalamud】"u8))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ResetCompletionData()
|
||||
{
|
||||
var uiModule = UIModule.Instance();
|
||||
if (uiModule == null)
|
||||
return;
|
||||
|
||||
uiModule->CompletionModule.ClearCompletionData();
|
||||
|
||||
// This happens in UIModule.Update. Just repeat it to fill CompletionData back up with defaults.
|
||||
uiModule->CompletionModule.Update(
|
||||
&uiModule->CompletionSheetName,
|
||||
&uiModule->CompletionOpenIconMacro,
|
||||
&uiModule->CompletionCloseIconMacro,
|
||||
0);
|
||||
}
|
||||
|
||||
private void ClearCachedCommands()
|
||||
{
|
||||
foreach (var entry in this.cachedCommands.Values)
|
||||
{
|
||||
entry.Dispose();
|
||||
}
|
||||
|
||||
this.cachedCommands.Clear();
|
||||
}
|
||||
|
||||
private class EntryStrings : IDisposable
|
||||
{
|
||||
public EntryStrings(string command)
|
||||
{
|
||||
var rssb = SeStringBuilder.SharedPool.Get();
|
||||
|
||||
this.Display = Utf8String.FromSequence(rssb
|
||||
.PushColorType(539)
|
||||
.Append(command)
|
||||
.PopColorType()
|
||||
.GetViewAsSpan());
|
||||
|
||||
SeStringBuilder.SharedPool.Return(rssb);
|
||||
|
||||
this.Match = Utf8String.FromString(command);
|
||||
}
|
||||
|
||||
public Utf8String* Display { get; }
|
||||
|
||||
public Utf8String* Match { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Display->Dtor(true);
|
||||
this.Match->Dtor(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Inventory.Records;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
|
|
@ -160,6 +162,29 @@ public unsafe struct GameInventoryItem : IEquatable<GameInventoryItem>
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of materia entries for this item. Exists as a user-friendly interface to <see cref="Materia"/> and
|
||||
/// <see cref="MateriaGrade"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<MateriaEntry> MateriaEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ItemUtil.IsEventItem(this.BaseItemId) || this.IsMateriaUsedForDate)
|
||||
return [];
|
||||
|
||||
var result = new List<MateriaEntry>();
|
||||
for (byte i = 0; i < this.InternalItem.GetMateriaCount(); i++)
|
||||
{
|
||||
var entry = new MateriaEntry(this.InternalItem.GetMateriaId(i), this.InternalItem.GetMateriaGrade(i));
|
||||
if (entry.IsValid())
|
||||
result.Add(entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of native inventory item in the game.<br />
|
||||
/// Can be 0 if this instance of <see cref="GameInventoryItem"/> does not point to a valid set of container type and slot.<br />
|
||||
|
|
|
|||
42
Dalamud/Game/Inventory/Records/MateriaEntry.cs
Normal file
42
Dalamud/Game/Inventory/Records/MateriaEntry.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using Dalamud.Data;
|
||||
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace Dalamud.Game.Inventory.Records;
|
||||
|
||||
/// <summary>
|
||||
/// A record to hold easy information about a given piece of Materia.
|
||||
/// </summary>
|
||||
public record MateriaEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MateriaEntry"/> class.
|
||||
/// </summary>
|
||||
/// <param name="typeId">The ID of the materia.</param>
|
||||
/// <param name="gradeValue">The grade of the materia.</param>
|
||||
public MateriaEntry(ushort typeId, byte gradeValue)
|
||||
{
|
||||
this.Type = LuminaUtils.CreateRef<Materia>(typeId);
|
||||
this.Grade = LuminaUtils.CreateRef<MateriaGrade>(gradeValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Lumina row for this Materia.
|
||||
/// </summary>
|
||||
public RowRef<Materia> Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Lumina row for this Materia's grade.
|
||||
/// </summary>
|
||||
public RowRef<MateriaGrade> Grade { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this MateriaEntry is valid.
|
||||
/// </summary>
|
||||
/// <returns>True if valid, false otherwise.</returns>
|
||||
internal bool IsValid()
|
||||
{
|
||||
return this.Type.IsValid && this.Grade.IsValid;
|
||||
}
|
||||
}
|
||||
|
|
@ -150,9 +150,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
|
|||
|
||||
private void OnHistoryReceived(IMarketBoardHistory history)
|
||||
{
|
||||
if (this.HistoryReceived == null) return;
|
||||
|
||||
foreach (var action in this.HistoryReceived.GetInvocationList().Cast<HistoryReceivedDelegate>())
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.HistoryReceived))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -167,9 +165,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
|
|||
|
||||
private void OnItemPurchased(IMarketBoardPurchase purchase)
|
||||
{
|
||||
if (this.ItemPurchased == null) return;
|
||||
|
||||
foreach (var action in this.ItemPurchased.GetInvocationList().Cast<ItemPurchasedDelegate>())
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.ItemPurchased))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -184,10 +180,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
|
|||
|
||||
private void OnOfferingsReceived(IMarketBoardCurrentOfferings currentOfferings)
|
||||
{
|
||||
if (this.OfferingsReceived == null) return;
|
||||
|
||||
foreach (var action in this.OfferingsReceived.GetInvocationList()
|
||||
.Cast<OfferingsReceivedDelegate>())
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.OfferingsReceived))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -202,9 +195,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
|
|||
|
||||
private void OnPurchaseRequested(IMarketBoardPurchaseHandler purchaseHandler)
|
||||
{
|
||||
if (this.PurchaseRequested == null) return;
|
||||
|
||||
foreach (var action in this.PurchaseRequested.GetInvocationList().Cast<PurchaseRequestedDelegate>())
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.PurchaseRequested))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -219,9 +210,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar
|
|||
|
||||
private void OnTaxRatesReceived(IMarketTaxRates taxRates)
|
||||
{
|
||||
if (this.TaxRatesReceived == null) return;
|
||||
|
||||
foreach (var action in this.TaxRatesReceived.GetInvocationList().Cast<TaxRatesReceivedDelegate>())
|
||||
foreach (var action in Delegate.EnumerateInvocationList(this.TaxRatesReceived))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
|||
96
Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs
Normal file
96
Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
|
||||
namespace Dalamud.Game.NativeWrapper;
|
||||
|
||||
/// <summary>
|
||||
/// A readonly wrapper for AgentInterface.
|
||||
/// </summary>
|
||||
/// <param name="address">The address to the AgentInterface.</param>
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
|
||||
public readonly unsafe struct AgentInterfacePtr(nint address) : IEquatable<AgentInterfacePtr>
|
||||
{
|
||||
/// <summary>
|
||||
/// The address to the AgentInterface.
|
||||
/// </summary>
|
||||
[FieldOffset(0x00)]
|
||||
public readonly nint Address = address;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the underlying pointer is a nullptr.
|
||||
/// </summary>
|
||||
public readonly bool IsNull => this.Address == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the agents addon is visible.
|
||||
/// </summary>
|
||||
public readonly AtkUnitBasePtr Addon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsNull)
|
||||
return 0;
|
||||
|
||||
var raptureAtkUnitManager = RaptureAtkUnitManager.Instance();
|
||||
if (raptureAtkUnitManager == null)
|
||||
return 0;
|
||||
|
||||
return (nint)raptureAtkUnitManager->GetAddonById(this.AddonId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the agent is active.
|
||||
/// </summary>
|
||||
public readonly ushort AddonId => (ushort)(this.IsNull ? 0 : this.Struct->GetAddonId());
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the agent is active.
|
||||
/// </summary>
|
||||
public readonly bool IsAgentActive => !this.IsNull && this.Struct->IsAgentActive();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the agents addon is ready.
|
||||
/// </summary>
|
||||
public readonly bool IsAddonReady => !this.IsNull && this.Struct->IsAddonReady();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the agents addon is visible.
|
||||
/// </summary>
|
||||
public readonly bool IsAddonShown => !this.IsNull && this.Struct->IsAddonShown();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AgentInterface*.
|
||||
/// </summary>
|
||||
/// <remarks> Internal use only. </remarks>
|
||||
internal readonly AgentInterface* Struct => (AgentInterface*)this.Address;
|
||||
|
||||
public static implicit operator nint(AgentInterfacePtr wrapper) => wrapper.Address;
|
||||
|
||||
public static implicit operator AgentInterfacePtr(nint address) => new(address);
|
||||
|
||||
public static implicit operator AgentInterfacePtr(void* ptr) => new((nint)ptr);
|
||||
|
||||
public static bool operator ==(AgentInterfacePtr left, AgentInterfacePtr right) => left.Address == right.Address;
|
||||
|
||||
public static bool operator !=(AgentInterfacePtr left, AgentInterfacePtr right) => left.Address != right.Address;
|
||||
|
||||
/// <summary>
|
||||
/// Focuses the AtkUnitBase.
|
||||
/// </summary>
|
||||
/// <returns> <c>true</c> when the addon was focused, <c>false</c> otherwise. </returns>
|
||||
public readonly bool FocusAddon() => this.IsNull && this.Struct->FocusAddon();
|
||||
|
||||
/// <summary>Determines whether the specified AgentInterfacePtr is equal to the current AgentInterfacePtr.</summary>
|
||||
/// <param name="other">The AgentInterfacePtr to compare with the current AgentInterfacePtr.</param>
|
||||
/// <returns><c>true</c> if the specified AgentInterfacePtr is equal to the current AgentInterfacePtr; otherwise, <c>false</c>.</returns>
|
||||
public readonly bool Equals(AgentInterfacePtr other) => this.Address == other.Address;
|
||||
|
||||
/// <inheritdoc cref="object.Equals(object?)"/>
|
||||
public override readonly bool Equals(object obj) => obj is AgentInterfacePtr wrapper && this.Equals(wrapper);
|
||||
|
||||
/// <inheritdoc cref="object.GetHashCode()"/>
|
||||
public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode();
|
||||
}
|
||||
171
Dalamud/Game/NativeWrapper/AtkUnitBasePtr.cs
Normal file
171
Dalamud/Game/NativeWrapper/AtkUnitBasePtr.cs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using FFXIVClientStructs.Interop;
|
||||
|
||||
namespace Dalamud.Game.NativeWrapper;
|
||||
|
||||
/// <summary>
|
||||
/// A readonly wrapper for AtkUnitBase.
|
||||
/// </summary>
|
||||
/// <param name="address">The address to the AtkUnitBase.</param>
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
|
||||
public readonly unsafe struct AtkUnitBasePtr(nint address) : IEquatable<AtkUnitBasePtr>
|
||||
{
|
||||
/// <summary>
|
||||
/// The address to the AtkUnitBase.
|
||||
/// </summary>
|
||||
[FieldOffset(0x00)]
|
||||
public readonly nint Address = address;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the underlying pointer is a nullptr.
|
||||
/// </summary>
|
||||
public readonly bool IsNull => this.Address == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the OnSetup function has been called.
|
||||
/// </summary>
|
||||
public readonly bool IsReady => !this.IsNull && this.Struct->IsReady;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the AtkUnitBase is visible.
|
||||
/// </summary>
|
||||
public readonly bool IsVisible => !this.IsNull && this.Struct->IsVisible;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
public readonly string Name => this.IsNull ? string.Empty : this.Struct->NameString;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id.
|
||||
/// </summary>
|
||||
public readonly ushort Id => this.IsNull ? (ushort)0 : this.Struct->Id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent id.
|
||||
/// </summary>
|
||||
public readonly ushort ParentId => this.IsNull ? (ushort)0 : this.Struct->ParentId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the host id.
|
||||
/// </summary>
|
||||
public readonly ushort HostId => this.IsNull ? (ushort)0 : this.Struct->HostId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scale.
|
||||
/// </summary>
|
||||
public readonly float Scale => this.IsNull ? 0f : this.Struct->Scale;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the x-position.
|
||||
/// </summary>
|
||||
public readonly short X => this.IsNull ? (short)0 : this.Struct->X;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the y-position.
|
||||
/// </summary>
|
||||
public readonly short Y => this.IsNull ? (short)0 : this.Struct->Y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width.
|
||||
/// </summary>
|
||||
public readonly float Width => this.IsNull ? 0f : this.Struct->GetScaledWidth(false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height.
|
||||
/// </summary>
|
||||
public readonly float Height => this.IsNull ? 0f : this.Struct->GetScaledHeight(false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaled width.
|
||||
/// </summary>
|
||||
public readonly float ScaledWidth => this.IsNull ? 0f : this.Struct->GetScaledWidth(true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaled height.
|
||||
/// </summary>
|
||||
public readonly float ScaledHeight => this.IsNull ? 0f : this.Struct->GetScaledHeight(true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position.
|
||||
/// </summary>
|
||||
public readonly Vector2 Position => new(this.X, this.Y);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size.
|
||||
/// </summary>
|
||||
public readonly Vector2 Size => new(this.Width, this.Height);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scaled size.
|
||||
/// </summary>
|
||||
public readonly Vector2 ScaledSize => new(this.ScaledWidth, this.ScaledHeight);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of <see cref="AtkValue"/> entries.
|
||||
/// </summary>
|
||||
public readonly int AtkValuesCount => this.Struct->AtkValuesCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable collection of <see cref="AtkValuePtr"/> of the addons current AtkValues.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="IEnumerable{T}"/> of <see cref="AtkValuePtr"/> corresponding to the addons AtkValues.
|
||||
/// </returns>
|
||||
public IEnumerable<AtkValuePtr> AtkValues
|
||||
{
|
||||
get
|
||||
{
|
||||
for (var i = 0; i < this.AtkValuesCount; i++)
|
||||
{
|
||||
AtkValuePtr ptr;
|
||||
unsafe
|
||||
{
|
||||
ptr = new AtkValuePtr((nint)this.Struct->AtkValuesSpan.GetPointer(i));
|
||||
}
|
||||
|
||||
yield return ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AtkUnitBase*.
|
||||
/// </summary>
|
||||
/// <remarks> Internal use only. </remarks>
|
||||
internal readonly AtkUnitBase* Struct => (AtkUnitBase*)this.Address;
|
||||
|
||||
public static implicit operator nint(AtkUnitBasePtr wrapper) => wrapper.Address;
|
||||
|
||||
public static implicit operator AtkUnitBasePtr(nint address) => new(address);
|
||||
|
||||
public static implicit operator AtkUnitBasePtr(void* ptr) => new((nint)ptr);
|
||||
|
||||
public static bool operator ==(AtkUnitBasePtr left, AtkUnitBasePtr right) => left.Address == right.Address;
|
||||
|
||||
public static bool operator !=(AtkUnitBasePtr left, AtkUnitBasePtr right) => left.Address != right.Address;
|
||||
|
||||
/// <summary>
|
||||
/// Focuses the AtkUnitBase.
|
||||
/// </summary>
|
||||
public readonly void Focus()
|
||||
{
|
||||
if (!this.IsNull)
|
||||
this.Struct->Focus();
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified AtkUnitBasePtr is equal to the current AtkUnitBasePtr.</summary>
|
||||
/// <param name="other">The AtkUnitBasePtr to compare with the current AtkUnitBasePtr.</param>
|
||||
/// <returns><c>true</c> if the specified AtkUnitBasePtr is equal to the current AtkUnitBasePtr; otherwise, <c>false</c>.</returns>
|
||||
public readonly bool Equals(AtkUnitBasePtr other) => this.Address == other.Address;
|
||||
|
||||
/// <inheritdoc cref="object.Equals(object?)"/>
|
||||
public override readonly bool Equals(object obj) => obj is AtkUnitBasePtr wrapper && this.Equals(wrapper);
|
||||
|
||||
/// <inheritdoc cref="object.GetHashCode()"/>
|
||||
public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode();
|
||||
}
|
||||
113
Dalamud/Game/NativeWrapper/AtkValuePtr.cs
Normal file
113
Dalamud/Game/NativeWrapper/AtkValuePtr.cs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace Dalamud.Game.NativeWrapper;
|
||||
|
||||
/// <summary>
|
||||
/// A readonly wrapper for AtkValue.
|
||||
/// </summary>
|
||||
/// <param name="address">The address to the AtkValue.</param>
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
|
||||
public readonly unsafe struct AtkValuePtr(nint address) : IEquatable<AtkValuePtr>
|
||||
{
|
||||
/// <summary>
|
||||
/// The address to the AtkValue.
|
||||
/// </summary>
|
||||
[FieldOffset(0x00)]
|
||||
public readonly nint Address = address;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the underlying pointer is a nullptr.
|
||||
/// </summary>
|
||||
public readonly bool IsNull => this.Address == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value type.
|
||||
/// </summary>
|
||||
public readonly AtkValueType ValueType => (AtkValueType)this.Struct->Type;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AtkValue*.
|
||||
/// </summary>
|
||||
/// <remarks> Internal use only. </remarks>
|
||||
internal readonly AtkValue* Struct => (AtkValue*)this.Address;
|
||||
|
||||
public static implicit operator nint(AtkValuePtr wrapper) => wrapper.Address;
|
||||
|
||||
public static implicit operator AtkValuePtr(nint address) => new(address);
|
||||
|
||||
public static implicit operator AtkValuePtr(void* ptr) => new((nint)ptr);
|
||||
|
||||
public static bool operator ==(AtkValuePtr left, AtkValuePtr right) => left.Address == right.Address;
|
||||
|
||||
public static bool operator !=(AtkValuePtr left, AtkValuePtr right) => left.Address != right.Address;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the underlying <see cref="AtkValue"/> as a boxed object, based on its <see cref="AtkValueType"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The boxed value represented by this <see cref="AtkValuePtr"/>, or <c>null</c> if the value is null or undefined.
|
||||
/// </returns>
|
||||
/// <exception cref="NotImplementedException">
|
||||
/// Thrown if the value type is not currently handled by this implementation.
|
||||
/// </exception>
|
||||
public unsafe object? GetValue()
|
||||
{
|
||||
if (this.Struct == null)
|
||||
return null;
|
||||
|
||||
return this.ValueType switch
|
||||
{
|
||||
AtkValueType.Undefined or AtkValueType.Null => null,
|
||||
AtkValueType.Bool => this.Struct->Bool,
|
||||
AtkValueType.Int => this.Struct->Int,
|
||||
AtkValueType.Int64 => this.Struct->Int64,
|
||||
AtkValueType.UInt => this.Struct->UInt,
|
||||
AtkValueType.UInt64 => this.Struct->UInt64,
|
||||
AtkValueType.Float => this.Struct->Float,
|
||||
AtkValueType.String or AtkValueType.String8 or AtkValueType.ManagedString => this.Struct->String.HasValue ? this.Struct->String.AsReadOnlySeString() : default,
|
||||
AtkValueType.WideString => this.Struct->WideString == null ? string.Empty : new string(this.Struct->WideString),
|
||||
AtkValueType.Pointer => (nint)this.Struct->Pointer,
|
||||
_ => throw new NotImplementedException($"AtkValueType {this.ValueType} is currently not supported"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the value as a strongly-typed object if the underlying type matches.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The expected value type to extract.</typeparam>
|
||||
/// <param name="result">
|
||||
/// When this method returns <c>true</c>, contains the extracted value of type <typeparamref name="T"/>.
|
||||
/// Otherwise, contains the default value of type <typeparamref name="T"/>.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the value was successfully extracted and matched <typeparamref name="T"/>; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public unsafe bool TryGet<T>([NotNullWhen(true)] out T? result) where T : struct
|
||||
{
|
||||
object? value = this.GetValue();
|
||||
if (value is T typed)
|
||||
{
|
||||
result = typed;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified AtkValuePtr is equal to the current AtkValuePtr.</summary>
|
||||
/// <param name="other">The AtkValuePtr to compare with the current AtkValuePtr.</param>
|
||||
/// <returns><c>true</c> if the specified AtkValuePtr is equal to the current AtkValuePtr; otherwise, <c>false</c>.</returns>
|
||||
public readonly bool Equals(AtkValuePtr other) => this.Address == other.Address || this.Struct->EqualTo(other.Struct);
|
||||
|
||||
/// <inheritdoc cref="object.Equals(object?)"/>
|
||||
public override readonly bool Equals(object obj) => obj is AtkValuePtr wrapper && this.Equals(wrapper);
|
||||
|
||||
/// <inheritdoc cref="object.GetHashCode()"/>
|
||||
public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode();
|
||||
}
|
||||
87
Dalamud/Game/NativeWrapper/AtkValueType.cs
Normal file
87
Dalamud/Game/NativeWrapper/AtkValueType.cs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
namespace Dalamud.Game.NativeWrapper;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the data type of the AtkValue.
|
||||
/// </summary>
|
||||
public enum AtkValueType
|
||||
{
|
||||
/// <summary>
|
||||
/// The value is undefined or invalid.
|
||||
/// </summary>
|
||||
Undefined = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The value is null.
|
||||
/// </summary>
|
||||
Null = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a boolean.
|
||||
/// </summary>
|
||||
Bool = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a 32-bit signed integer.
|
||||
/// </summary>
|
||||
Int = 0x3,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a 64-bit signed integer.
|
||||
/// </summary>
|
||||
Int64 = 0x4,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a 32-bit unsigned integer.
|
||||
/// </summary>
|
||||
UInt = 0x5,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a 64-bit unsigned integer.
|
||||
/// </summary>
|
||||
UInt64 = 0x6,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a 32-bit floating-point number.
|
||||
/// </summary>
|
||||
Float = 0x7,
|
||||
|
||||
/// <summary>
|
||||
/// The value points to a null-terminated 8-bit character string (ASCII or UTF-8).
|
||||
/// </summary>
|
||||
String = 0x8,
|
||||
|
||||
/// <summary>
|
||||
/// The value points to a null-terminated 16-bit character string (UTF-16 / wide string).
|
||||
/// </summary>
|
||||
WideString = 0x9,
|
||||
|
||||
/// <summary>
|
||||
/// The value points to a constant null-terminated 8-bit character string (const char*).
|
||||
/// </summary>
|
||||
String8 = 0xA,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a vector.
|
||||
/// </summary>
|
||||
Vector = 0xB,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a pointer.
|
||||
/// </summary>
|
||||
Pointer = 0xC,
|
||||
|
||||
/// <summary>
|
||||
/// The value is pointing to an array of AtkValue entries.
|
||||
/// </summary>
|
||||
AtkValues = 0xD,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a managed string. See <see cref="String"/>.
|
||||
/// </summary>
|
||||
ManagedString = 0x28,
|
||||
|
||||
/// <summary>
|
||||
/// The value is a managed vector. See <see cref="Vector"/>.
|
||||
/// </summary>
|
||||
ManagedVector = 0x2B,
|
||||
}
|
||||
51
Dalamud/Game/NativeWrapper/UIModulePtr.cs
Normal file
51
Dalamud/Game/NativeWrapper/UIModulePtr.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
|
||||
namespace Dalamud.Game.NativeWrapper;
|
||||
|
||||
/// <summary>
|
||||
/// A readonly wrapper for UIModule.
|
||||
/// </summary>
|
||||
/// <param name="address">The address to the UIModule.</param>
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
|
||||
public readonly unsafe struct UIModulePtr(nint address) : IEquatable<UIModulePtr>
|
||||
{
|
||||
/// <summary>
|
||||
/// The address to the UIModule.
|
||||
/// </summary>
|
||||
[FieldOffset(0x00)]
|
||||
public readonly nint Address = address;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the underlying pointer is a nullptr.
|
||||
/// </summary>
|
||||
public readonly bool IsNull => this.Address == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UIModule*.
|
||||
/// </summary>
|
||||
/// <remarks> Internal use only. </remarks>
|
||||
internal readonly UIModule* Struct => (UIModule*)this.Address;
|
||||
|
||||
public static implicit operator nint(UIModulePtr wrapper) => wrapper.Address;
|
||||
|
||||
public static implicit operator UIModulePtr(nint address) => new(address);
|
||||
|
||||
public static implicit operator UIModulePtr(void* ptr) => new((nint)ptr);
|
||||
|
||||
public static bool operator ==(UIModulePtr left, UIModulePtr right) => left.Address == right.Address;
|
||||
|
||||
public static bool operator !=(UIModulePtr left, UIModulePtr right) => left.Address != right.Address;
|
||||
|
||||
/// <summary>Determines whether the specified UIModulePtr is equal to the current UIModulePtr.</summary>
|
||||
/// <param name="other">The UIModulePtr to compare with the current UIModulePtr.</param>
|
||||
/// <returns><c>true</c> if the specified UIModulePtr is equal to the current UIModulePtr; otherwise, <c>false</c>.</returns>
|
||||
public readonly bool Equals(UIModulePtr other) => this.Address == other.Address;
|
||||
|
||||
/// <inheritdoc cref="object.Equals(object?)"/>
|
||||
public override readonly bool Equals(object obj) => obj is UIModulePtr wrapper && this.Equals(wrapper);
|
||||
|
||||
/// <inheritdoc cref="object.GetHashCode()"/>
|
||||
public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode();
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ namespace Dalamud.Game.Network;
|
|||
/// This class handles interacting with game network events.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetwork
|
||||
internal sealed unsafe class GameNetwork : IInternalDisposableService
|
||||
{
|
||||
private readonly GameNetworkAddressResolver address;
|
||||
private readonly Hook<PacketDispatcher.Delegates.OnReceivePacket> processZonePacketDownHook;
|
||||
|
|
@ -28,7 +28,7 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw
|
|||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private unsafe GameNetwork(TargetSigScanner sigScanner)
|
||||
{
|
||||
|
|
@ -51,11 +51,23 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw
|
|||
this.processZonePacketUpHook.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The delegate type of a network message event.
|
||||
/// </summary>
|
||||
/// <param name="dataPtr">The pointer to the raw data.</param>
|
||||
/// <param name="opCode">The operation ID code.</param>
|
||||
/// <param name="sourceActorId">The source actor ID.</param>
|
||||
/// <param name="targetActorId">The taret actor ID.</param>
|
||||
/// <param name="direction">The direction of the packed.</param>
|
||||
public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage;
|
||||
/// <summary>
|
||||
/// Event that is called when a network message is sent/received.
|
||||
/// </summary>
|
||||
public event OnNetworkMessageDelegate? NetworkMessage;
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
|
|
@ -71,32 +83,36 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw
|
|||
// Go back 0x10 to get back to the start of the packet header
|
||||
dataPtr -= 0x10;
|
||||
|
||||
try
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.NetworkMessage))
|
||||
{
|
||||
// Call events
|
||||
this.NetworkMessage?.Invoke(dataPtr + 0x20, (ushort)Marshal.ReadInt16(dataPtr, 0x12), 0, targetId, NetworkMessageDirection.ZoneDown);
|
||||
|
||||
this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string header;
|
||||
try
|
||||
{
|
||||
var data = new byte[32];
|
||||
Marshal.Copy(dataPtr, data, 0, 32);
|
||||
header = BitConverter.ToString(data);
|
||||
d.Invoke(
|
||||
dataPtr + 0x20,
|
||||
(ushort)Marshal.ReadInt16(dataPtr, 0x12),
|
||||
0,
|
||||
targetId,
|
||||
NetworkMessageDirection.ZoneDown);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
header = "failed";
|
||||
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 ProcessZonePacketDown hook. Header: " + header);
|
||||
}
|
||||
|
||||
Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header);
|
||||
|
||||
this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10);
|
||||
}
|
||||
|
||||
this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10);
|
||||
this.hitchDetectorDown.Stop();
|
||||
}
|
||||
|
||||
|
|
@ -132,39 +148,3 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw
|
|||
return this.processZonePacketUpHook.Original(a1, dataPtr, a3, a4);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plugin-scoped version of a AddonLifecycle service.
|
||||
/// </summary>
|
||||
[PluginInterface]
|
||||
[ServiceManager.ScopedService]
|
||||
#pragma warning disable SA1015
|
||||
[ResolveVia<IGameNetwork>]
|
||||
#pragma warning restore SA1015
|
||||
internal class GameNetworkPluginScoped : IInternalDisposableService, IGameNetwork
|
||||
{
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly GameNetwork gameNetworkService = Service<GameNetwork>.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GameNetworkPluginScoped"/> class.
|
||||
/// </summary>
|
||||
internal GameNetworkPluginScoped()
|
||||
{
|
||||
this.gameNetworkService.NetworkMessage += this.NetworkMessageForward;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage;
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
this.gameNetworkService.NetworkMessage -= this.NetworkMessageForward;
|
||||
|
||||
this.NetworkMessage = null;
|
||||
}
|
||||
|
||||
private void NetworkMessageForward(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction)
|
||||
=> this.NetworkMessage?.Invoke(dataPtr, opCode, sourceActorId, targetActorId, direction);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,4 +55,37 @@ internal sealed class WinSockHandlers : IInternalDisposableService
|
|||
|
||||
return socket;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Native ws2_32 functions.
|
||||
/// </summary>
|
||||
private static class NativeFunctions
|
||||
{
|
||||
/// <summary>
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-setsockopt.
|
||||
/// The setsockopt function sets a socket option.
|
||||
/// </summary>
|
||||
/// <param name="socket">
|
||||
/// A descriptor that identifies a socket.
|
||||
/// </param>
|
||||
/// <param name="level">
|
||||
/// The level at which the option is defined (for example, SOL_SOCKET).
|
||||
/// </param>
|
||||
/// <param name="optName">
|
||||
/// The socket option for which the value is to be set (for example, SO_BROADCAST). The optname parameter must be a
|
||||
/// socket option defined within the specified level, or behavior is undefined.
|
||||
/// </param>
|
||||
/// <param name="optVal">
|
||||
/// A pointer to the buffer in which the value for the requested option is specified.
|
||||
/// </param>
|
||||
/// <param name="optLen">
|
||||
/// The size, in bytes, of the buffer pointed to by the optval parameter.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If no error occurs, setsockopt returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error
|
||||
/// code can be retrieved by calling WSAGetLastError.
|
||||
/// </returns>
|
||||
[DllImport("ws2_32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "setsockopt")]
|
||||
public static extern int SetSockOpt(IntPtr socket, SocketOptionLevel level, SocketOptionName optName, ref IntPtr optVal, int optLen);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using Dalamud.Utility;
|
|||
|
||||
using Lumina.Extensions;
|
||||
|
||||
using ItemKind = Dalamud.Game.Text.SeStringHandling.Payloads.ItemPayload.ItemKind;
|
||||
using LSheets = Lumina.Excel.Sheets;
|
||||
|
||||
namespace Dalamud.Game.Text.Evaluator.Internal;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ using Dalamud.Plugin.Services;
|
|||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||
|
|
@ -36,11 +35,11 @@ using Lumina.Text.Payloads;
|
|||
using Lumina.Text.ReadOnly;
|
||||
|
||||
using AddonSheet = Lumina.Excel.Sheets.Addon;
|
||||
using PlayerState = FFXIVClientStructs.FFXIV.Client.Game.UI.PlayerState;
|
||||
using StatusSheet = Lumina.Excel.Sheets.Status;
|
||||
|
||||
namespace Dalamud.Game.Text.Evaluator;
|
||||
|
||||
#pragma warning disable SeStringEvaluator
|
||||
|
||||
/// <summary>
|
||||
/// Evaluator for SeStrings.
|
||||
/// </summary>
|
||||
|
|
@ -113,6 +112,15 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ReadOnlySeString EvaluateMacroString(
|
||||
string macroString,
|
||||
Span<SeStringParameter> localParameters = default,
|
||||
ClientLanguage? language = null)
|
||||
{
|
||||
return this.Evaluate(ReadOnlySeString.FromMacroString(macroString).AsSpan(), localParameters, language);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ReadOnlySeString EvaluateFromAddon(
|
||||
uint addonId,
|
||||
|
|
@ -727,84 +735,186 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator
|
|||
this.TryResolveUInt(in context, enu.Current, out eColParamValue);
|
||||
|
||||
var resolvedSheetName = this.Evaluate(eSheetNameStr, context.LocalParameters, context.Language).ExtractText();
|
||||
|
||||
this.sheetRedirectResolver.Resolve(ref resolvedSheetName, ref eRowIdValue, ref eColIndexValue);
|
||||
var originalRowIdValue = eRowIdValue;
|
||||
var flags = this.sheetRedirectResolver.Resolve(ref resolvedSheetName, ref eRowIdValue, ref eColIndexValue);
|
||||
|
||||
if (string.IsNullOrEmpty(resolvedSheetName))
|
||||
return false;
|
||||
|
||||
if (!this.dataManager.Excel.SheetNames.Contains(resolvedSheetName))
|
||||
var text = this.FormatSheetValue(context.Language, resolvedSheetName, eRowIdValue, eColIndexValue, eColParamValue);
|
||||
if (text.IsEmpty)
|
||||
return false;
|
||||
|
||||
if (!this.dataManager.GetExcelSheet<RawRow>(context.Language, resolvedSheetName)
|
||||
.TryGetRow(eRowIdValue, out var row))
|
||||
return false;
|
||||
this.AddSheetRedirectItemDecoration(context, ref text, flags, eRowIdValue);
|
||||
|
||||
if (eColIndexValue >= row.Columns.Count)
|
||||
return false;
|
||||
if (resolvedSheetName != "DescriptionString")
|
||||
eColParamValue = originalRowIdValue;
|
||||
|
||||
var column = row.Columns[(int)eColIndexValue];
|
||||
switch (column.Type)
|
||||
// Note: The link marker symbol is added by RaptureLogMessage, probably somewhere in it's Update function.
|
||||
// It is not part of this generated link.
|
||||
this.CreateSheetLink(context, resolvedSheetName, text, eRowIdValue, eColParamValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ReadOnlySeString FormatSheetValue(ClientLanguage language, string sheetName, uint rowId, uint colIndex, uint colParam)
|
||||
{
|
||||
if (!this.dataManager.Excel.SheetNames.Contains(sheetName))
|
||||
return default;
|
||||
|
||||
if (!this.dataManager.GetExcelSheet<RawRow>(language, sheetName)
|
||||
.TryGetRow(rowId, out var row))
|
||||
return default;
|
||||
|
||||
if (colIndex >= row.Columns.Count)
|
||||
return default;
|
||||
|
||||
var column = row.Columns[(int)colIndex];
|
||||
return column.Type switch
|
||||
{
|
||||
case ExcelColumnDataType.String:
|
||||
context.Builder.Append(this.Evaluate(row.ReadString(column.Offset), [eColParamValue], context.Language));
|
||||
return true;
|
||||
case ExcelColumnDataType.Bool:
|
||||
context.Builder.Append((row.ReadBool(column.Offset) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.Int8:
|
||||
context.Builder.Append(row.ReadInt8(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.UInt8:
|
||||
context.Builder.Append(row.ReadUInt8(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.Int16:
|
||||
context.Builder.Append(row.ReadInt16(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.UInt16:
|
||||
context.Builder.Append(row.ReadUInt16(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.Int32:
|
||||
context.Builder.Append(row.ReadInt32(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.UInt32:
|
||||
context.Builder.Append(row.ReadUInt32(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.Float32:
|
||||
context.Builder.Append(row.ReadFloat32(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.Int64:
|
||||
context.Builder.Append(row.ReadInt64(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.UInt64:
|
||||
context.Builder.Append(row.ReadUInt64(column.Offset).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.PackedBool0:
|
||||
context.Builder.Append((row.ReadPackedBool(column.Offset, 0) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.PackedBool1:
|
||||
context.Builder.Append((row.ReadPackedBool(column.Offset, 1) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.PackedBool2:
|
||||
context.Builder.Append((row.ReadPackedBool(column.Offset, 2) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.PackedBool3:
|
||||
context.Builder.Append((row.ReadPackedBool(column.Offset, 3) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.PackedBool4:
|
||||
context.Builder.Append((row.ReadPackedBool(column.Offset, 4) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.PackedBool5:
|
||||
context.Builder.Append((row.ReadPackedBool(column.Offset, 5) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.PackedBool6:
|
||||
context.Builder.Append((row.ReadPackedBool(column.Offset, 6) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
case ExcelColumnDataType.PackedBool7:
|
||||
context.Builder.Append((row.ReadPackedBool(column.Offset, 7) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture));
|
||||
return true;
|
||||
ExcelColumnDataType.String => this.Evaluate(row.ReadString(column.Offset), [colParam], language),
|
||||
ExcelColumnDataType.Bool => (row.ReadBool(column.Offset) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.Int8 => row.ReadInt8(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.UInt8 => row.ReadUInt8(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.Int16 => row.ReadInt16(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.UInt16 => row.ReadUInt16(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.Int32 => row.ReadInt32(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.UInt32 => row.ReadUInt32(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.Float32 => row.ReadFloat32(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.Int64 => row.ReadInt64(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.UInt64 => row.ReadUInt64(column.Offset).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.PackedBool0 => (row.ReadPackedBool(column.Offset, 0) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.PackedBool1 => (row.ReadPackedBool(column.Offset, 1) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.PackedBool2 => (row.ReadPackedBool(column.Offset, 2) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.PackedBool3 => (row.ReadPackedBool(column.Offset, 3) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.PackedBool4 => (row.ReadPackedBool(column.Offset, 4) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.PackedBool5 => (row.ReadPackedBool(column.Offset, 5) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.PackedBool6 => (row.ReadPackedBool(column.Offset, 6) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
ExcelColumnDataType.PackedBool7 => (row.ReadPackedBool(column.Offset, 7) ? 1u : 0).ToString("D", CultureInfo.InvariantCulture),
|
||||
_ => default,
|
||||
};
|
||||
}
|
||||
|
||||
private void AddSheetRedirectItemDecoration(in SeStringContext context, ref ReadOnlySeString text, SheetRedirectFlags flags, uint eRowIdValue)
|
||||
{
|
||||
if (!flags.HasFlag(SheetRedirectFlags.Item))
|
||||
return;
|
||||
|
||||
var rarity = 1u;
|
||||
var skipLink = false;
|
||||
|
||||
if (flags.HasFlag(SheetRedirectFlags.EventItem))
|
||||
{
|
||||
rarity = 8;
|
||||
skipLink = true;
|
||||
}
|
||||
|
||||
var itemId = eRowIdValue;
|
||||
|
||||
if (this.dataManager.GetExcelSheet<Item>(context.Language).TryGetRow(itemId, out var itemRow))
|
||||
{
|
||||
rarity = itemRow.Rarity;
|
||||
if (rarity == 0)
|
||||
rarity = 1;
|
||||
|
||||
if (itemRow.FilterGroup is 38 or 50)
|
||||
skipLink = true;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(SheetRedirectFlags.Collectible))
|
||||
{
|
||||
itemId += 500000;
|
||||
}
|
||||
else if (flags.HasFlag(SheetRedirectFlags.HighQuality))
|
||||
{
|
||||
itemId += 1000000;
|
||||
}
|
||||
|
||||
var sb = SeStringBuilder.SharedPool.Get();
|
||||
|
||||
sb.Append(this.EvaluateFromAddon(6, [rarity], context.Language));
|
||||
|
||||
if (!skipLink)
|
||||
sb.PushLink(LinkMacroPayloadType.Item, itemId, rarity, 0u); // arg3 = some LogMessage flag based on LogKind RowId? => "89 5C 24 20 E8 ?? ?? ?? ?? 48 8B 1F"
|
||||
|
||||
// there is code here for handling noun link markers (//), but i don't know why
|
||||
|
||||
sb.Append(text);
|
||||
|
||||
if (flags.HasFlag(SheetRedirectFlags.HighQuality)
|
||||
&& this.dataManager.GetExcelSheet<AddonSheet>(context.Language).TryGetRow(9, out var hqSymbol))
|
||||
{
|
||||
sb.Append(hqSymbol.Text);
|
||||
}
|
||||
else if (flags.HasFlag(SheetRedirectFlags.Collectible)
|
||||
&& this.dataManager.GetExcelSheet<AddonSheet>(context.Language).TryGetRow(150, out var collectibleSymbol))
|
||||
{
|
||||
sb.Append(collectibleSymbol.Text);
|
||||
}
|
||||
|
||||
if (!skipLink)
|
||||
sb.PopLink();
|
||||
|
||||
text = sb.ToReadOnlySeString();
|
||||
SeStringBuilder.SharedPool.Return(sb);
|
||||
}
|
||||
|
||||
private void CreateSheetLink(in SeStringContext context, string resolvedSheetName, ReadOnlySeString text, uint eRowIdValue, uint eColParamValue)
|
||||
{
|
||||
switch (resolvedSheetName)
|
||||
{
|
||||
case "Achievement":
|
||||
context.Builder.PushLink(LinkMacroPayloadType.Achievement, eRowIdValue, 0u, 0u, text.AsSpan());
|
||||
context.Builder.Append(text);
|
||||
context.Builder.PopLink();
|
||||
return;
|
||||
|
||||
case "HowTo":
|
||||
context.Builder.PushLink(LinkMacroPayloadType.HowTo, eRowIdValue, 0u, 0u, text.AsSpan());
|
||||
context.Builder.Append(text);
|
||||
context.Builder.PopLink();
|
||||
return;
|
||||
|
||||
case "Status" when this.dataManager.GetExcelSheet<StatusSheet>(context.Language).TryGetRow(eRowIdValue, out var statusRow):
|
||||
context.Builder.PushLink(LinkMacroPayloadType.Status, eRowIdValue, 0u, 0u, []);
|
||||
|
||||
switch (statusRow.StatusCategory)
|
||||
{
|
||||
case 1: context.Builder.Append(this.EvaluateFromAddon(376)); break; // buff symbol
|
||||
case 2: context.Builder.Append(this.EvaluateFromAddon(377)); break; // debuff symbol
|
||||
}
|
||||
|
||||
context.Builder.Append(text);
|
||||
context.Builder.PopLink();
|
||||
return;
|
||||
|
||||
case "AkatsukiNoteString":
|
||||
context.Builder.PushLink(LinkMacroPayloadType.AkatsukiNote, eColParamValue, 0u, 0u, text.AsSpan());
|
||||
context.Builder.Append(text);
|
||||
context.Builder.PopLink();
|
||||
return;
|
||||
|
||||
case "DescriptionString" when eColParamValue > 0:
|
||||
context.Builder.PushLink((LinkMacroPayloadType)11, eRowIdValue, eColParamValue, 0u, text.AsSpan());
|
||||
context.Builder.Append(text);
|
||||
context.Builder.PopLink();
|
||||
return;
|
||||
|
||||
case "WKSPioneeringTrailString":
|
||||
context.Builder.PushLink((LinkMacroPayloadType)12, eRowIdValue, eColParamValue, 0u, text.AsSpan());
|
||||
context.Builder.Append(text);
|
||||
context.Builder.PopLink();
|
||||
return;
|
||||
|
||||
case "MKDLore":
|
||||
context.Builder.PushLink((LinkMacroPayloadType)13, eRowIdValue, 0u, 0u, text.AsSpan());
|
||||
context.Builder.Append(text);
|
||||
context.Builder.PopLink();
|
||||
return;
|
||||
|
||||
default:
|
||||
return false;
|
||||
context.Builder.Append(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public class DalamudLinkPayload : Payload
|
|||
public override PayloadType Type => PayloadType.DalamudLink;
|
||||
|
||||
/// <summary>Gets the plugin command ID to be linked.</summary>
|
||||
public uint CommandId { get; internal set; }
|
||||
public Guid CommandId { get; internal set; }
|
||||
|
||||
/// <summary>Gets an optional extra integer value 1.</summary>
|
||||
public int Extra1 { get; internal set; }
|
||||
|
|
@ -40,7 +40,7 @@ public class DalamudLinkPayload : Payload
|
|||
var ssb = Lumina.Text.SeStringBuilder.SharedPool.Get();
|
||||
var res = ssb.BeginMacro(MacroCode.Link)
|
||||
.AppendIntExpression((int)EmbeddedInfoType.DalamudLink - 1)
|
||||
.AppendUIntExpression(this.CommandId)
|
||||
.AppendStringExpression(this.CommandId.ToString())
|
||||
.AppendIntExpression(this.Extra1)
|
||||
.AppendIntExpression(this.Extra2)
|
||||
.BeginStringExpression()
|
||||
|
|
@ -72,15 +72,15 @@ public class DalamudLinkPayload : Payload
|
|||
if (!pluginExpression.TryGetString(out var pluginString))
|
||||
return;
|
||||
|
||||
if (!commandIdExpression.TryGetUInt(out var commandId))
|
||||
if (!commandIdExpression.TryGetString(out var commandId))
|
||||
return;
|
||||
|
||||
this.Plugin = pluginString.ExtractText();
|
||||
this.CommandId = commandId;
|
||||
this.CommandId = Guid.Parse(commandId.ExtractText());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!commandIdExpression.TryGetUInt(out var commandId))
|
||||
if (!commandIdExpression.TryGetString(out var commandId))
|
||||
return;
|
||||
|
||||
if (!extra1Expression.TryGetInt(out var extra1))
|
||||
|
|
@ -102,7 +102,7 @@ public class DalamudLinkPayload : Payload
|
|||
return;
|
||||
}
|
||||
|
||||
this.CommandId = commandId;
|
||||
this.CommandId = Guid.Parse(commandId.ExtractText());
|
||||
this.Extra1 = extra1;
|
||||
this.Extra2 = extra2;
|
||||
this.Plugin = extraData[0];
|
||||
|
|
|
|||
|
|
@ -72,33 +72,6 @@ public class ItemPayload : Payload
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kinds of items that can be fetched from this payload.
|
||||
/// </summary>
|
||||
[Api13ToDo("Move this out of ItemPayload. It's used in other classes too.")]
|
||||
public enum ItemKind : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal items.
|
||||
/// </summary>
|
||||
Normal,
|
||||
|
||||
/// <summary>
|
||||
/// Collectible Items.
|
||||
/// </summary>
|
||||
Collectible = 500_000,
|
||||
|
||||
/// <summary>
|
||||
/// High-Quality items.
|
||||
/// </summary>
|
||||
Hq = 1_000_000,
|
||||
|
||||
/// <summary>
|
||||
/// Event/Key items.
|
||||
/// </summary>
|
||||
EventItem = 2_000_000,
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override PayloadType Type => PayloadType.Item;
|
||||
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ public class SeString
|
|||
/// <param name="displayNameOverride">An optional name override to display, instead of the actual item name.</param>
|
||||
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
|
||||
public static SeString CreateItemLink(uint itemId, bool isHq, string? displayNameOverride = null) =>
|
||||
CreateItemLink(itemId, isHq ? ItemPayload.ItemKind.Hq : ItemPayload.ItemKind.Normal, displayNameOverride);
|
||||
CreateItemLink(itemId, isHq ? ItemKind.Hq : ItemKind.Normal, displayNameOverride);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log.
|
||||
|
|
@ -190,7 +190,7 @@ public class SeString
|
|||
/// <param name="kind">The kind of item to link.</param>
|
||||
/// <param name="displayNameOverride">An optional name override to display, instead of the actual item name.</param>
|
||||
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
|
||||
public static SeString CreateItemLink(uint itemId, ItemPayload.ItemKind kind = ItemPayload.ItemKind.Normal, string? displayNameOverride = null)
|
||||
public static SeString CreateItemLink(uint itemId, ItemKind kind = ItemKind.Normal, string? displayNameOverride = null)
|
||||
{
|
||||
var clientState = Service<ClientState.ClientState>.Get();
|
||||
var seStringEvaluator = Service<SeStringEvaluator>.Get();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Utility;
|
||||
|
||||
namespace Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
|
|
@ -126,7 +127,7 @@ public class SeStringBuilder
|
|||
/// <param name="kind">Kind of item to encode.</param>
|
||||
/// <param name="itemNameOverride">Override for the item's name.</param>
|
||||
/// <returns>The current builder.</returns>
|
||||
public SeStringBuilder AddItemLink(uint itemId, ItemPayload.ItemKind kind = ItemPayload.ItemKind.Normal, string? itemNameOverride = null) =>
|
||||
public SeStringBuilder AddItemLink(uint itemId, ItemKind kind = ItemKind.Normal, string? itemNameOverride = null) =>
|
||||
this.Append(SeString.CreateItemLink(itemId, kind, itemNameOverride));
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -201,11 +201,11 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate
|
|||
if (EnvironmentConfiguration.DalamudForceMinHook)
|
||||
useMinHook = true;
|
||||
|
||||
var moduleHandle = NativeFunctions.GetModuleHandleW(moduleName);
|
||||
if (moduleHandle == IntPtr.Zero)
|
||||
using var moduleHandle = Windows.Win32.PInvoke.GetModuleHandle(moduleName);
|
||||
if (moduleHandle.IsInvalid)
|
||||
throw new Exception($"Could not get a handle to module {moduleName}");
|
||||
|
||||
var procAddress = NativeFunctions.GetProcAddress(moduleHandle, exportName);
|
||||
var procAddress = (nint)Windows.Win32.PInvoke.GetProcAddress(moduleHandle, exportName);
|
||||
if (procAddress == IntPtr.Zero)
|
||||
throw new Exception($"Could not get the address of {moduleName}::{exportName}");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Memory;
|
||||
using JetBrains.Annotations;
|
||||
using Windows.Win32.System.Memory;
|
||||
|
||||
using Win32Exception = System.ComponentModel.Win32Exception;
|
||||
|
||||
namespace Dalamud.Hooking.Internal;
|
||||
|
||||
|
|
@ -12,7 +13,7 @@ namespace Dalamud.Hooking.Internal;
|
|||
/// Manages a hook with MinHook.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Delegate type to represents a function prototype. This must be the same prototype as original function do.</typeparam>
|
||||
internal class FunctionPointerVariableHook<T> : Hook<T>
|
||||
internal unsafe class FunctionPointerVariableHook<T> : Hook<T>
|
||||
where T : Delegate
|
||||
{
|
||||
private readonly nint pfnDetour;
|
||||
|
|
@ -55,11 +56,11 @@ internal class FunctionPointerVariableHook<T> : Hook<T>
|
|||
// Note: WINE seemingly tries to clean up all heap allocations on process exit.
|
||||
// We want our allocation to be kept there forever, until no running thread remains.
|
||||
// Therefore we're using VirtualAlloc instead of HeapCreate/HeapAlloc.
|
||||
var pfnThunkBytes = (byte*)NativeFunctions.VirtualAlloc(
|
||||
0,
|
||||
var pfnThunkBytes = (byte*)Windows.Win32.PInvoke.VirtualAlloc(
|
||||
null,
|
||||
12,
|
||||
NativeFunctions.AllocationType.Reserve | NativeFunctions.AllocationType.Commit,
|
||||
MemoryProtection.ExecuteReadWrite);
|
||||
VIRTUAL_ALLOCATION_TYPE.MEM_RESERVE | VIRTUAL_ALLOCATION_TYPE.MEM_COMMIT,
|
||||
PAGE_PROTECTION_FLAGS.PAGE_EXECUTE_READWRITE);
|
||||
if (pfnThunkBytes == null)
|
||||
{
|
||||
throw new OutOfMemoryException("Failed to allocate memory for import hooks.");
|
||||
|
|
@ -78,10 +79,10 @@ internal class FunctionPointerVariableHook<T> : Hook<T>
|
|||
|
||||
this.ppfnThunkJumpTarget = this.pfnThunk + 2;
|
||||
|
||||
if (!NativeFunctions.VirtualProtect(
|
||||
this.Address,
|
||||
if (!Windows.Win32.PInvoke.VirtualProtect(
|
||||
this.Address.ToPointer(),
|
||||
(UIntPtr)Marshal.SizeOf<IntPtr>(),
|
||||
MemoryProtection.ExecuteReadWrite,
|
||||
PAGE_PROTECTION_FLAGS.PAGE_EXECUTE_READWRITE,
|
||||
out var oldProtect))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
|
|
@ -93,7 +94,7 @@ internal class FunctionPointerVariableHook<T> : Hook<T>
|
|||
Marshal.WriteIntPtr(this.Address, this.pfnThunk);
|
||||
|
||||
// This really should not fail, but then even if it does, whatever.
|
||||
NativeFunctions.VirtualProtect(this.Address, (UIntPtr)Marshal.SizeOf<IntPtr>(), oldProtect, out _);
|
||||
Windows.Win32.PInvoke.VirtualProtect(this.Address.ToPointer(), (UIntPtr)Marshal.SizeOf<IntPtr>(), oldProtect, out _);
|
||||
|
||||
// Add afterwards, so the hookIdent starts at 0.
|
||||
indexList.Add(this);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ internal sealed class WndProcHookManager : IInternalDisposableService
|
|||
this.dispatchMessageWHook.Enable();
|
||||
|
||||
// Capture the game main window handle,
|
||||
// so that no guarantees would have to be made on the service dispose order.
|
||||
// so that no guarantees would have to be made on the service dispose order.
|
||||
Service<InterfaceManager.InterfaceManagerWithScene>
|
||||
.GetAsync()
|
||||
.ContinueWith(r => this.mainWindowHwnd = (HWND)r.Result.Manager.GameWindowHandle);
|
||||
|
|
@ -82,13 +82,16 @@ internal sealed class WndProcHookManager : IInternalDisposableService
|
|||
/// <param name="args">The arguments.</param>
|
||||
internal void InvokePreWndProc(WndProcEventArgs args)
|
||||
{
|
||||
try
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.PreWndProc))
|
||||
{
|
||||
this.PreWndProc?.Invoke(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, $"{nameof(this.PreWndProc)} error");
|
||||
try
|
||||
{
|
||||
d(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, $"{nameof(this.PreWndProc)} error calling {d.Method.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,13 +101,16 @@ internal sealed class WndProcHookManager : IInternalDisposableService
|
|||
/// <param name="args">The arguments.</param>
|
||||
internal void InvokePostWndProc(WndProcEventArgs args)
|
||||
{
|
||||
try
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.PostWndProc))
|
||||
{
|
||||
this.PostWndProc?.Invoke(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, $"{nameof(this.PostWndProc)} error");
|
||||
try
|
||||
{
|
||||
d(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, $"{nameof(this.PostWndProc)} error calling {d.Method.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -45,7 +43,7 @@ public static partial class ImGuiComponents
|
|||
{
|
||||
using (ImRaii.TextWrapPos(ImGui.GetFontSize() * 35.0f))
|
||||
{
|
||||
ImGui.TextUnformatted(helpText);
|
||||
ImGui.Text(helpText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
using System.Numerics;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using ImGuiNET;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
|
|
@ -12,6 +12,6 @@ public static partial class ImGuiComponents
|
|||
/// </summary>
|
||||
public static void Test()
|
||||
{
|
||||
ImGui.Text("You are viewing the test component. The test was a success.");
|
||||
ImGui.Text("You are viewing the test component. The test was a success."u8);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -30,7 +29,7 @@ public static partial class ImGuiComponents
|
|||
{
|
||||
using (ImRaii.Tooltip())
|
||||
{
|
||||
ImGui.TextUnformatted(hint);
|
||||
ImGui.Text(hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using System.Numerics;
|
||||
|
||||
using ImGuiNET;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
|
||||
namespace Dalamud.Interface.Components;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
|
||||
using ImGuiNET;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Interface.DragDrop;
|
||||
|
|
@ -32,7 +31,7 @@ internal partial class DragDropManager : IInternalDisposableService, IDragDropMa
|
|||
Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync()
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
this.windowHandlePtr = t.Result.Manager.WindowHandlePtr;
|
||||
this.windowHandlePtr = t.Result.Manager.GameWindowHandle;
|
||||
this.Enable();
|
||||
});
|
||||
}
|
||||
|
|
@ -103,7 +102,7 @@ internal partial class DragDropManager : IInternalDisposableService, IDragDropMa
|
|||
}
|
||||
|
||||
/// <inheritdoc cref="IDragDropManager.CreateImGuiSource(string, Func{IDragDropManager, bool}, Func{IDragDropManager, bool})"/>
|
||||
public void CreateImGuiSource(string label, Func<IDragDropManager, bool> validityCheck, Func<IDragDropManager, bool> tooltipBuilder)
|
||||
public unsafe void CreateImGuiSource(string label, Func<IDragDropManager, bool> validityCheck, Func<IDragDropManager, bool> tooltipBuilder)
|
||||
{
|
||||
if (!this.IsDragging && !this.IsDropping())
|
||||
{
|
||||
|
|
@ -115,7 +114,7 @@ internal partial class DragDropManager : IInternalDisposableService, IDragDropMa
|
|||
return;
|
||||
}
|
||||
|
||||
ImGui.SetDragDropPayload(label, nint.Zero, 0);
|
||||
ImGui.SetDragDropPayload(label, null, 0);
|
||||
if (this.CheckTooltipFrame(out var frame) && tooltipBuilder(this))
|
||||
{
|
||||
this.lastTooltipFrame = frame;
|
||||
|
|
@ -136,7 +135,7 @@ internal partial class DragDropManager : IInternalDisposableService, IDragDropMa
|
|||
|
||||
unsafe
|
||||
{
|
||||
if (ImGui.AcceptDragDropPayload(label, ImGuiDragDropFlags.AcceptBeforeDelivery).NativePtr != null && this.IsDropping())
|
||||
if (ImGui.AcceptDragDropPayload(label, ImGuiDragDropFlags.AcceptBeforeDelivery).Handle != null && this.IsDropping())
|
||||
{
|
||||
this.lastDropFrame = -2;
|
||||
files = this.Files;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ using System.Linq;
|
|||
using System.Runtime.InteropServices.ComTypes;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Interface.DragDrop;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Storage.Assets;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,9 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Interface.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Interface.FontIdentifier;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
|
|
@ -38,7 +35,7 @@ public sealed class SystemFontId : IFontId
|
|||
this.EnglishName = name;
|
||||
else if (this.LocaleNames.TryGetValue("en", out name))
|
||||
this.EnglishName = name;
|
||||
else
|
||||
else
|
||||
this.EnglishName = this.LocaleNames.Values.First();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
using ImGuiNET;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
|
||||
namespace Dalamud.Interface.GameFonts;
|
||||
|
||||
|
|
|
|||
252
Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs
Normal file
252
Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Bindings.ImGuizmo;
|
||||
using Dalamud.Bindings.ImPlot;
|
||||
using Dalamud.Interface.ImGuiBackend.Helpers;
|
||||
using Dalamud.Interface.ImGuiBackend.InputHandler;
|
||||
using Dalamud.Interface.ImGuiBackend.Renderers;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend;
|
||||
|
||||
/// <summary>
|
||||
/// Backend for ImGui, using <see cref="Dx11Renderer"/> and <see cref="Win32InputHandler"/>.
|
||||
/// </summary>
|
||||
[SuppressMessage(
|
||||
"StyleCop.CSharp.LayoutRules",
|
||||
"SA1519:Braces should not be omitted from multi-line child statement",
|
||||
Justification = "Multiple fixed/using scopes")]
|
||||
internal sealed unsafe class Dx11Win32Backend : IWin32Backend
|
||||
{
|
||||
private readonly Dx11Renderer imguiRenderer;
|
||||
private readonly Win32InputHandler imguiInput;
|
||||
|
||||
private ComPtr<IDXGISwapChain> swapChainPossiblyWrapped;
|
||||
private ComPtr<IDXGISwapChain> swapChain;
|
||||
private ComPtr<ID3D11Device> device;
|
||||
private ComPtr<ID3D11DeviceContext> deviceContext;
|
||||
|
||||
private int targetWidth;
|
||||
private int targetHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dx11Win32Backend"/> class.
|
||||
/// </summary>
|
||||
/// <param name="swapChain">The pointer to an instance of <see cref="IDXGISwapChain"/>. The reference is copied.</param>
|
||||
public Dx11Win32Backend(IDXGISwapChain* swapChain)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.swapChainPossiblyWrapped = new(swapChain);
|
||||
this.swapChain = new(swapChain);
|
||||
fixed (ComPtr<IDXGISwapChain>* ppSwapChain = &this.swapChain)
|
||||
ReShadePeeler.PeelSwapChain(ppSwapChain);
|
||||
|
||||
fixed (Guid* guid = &IID.IID_ID3D11Device)
|
||||
fixed (ID3D11Device** pp = &this.device.GetPinnableReference())
|
||||
this.swapChain.Get()->GetDevice(guid, (void**)pp).ThrowOnError();
|
||||
|
||||
fixed (ID3D11DeviceContext** pp = &this.deviceContext.GetPinnableReference())
|
||||
this.device.Get()->GetImmediateContext(pp);
|
||||
|
||||
using var buffer = default(ComPtr<ID3D11Resource>);
|
||||
fixed (Guid* guid = &IID.IID_ID3D11Resource)
|
||||
this.swapChain.Get()->GetBuffer(0, guid, (void**)buffer.GetAddressOf()).ThrowOnError();
|
||||
|
||||
var desc = default(DXGI_SWAP_CHAIN_DESC);
|
||||
this.swapChain.Get()->GetDesc(&desc).ThrowOnError();
|
||||
this.targetWidth = (int)desc.BufferDesc.Width;
|
||||
this.targetHeight = (int)desc.BufferDesc.Height;
|
||||
this.WindowHandle = desc.OutputWindow;
|
||||
|
||||
var ctx = ImGui.CreateContext();
|
||||
ImGuizmo.SetImGuiContext(ctx);
|
||||
ImPlot.SetImGuiContext(ctx);
|
||||
ImPlot.CreateContext();
|
||||
|
||||
ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable;
|
||||
|
||||
this.imguiRenderer = new(this.SwapChain, this.Device, this.DeviceContext);
|
||||
this.imguiInput = new(this.WindowHandle);
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="Dx11Win32Backend"/> class.
|
||||
/// </summary>
|
||||
~Dx11Win32Backend() => this.ReleaseUnmanagedResources();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IImGuiBackend.BuildUiDelegate? BuildUi;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IImGuiBackend.NewInputFrameDelegate? NewInputFrame;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IImGuiBackend.NewRenderFrameDelegate? NewRenderFrame;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool UpdateCursor
|
||||
{
|
||||
get => this.imguiInput.UpdateCursor;
|
||||
set => this.imguiInput.UpdateCursor = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? IniPath
|
||||
{
|
||||
get => this.imguiInput.IniPath;
|
||||
set => this.imguiInput.IniPath = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IImGuiInputHandler InputHandler => this.imguiInput;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IImGuiRenderer Renderer => this.imguiRenderer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to an instance of <see cref="IDXGISwapChain"/>.
|
||||
/// </summary>
|
||||
public IDXGISwapChain* SwapChain => this.swapChain;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to an instance of <see cref="ID3D11Device"/>.
|
||||
/// </summary>
|
||||
public ID3D11Device* Device => this.device;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to an instance of <see cref="ID3D11Device"/>, in <see cref="nint"/>.
|
||||
/// </summary>
|
||||
public nint DeviceHandle => (nint)this.device.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to an instance of <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
public ID3D11DeviceContext* DeviceContext => this.deviceContext;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the window handle.
|
||||
/// </summary>
|
||||
public HWND WindowHandle { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.ReleaseUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public nint? ProcessWndProcW(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) =>
|
||||
this.imguiInput.ProcessWndProcW(hWnd, msg, wParam, lParam);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Render()
|
||||
{
|
||||
this.imguiRenderer.OnNewFrame();
|
||||
this.NewRenderFrame?.Invoke();
|
||||
this.imguiInput.NewFrame(this.targetWidth, this.targetHeight);
|
||||
this.NewInputFrame?.Invoke();
|
||||
|
||||
ImGui.NewFrame();
|
||||
ImGuizmo.BeginFrame();
|
||||
|
||||
this.BuildUi?.Invoke();
|
||||
|
||||
ImGui.Render();
|
||||
|
||||
this.imguiRenderer.RenderDrawData(ImGui.GetDrawData());
|
||||
|
||||
ImGui.UpdatePlatformWindows();
|
||||
ImGui.RenderPlatformWindowsDefault();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnPreResize() => this.imguiRenderer.OnPreResize();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnPostResize(int newWidth, int newHeight)
|
||||
{
|
||||
this.imguiRenderer.OnPostResize(newWidth, newHeight);
|
||||
this.targetWidth = newWidth;
|
||||
this.targetHeight = newHeight;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InvalidateFonts() => this.imguiRenderer.RebuildFontTexture();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsImGuiCursor(nint cursorHandle) => this.imguiInput.IsImGuiCursor(cursorHandle);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAttachedToPresentationTarget(nint targetHandle) =>
|
||||
AreIUnknownEqual(this.swapChain.Get(), (IUnknown*)targetHandle)
|
||||
|| AreIUnknownEqual(this.swapChainPossiblyWrapped.Get(), (IUnknown*)targetHandle);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMainViewportFullScreen()
|
||||
{
|
||||
BOOL fullscreen;
|
||||
this.swapChain.Get()->GetFullscreenState(&fullscreen, null);
|
||||
return fullscreen;
|
||||
}
|
||||
|
||||
private static bool AreIUnknownEqual<T1, T2>(T1* punk1, T2* punk2)
|
||||
where T1 : unmanaged, IUnknown.Interface
|
||||
where T2 : unmanaged, IUnknown.Interface
|
||||
{
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
|
||||
// For any given COM object (also known as a COM component), a specific query for the IUnknown interface on any
|
||||
// of the object's interfaces must always return the same pointer value.
|
||||
|
||||
if (punk1 is null || punk2 is null)
|
||||
return false;
|
||||
|
||||
fixed (Guid* iid = &IID.IID_IUnknown)
|
||||
{
|
||||
using var u1 = default(ComPtr<IUnknown>);
|
||||
if (punk1->QueryInterface(iid, (void**)u1.GetAddressOf()).FAILED)
|
||||
return false;
|
||||
|
||||
using var u2 = default(ComPtr<IUnknown>);
|
||||
if (punk2->QueryInterface(iid, (void**)u2.GetAddressOf()).FAILED)
|
||||
return false;
|
||||
|
||||
return u1.Get() == u2.Get();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseUnmanagedResources()
|
||||
{
|
||||
if (this.device.IsEmpty())
|
||||
return;
|
||||
|
||||
this.imguiRenderer?.Dispose();
|
||||
this.imguiInput?.Dispose();
|
||||
|
||||
ImPlot.DestroyContext();
|
||||
ImGui.DestroyContext();
|
||||
|
||||
this.swapChain.Dispose();
|
||||
this.deviceContext.Dispose();
|
||||
this.device.Dispose();
|
||||
this.swapChainPossiblyWrapped.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,655 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Helpers.D3D11;
|
||||
|
||||
/// <summary>
|
||||
/// Captures states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
internal unsafe struct D3D11DeviceContextStateBackup : IDisposable
|
||||
{
|
||||
private InputAssemblerState inputAssemblerState;
|
||||
private RasterizerState rasterizerState;
|
||||
private OutputMergerState outputMergerState;
|
||||
private VertexShaderState vertexShaderState;
|
||||
private HullShaderState hullShaderState;
|
||||
private DomainShaderState domainShaderState;
|
||||
private GeometryShaderState geometryShaderState;
|
||||
private PixelShaderState pixelShaderState;
|
||||
private ComputeShaderState computeShaderState;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="D3D11DeviceContextStateBackup"/> struct,
|
||||
/// by capturing all states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="featureLevel">The feature level.</param>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
public D3D11DeviceContextStateBackup(D3D_FEATURE_LEVEL featureLevel, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
this.inputAssemblerState = InputAssemblerState.From(ctx);
|
||||
this.rasterizerState = RasterizerState.From(ctx);
|
||||
this.outputMergerState = OutputMergerState.From(featureLevel, ctx);
|
||||
this.vertexShaderState = VertexShaderState.From(ctx);
|
||||
this.hullShaderState = HullShaderState.From(ctx);
|
||||
this.domainShaderState = DomainShaderState.From(ctx);
|
||||
this.geometryShaderState = GeometryShaderState.From(ctx);
|
||||
this.pixelShaderState = PixelShaderState.From(ctx);
|
||||
this.computeShaderState = ComputeShaderState.From(featureLevel, ctx);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.inputAssemblerState.Dispose();
|
||||
this.rasterizerState.Dispose();
|
||||
this.outputMergerState.Dispose();
|
||||
this.vertexShaderState.Dispose();
|
||||
this.hullShaderState.Dispose();
|
||||
this.domainShaderState.Dispose();
|
||||
this.geometryShaderState.Dispose();
|
||||
this.pixelShaderState.Dispose();
|
||||
this.computeShaderState.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Input Assembler states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct InputAssemblerState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT;
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11InputLayout> layout;
|
||||
private ComPtr<ID3D11Buffer> indexBuffer;
|
||||
private DXGI_FORMAT indexFormat;
|
||||
private uint indexOffset;
|
||||
private D3D_PRIMITIVE_TOPOLOGY topology;
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed uint strides[BufferCount];
|
||||
private fixed uint offsets[BufferCount];
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="InputAssemblerState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static InputAssemblerState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(InputAssemblerState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
ctx->IAGetInputLayout(state.layout.GetAddressOf());
|
||||
ctx->IAGetPrimitiveTopology(&state.topology);
|
||||
ctx->IAGetIndexBuffer(state.indexBuffer.GetAddressOf(), &state.indexFormat, &state.indexOffset);
|
||||
ctx->IAGetVertexBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers, state.strides, state.offsets);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (InputAssemblerState* pThis = &this)
|
||||
{
|
||||
ctx->IASetInputLayout(pThis->layout);
|
||||
ctx->IASetPrimitiveTopology(pThis->topology);
|
||||
ctx->IASetIndexBuffer(pThis->indexBuffer, pThis->indexFormat, pThis->indexOffset);
|
||||
ctx->IASetVertexBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers, pThis->strides, pThis->offsets);
|
||||
|
||||
pThis->context.Dispose();
|
||||
pThis->layout.Dispose();
|
||||
pThis->indexBuffer.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Rasterizer states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RasterizerState : IDisposable
|
||||
{
|
||||
private const int Count = TerraFX.Interop.DirectX.D3D11.D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX;
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11RasterizerState> state;
|
||||
private fixed byte viewports[24 * Count];
|
||||
private fixed ulong scissorRects[16 * Count];
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="RasterizerState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static RasterizerState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(RasterizerState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
ctx->RSGetState(state.state.GetAddressOf());
|
||||
uint n = Count;
|
||||
ctx->RSGetViewports(&n, (D3D11_VIEWPORT*)state.viewports);
|
||||
n = Count;
|
||||
ctx->RSGetScissorRects(&n, (RECT*)state.scissorRects);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (RasterizerState* pThis = &this)
|
||||
{
|
||||
ctx->RSSetState(pThis->state);
|
||||
ctx->RSSetViewports(Count, (D3D11_VIEWPORT*)pThis->viewports);
|
||||
ctx->RSSetScissorRects(Count, (RECT*)pThis->scissorRects);
|
||||
|
||||
pThis->context.Dispose();
|
||||
pThis->state.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Output Merger states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct OutputMergerState : IDisposable
|
||||
{
|
||||
private const int RtvCount = TerraFX.Interop.DirectX.D3D11.D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT;
|
||||
private const int UavCountMax = TerraFX.Interop.DirectX.D3D11.D3D11_1_UAV_SLOT_COUNT;
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11BlendState> blendState;
|
||||
private fixed float blendFactor[4];
|
||||
private uint sampleMask;
|
||||
private uint stencilRef;
|
||||
private ComPtr<ID3D11DepthStencilState> depthStencilState;
|
||||
private fixed ulong rtvs[RtvCount]; // ID3D11RenderTargetView*[RtvCount]
|
||||
private ComPtr<ID3D11DepthStencilView> dsv;
|
||||
private fixed ulong uavs[UavCountMax]; // ID3D11UnorderedAccessView*[UavCount]
|
||||
private int uavCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="OutputMergerState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="featureLevel">The feature level.</param>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static OutputMergerState From(D3D_FEATURE_LEVEL featureLevel, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(OutputMergerState);
|
||||
state.uavCount = featureLevel >= D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_11_1
|
||||
? TerraFX.Interop.DirectX.D3D11.D3D11_1_UAV_SLOT_COUNT
|
||||
: TerraFX.Interop.DirectX.D3D11.D3D11_PS_CS_UAV_REGISTER_COUNT;
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
ctx->OMGetBlendState(state.blendState.GetAddressOf(), state.blendFactor, &state.sampleMask);
|
||||
ctx->OMGetDepthStencilState(state.depthStencilState.GetAddressOf(), &state.stencilRef);
|
||||
ctx->OMGetRenderTargetsAndUnorderedAccessViews(
|
||||
RtvCount,
|
||||
(ID3D11RenderTargetView**)state.rtvs,
|
||||
state.dsv.GetAddressOf(),
|
||||
0,
|
||||
(uint)state.uavCount,
|
||||
(ID3D11UnorderedAccessView**)state.uavs);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (OutputMergerState* pThis = &this)
|
||||
{
|
||||
ctx->OMSetBlendState(pThis->blendState, pThis->blendFactor, pThis->sampleMask);
|
||||
ctx->OMSetDepthStencilState(pThis->depthStencilState, pThis->stencilRef);
|
||||
var rtvc = (uint)RtvCount;
|
||||
while (rtvc > 0 && pThis->rtvs[rtvc - 1] == 0)
|
||||
rtvc--;
|
||||
|
||||
var uavlb = rtvc;
|
||||
while (uavlb < this.uavCount && pThis->uavs[uavlb] == 0)
|
||||
uavlb++;
|
||||
|
||||
var uavc = (uint)this.uavCount;
|
||||
while (uavc > uavlb && pThis->uavs[uavc - 1] == 0)
|
||||
uavlb--;
|
||||
uavc -= uavlb;
|
||||
|
||||
ctx->OMSetRenderTargetsAndUnorderedAccessViews(
|
||||
rtvc,
|
||||
(ID3D11RenderTargetView**)pThis->rtvs,
|
||||
pThis->dsv,
|
||||
uavc == 0 ? 0 : uavlb,
|
||||
uavc,
|
||||
uavc == 0 ? null : (ID3D11UnorderedAccessView**)pThis->uavs,
|
||||
null);
|
||||
|
||||
this.context.Reset();
|
||||
this.blendState.Reset();
|
||||
this.depthStencilState.Reset();
|
||||
this.dsv.Reset();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11RenderTargetView>>(pThis->rtvs, RtvCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11UnorderedAccessView>>(pThis->uavs, this.uavCount))
|
||||
b.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Vertex Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct VertexShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11VertexShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="VertexShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static VertexShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(VertexShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->VSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->VSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->VSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->VSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (VertexShaderState* pThis = &this)
|
||||
{
|
||||
ctx->VSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->VSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->VSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->VSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Hull Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HullShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11HullShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="HullShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static HullShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(HullShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->HSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->HSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->HSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->HSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (HullShaderState* pThis = &this)
|
||||
{
|
||||
ctx->HSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->HSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->HSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->HSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Domain Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DomainShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11DomainShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DomainShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static DomainShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(DomainShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->DSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->DSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->DSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->DSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (DomainShaderState* pThis = &this)
|
||||
{
|
||||
ctx->DSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->DSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->DSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->DSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Geometry Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct GeometryShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11GeometryShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="GeometryShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static GeometryShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(GeometryShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->GSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->GSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->GSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->GSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (GeometryShaderState* pThis = &this)
|
||||
{
|
||||
ctx->GSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->GSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->GSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->GSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Pixel Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PixelShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11PixelShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PixelShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static PixelShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(PixelShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->PSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->PSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->PSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->PSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (PixelShaderState* pThis = &this)
|
||||
{
|
||||
ctx->PSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->PSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->PSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->PSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Compute Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ComputeShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int InstanceCount = 256; // According to msdn
|
||||
private const int UavCountMax = TerraFX.Interop.DirectX.D3D11.D3D11_1_UAV_SLOT_COUNT;
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11ComputeShader> shader;
|
||||
private fixed ulong insts[InstanceCount]; // ID3D11ClassInstance*[BufferCount]
|
||||
private fixed ulong buffers[BufferCount]; // ID3D11Buffer*[BufferCount]
|
||||
private fixed ulong samplers[SamplerCount]; // ID3D11SamplerState*[SamplerCount]
|
||||
private fixed ulong resources[ResourceCount]; // ID3D11ShaderResourceView*[ResourceCount]
|
||||
private fixed ulong uavs[UavCountMax]; // ID3D11UnorderedAccessView*[UavCountMax]
|
||||
private uint instCount;
|
||||
private int uavCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="ComputeShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="featureLevel">The feature level.</param>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static ComputeShaderState From(D3D_FEATURE_LEVEL featureLevel, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(ComputeShaderState);
|
||||
state.uavCount = featureLevel >= D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_11_1
|
||||
? TerraFX.Interop.DirectX.D3D11.D3D11_1_UAV_SLOT_COUNT
|
||||
: TerraFX.Interop.DirectX.D3D11.D3D11_PS_CS_UAV_REGISTER_COUNT;
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = InstanceCount;
|
||||
ctx->CSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->CSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->CSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->CSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
ctx->CSGetUnorderedAccessViews(0, (uint)state.uavCount, (ID3D11UnorderedAccessView**)state.uavs);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (ComputeShaderState* pThis = &this)
|
||||
{
|
||||
ctx->CSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->CSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->CSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->CSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
ctx->CSSetUnorderedAccessViews(0, (uint)this.uavCount, (ID3D11UnorderedAccessView**)pThis->uavs, null);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11UnorderedAccessView>>(pThis->uavs, this.uavCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Dalamud/Interface/ImGuiBackend/Helpers/D3D11/Extensions.cs
Normal file
26
Dalamud/Interface/ImGuiBackend/Helpers/D3D11/Extensions.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using System.Text;
|
||||
|
||||
using Dalamud.Utility;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Helpers.D3D11;
|
||||
|
||||
/// <summary>Utility extension methods for D3D11 objects.</summary>
|
||||
internal static class Extensions
|
||||
{
|
||||
/// <summary>Sets the name for debugging.</summary>
|
||||
/// <param name="child">D3D11 object.</param>
|
||||
/// <param name="name">Debug name.</param>
|
||||
/// <typeparam name="T">Object type.</typeparam>
|
||||
public static unsafe void SetDebugName<T>(ref this T child, string name)
|
||||
where T : unmanaged, ID3D11DeviceChild.Interface
|
||||
{
|
||||
var len = Encoding.UTF8.GetByteCount(name);
|
||||
var buf = stackalloc byte[len + 1];
|
||||
Encoding.UTF8.GetBytes(name, new(buf, len + 1));
|
||||
buf[len] = 0;
|
||||
fixed (Guid* pId = &DirectX.WKPDID_D3DDebugObjectName)
|
||||
child.SetPrivateData(pId, (uint)(len + 1), buf).ThrowOnError();
|
||||
}
|
||||
}
|
||||
165
Dalamud/Interface/ImGuiBackend/Helpers/ImGuiViewportHelpers.cs
Normal file
165
Dalamud/Interface/ImGuiBackend/Helpers/ImGuiViewportHelpers.cs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helpers for using ImGui Viewports.
|
||||
/// </summary>
|
||||
internal static class ImGuiViewportHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate to be called when a window should be created.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void CreateWindowDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when a window should be destroyed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void DestroyWindowDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when a window should be resized.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="size">Size of the new window.</param>
|
||||
public delegate void SetWindowSizeDelegate(ImGuiViewportPtr viewport, Vector2 size);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when a window should be rendered.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="v">Custom user-provided argument from <see cref="ImGui.RenderPlatformWindowsDefault()"/>.</param>
|
||||
public delegate void RenderWindowDelegate(ImGuiViewportPtr viewport, nint v);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when buffers for the window should be swapped.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="v">Custom user-provided argument from <see cref="ImGui.RenderPlatformWindowsDefault()"/>.</param>
|
||||
public delegate void SwapBuffersDelegate(ImGuiViewportPtr viewport, nint v);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window should be showed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void ShowWindowDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window should be updated.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void UpdateWindowDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window position is queried.
|
||||
/// </summary>
|
||||
/// <param name="returnStorage">The return value storage.</param>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>Same value with <paramref name="returnStorage"/>.</returns>
|
||||
public unsafe delegate Vector2* GetWindowPosDelegate(Vector2* returnStorage, ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window should be moved.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="pos">The new position.</param>
|
||||
public delegate void SetWindowPosDelegate(ImGuiViewportPtr viewport, Vector2 pos);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window size is queried.
|
||||
/// </summary>
|
||||
/// <param name="returnStorage">The return value storage.</param>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>Same value with <paramref name="returnStorage"/>.</returns>
|
||||
public unsafe delegate Vector2* GetWindowSizeDelegate(Vector2* returnStorage, ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window should be given focus.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void SetWindowFocusDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window is focused.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>Whether the window is focused.</returns>
|
||||
public delegate bool GetWindowFocusDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when whether the window is minimized is queried.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>Whether the window is minimized.</returns>
|
||||
public delegate bool GetWindowMinimizedDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window title should be changed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="title">The new title.</param>
|
||||
public delegate void SetWindowTitleDelegate(ImGuiViewportPtr viewport, string title);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window alpha should be changed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="alpha">The new alpha.</param>
|
||||
public delegate void SetWindowAlphaDelegate(ImGuiViewportPtr viewport, float alpha);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the IME input position should be changed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="pos">The new position.</param>
|
||||
public delegate void SetImeInputPosDelegate(ImGuiViewportPtr viewport, Vector2 pos);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window's DPI scale value is queried.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>The DPI scale.</returns>
|
||||
public delegate float GetWindowDpiScaleDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when viewport is changed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void ChangedViewportDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Disables ImGui from disabling alpha for Viewport window backgrounds.
|
||||
/// </summary>
|
||||
public static unsafe void EnableViewportWindowBackgroundAlpha()
|
||||
{
|
||||
// TODO: patch imgui.cpp:6126, which disables background transparency for extra viewport windows
|
||||
var offset = 0x00007FFB6ADA632C - 0x00007FFB6AD60000;
|
||||
offset += Process.GetCurrentProcess().Modules.Cast<ProcessModule>().First(x => x.ModuleName == "cimgui.dll")
|
||||
.BaseAddress;
|
||||
var b = (byte*)offset;
|
||||
uint old;
|
||||
if (!VirtualProtect(b, 1, PAGE.PAGE_EXECUTE_READWRITE, &old))
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())
|
||||
?? throw new InvalidOperationException($"{nameof(VirtualProtect)} failed.");
|
||||
}
|
||||
|
||||
*b = 0xEB;
|
||||
if (!VirtualProtect(b, 1, old, &old))
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())
|
||||
?? throw new InvalidOperationException($"{nameof(VirtualProtect)} failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
230
Dalamud/Interface/ImGuiBackend/Helpers/ReShadePeeler.cs
Normal file
230
Dalamud/Interface/ImGuiBackend/Helpers/ReShadePeeler.cs
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Peels ReShade off stuff.
|
||||
/// </summary>
|
||||
[SuppressMessage(
|
||||
"StyleCop.CSharp.LayoutRules",
|
||||
"SA1519:Braces should not be omitted from multi-line child statement",
|
||||
Justification = "Multiple fixed blocks")]
|
||||
internal static unsafe class ReShadePeeler
|
||||
{
|
||||
/// <summary>
|
||||
/// Peels <see cref="IDXGISwapChain"/> if it is wrapped by ReShade.
|
||||
/// </summary>
|
||||
/// <param name="comptr">[inout] The COM pointer to an instance of <see cref="IDXGISwapChain"/>.</param>
|
||||
/// <typeparam name="T">A COM type that is or extends <see cref="IDXGISwapChain"/>.</typeparam>
|
||||
/// <returns><c>true</c> if peeled.</returns>
|
||||
public static bool PeelSwapChain<T>(ComPtr<T>* comptr)
|
||||
where T : unmanaged, IDXGISwapChain.Interface =>
|
||||
PeelIUnknown(comptr, sizeof(IDXGISwapChain.Vtbl<IDXGISwapChain>));
|
||||
|
||||
private static bool PeelIUnknown<T>(ComPtr<T>* comptr, nint vtblSize)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
var changed = false;
|
||||
while (comptr->Get() != null && IsReShadedComObject(comptr->Get()))
|
||||
{
|
||||
// Expectation: the pointer to the underlying object should come early after the overriden vtable.
|
||||
for (nint i = 8; i <= 0x20; i += 8)
|
||||
{
|
||||
var ppObjectBehind = (nint)comptr->Get() + i;
|
||||
|
||||
// Is the thing directly pointed from the address an actual something in the memory?
|
||||
if (!IsValidReadableMemoryAddress(ppObjectBehind, 8))
|
||||
continue;
|
||||
|
||||
var pObjectBehind = *(nint*)ppObjectBehind;
|
||||
|
||||
// Is the address of vtable readable?
|
||||
if (!IsValidReadableMemoryAddress(pObjectBehind, sizeof(nint)))
|
||||
continue;
|
||||
var pObjectBehindVtbl = *(nint*)pObjectBehind;
|
||||
|
||||
// Is the vtable itself readable?
|
||||
if (!IsValidReadableMemoryAddress(pObjectBehindVtbl, vtblSize))
|
||||
continue;
|
||||
|
||||
// Are individual functions in vtable executable?
|
||||
var valid = true;
|
||||
for (var j = 0; valid && j < vtblSize; j += sizeof(nint))
|
||||
valid &= IsValidExecutableMemoryAddress(*(nint*)(pObjectBehindVtbl + j), 1);
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
// Interpret the object as an IUnknown.
|
||||
// Note that `using` is not used, and `Attach` is used. We do not alter the reference count yet.
|
||||
var punk = default(ComPtr<IUnknown>);
|
||||
punk.Attach((IUnknown*)pObjectBehind);
|
||||
|
||||
// Is the IUnknown object also the type we want?
|
||||
using var comptr2 = default(ComPtr<T>);
|
||||
if (punk.As(&comptr2).FAILED)
|
||||
continue;
|
||||
|
||||
comptr2.Swap(comptr);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
break;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static bool BelongsInReShadeDll(nint ptr)
|
||||
{
|
||||
foreach (ProcessModule processModule in Process.GetCurrentProcess().Modules)
|
||||
{
|
||||
if (ptr < processModule.BaseAddress)
|
||||
continue;
|
||||
|
||||
var dosh = (IMAGE_DOS_HEADER*)processModule.BaseAddress;
|
||||
var nth = (IMAGE_NT_HEADERS64*)(processModule.BaseAddress + dosh->e_lfanew);
|
||||
if (ptr >= processModule.BaseAddress + nth->OptionalHeader.SizeOfImage)
|
||||
continue;
|
||||
|
||||
fixed (byte* pfn0 = "CreateDXGIFactory"u8)
|
||||
fixed (byte* pfn1 = "D2D1CreateDevice"u8)
|
||||
fixed (byte* pfn2 = "D3D10CreateDevice"u8)
|
||||
fixed (byte* pfn3 = "D3D11CreateDevice"u8)
|
||||
fixed (byte* pfn4 = "D3D12CreateDevice"u8)
|
||||
fixed (byte* pfn5 = "glBegin"u8)
|
||||
fixed (byte* pfn6 = "vkCreateDevice"u8)
|
||||
{
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn0) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn1) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn2) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn3) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn4) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn5) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn6) == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileInfo = FileVersionInfo.GetVersionInfo(processModule.FileName);
|
||||
|
||||
if (fileInfo.FileDescription == null)
|
||||
continue;
|
||||
|
||||
if (!fileInfo.FileDescription.Contains("GShade") && !fileInfo.FileDescription.Contains("ReShade"))
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsReShadedComObject<T>(T* obj)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
if (!IsValidReadableMemoryAddress((nint)obj, sizeof(nint)))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var vtbl = (nint**)Marshal.ReadIntPtr((nint)obj);
|
||||
if (!IsValidReadableMemoryAddress((nint)vtbl, sizeof(nint) * 3))
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var pfn = Marshal.ReadIntPtr((nint)(vtbl + i));
|
||||
if (!IsValidExecutableMemoryAddress(pfn, 1))
|
||||
return false;
|
||||
if (!BelongsInReShadeDll(pfn))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidReadableMemoryAddress(nint p, nint size)
|
||||
{
|
||||
while (size > 0)
|
||||
{
|
||||
if (!IsValidUserspaceMemoryAddress(p))
|
||||
return false;
|
||||
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (VirtualQuery((void*)p, &mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
||||
return false;
|
||||
|
||||
if (mbi is not
|
||||
{
|
||||
State: MEM.MEM_COMMIT,
|
||||
Protect: PAGE.PAGE_READONLY or PAGE.PAGE_READWRITE or PAGE.PAGE_EXECUTE_READ
|
||||
or PAGE.PAGE_EXECUTE_READWRITE,
|
||||
})
|
||||
return false;
|
||||
|
||||
var regionSize = (nint)((mbi.RegionSize + 0xFFFUL) & ~0x1000UL);
|
||||
var checkedSize = ((nint)mbi.BaseAddress + regionSize) - p;
|
||||
size -= checkedSize;
|
||||
p += checkedSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsValidExecutableMemoryAddress(nint p, nint size)
|
||||
{
|
||||
while (size > 0)
|
||||
{
|
||||
if (!IsValidUserspaceMemoryAddress(p))
|
||||
return false;
|
||||
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (VirtualQuery((void*)p, &mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION)) == 0)
|
||||
return false;
|
||||
|
||||
if (mbi is not
|
||||
{
|
||||
State: MEM.MEM_COMMIT,
|
||||
Protect: PAGE.PAGE_EXECUTE or PAGE.PAGE_EXECUTE_READ or PAGE.PAGE_EXECUTE_READWRITE
|
||||
or PAGE.PAGE_EXECUTE_WRITECOPY,
|
||||
})
|
||||
return false;
|
||||
|
||||
var regionSize = (nint)((mbi.RegionSize + 0xFFFUL) & ~0x1000UL);
|
||||
var checkedSize = ((nint)mbi.BaseAddress + regionSize) - p;
|
||||
size -= checkedSize;
|
||||
p += checkedSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsValidUserspaceMemoryAddress(nint p)
|
||||
{
|
||||
// https://learn.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/virtual-address-spaces
|
||||
// A 64-bit process on 64-bit Windows has a virtual address space within the 128-terabyte range
|
||||
// 0x000'00000000 through 0x7FFF'FFFFFFFF.
|
||||
return p >= 0x10000 && p <= unchecked((nint)0x7FFF_FFFFFFFFUL);
|
||||
}
|
||||
}
|
||||
72
Dalamud/Interface/ImGuiBackend/IImGuiBackend.cs
Normal file
72
Dalamud/Interface/ImGuiBackend/IImGuiBackend.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
using Dalamud.Interface.ImGuiBackend.InputHandler;
|
||||
using Dalamud.Interface.ImGuiBackend.Renderers;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend;
|
||||
|
||||
/// <summary>Backend for ImGui.</summary>
|
||||
internal interface IImGuiBackend : IDisposable
|
||||
{
|
||||
/// <summary>Delegate to be called when ImGui should be used to layout now.</summary>
|
||||
public delegate void BuildUiDelegate();
|
||||
|
||||
/// <summary>Delegate to be called on new input frame.</summary>
|
||||
public delegate void NewInputFrameDelegate();
|
||||
|
||||
/// <summary>Delegaet to be called on new render frame.</summary>
|
||||
public delegate void NewRenderFrameDelegate();
|
||||
|
||||
/// <summary>User methods invoked every ImGui frame to construct custom UIs.</summary>
|
||||
event BuildUiDelegate? BuildUi;
|
||||
|
||||
/// <summary>User methods invoked every ImGui frame on handling inputs.</summary>
|
||||
event NewInputFrameDelegate? NewInputFrame;
|
||||
|
||||
/// <summary>User methods invoked every ImGui frame on handling renders.</summary>
|
||||
event NewRenderFrameDelegate? NewRenderFrame;
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether the cursor should be overridden with the ImGui cursor.
|
||||
/// </summary>
|
||||
bool UpdateCursor { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the path of ImGui configuration .ini file.</summary>
|
||||
string? IniPath { get; set; }
|
||||
|
||||
/// <summary>Gets the device handle.</summary>
|
||||
nint DeviceHandle { get; }
|
||||
|
||||
/// <summary>Gets the input handler.</summary>
|
||||
IImGuiInputHandler InputHandler { get; }
|
||||
|
||||
/// <summary>Gets the renderer.</summary>
|
||||
IImGuiRenderer Renderer { get; }
|
||||
|
||||
/// <summary>Performs a render cycle.</summary>
|
||||
void Render();
|
||||
|
||||
/// <summary>Handles stuff before resizing happens.</summary>
|
||||
void OnPreResize();
|
||||
|
||||
/// <summary>Handles stuff after resizing happens.</summary>
|
||||
/// <param name="newWidth">The new width.</param>
|
||||
/// <param name="newHeight">The new height.</param>
|
||||
void OnPostResize(int newWidth, int newHeight);
|
||||
|
||||
/// <summary>Invalidates fonts immediately.</summary>
|
||||
/// <remarks>Call this while handling <see cref="NewRenderFrame"/>.</remarks>
|
||||
void InvalidateFonts();
|
||||
|
||||
/// <summary>Determines if <paramref name="cursorHandle"/> is owned by this.</summary>
|
||||
/// <param name="cursorHandle">The cursor.</param>
|
||||
/// <returns>Whether it is the case.</returns>
|
||||
bool IsImGuiCursor(nint cursorHandle);
|
||||
|
||||
/// <summary>Determines if this instance of <see cref="IImGuiBackend"/> is rendering to
|
||||
/// <paramref name="targetHandle"/>. </summary>
|
||||
/// <param name="targetHandle">The present target handle.</param>
|
||||
/// <returns>Whether it is the case.</returns>
|
||||
bool IsAttachedToPresentationTarget(nint targetHandle);
|
||||
|
||||
/// <summary>Determines if the main viewport is full screen. </summary>
|
||||
/// <returns>Whether it is the case.</returns>
|
||||
bool IsMainViewportFullScreen();
|
||||
}
|
||||
15
Dalamud/Interface/ImGuiBackend/IWin32Backend.cs
Normal file
15
Dalamud/Interface/ImGuiBackend/IWin32Backend.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend;
|
||||
|
||||
/// <summary><see cref="IImGuiBackend"/> with Win32 support.</summary>
|
||||
internal interface IWin32Backend : IImGuiBackend
|
||||
{
|
||||
/// <summary>Processes window messages.</summary>
|
||||
/// <param name="hWnd">Handle of the window.</param>
|
||||
/// <param name="msg">Type of window message.</param>
|
||||
/// <param name="wParam">wParam.</param>
|
||||
/// <param name="lParam">lParam.</param>
|
||||
/// <returns>Return value.</returns>
|
||||
public nint? ProcessWndProcW(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam);
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Dalamud.Interface.ImGuiBackend.InputHandler;
|
||||
|
||||
/// <summary>A simple shared public interface that all ImGui input implementations follows.</summary>
|
||||
internal interface IImGuiInputHandler : IDisposable
|
||||
{
|
||||
/// <summary>Gets or sets a value indicating whether the cursor should be overridden with the ImGui cursor.
|
||||
/// </summary>
|
||||
public bool UpdateCursor { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the path of ImGui configuration .ini file.</summary>
|
||||
string? IniPath { get; set; }
|
||||
|
||||
/// <summary>Determines if <paramref name="cursorHandle"/> is owned by this.</summary>
|
||||
/// <param name="cursorHandle">The cursor.</param>
|
||||
/// <returns>Whether it is the case.</returns>
|
||||
public bool IsImGuiCursor(nint cursorHandle);
|
||||
|
||||
/// <summary>Marks the beginning of a new frame.</summary>
|
||||
/// <param name="width">The width of the new frame.</param>
|
||||
/// <param name="height">The height of the new frame.</param>
|
||||
void NewFrame(int width, int height);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue