Allow ApiHawk impl classes to have any param order in their ctors

This commit is contained in:
YoshiRulz 2020-11-29 16:39:26 +10:00
parent 1f9188da5e
commit ebef47203d
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
8 changed files with 45 additions and 51 deletions

View File

@ -1,7 +1,5 @@
#nullable enable
using System;
namespace BizHawk.Client.Common
{
public sealed class CommApi : ICommApi
@ -18,10 +16,7 @@ namespace BizHawk.Client.Common
public WebSocketServer? WebSockets => _wsServer;
public CommApi(Action<string> logCallback, IMainFormForApi mainForm)
{
_networkingHelpers = mainForm.NetworkingHelpers;
}
public CommApi(IMainFormForApi mainForm) => _networkingHelpers = mainForm.NetworkingHelpers;
public string? HttpTest() => HTTP == null ? null : string.Join("\n", HttpTestGet(), HTTP.SendScreenshot(), "done testing");

View File

@ -3,32 +3,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BizHawk.Client.Common;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
public static class ApiManager
{
/// <remarks>keys are impl., values are interface</remarks>
private static readonly IReadOnlyDictionary<Type, Type> _apiTypes
= Client.Common.ReflectionCache.Types.Concat(EmuHawk.ReflectionCache.Types)
.Where(t => /*t.IsClass &&*/t.IsSealed) // small optimisation; api impl. types are all sealed classes
.Select(t => (t, t.GetInterfaces().FirstOrDefault(t1 => typeof(IExternalApi).IsAssignableFrom(t1) && t1 != typeof(IExternalApi)))) // grab interface from impl. type...
.Where(tuple => tuple.Item2 != null) // ...if we couldn't determine what it's implementing, then it's not an api impl. type
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2);
private static readonly IReadOnlyList<(Type ImplType, Type InterfaceType, ConstructorInfo Ctor, Type[] CtorTypes)> _apiTypes;
private static readonly Type[] _ctorParamTypesA = { typeof(Action<string>) };
private static readonly Type[] _ctorParamTypesB = { typeof(Action<string>), typeof(IMainFormForApi) };
private static readonly Type[] _ctorParamTypesC = { typeof(Action<string>), typeof(InputManager), typeof(IMovieSession) };
private static readonly Type[] _ctorParamTypesD = { typeof(Action<string>), typeof(IMainFormForApi), typeof(DisplayManager), typeof(InputManager), typeof(Config), typeof(IEmulator), typeof(IGameInfo) };
private static readonly Type[] _ctorParamTypesTools = { typeof(ToolManager) };
static ApiManager()
{
var list = new List<(Type, Type, ConstructorInfo, Type[])>();
foreach (var implType in Common.ReflectionCache.Types.Concat(ReflectionCache.Types)
.Where(t => /*t.IsClass &&*/t.IsSealed)) // small optimisation; api impl. types are all sealed classes
{
var interfaceType = implType.GetInterfaces().FirstOrDefault(t => typeof(IExternalApi).IsAssignableFrom(t) && t != typeof(IExternalApi));
if (interfaceType == null) continue; // if we couldn't determine what it's implementing, then it's not an api impl. type
var ctor = implType.GetConstructors().Single();
list.Add((implType, interfaceType, ctor, ctor.GetParameters().Select(pi => pi.ParameterType).ToArray()));
}
_apiTypes = list.ToArray();
}
/// <remarks>TODO do we need to keep references to these because of GC weirdness? --yoshi</remarks>
private static ApiContainer? _container;
@ -47,20 +45,27 @@ namespace BizHawk.Client.EmuHawk
IEmulator emulator,
IGameInfo game)
{
var libDict = _apiTypes.Keys.Where(t => ServiceInjector.IsAvailable(serviceProvider, t))
var avail = new Dictionary<Type, object>
{
[typeof(Action<string>)] = logCallback,
[typeof(IMainFormForApi)] = mainForm,
[typeof(DisplayManager)] = displayManager,
[typeof(InputManager)] = inputManager,
[typeof(IMovieSession)] = movieSession,
[typeof(ToolManager)] = toolManager,
[typeof(Config)] = config,
[typeof(IEmulator)] = emulator,
[typeof(IGameInfo)] = game,
};
return new ApiContainer(_apiTypes.Where(tuple => ServiceInjector.IsAvailable(serviceProvider, tuple.ImplType))
.ToDictionary(
t => _apiTypes[t],
t => (IExternalApi) (
t.GetConstructor(_ctorParamTypesD)?.Invoke(new object[] { logCallback, mainForm, displayManager, inputManager, config, emulator, game })
?? t.GetConstructor(_ctorParamTypesC)?.Invoke(new object[] { logCallback, inputManager, movieSession })
?? t.GetConstructor(_ctorParamTypesB)?.Invoke(new object[] { logCallback, mainForm })
?? t.GetConstructor(_ctorParamTypesA)?.Invoke(new object[] { logCallback })
?? t.GetConstructor(_ctorParamTypesTools)?.Invoke(new object[] { toolManager })
?? Activator.CreateInstance(t)
)
);
foreach (var instance in libDict.Values) ServiceInjector.UpdateServices(serviceProvider, instance);
return new ApiContainer(libDict);
tuple => tuple.InterfaceType,
tuple =>
{
var instance = tuple.Ctor.Invoke(tuple.CtorTypes.Select(t => avail[t]).ToArray());
ServiceInjector.UpdateServices(serviceProvider, instance);
return (IExternalApi) instance;
}));
}
public static IExternalApiProvider Restart(

View File

@ -59,7 +59,7 @@ namespace BizHawk.Client.EmuHawk
public Action YieldCallback { get; set; }
public EmulationApi(Action<string> logCallback, IMainFormForApi mainForm, DisplayManager displayManager, InputManager inputManager, Config config, IEmulator emulator, IGameInfo game)
public EmulationApi(Action<string> logCallback, Config config, IGameInfo game)
{
_config = config;
_game = game;

View File

@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
@ -12,8 +12,7 @@ namespace BizHawk.Client.EmuHawk
private readonly IGameInfo _game;
public GameInfoApi(Action<string> logCallback, IMainFormForApi mainForm, DisplayManager displayManager, InputManager inputManager, Config config, IEmulator emulator, IGameInfo game)
=> _game = game;
public GameInfoApi(IGameInfo game) => _game = game;
public string GetRomName() => _game?.Name ?? "";

View File

@ -47,7 +47,7 @@ namespace BizHawk.Client.EmuHawk
public bool HasGUISurface => _GUISurface != null;
public GuiApi(Action<string> logCallback, IMainFormForApi mainForm, DisplayManager displayManager, InputManager inputManager, Config config, IEmulator emulator, IGameInfo game)
public GuiApi(Action<string> logCallback, DisplayManager displayManager)
{
LogCallback = logCallback;
_displayManager = displayManager;

View File

@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
@ -16,7 +14,7 @@ namespace BizHawk.Client.EmuHawk
private readonly IMainFormForApi _mainForm;
public InputApi(Action<string> logCallback, IMainFormForApi mainForm, DisplayManager displayManager, InputManager inputManager, Config config, IEmulator emulator, IGameInfo game)
public InputApi(IMainFormForApi mainForm, DisplayManager displayManager, InputManager inputManager)
{
_displayManager = displayManager;
_inputManager = inputManager;

View File

@ -13,7 +13,7 @@ namespace BizHawk.Client.EmuHawk
private readonly Action<string> LogCallback;
public MovieApi(Action<string> logCallback, InputManager inputManager, IMovieSession movieSession)
public MovieApi(Action<string> logCallback, IMovieSession movieSession)
{
LogCallback = logCallback;
_movieSession = movieSession;

View File

@ -7,10 +7,7 @@ namespace BizHawk.Client.EmuHawk
{
private readonly IMovieSession _movieSession;
public UserDataApi(Action<string> logCallback, InputManager inputManager, IMovieSession movieSession)
{
_movieSession = movieSession;
}
public UserDataApi(IMovieSession movieSession) => _movieSession = movieSession;
/// <exception cref="InvalidOperationException">type of <paramref name="value"/> cannot be used in userdata</exception>
public void Set(string name, object value)