drop support for pre-Pintail proxying
This commit is contained in:
parent
769475166a
commit
db578c389e
|
@ -7,6 +7,7 @@
|
|||
* For mod authors:
|
||||
* Added a new `IRawTextureData` asset type.
|
||||
_You can now load image files through `helper.ModContent` as `IRawTextureData` instead of `Texture2D`. This provides the image size and raw pixel data, which you can pass into other SMAPI APIs like `asset.AsImage().PatchImage`. This is much more efficient when you don't need a full `Texture2D` instance, since it bypasses the GPU operations needed to create one._
|
||||
* Removed transitional `UsePintail` option added in 3.14.0 (now always enabled).
|
||||
|
||||
* For mod authors:
|
||||
* Fixed map edits which change warps sometimes rebuilding the NPC pathfinding cache unnecessarily, which could cause a noticeable delay for players.
|
||||
|
|
|
@ -29,11 +29,8 @@ namespace SMAPI.Tests.Core
|
|||
/// <summary>The random number generator with which to create sample values.</summary>
|
||||
private readonly Random Random = new();
|
||||
|
||||
/// <summary>Sample user inputs for season names.</summary>
|
||||
private static readonly IInterfaceProxyFactory[] ProxyFactories = {
|
||||
new InterfaceProxyFactory(),
|
||||
new OriginalInterfaceProxyFactory()
|
||||
};
|
||||
/// <summary>The proxy factory to use in unit tests.</summary>
|
||||
private static readonly IInterfaceProxyFactory[] ProxyFactories = { new InterfaceProxyFactory() };
|
||||
|
||||
|
||||
/*********
|
||||
|
|
|
@ -22,7 +22,6 @@ namespace StardewModdingAPI.Framework.Models
|
|||
[nameof(WebApiBaseUrl)] = "https://smapi.io/api/",
|
||||
[nameof(LogNetworkTraffic)] = false,
|
||||
[nameof(RewriteMods)] = true,
|
||||
[nameof(UsePintail)] = true,
|
||||
[nameof(UseRawImageLoading)] = true,
|
||||
[nameof(UseCaseInsensitivePaths)] = Constants.Platform is Platform.Android or Platform.Linux
|
||||
};
|
||||
|
@ -64,9 +63,6 @@ namespace StardewModdingAPI.Framework.Models
|
|||
/// <summary>Whether SMAPI should rewrite mods for compatibility.</summary>
|
||||
public bool RewriteMods { get; }
|
||||
|
||||
/// <summary>Whether to use the experimental Pintail API proxying library, instead of the original proxying built into SMAPI itself.</summary>
|
||||
public bool UsePintail { get; }
|
||||
|
||||
/// <summary>Whether to use raw image data when possible, instead of initializing an XNA Texture2D instance through the GPU.</summary>
|
||||
public bool UseRawImageLoading { get; }
|
||||
|
||||
|
@ -95,7 +91,6 @@ namespace StardewModdingAPI.Framework.Models
|
|||
/// <param name="webApiBaseUrl">The base URL for SMAPI's web API, used to perform update checks.</param>
|
||||
/// <param name="verboseLogging">The log contexts for which to enable verbose logging, which may show a lot more information to simplify troubleshooting.</param>
|
||||
/// <param name="rewriteMods">Whether SMAPI should rewrite mods for compatibility.</param>
|
||||
/// <param name="usePintail">Whether to use the experimental Pintail API proxying library, instead of the original proxying built into SMAPI itself.</param>
|
||||
/// <param name="useRawImageLoading">Whether to use raw image data when possible, instead of initializing an XNA Texture2D instance through the GPU.</param>
|
||||
/// <param name="useCaseInsensitivePaths">>Whether to make SMAPI file APIs case-insensitive, even on Linux.</param>
|
||||
/// <param name="logNetworkTraffic">Whether SMAPI should log network traffic.</param>
|
||||
|
@ -111,7 +106,6 @@ namespace StardewModdingAPI.Framework.Models
|
|||
this.WebApiBaseUrl = webApiBaseUrl;
|
||||
this.VerboseLogging = new HashSet<string>(verboseLogging ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
|
||||
this.RewriteMods = rewriteMods ?? (bool)SConfig.DefaultValues[nameof(this.RewriteMods)];
|
||||
this.UsePintail = usePintail ?? (bool)SConfig.DefaultValues[nameof(this.UsePintail)];
|
||||
this.UseRawImageLoading = useRawImageLoading ?? (bool)SConfig.DefaultValues[nameof(this.UseRawImageLoading)];
|
||||
this.UseCaseInsensitivePaths = useCaseInsensitivePaths ?? (bool)SConfig.DefaultValues[nameof(this.UseCaseInsensitivePaths)];
|
||||
this.LogNetworkTraffic = logNetworkTraffic ?? (bool)SConfig.DefaultValues[nameof(this.LogNetworkTraffic)];
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace StardewModdingAPI.Framework.Reflection
|
||||
{
|
||||
/// <summary>Generates a proxy class to access a mod API through an arbitrary interface.</summary>
|
||||
internal class OriginalInterfaceProxyBuilder
|
||||
{
|
||||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The target class type.</summary>
|
||||
private readonly Type TargetType;
|
||||
|
||||
/// <summary>The generated proxy type.</summary>
|
||||
private readonly Type ProxyType;
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="name">The type name to generate.</param>
|
||||
/// <param name="moduleBuilder">The CLR module in which to create proxy classes.</param>
|
||||
/// <param name="interfaceType">The interface type to implement.</param>
|
||||
/// <param name="targetType">The target type.</param>
|
||||
public OriginalInterfaceProxyBuilder(string name, ModuleBuilder moduleBuilder, Type interfaceType, Type targetType)
|
||||
{
|
||||
// validate
|
||||
if (name == null)
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
if (targetType == null)
|
||||
throw new ArgumentNullException(nameof(targetType));
|
||||
|
||||
// define proxy type
|
||||
TypeBuilder proxyBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class);
|
||||
proxyBuilder.AddInterfaceImplementation(interfaceType);
|
||||
|
||||
// create field to store target instance
|
||||
FieldBuilder targetField = proxyBuilder.DefineField("__Target", targetType, FieldAttributes.Private);
|
||||
|
||||
// create constructor which accepts target instance and sets field
|
||||
{
|
||||
ConstructorBuilder constructor = proxyBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard | CallingConventions.HasThis, new[] { targetType });
|
||||
ILGenerator il = constructor.GetILGenerator();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0); // this
|
||||
// ReSharper disable once AssignNullToNotNullAttribute -- never null
|
||||
il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)!); // call base constructor
|
||||
il.Emit(OpCodes.Ldarg_0); // this
|
||||
il.Emit(OpCodes.Ldarg_1); // load argument
|
||||
il.Emit(OpCodes.Stfld, targetField); // set field to loaded argument
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
// proxy methods
|
||||
foreach (MethodInfo proxyMethod in interfaceType.GetMethods())
|
||||
{
|
||||
var targetMethod = targetType.GetMethod(proxyMethod.Name, proxyMethod.GetParameters().Select(a => a.ParameterType).ToArray());
|
||||
if (targetMethod == null)
|
||||
throw new InvalidOperationException($"The {interfaceType.FullName} interface defines method {proxyMethod.Name} which doesn't exist in the API.");
|
||||
|
||||
this.ProxyMethod(proxyBuilder, targetMethod, targetField);
|
||||
}
|
||||
|
||||
// save info
|
||||
this.TargetType = targetType;
|
||||
this.ProxyType = proxyBuilder.CreateType()!;
|
||||
}
|
||||
|
||||
/// <summary>Create an instance of the proxy for a target instance.</summary>
|
||||
/// <param name="targetInstance">The target instance.</param>
|
||||
public object CreateInstance(object targetInstance)
|
||||
{
|
||||
ConstructorInfo? constructor = this.ProxyType.GetConstructor(new[] { this.TargetType });
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException($"Couldn't find the constructor for generated proxy type '{this.ProxyType.Name}'."); // should never happen
|
||||
return constructor.Invoke(new[] { targetInstance });
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** Private methods
|
||||
*********/
|
||||
/// <summary>Define a method which proxies access to a method on the target.</summary>
|
||||
/// <param name="proxyBuilder">The proxy type being generated.</param>
|
||||
/// <param name="target">The target method.</param>
|
||||
/// <param name="instanceField">The proxy field containing the API instance.</param>
|
||||
private void ProxyMethod(TypeBuilder proxyBuilder, MethodInfo target, FieldBuilder instanceField)
|
||||
{
|
||||
Type[] argTypes = target.GetParameters().Select(a => a.ParameterType).ToArray();
|
||||
|
||||
// create method
|
||||
MethodBuilder methodBuilder = proxyBuilder.DefineMethod(target.Name, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual);
|
||||
methodBuilder.SetParameters(argTypes);
|
||||
methodBuilder.SetReturnType(target.ReturnType);
|
||||
|
||||
// create method body
|
||||
{
|
||||
ILGenerator il = methodBuilder.GetILGenerator();
|
||||
|
||||
// load target instance
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, instanceField);
|
||||
|
||||
// invoke target method on instance
|
||||
for (int i = 0; i < argTypes.Length; i++)
|
||||
il.Emit(OpCodes.Ldarg, i + 1);
|
||||
il.Emit(OpCodes.Call, target);
|
||||
|
||||
// return result
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace StardewModdingAPI.Framework.Reflection
|
||||
{
|
||||
/// <inheritdoc />
|
||||
internal class OriginalInterfaceProxyFactory : IInterfaceProxyFactory
|
||||
{
|
||||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The CLR module in which to create proxy classes.</summary>
|
||||
private readonly ModuleBuilder ModuleBuilder;
|
||||
|
||||
/// <summary>The generated proxy types.</summary>
|
||||
private readonly IDictionary<string, OriginalInterfaceProxyBuilder> Builders = new Dictionary<string, OriginalInterfaceProxyBuilder>();
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
public OriginalInterfaceProxyFactory()
|
||||
{
|
||||
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName($"StardewModdingAPI.Proxies, Version={this.GetType().Assembly.GetName().Version}, Culture=neutral"), AssemblyBuilderAccess.Run);
|
||||
this.ModuleBuilder = assemblyBuilder.DefineDynamicModule("StardewModdingAPI.Proxies");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TInterface CreateProxy<TInterface>(object instance, string sourceModID, string targetModID)
|
||||
where TInterface : class
|
||||
{
|
||||
lock (this.Builders)
|
||||
{
|
||||
// validate
|
||||
if (instance == null)
|
||||
throw new InvalidOperationException("Can't proxy access to a null API.");
|
||||
if (!typeof(TInterface).IsInterface)
|
||||
throw new InvalidOperationException("The proxy type must be an interface, not a class.");
|
||||
|
||||
// get proxy type
|
||||
Type targetType = instance.GetType();
|
||||
string proxyTypeName = $"StardewModdingAPI.Proxies.From<{sourceModID}_{typeof(TInterface).FullName}>_To<{targetModID}_{targetType.FullName}>";
|
||||
if (!this.Builders.TryGetValue(proxyTypeName, out OriginalInterfaceProxyBuilder? builder))
|
||||
{
|
||||
builder = new OriginalInterfaceProxyBuilder(proxyTypeName, this.ModuleBuilder, typeof(TInterface), targetType);
|
||||
this.Builders[proxyTypeName] = builder;
|
||||
}
|
||||
|
||||
// create instance
|
||||
return (TInterface)builder.CreateInstance(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1610,9 +1610,7 @@ namespace StardewModdingAPI.Framework
|
|||
{
|
||||
// init
|
||||
HashSet<string> suppressUpdateChecks = this.Settings.SuppressUpdateChecks;
|
||||
IInterfaceProxyFactory proxyFactory = this.Settings.UsePintail
|
||||
? new InterfaceProxyFactory()
|
||||
: new OriginalInterfaceProxyFactory();
|
||||
IInterfaceProxyFactory proxyFactory = new InterfaceProxyFactory();
|
||||
|
||||
// load mods
|
||||
foreach (IModMetadata mod in mods)
|
||||
|
|
Loading…
Reference in New Issue