mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 13:14:17 +01:00
merge
This commit is contained in:
commit
933eb9fa96
8 changed files with 166 additions and 60 deletions
|
|
@ -380,7 +380,7 @@ namespace Dalamud.Injector
|
|||
startInfo.BootShowConsole = args.Contains("--console");
|
||||
startInfo.BootEnableEtw = args.Contains("--etw");
|
||||
startInfo.BootLogPath = GetLogPath(startInfo.LogPath, "dalamud.boot", startInfo.LogName);
|
||||
startInfo.BootEnabledGameFixes = new List<string> { "prevent_devicechange_crashes", "disable_game_openprocess_access_check", "redirect_openprocess", "backup_userdata_save", "clr_failfast_hijack", "prevent_icmphandle_crashes" };
|
||||
startInfo.BootEnabledGameFixes = new List<string> { "prevent_devicechange_crashes", "disable_game_openprocess_access_check", "redirect_openprocess", "backup_userdata_save", "prevent_icmphandle_crashes" };
|
||||
startInfo.BootDotnetOpenProcessHookMode = 0;
|
||||
startInfo.BootWaitMessageBox |= args.Contains("--msgbox1") ? 1 : 0;
|
||||
startInfo.BootWaitMessageBox |= args.Contains("--msgbox2") ? 2 : 0;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
|
|
@ -16,9 +17,11 @@ using JetBrains.Annotations;
|
|||
using Lumina;
|
||||
using Lumina.Data;
|
||||
using Lumina.Data.Files;
|
||||
using Lumina.Data.Parsing.Tex.Buffers;
|
||||
using Lumina.Excel;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using SharpDX.DXGI;
|
||||
|
||||
namespace Dalamud.Data;
|
||||
|
||||
|
|
@ -267,9 +270,31 @@ public sealed class DataManager : IDisposable, IServiceType, IDataManager
|
|||
|
||||
/// <inheritdoc/>
|
||||
[Obsolete("Use ITextureProvider instead")]
|
||||
[return: NotNullIfNotNull(nameof(tex))]
|
||||
public TextureWrap? GetImGuiTexture(TexFile? tex)
|
||||
=> tex == null ? null : Service<InterfaceManager>.Get().LoadImageRaw(tex.GetRgbaImageData(), tex.Header.Width, tex.Header.Height, 4);
|
||||
{
|
||||
if (tex is null)
|
||||
return null;
|
||||
|
||||
var im = Service<InterfaceManager>.Get();
|
||||
var buffer = tex.TextureBuffer;
|
||||
var bpp = 1 << (((int)tex.Header.Format & (int)TexFile.TextureFormat.BppMask) >>
|
||||
(int)TexFile.TextureFormat.BppShift);
|
||||
|
||||
var (dxgiFormat, conversion) = TexFile.GetDxgiFormatFromTextureFormat(tex.Header.Format, false);
|
||||
if (conversion != TexFile.DxgiFormatConversion.NoConversion || !im.SupportsDxgiFormat((Format)dxgiFormat))
|
||||
{
|
||||
dxgiFormat = (int)Format.B8G8R8A8_UNorm;
|
||||
buffer = buffer.Filter(0, 0, TexFile.TextureFormat.B8G8R8A8);
|
||||
bpp = 32;
|
||||
}
|
||||
|
||||
var pitch = buffer is BlockCompressionTextureBuffer
|
||||
? Math.Max(1, (buffer.Width + 3) / 4) * 2 * bpp
|
||||
: ((buffer.Width * bpp) + 7) / 8;
|
||||
return im.LoadImageFromDxgiFormat(buffer.RawData, pitch, buffer.Width, buffer.Height, (Format)dxgiFormat);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
[Obsolete("Use ITextureProvider instead")]
|
||||
public TextureWrap? GetImGuiTexture(string path)
|
||||
|
|
|
|||
|
|
@ -26,52 +26,52 @@ public unsafe class Character : GameObject
|
|||
/// <summary>
|
||||
/// Gets the current HP of this Chara.
|
||||
/// </summary>
|
||||
public uint CurrentHp => this.Struct->Health;
|
||||
public uint CurrentHp => this.Struct->CharacterData.Health;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum HP of this Chara.
|
||||
/// </summary>
|
||||
public uint MaxHp => this.Struct->MaxHealth;
|
||||
public uint MaxHp => this.Struct->CharacterData.MaxHealth;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current MP of this Chara.
|
||||
/// </summary>
|
||||
public uint CurrentMp => this.Struct->Mana;
|
||||
public uint CurrentMp => this.Struct->CharacterData.Mana;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum MP of this Chara.
|
||||
/// </summary>
|
||||
public uint MaxMp => this.Struct->MaxMana;
|
||||
public uint MaxMp => this.Struct->CharacterData.MaxMana;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current GP of this Chara.
|
||||
/// </summary>
|
||||
public uint CurrentGp => this.Struct->GatheringPoints;
|
||||
public uint CurrentGp => this.Struct->CharacterData.GatheringPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum GP of this Chara.
|
||||
/// </summary>
|
||||
public uint MaxGp => this.Struct->MaxGatheringPoints;
|
||||
public uint MaxGp => this.Struct->CharacterData.MaxGatheringPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current CP of this Chara.
|
||||
/// </summary>
|
||||
public uint CurrentCp => this.Struct->CraftingPoints;
|
||||
public uint CurrentCp => this.Struct->CharacterData.CraftingPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum CP of this Chara.
|
||||
/// </summary>
|
||||
public uint MaxCp => this.Struct->MaxCraftingPoints;
|
||||
public uint MaxCp => this.Struct->CharacterData.MaxCraftingPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ClassJob of this Chara.
|
||||
/// </summary>
|
||||
public ExcelResolver<ClassJob> ClassJob => new(this.Struct->ClassJob);
|
||||
public ExcelResolver<ClassJob> ClassJob => new(this.Struct->CharacterData.ClassJob);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level of this Chara.
|
||||
/// </summary>
|
||||
public byte Level => this.Struct->Level;
|
||||
public byte Level => this.Struct->CharacterData.Level;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a byte array describing the visual appearance of this Chara.
|
||||
|
|
@ -97,7 +97,7 @@ public unsafe class Character : GameObject
|
|||
/// <summary>
|
||||
/// Gets the current online status of the character.
|
||||
/// </summary>
|
||||
public ExcelResolver<OnlineStatus> OnlineStatus => new(this.Struct->OnlineStatus);
|
||||
public ExcelResolver<OnlineStatus> OnlineStatus => new(this.Struct->CharacterData.OnlineStatus);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status flags.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ using ImGuiNET;
|
|||
using ImGuiScene;
|
||||
using PInvoke;
|
||||
using Serilog;
|
||||
using SharpDX;
|
||||
using SharpDX.Direct3D;
|
||||
using SharpDX.Direct3D11;
|
||||
using SharpDX.DXGI;
|
||||
|
||||
// general dev notes, here because it's easiest
|
||||
|
||||
|
|
@ -303,6 +307,62 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether the current D3D11 Device supports the given DXGI format.
|
||||
/// </summary>
|
||||
/// <param name="dxgiFormat">DXGI format to check.</param>
|
||||
/// <returns>Whether it is supported.</returns>
|
||||
public bool SupportsDxgiFormat(Format dxgiFormat) => this.scene is null
|
||||
? throw new InvalidOperationException("Scene isn't ready.")
|
||||
: this.scene.Device.CheckFormatSupport(dxgiFormat).HasFlag(FormatSupport.Texture2D);
|
||||
|
||||
/// <summary>
|
||||
/// Load an image from a span of bytes of specified format.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to load.</param>
|
||||
/// <param name="pitch">The pitch(stride) in bytes.</param>
|
||||
/// <param name="width">The width in pixels.</param>
|
||||
/// <param name="height">The height in pixels.</param>
|
||||
/// <param name="dxgiFormat">Format of the texture.</param>
|
||||
/// <returns>A texture, ready to use in ImGui.</returns>
|
||||
public TextureWrap LoadImageFromDxgiFormat(Span<byte> data, int pitch, int width, int height, Format dxgiFormat)
|
||||
{
|
||||
if (this.scene == null)
|
||||
throw new InvalidOperationException("Scene isn't ready.");
|
||||
|
||||
ShaderResourceView resView;
|
||||
unsafe
|
||||
{
|
||||
fixed (void* pData = data)
|
||||
{
|
||||
var texDesc = new Texture2DDescription
|
||||
{
|
||||
Width = width,
|
||||
Height = height,
|
||||
MipLevels = 1,
|
||||
ArraySize = 1,
|
||||
Format = dxgiFormat,
|
||||
SampleDescription = new(1, 0),
|
||||
Usage = ResourceUsage.Immutable,
|
||||
BindFlags = BindFlags.ShaderResource,
|
||||
CpuAccessFlags = CpuAccessFlags.None,
|
||||
OptionFlags = ResourceOptionFlags.None,
|
||||
};
|
||||
|
||||
using var texture = new Texture2D(this.Device, texDesc, new DataRectangle(new(pData), pitch));
|
||||
resView = new(this.Device, texture, new()
|
||||
{
|
||||
Format = texDesc.Format,
|
||||
Dimension = ShaderResourceViewDimension.Texture2D,
|
||||
Texture2D = { MipLevels = texDesc.MipLevels },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// no sampler for now because the ImGui implementation we copied doesn't allow for changing it
|
||||
return new D3DTextureWrap(resView, width, height);
|
||||
}
|
||||
|
||||
#nullable restore
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ namespace Dalamud.IoC.Internal;
|
|||
|
||||
/// <summary>
|
||||
/// A simple singleton-only IOC container that provides (optional) version-based dependency resolution.
|
||||
///
|
||||
/// This is only used to resolve dependencies for plugins.
|
||||
/// Dalamud services are constructed via Service{T}.ConstructObject at the moment.
|
||||
/// </summary>
|
||||
internal class ServiceContainer : IServiceProvider, IServiceType
|
||||
{
|
||||
|
|
@ -31,7 +34,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
/// Register a singleton object of any type into the current IOC container.
|
||||
/// </summary>
|
||||
/// <param name="instance">The existing instance to register in the container.</param>
|
||||
/// <typeparam name="T">The interface to register.</typeparam>
|
||||
/// <typeparam name="T">The type to register.</typeparam>
|
||||
public void RegisterSingleton<T>(Task<T> instance)
|
||||
{
|
||||
if (instance == null)
|
||||
|
|
@ -40,19 +43,27 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
}
|
||||
|
||||
this.instances[typeof(T)] = new(instance.ContinueWith(x => new WeakReference(x.Result)), typeof(T));
|
||||
this.RegisterInterfaces(typeof(T));
|
||||
}
|
||||
|
||||
var resolveViaTypes = typeof(T)
|
||||
.GetCustomAttributes()
|
||||
.OfType<ResolveViaAttribute>()
|
||||
.Select(x => x.GetType().GetGenericArguments().First());
|
||||
/// <summary>
|
||||
/// Register the interfaces that can resolve this type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to register.</param>
|
||||
public void RegisterInterfaces(Type type)
|
||||
{
|
||||
var resolveViaTypes = type
|
||||
.GetCustomAttributes()
|
||||
.OfType<ResolveViaAttribute>()
|
||||
.Select(x => x.GetType().GetGenericArguments().First());
|
||||
foreach (var resolvableType in resolveViaTypes)
|
||||
{
|
||||
Log.Verbose("=> {InterfaceName} provides for {TName}", resolvableType.FullName ?? "???", typeof(T).FullName ?? "???");
|
||||
Log.Verbose("=> {InterfaceName} provides for {TName}", resolvableType.FullName ?? "???", type.FullName ?? "???");
|
||||
|
||||
Debug.Assert(!this.interfaceToTypeMap.ContainsKey(resolvableType), "A service already implements this interface, this is not allowed");
|
||||
Debug.Assert(typeof(T).IsAssignableTo(resolvableType), "Service does not inherit from indicated ResolveVia type");
|
||||
Debug.Assert(type.IsAssignableTo(resolvableType), "Service does not inherit from indicated ResolveVia type");
|
||||
|
||||
this.interfaceToTypeMap[resolvableType] = typeof(T);
|
||||
this.interfaceToTypeMap[resolvableType] = type;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,18 +106,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
parameters
|
||||
.Select(async p =>
|
||||
{
|
||||
if (p.parameterType.GetCustomAttribute<ServiceManager.ScopedService>() != null)
|
||||
{
|
||||
if (scopeImpl == null)
|
||||
{
|
||||
Log.Error("Failed to create {TypeName}, depends on scoped service but no scope", objectType.FullName!);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await scopeImpl.CreatePrivateScopedObject(p.parameterType, scopedObjects);
|
||||
}
|
||||
|
||||
var service = await this.GetService(p.parameterType, scopedObjects);
|
||||
var service = await this.GetService(p.parameterType, scopeImpl, scopedObjects);
|
||||
|
||||
if (service == null)
|
||||
{
|
||||
|
|
@ -168,22 +168,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
object service = null;
|
||||
|
||||
if (prop.propertyInfo.PropertyType.GetCustomAttribute<ServiceManager.ScopedService>() != null)
|
||||
{
|
||||
if (scopeImpl == null)
|
||||
{
|
||||
Log.Error("Failed to create {TypeName}, depends on scoped service but no scope", objectType.FullName!);
|
||||
}
|
||||
else
|
||||
{
|
||||
service = await scopeImpl.CreatePrivateScopedObject(prop.propertyInfo.PropertyType, publicScopes);
|
||||
}
|
||||
}
|
||||
|
||||
service ??= await this.GetService(prop.propertyInfo.PropertyType, publicScopes);
|
||||
|
||||
var service = await this.GetService(prop.propertyInfo.PropertyType, scopeImpl, publicScopes);
|
||||
if (service == null)
|
||||
{
|
||||
Log.Error("Requested service type {TypeName} was not available (null)", prop.propertyInfo.PropertyType.FullName!);
|
||||
|
|
@ -203,7 +188,7 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
public IServiceScope GetScope() => new ServiceScopeImpl(this);
|
||||
|
||||
/// <inheritdoc/>
|
||||
object? IServiceProvider.GetService(Type serviceType) => this.GetService(serviceType);
|
||||
object? IServiceProvider.GetService(Type serviceType) => this.GetSingletonService(serviceType);
|
||||
|
||||
private static bool CheckInterfaceVersion(RequiredVersionAttribute? requiredVersion, Type parameterType)
|
||||
{
|
||||
|
|
@ -228,9 +213,23 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
return false;
|
||||
}
|
||||
|
||||
private async Task<object?> GetService(Type serviceType, object[] scopedObjects)
|
||||
private async Task<object?> GetService(Type serviceType, ServiceScopeImpl? scope, object[] scopedObjects)
|
||||
{
|
||||
var singletonService = await this.GetService(serviceType);
|
||||
if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType))
|
||||
serviceType = implementingType;
|
||||
|
||||
if (serviceType.GetCustomAttribute<ServiceManager.ScopedService>() != null)
|
||||
{
|
||||
if (scope == null)
|
||||
{
|
||||
Log.Error("Failed to create {TypeName}, is scoped but no scope provided", serviceType.FullName!);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await scope.CreatePrivateScopedObject(serviceType, scopedObjects);
|
||||
}
|
||||
|
||||
var singletonService = await this.GetSingletonService(serviceType, false);
|
||||
if (singletonService != null)
|
||||
{
|
||||
return singletonService;
|
||||
|
|
@ -246,9 +245,9 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
return scoped;
|
||||
}
|
||||
|
||||
private async Task<object?> GetService(Type serviceType)
|
||||
private async Task<object?> GetSingletonService(Type serviceType, bool tryGetInterface = true)
|
||||
{
|
||||
if (this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType))
|
||||
if (tryGetInterface && this.interfaceToTypeMap.TryGetValue(serviceType, out var implementingType))
|
||||
serviceType = implementingType;
|
||||
|
||||
if (!this.instances.TryGetValue(serviceType, out var service))
|
||||
|
|
@ -285,13 +284,24 @@ internal class ServiceContainer : IServiceProvider, IServiceType
|
|||
|
||||
private bool ValidateCtor(ConstructorInfo ctor, Type[] types)
|
||||
{
|
||||
bool IsTypeValid(Type type)
|
||||
{
|
||||
var contains = types.Any(x => x.IsAssignableTo(type));
|
||||
|
||||
// Scoped services are created on-demand
|
||||
return contains || type.GetCustomAttribute<ServiceManager.ScopedService>() != null;
|
||||
}
|
||||
|
||||
var parameters = ctor.GetParameters();
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
var contains = types.Any(x => x.IsAssignableTo(parameter.ParameterType));
|
||||
var valid = IsTypeValid(parameter.ParameterType);
|
||||
|
||||
// If this service is provided by an interface
|
||||
if (!valid && this.interfaceToTypeMap.TryGetValue(parameter.ParameterType, out var implementationType))
|
||||
valid = IsTypeValid(implementationType);
|
||||
|
||||
// Scoped services are created on-demand
|
||||
if (!contains && parameter.ParameterType.GetCustomAttribute<ServiceManager.ScopedService>() == null)
|
||||
if (!valid)
|
||||
{
|
||||
Log.Error("Failed to validate {TypeName}, unable to find any services that satisfy the type", parameter.ParameterType.FullName!);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using ImGuiScene;
|
||||
using Lumina;
|
||||
|
|
@ -147,6 +148,7 @@ public interface IDataManager
|
|||
/// <param name="tex">The Lumina <see cref="TexFile"/>.</param>
|
||||
/// <returns>A <see cref="TextureWrap"/> that can be used to draw the texture.</returns>
|
||||
[Obsolete("Use ITextureProvider instead")]
|
||||
[return: NotNullIfNotNull(nameof(tex))]
|
||||
public TextureWrap? GetImGuiTexture(TexFile? tex);
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -132,11 +132,20 @@ internal static class ServiceManager
|
|||
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
|
||||
var getAsyncTaskMap = new Dictionary<Type, Task>();
|
||||
|
||||
var serviceContainer = Service<ServiceContainer>.Get();
|
||||
|
||||
foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes())
|
||||
{
|
||||
var serviceKind = serviceType.GetServiceKind();
|
||||
if (serviceKind is ServiceKind.None or ServiceKind.ScopedService)
|
||||
if (serviceKind is ServiceKind.None)
|
||||
continue;
|
||||
|
||||
// Scoped service do not go through Service<T>, so we must let ServiceContainer know what their interfaces map to
|
||||
if (serviceKind is ServiceKind.ScopedService)
|
||||
{
|
||||
serviceContainer.RegisterInterfaces(serviceType);
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Assert(
|
||||
!serviceKind.HasFlag(ServiceKind.ManualService) && !serviceKind.HasFlag(ServiceKind.ScopedService),
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 8962a47b95f96bec53e58680bd9d1e7f38610d40
|
||||
Subproject commit 93db21d9b6fb5cc671cd25c79a6ac933f3ca6710
|
||||
Loading…
Add table
Add a link
Reference in a new issue