diff --git a/MaaFramework.Binding.sln b/MaaFramework.Binding.sln
index 3a50aff..3c4b511 100644
--- a/MaaFramework.Binding.sln
+++ b/MaaFramework.Binding.sln
@@ -8,8 +8,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
- Directory.Build.props = Directory.Build.props
- Directory.Packages.props = Directory.Packages.props
+ src\Directory.Build.props = src\Directory.Build.props
+ src\Directory.Packages.props = src\Directory.Packages.props
LICENSE.md = LICENSE.md
README.md = README.md
README.zh_cn.md = README.zh_cn.md
diff --git a/sample/Agent/.config/dotnet-tools.json b/sample/Agent/.config/dotnet-tools.json
new file mode 100644
index 0000000..fb26112
--- /dev/null
+++ b/sample/Agent/.config/dotnet-tools.json
@@ -0,0 +1,13 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-script": {
+ "version": "1.6.0",
+ "commands": [
+ "dotnet-script"
+ ],
+ "rollForward": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample/Agent/AgentChild.cs b/sample/Agent/AgentChild.cs
new file mode 100644
index 0000000..1586026
--- /dev/null
+++ b/sample/Agent/AgentChild.cs
@@ -0,0 +1,58 @@
+#!/usr/bin/env dotnet-script
+#r "nuget: Maa.Framework.Binding.Native, 4.0.0-preview.25163.6"
+
+using MaaFramework.Binding;
+using MaaFramework.Binding.Buffers;
+using MaaFramework.Binding.Custom;
+
+var commandLineArgs = Environment.GetCommandLineArgs();
+if (commandLineArgs.Length < 4)
+{
+ Console.WriteLine("Call AgentMain.cs instead of this file.");
+ return 1;
+}
+
+var socketId = commandLineArgs[^1];
+var userPath = commandLineArgs[^2];
+var dllPath = commandLineArgs[^3];
+
+NativeBindingInfo.Set(isAgentServer: true, dllPath); // First step
+_ = new MaaToolkit(true, userPath);
+var agentServer = new MaaAgentServer();
+agentServer.Register(new MyRec());
+agentServer.Register(new MyAct());
+agentServer.StartUp(socketId);
+agentServer.Join();
+agentServer.ShutDown();
+
+Console.Write("Press any key to exit:");
+Console.ReadKey();
+return 0;
+
+internal sealed class MyRec : IMaaCustomRecognition
+{
+ public string Name { get; set; } = nameof(MyRec);
+
+ public bool Analyze(in IMaaContext context, in AnalyzeArgs args, in AnalyzeResults results)
+ {
+ Console.WriteLine("{0} Called", Name);
+
+ results.Box.SetValues(0, 0, 100, 100);
+ results.Detail.SetValue("Hello Client!");
+ return true;
+ }
+}
+
+internal sealed class MyAct : IMaaCustomAction
+{
+ public string Name { get; set; } = nameof(MyAct);
+
+ public bool Run(in IMaaContext context, in RunArgs args)
+ {
+ Console.WriteLine("{0} Called", Name);
+ Console.WriteLine("recognition detail: {0}", args.RecognitionDetail);
+ Console.WriteLine("custom action param: {0}", args.ActionParam);
+
+ return true;
+ }
+}
diff --git a/sample/Agent/AgentMain.cs b/sample/Agent/AgentMain.cs
new file mode 100644
index 0000000..fce0db6
--- /dev/null
+++ b/sample/Agent/AgentMain.cs
@@ -0,0 +1,68 @@
+#!/usr/bin/env dotnet-script
+#r "nuget: Maa.Framework.Native, 4.0.0-preview.25163.6"
+#r "nuget: Maa.Framework.Runtime.win-x64, 4.0.0"
+
+using System.Diagnostics;
+using MaaFramework.Binding;
+
+var toolkit = new MaaToolkit(true);
+var resource = new MaaResource();
+var maa = new MaaTasker
+{
+ Controller = toolkit.AdbDevice.Find().First().ToAdbController(),
+ Resource = resource,
+ DisposeOptions = DisposeOptions.All,
+ Toolkit = toolkit,
+};
+
+if (!maa.Initialized)
+ throw new InvalidOperationException("Failed to init tasker.");
+
+var agent = new MaaAgentClient
+{
+ Resource = resource,
+ DisposeOptions = DisposeOptions.All,
+};
+
+var socketId = agent.CreateSocket(string.Empty)
+ ?? throw new InvalidOperationException("Failed to create socket.");
+
+var p = Process.Start(new ProcessStartInfo(
+ "dotnet",["script",
+ "AgentChild.cs",
+ NativeBindingInfo.NativeAssemblyDirectory
+ ?? throw new ArgumentNullException("Native.BindingInfo.NativeAssemblyDirectory"),
+ Environment.CurrentDirectory,
+ socketId]) { UseShellExecute = true });
+
+if (!agent.LinkStart())
+ throw new InvalidOperationException("Failed to connect.");
+
+var ppover = """
+{
+ "Entry": {"next": "Rec"},
+ "Rec": {
+ "recognition": "Custom",
+ "custom_recognition": "MyRec",
+ "action": "Custom",
+ "custom_action": "MyAct",
+ "custom_action_param": {
+ "param": "Hello Server!"
+ }
+ }
+}
+""";
+Console.WriteLine(ppover);
+
+var detail = maa
+ .AppendTask("Entry", ppover)
+ .WaitFor(MaaJobStatus.Succeeded)
+ .QueryTaskDetail()
+ ?? throw new InvalidOperationException("Failed to pipeline.");
+Console.WriteLine($"pipeline detail: {detail}");
+Console.WriteLine($"MyRec detail: {detail.QueryRecognitionDetail(maa, 1)?.Detail}");
+
+agent.LinkStop();
+
+Console.Write("Press any key to exit:");
+Console.ReadKey();
diff --git a/sample/Agent/QuickStart.ps1 b/sample/Agent/QuickStart.ps1
new file mode 100644
index 0000000..f5988bd
--- /dev/null
+++ b/sample/Agent/QuickStart.ps1
@@ -0,0 +1,3 @@
+$ENV:DOTNET_SCRIPT_CACHE_LOCATION="$PSScriptRoot\.cache"
+dotnet tool restore
+dotnet script AgentMain.cs -s https://api.nuget.org/v3/index.json -s https://maaxyz.github.io/pkg/nuget/index.json
\ No newline at end of file
diff --git a/Directory.Build.props b/src/Directory.Build.props
similarity index 100%
rename from Directory.Build.props
rename to src/Directory.Build.props
diff --git a/Directory.Packages.props b/src/Directory.Packages.props
similarity index 81%
rename from Directory.Packages.props
rename to src/Directory.Packages.props
index 4d30807..1c088bd 100644
--- a/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -4,8 +4,8 @@
true
-
-
+
+
diff --git a/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClient.cs
new file mode 100644
index 0000000..fbb2b60
--- /dev/null
+++ b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClient.cs
@@ -0,0 +1,41 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记
+#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
+
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+
+namespace MaaFramework.Binding.Interop.Native;
+
+public static partial class MaaAgentClient
+{
+ [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)]
+ public static partial MaaAgentClientHandle MaaAgentClientCreate();
+
+ [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)]
+ public static partial void MaaAgentClientDestroy(MaaAgentClientHandle client);
+
+ [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool MaaAgentClientBindResource(MaaAgentClientHandle client, MaaResourceHandle res);
+
+ [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool MaaAgentClientCreateSocket(MaaAgentClientHandle client, MaaStringBufferHandle identifier);
+
+ [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool MaaAgentClientConnect(MaaAgentClientHandle client);
+
+ [LibraryImport("MaaAgentClient", StringMarshalling = StringMarshalling.Utf8)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool MaaAgentClientDisconnect(MaaAgentClientHandle client);
+}
diff --git a/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs
new file mode 100644
index 0000000..ac2ae0c
--- /dev/null
+++ b/src/MaaFramework.Binding.Native/Interop/AgentClient/MaaAgentClientDef.cs
@@ -0,0 +1,16 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记
+#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
+
+global using MaaAgentClientHandle = nint;
+
+namespace MaaFramework.Binding.Interop.Native;
+
diff --git a/src/MaaFramework.Binding.Native/Interop/AgentServer/MaaAgentServer.cs b/src/MaaFramework.Binding.Native/Interop/AgentServer/MaaAgentServer.cs
new file mode 100644
index 0000000..e0cf4c8
--- /dev/null
+++ b/src/MaaFramework.Binding.Native/Interop/AgentServer/MaaAgentServer.cs
@@ -0,0 +1,40 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记
+#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
+
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+
+namespace MaaFramework.Binding.Interop.Native;
+
+public static partial class MaaAgentServer
+{
+ [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool MaaAgentServerRegisterCustomRecognition(string name, MaaCustomRecognitionCallback recognition, nint transArg);
+
+ [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool MaaAgentServerRegisterCustomAction(string name, MaaCustomActionCallback action, nint transArg);
+
+ [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool MaaAgentServerStartUp(string identifier);
+
+ [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)]
+ public static partial void MaaAgentServerShutDown();
+
+ [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)]
+ public static partial void MaaAgentServerJoin();
+
+ [LibraryImport("MaaAgentServer", StringMarshalling = StringMarshalling.Utf8)]
+ public static partial void MaaAgentServerDetach();
+}
diff --git a/src/MaaFramework.Binding.Native/Interop/MaaCustomExtensions.cs b/src/MaaFramework.Binding.Native/Interop/MaaCustomExtensions.cs
index 1363caf..2e52c80 100644
--- a/src/MaaFramework.Binding.Native/Interop/MaaCustomExtensions.cs
+++ b/src/MaaFramework.Binding.Native/Interop/MaaCustomExtensions.cs
@@ -44,7 +44,8 @@ nint transArg
ActionParam: customActionParam,
RecognitionDetail: recognitionDetail,
RecognitionBox: new MaaRectBuffer(boxHandle)
- )
+ ),
+ new RunResults()
);
};
return callback;
diff --git a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs
index b2fd190..2cfd936 100644
--- a/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs
+++ b/src/MaaFramework.Binding.Native/Interop/NativeLibrary.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace MaaFramework.Binding.Interop.Native;
@@ -7,79 +8,124 @@ internal static partial class NativeLibrary
{
private static readonly Assembly s_assembly = typeof(NativeLibrary).Assembly;
- public static void Init()
- => SetDllImportResolver(s_assembly, NativeAssemblyResolver);
+#pragma warning disable CA2255 // 不应在库中使用 “ModuleInitializer” 属性
+#pragma warning disable S2223 // Non-constant static fields should not be visible
- public static IntPtr NativeAssemblyResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ internal static bool IsLoaded;
+ internal static string LoadedDirectory = string.Empty;
+ internal static readonly Dictionary LoadedLibraryHandles = [];
+
+ internal static ApiInfoFlags ApiInfo;
+ internal static readonly List SearchPath = [];
+
+ [ModuleInitializer]
+ internal static void SetNativeLibraryResolver() => SetDllImportResolver(s_assembly, NativeLibraryResolver);
+
+#pragma warning restore S2223 // Non-constant static fields should not be visible
+#pragma warning restore CA2255 // 不应在库中使用 “ModuleInitializer” 属性
+
+ public static nint NativeLibraryResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) => libraryName switch
{
- var libHandle = IntPtr.Zero;
- if (!libraryName.Equals("MaaFramework", StringComparison.Ordinal)
- && !libraryName.Equals("MaaToolkit", StringComparison.Ordinal))
- {
- return libHandle;
- }
+ "MaaFramework" or "MaaToolkit"
+ or "MaaAgentServer" or "MaaAgentClient" => GetLibraryHandle(libraryName),
- if (TryGetRuntimesPath(libraryName, out var dllPath))
- {
- _ = TryLoad(dllPath, assembly, searchPath, out libHandle);
- }
- return libHandle;
+ _ => IntPtr.Zero,
+ };
+
+ private static nint GetLibraryHandle(string libraryName)
+ {
+ if (LoadedLibraryHandles.TryGetValue(libraryName, out var libraryHandle))
+ return libraryHandle;
+
+ IsLoaded = true;
+ var resolver = TryGetRuntimesPath(libraryName, out var dllPath) && TryLoad(dllPath, out libraryHandle)
+ ? ApiInfoFlags.UseBindingResolver
+ : ApiInfoFlags.UseDefaultResolver;
+ SetBindingContext(
+ resolver,
+ dllDir: Path.GetDirectoryName(dllPath) ?? string.Empty,
+ isFirst: LoadedLibraryHandles.Count == 0);
+
+ LoadedLibraryHandles.Add(libraryName, libraryHandle);
+ return libraryHandle;
+ }
+
+ private static void SetBindingContext(ApiInfoFlags resolver, string dllDir, bool isFirst)
+ {
+ if (ApiInfo.HasFlag_ResolverExcept(resolver))
+ throw new InvalidOperationException($"The resolver '{ApiInfo}' was attempted to switch to '{resolver}'.");
+
+ if (isFirst)
+ LoadedDirectory = dllDir;
+
+ if (LoadedDirectory != dllDir)
+ throw new InvalidOperationException($"The native library directory '{LoadedDirectory}' was attempted to switch to '{dllDir}'.");
+
+ ApiInfo |= resolver;
+ if (!ApiInfo.HasFlag_Context())
+ ApiInfo |= ApiInfoFlags.InFrameworkContext;
}
private static bool TryGetRuntimesPath(string libraryName, out string dllPath)
{
+ libraryName = GetFullLibraryName(libraryName);
dllPath = GetRuntimesPaths(libraryName).FirstOrDefault(File.Exists, string.Empty);
return !string.IsNullOrEmpty(dllPath);
}
- private static IEnumerable GetRuntimesPaths(string libraryName)
+ private static IEnumerable GetRuntimesPaths(string libraryFullName)
{
- GetArchitectureNameAndExtensionName(out var arch, out var ext);
- var args1 = new string[]
- {
- Path.GetDirectoryName(s_assembly.Location) ?? "./",
+ var searchPaths = SearchPath.Concat(
+ [
+ Environment.GetEnvironmentVariable("MAAFW_BINARY_PATH"),
+ Path.GetDirectoryName(s_assembly.Location),
Environment.CurrentDirectory,
- };
- var args2 = new string[]
- {
- $"/runtimes/{arch}/native/",
- "/"
- };
- var args3 = new string[]
+ ]).Where(path => !string.IsNullOrWhiteSpace(path));
+
+ var runtimePaths = new[]
{
- $"{libraryName}.{ext}",
- $"lib{libraryName}.{ext}"
+ "./",
+ $"./runtimes/{GetArchitectureName()}/native/",
};
- return from arg1 in args1
- from arg2 in args2
- from arg3 in args3
+ return from searchPath in searchPaths
+ from runtimePath in runtimePaths
select Path.GetFullPath(
- string.Concat(arg1, arg2, arg3));
+ Path.Combine(searchPath, runtimePath, libraryFullName));
}
- private static void GetArchitectureNameAndExtensionName(out string arch, out string ext)
+#pragma warning disable IDE0072 // 添加缺失的事例
+ private static string GetArchitectureName() => RuntimeInformation.OSArchitecture switch
+ {
+ Architecture.X64 when IsWindows => "win-x64",
+ // Architecture.Arm64 when IsWindows => "win-arm64",
+ Architecture.X64 when IsLinux => "linux-x64",
+ Architecture.Arm64 when IsLinux => "linux-arm64",
+ Architecture.X64 when IsOSX => "osx-x64",
+ Architecture.Arm64 when IsOSX => "osx-arm64",
+ Architecture.X64 when IsAndroid => "android-x64",
+ Architecture.Arm64 when IsAndroid => "android-arm64",
+ _ => throw new PlatformNotSupportedException(),
+ };
+#pragma warning restore IDE0072 // 添加缺失的事例
+
+ private static string GetFullLibraryName(string libraryName)
{
- if (IsWindows) arch = "win";
- else if (IsLinux) arch = "linux";
- else if (IsOSX) arch = "osx";
- else if (IsAndroid) arch = "android";
- else throw new PlatformNotSupportedException();
-
- if (IsX64) arch += "-x64";
- else if (IsArm64) arch += "-arm64";
- else throw new PlatformNotSupportedException();
-
- if (IsWindows) ext = "dll";
- else if (IsLinux || IsAndroid) ext = "so";
- else if (IsOSX) ext = "dylib";
- else throw new PlatformNotSupportedException();
+ if (libraryName == "MaaFramework" && ApiInfo.HasFlag(ApiInfoFlags.InAgentServerContext))
+ libraryName = "MaaAgentServer";
+
+ if (IsWindows)
+ return $"{libraryName}.dll";
+ if (IsLinux || IsAndroid)
+ return $"lib{libraryName}.so";
+ if (IsOSX)
+ return $"lib{libraryName}.dylib";
+
+ throw new PlatformNotSupportedException();
}
private static bool IsWindows => OperatingSystem.IsWindows();
private static bool IsLinux => OperatingSystem.IsLinux();
private static bool IsOSX => OperatingSystem.IsMacOS();
private static bool IsAndroid => OperatingSystem.IsAndroid();
- private static bool IsX64 => RuntimeInformation.OSArchitecture == Architecture.X64;
- private static bool IsArm64 => RuntimeInformation.OSArchitecture == Architecture.Arm64;
}
diff --git a/src/MaaFramework.Binding.Native/MaaAgentClient.cs b/src/MaaFramework.Binding.Native/MaaAgentClient.cs
new file mode 100644
index 0000000..8ac875a
--- /dev/null
+++ b/src/MaaFramework.Binding.Native/MaaAgentClient.cs
@@ -0,0 +1,220 @@
+using MaaFramework.Binding.Abstractions;
+using MaaFramework.Binding.Buffers;
+using MaaFramework.Binding.Interop.Native;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using static MaaFramework.Binding.Interop.Native.MaaAgentClient;
+
+namespace MaaFramework.Binding;
+
+///
+/// A wrapper class providing a reference implementation for .
+///
+[DebuggerDisplay("{DebuggerDisplay,nq}")]
+public class MaaAgentClient : MaaDisposableHandle, IMaaAgentClient
+{
+ private bool _isConnected;
+ private Process? _agentServerProcess;
+
+ [ExcludeFromCodeCoverage(Justification = "Debugger display.")]
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private string DebuggerDisplay => IsInvalid
+ ? $"Invalid {GetType().Name}"
+ : $"{GetType().Name} {{ Id = {Id}, IsConnected = {_isConnected} }}";
+
+ ///
+ /// Creates a instance.
+ ///
+ /// The unique identifier used to communicate with the agent server.
+ ///
+ /// Wrapper of .
+ ///
+ protected MaaAgentClient(string identifier = "")
+ : base(invalidHandleValue: MaaAgentClientHandle.Zero)
+ {
+ var handle = MaaAgentClientCreate();
+ SetHandle(handle, needReleased: true);
+ Id = CreateSocket(identifier).ThrowIfNull();
+
+ if (!string.IsNullOrEmpty(identifier))
+ _ = Id.ThrowIfNotEquals(identifier);
+ }
+
+ ///
+ /// Creates a instance.
+ ///
+ /// The unique identifier used to communicate with the agent server.
+ /// The resource.
+ /// The instance.
+ public static MaaAgentClient Create(string identifier, MaaResource resource)
+ => new(identifier) { Resource = resource, };
+
+ ///
+ public static MaaAgentClient Create(MaaResource resource)
+ => new() { Resource = resource, };
+
+ ///
+ public string Id { get; }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ KillAndDisposeAgentServerProcess();
+ }
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ protected override void ReleaseHandle()
+ => MaaAgentClientDestroy(Handle);
+
+ ///
+ IMaaResource IMaaAgentClient.Resource
+ {
+ get => Resource;
+ set => Resource = (MaaResource)value;
+ }
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public required MaaResource Resource
+ {
+ get => field;
+ set
+ {
+ ArgumentNullException.ThrowIfNull(value);
+ _ = MaaAgentClientBindResource(Handle, value.Handle).ThrowIfFalse(MaaInteroperationException.ResourceBindingFailedMessage);
+ field = value;
+ }
+ }
+
+ ///
+ /// Creates a socket connection with the specified identifier.
+ ///
+ /// The specified identifier.
+ /// if the socket was created successfully; otherwise, .
+ ///
+ /// Wrapper of .
+ ///
+ protected string? CreateSocket(string identifier = "")
+ => MaaStringBuffer.TryGetValue(out var socketId, handle
+ => MaaStringBuffer.TrySetValue(handle, identifier)
+ && MaaAgentClientCreateSocket(Handle, handle))
+ ? socketId
+ : null;
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public bool LinkStart()
+ => _isConnected = MaaAgentClientConnect(Handle);
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public bool LinkStart(ProcessStartInfo info, CancellationToken cancellationToken = default)
+ {
+ if (_agentServerProcess is null or { HasExited: true })
+ {
+ _agentServerProcess?.Dispose();
+ _agentServerProcess = Process.Start(info);
+
+ if (_agentServerProcess is null or { HasExited: true })
+ return false;
+ }
+
+ return LinkStartUnlessProcessExit(_agentServerProcess, cancellationToken).GetAwaiter().GetResult();
+ }
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public bool LinkStart(IMaaAgentClient.AgentServerStartupMethod method, CancellationToken cancellationToken = default)
+ {
+ if (_agentServerProcess is null or { HasExited: true })
+ {
+ _agentServerProcess?.Dispose();
+ _agentServerProcess = method.Invoke(Id, NativeBindingContext.LoadedNativeLibraryDirectory);
+
+ if (_agentServerProcess is null or { HasExited: true })
+ return false;
+ }
+
+ return LinkStartUnlessProcessExit(_agentServerProcess, cancellationToken).GetAwaiter().GetResult();
+ }
+
+ ///
+ public async Task LinkStartUnlessProcessExit(Process process, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(process);
+ using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ if (process.HasExited)
+ return false;
+
+ var serverExitTask = process.WaitForExitAsync(cts.Token);
+ var linkStartTask = Task.Run(LinkStart, cts.Token);
+ var completedTask = await Task.WhenAny(linkStartTask, serverExitTask).ConfigureAwait(false);
+
+ try
+ {
+ cts.Token.ThrowIfCancellationRequested();
+ if (completedTask == serverExitTask)
+ return false;
+
+ return linkStartTask.Result;
+ }
+ finally
+ {
+#if NET8_0_OR_GREATER
+ await cts.CancelAsync();
+#else
+ cts.Cancel();
+#endif
+ }
+ }
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public bool LinkStop()
+ {
+ if (_isConnected)
+ {
+ _isConnected = false;
+ if (!MaaAgentClientDisconnect(Handle))
+ {
+ _isConnected = true;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ public Process AgentServerProcess => _agentServerProcess
+ ?? throw new InvalidOperationException($"The agent server process is unavailable or not managed by {nameof(MaaAgentClient)}.");
+
+ private void KillAndDisposeAgentServerProcess()
+ {
+ if (_agentServerProcess is null)
+ return;
+
+ if (!_agentServerProcess.HasExited)
+ {
+ _agentServerProcess.Kill(entireProcessTree: true);
+ _agentServerProcess.WaitForExit();
+ }
+
+ _agentServerProcess.Dispose();
+ _agentServerProcess = null;
+ }
+}
diff --git a/src/MaaFramework.Binding.Native/MaaAgentServer.cs b/src/MaaFramework.Binding.Native/MaaAgentServer.cs
new file mode 100644
index 0000000..7bfa192
--- /dev/null
+++ b/src/MaaFramework.Binding.Native/MaaAgentServer.cs
@@ -0,0 +1,152 @@
+using MaaFramework.Binding.Custom;
+using MaaFramework.Binding.Interop.Native;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using static MaaFramework.Binding.Interop.Native.MaaAgentServer;
+
+namespace MaaFramework.Binding;
+
+///
+/// A wrapper class providing a reference implementation for .
+///
+[DebuggerDisplay("{DebuggerDisplay,nq}")]
+public sealed class MaaAgentServer : IMaaAgentServer
+{
+ ///
+ /// Gets the unique identifier used to communicate with the agent client.
+ ///
+ public static string CurrentId { get; private set; }
+
+ ///
+ /// Gets the current instance.
+ ///
+ public static MaaAgentServer Current { get; }
+
+ ///
+ /// Creates a instance.
+ ///
+ private MaaAgentServer() { }
+ static MaaAgentServer()
+ {
+ NativeBindingContext.SwitchToAgentServerContext();
+ CurrentId = string.Empty;
+ Current = new();
+ }
+
+ private readonly MaaMarshaledApiRegistry _actions = new();
+ private readonly MaaMarshaledApiRegistry _recognitions = new();
+
+ [ExcludeFromCodeCoverage(Justification = "Debugger display.")]
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private string DebuggerDisplay => $"{GetType().Name} {{ {nameof(CurrentId)} = {CurrentId}, CustomActions = [{string.Join(", ", _actions.Names)}] , CustomRecognitions = [{string.Join(" & ", _recognitions.Names)}] }}";
+
+ IMaaAgentServer IMaaAgentServer.WithIdentifier(string identifier) => WithIdentifier(identifier);
+ IMaaAgentServer IMaaAgentServer.Register(string name, T custom) => Register(name, custom);
+ IMaaAgentServer IMaaAgentServer.Register(T custom) => Register(custom);
+ IMaaAgentServer IMaaAgentServer.StartUp() => StartUp();
+ IMaaAgentServer IMaaAgentServer.ShutDown() => ShutDown();
+ IMaaAgentServer IMaaAgentServer.Join() => Join();
+ IMaaAgentServer IMaaAgentServer.Detach() => Detach();
+
+ ///
+ public MaaAgentServer WithIdentifier(string identifier)
+ {
+ CurrentId = identifier;
+ return this;
+ }
+
+ ///
+ public MaaAgentServer Register(string name, T custom) where T : IMaaCustomResource
+ {
+ custom.Name = name;
+ return Register(custom);
+ }
+
+ ///
+ ///
+ /// Wrapper of and .
+ ///
+ public MaaAgentServer Register(T custom) where T : IMaaCustomResource
+ {
+ var ret = custom switch
+ {
+ IMaaCustomAction res
+ => MaaAgentServerRegisterCustomAction(res.Name, res.Convert(out var callback), nint.Zero)
+ && _actions.Register(res.Name, callback),
+ IMaaCustomRecognition res
+ => MaaAgentServerRegisterCustomRecognition(res.Name, res.Convert(out var callback), nint.Zero)
+ && _recognitions.Register(res.Name, callback),
+ _
+ => throw new NotImplementedException($"Type '{typeof(T)}' is not implemented."),
+ };
+ _ = ret.ThrowIfFalse();
+ return this;
+ }
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public MaaAgentServer StartUp()
+ {
+ if (string.IsNullOrEmpty(CurrentId))
+ throw new InvalidOperationException("Identifier is not set. Use 'WithIdentifier' method to set it.");
+ _ = MaaAgentServerStartUp(CurrentId).ThrowIfFalse();
+ return this;
+ }
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public MaaAgentServer ShutDown()
+ {
+ MaaAgentServerShutDown();
+ return this;
+ }
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public MaaAgentServer Join()
+ {
+ MaaAgentServerJoin();
+ return this;
+ }
+
+ ///
+ ///
+ /// Wrapper of .
+ ///
+ public MaaAgentServer Detach()
+ {
+ MaaAgentServerDetach();
+ return this;
+ }
+}
+
+///
+/// A static class providing extension methods for .
+///
+public static class MaaAgentServerExtensions
+{
+ ///
+ ///
+ public static MaaAgentServer WithToolkitConfig_InitOption(this MaaAgentServer server, string userPath = nameof(Environment.CurrentDirectory), [StringSyntax("Json")] string defaultJson = "{}")
+ {
+ _ = MaaToolkit.Shared.Config.InitOption(userPath, defaultJson).ThrowIfFalse();
+ return server;
+ }
+
+ ///
+ /// Configures the MaaAgentServer to use the specified native libraries.
+ ///
+ /// The server.
+ /// The directory paths to search for native libraries.
+ public static MaaAgentServer WithNativeLibrary(this MaaAgentServer server, params string[] paths)
+ {
+ NativeBindingContext.AppendNativeLibrarySearchPaths(paths);
+ return server;
+ }
+}
diff --git a/src/MaaFramework.Binding.Native/MaaContext.cs b/src/MaaFramework.Binding.Native/MaaContext.cs
index ff99598..a52a75d 100644
--- a/src/MaaFramework.Binding.Native/MaaContext.cs
+++ b/src/MaaFramework.Binding.Native/MaaContext.cs
@@ -22,13 +22,16 @@ public MaaContext(MaaContextHandle contextHandle)
if (contextHandle == MaaContextHandle.Zero)
throw new ArgumentException($"Value cannot be {MaaContextHandle.Zero}.", nameof(contextHandle));
Handle = contextHandle;
+
+ var taskerHandle = MaaContextGetTasker(Handle);
+ Tasker = NativeBindingContext.IsStatelessMode ? new MaaTasker(taskerHandle) : MaaTasker.Instances[taskerHandle];
}
///
///
/// Wrapper of .
///
- public TaskDetail? RunTask(string entry, [StringSyntax("Json")] string pipelineOverride)
+ public TaskDetail? RunTask(string entry, [StringSyntax("Json")] string pipelineOverride = "{}")
{
var taskId = MaaContextRunTask(Handle, entry, pipelineOverride);
return taskId == Interop.Native.MaaDef.MaaInvalidId
@@ -37,14 +40,14 @@ public MaaContext(MaaContextHandle contextHandle)
}
///
- public RecognitionDetail? RunRecognition(string entry, [StringSyntax("Json")] string pipelineOverride, IMaaImageBuffer image)
- => RunRecognition(entry, pipelineOverride, (MaaImageBuffer)image);
+ public RecognitionDetail? RunRecognition(string entry, IMaaImageBuffer image, [StringSyntax("Json")] string pipelineOverride = "{}")
+ => RunRecognition(entry, (MaaImageBuffer)image, pipelineOverride);
///
///
/// Wrapper of .
///
- public RecognitionDetail? RunRecognition(string entry, [StringSyntax("Json")] string pipelineOverride, MaaImageBuffer image)
+ public RecognitionDetail? RunRecognition(string entry, MaaImageBuffer image, [StringSyntax("Json")] string pipelineOverride = "{}")
{
ArgumentNullException.ThrowIfNull(image);
var recognitionId = MaaContextRunRecognition(Handle, entry, pipelineOverride, image.Handle);
@@ -54,14 +57,14 @@ public MaaContext(MaaContextHandle contextHandle)
}
///
- public NodeDetail? RunAction(string entry, [StringSyntax("Json")] string pipelineOverride, IMaaRectBuffer recognitionBox, string recognitionDetail)
- => RunAction(entry, pipelineOverride, (MaaRectBuffer)recognitionBox, recognitionDetail);
+ public NodeDetail? RunAction(string entry, IMaaRectBuffer recognitionBox, string recognitionDetail, [StringSyntax("Json")] string pipelineOverride = "{}")
+ => RunAction(entry, (MaaRectBuffer)recognitionBox, recognitionDetail, pipelineOverride);
///
///
/// Wrapper of .
///
- public NodeDetail? RunAction(string entry, [StringSyntax("Json")] string pipelineOverride, MaaRectBuffer recognitionBox, string recognitionDetail)
+ public NodeDetail? RunAction(string entry, MaaRectBuffer recognitionBox, string recognitionDetail, [StringSyntax("Json")] string pipelineOverride = "{}")
{
ArgumentNullException.ThrowIfNull(recognitionBox);
var nodeId = MaaContextRunAction(Handle, entry, pipelineOverride, recognitionBox.Handle, recognitionDetail);
@@ -97,7 +100,7 @@ public bool OverrideNext(string nodeName, IEnumerable nextList)
///
/// Wrapper of .
///
- public MaaTasker Tasker => MaaTasker.Instances[MaaContextGetTasker(Handle)];
+ public MaaTasker Tasker { get; }
object ICloneable.Clone()
=> Clone();
diff --git a/src/MaaFramework.Binding.Native/MaaController.cs b/src/MaaFramework.Binding.Native/MaaController.cs
index e0d2317..8dfdca4 100644
--- a/src/MaaFramework.Binding.Native/MaaController.cs
+++ b/src/MaaFramework.Binding.Native/MaaController.cs
@@ -11,7 +11,7 @@ namespace MaaFramework.Binding;
/// A wrapper class providing a reference implementation for .
///
[DebuggerDisplay("{DebuggerDisplay,nq}")]
-public abstract class MaaController : MaaCommon, IMaaController
+public class MaaController : MaaCommon, IMaaController
{
[ExcludeFromCodeCoverage(Justification = "Debugger display.")]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
@@ -19,6 +19,11 @@ public abstract class MaaController : MaaCommon, IMaaController
? $"Invalid {GetType().Name}"
: $"{GetType().Name} {{ }}";
+ internal MaaController(MaaControllerHandle handle)
+ {
+ SetHandle(handle, needReleased: false);
+ }
+
///
/// Creates a instance.
///
diff --git a/src/MaaFramework.Binding.Native/MaaResource.cs b/src/MaaFramework.Binding.Native/MaaResource.cs
index d2074b6..3c15f53 100644
--- a/src/MaaFramework.Binding.Native/MaaResource.cs
+++ b/src/MaaFramework.Binding.Native/MaaResource.cs
@@ -9,14 +9,19 @@ namespace MaaFramework.Binding;
///
/// A wrapper class providing a reference implementation for .
///
-public class MaaResource : MaaCommon, IMaaResource
+public class MaaResource : MaaCommon, IMaaResource
{
private readonly HashSet _postedPaths = [];
///
public override string ToString() => IsInvalid
? $"Invalid {GetType().Name}"
- : $"{GetType().Name} {{ Paths = {string.Join(" & ", _postedPaths)}, CustomActions = {string.Join(" & ", _actions.Names)}, CustomRecognitions = {string.Join(" & ", _recognitions.Names)} }}";
+ : $"{GetType().Name} {{ Paths = [{string.Join(", ", _postedPaths)}], CustomActions = [{string.Join(", ", _actions.Names)}] , CustomRecognitions = [{string.Join(" & ", _recognitions.Names)}] }}";
+
+ internal MaaResource(MaaResourceHandle handle)
+ {
+ SetHandle(handle, needReleased: false);
+ }
///
/// Creates a instance.
diff --git a/src/MaaFramework.Binding.Native/MaaTasker.cs b/src/MaaFramework.Binding.Native/MaaTasker.cs
index 2e1f403..1839f20 100644
--- a/src/MaaFramework.Binding.Native/MaaTasker.cs
+++ b/src/MaaFramework.Binding.Native/MaaTasker.cs
@@ -28,6 +28,19 @@ public class MaaTasker : MaaCommon, IMaaTasker
///
protected internal static ConcurrentDictionary Instances { get; } = [];
+#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。
+ [SetsRequiredMembers]
+ internal MaaTasker(MaaTaskerHandle handle)
+ {
+ SetHandle(handle, needReleased: false);
+ _resource = new MaaResource(MaaTaskerGetResource(handle));
+ _controller = new MaaController(MaaTaskerGetController(handle));
+ DisposeOptions = DisposeOptions.None;
+ Toolkit = MaaToolkit.Shared;
+ Utility = MaaUtility.Shared;
+ }
+#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。
+
///
/// Creates a instance.
///
@@ -43,8 +56,10 @@ public MaaTasker(bool toolkitInit = false)
throw new InvalidOperationException($"This {nameof(MaaTasker)} already added to {nameof(Instances)}.");
SetHandle(handle, needReleased: true);
- Toolkit = new MaaToolkit(toolkitInit);
- Utility = new MaaUtility();
+ Toolkit = MaaToolkit.Shared;
+ Utility = MaaUtility.Shared;
+ if (toolkitInit)
+ _ = Toolkit.Config.InitOption().ThrowIfFalse();
}
/// The controller.
@@ -67,18 +82,13 @@ public MaaTasker(MaaController controller, MaaResource resource, DisposeOptions
///
protected override void Dispose(bool disposing)
{
- // Cannot destroy Instance before disposing Controller and Resource.
-
if (DisposeOptions.HasFlag(DisposeOptions.Controller))
- {
Controller.Dispose();
- }
if (DisposeOptions.HasFlag(DisposeOptions.Resource))
- {
Resource.Dispose();
- }
+ _ = Instances.TryRemove(new KeyValuePair(Handle, this));
base.Dispose(disposing);
}
@@ -109,6 +119,9 @@ public bool SetOption(TaskerOption opt, T value)
#pragma warning restore
}
+ private MaaResource _resource = null!;
+ private MaaController _controller = null!;
+
IMaaResource IMaaTasker.Resource
{
get => Resource;
@@ -130,14 +143,14 @@ public required MaaResource Resource
get
{
if (!IsInvalid)
- _ = MaaTaskerGetResource(Handle).ThrowIfNotEquals(field.Handle, MaaInteroperationException.ResourceModifiedMessage);
- return field;
+ _ = MaaTaskerGetResource(Handle).ThrowIfNotEquals(_resource.Handle, MaaInteroperationException.ResourceModifiedMessage);
+ return _resource;
}
set
{
ArgumentNullException.ThrowIfNull(value);
_ = MaaTaskerBindResource(Handle, value.Handle).ThrowIfFalse(MaaInteroperationException.ResourceBindingFailedMessage);
- field = value;
+ _resource = value;
}
}
@@ -150,14 +163,14 @@ public required MaaController Controller
get
{
if (!IsInvalid)
- _ = MaaTaskerGetController(Handle).ThrowIfNotEquals(field.Handle, MaaInteroperationException.ControllerModifiedMessage);
- return field;
+ _ = MaaTaskerGetController(Handle).ThrowIfNotEquals(_controller.Handle, MaaInteroperationException.ControllerModifiedMessage);
+ return _controller;
}
set
{
ArgumentNullException.ThrowIfNull(value);
_ = MaaTaskerBindController(Handle, value.Handle).ThrowIfFalse(MaaInteroperationException.ControllerBindingFailedMessage);
- field = value;
+ _controller = value;
}
}
diff --git a/src/MaaFramework.Binding.Native/MaaToolkit.cs b/src/MaaFramework.Binding.Native/MaaToolkit.cs
index 357ce6e..7c514e1 100644
--- a/src/MaaFramework.Binding.Native/MaaToolkit.cs
+++ b/src/MaaFramework.Binding.Native/MaaToolkit.cs
@@ -12,6 +12,11 @@ namespace MaaFramework.Binding;
///
public class MaaToolkit : IMaaToolkit
{
+ ///
+ /// Gets the shared instance.
+ ///
+ public static MaaToolkit Shared { get; } = new();
+
///
/// Creates a instance.
///
diff --git a/src/MaaFramework.Binding.Native/MaaUtility.cs b/src/MaaFramework.Binding.Native/MaaUtility.cs
index af10545..2d8cbec 100644
--- a/src/MaaFramework.Binding.Native/MaaUtility.cs
+++ b/src/MaaFramework.Binding.Native/MaaUtility.cs
@@ -8,6 +8,11 @@ namespace MaaFramework.Binding;
///
public class MaaUtility : IMaaUtility
{
+ ///
+ /// Gets the shared instance.
+ ///
+ public static MaaUtility Shared { get; } = new();
+
///
///
/// Wrapper of .
diff --git a/src/MaaFramework.Binding.Native/NativeBindingContext.cs b/src/MaaFramework.Binding.Native/NativeBindingContext.cs
new file mode 100644
index 0000000..1201dae
--- /dev/null
+++ b/src/MaaFramework.Binding.Native/NativeBindingContext.cs
@@ -0,0 +1,85 @@
+using MaaFramework.Binding.Interop.Native;
+using System.Diagnostics;
+
+namespace MaaFramework.Binding;
+
+///
+/// Provides information and configuration for native bindings in the MaaFramework.
+///
+public static class NativeBindingContext
+{
+ ///
+ /// Gets a value indicating whether NativeLibrary is already loaded.
+ ///
+ public static bool IsLoaded => NativeLibrary.IsLoaded;
+
+ ///
+ /// Gets the loaded directory path where native libraries are located.
+ ///
+ public static string LoadedNativeLibraryDirectory
+ {
+ get
+ {
+ if (!NativeLibrary.ApiInfo.HasFlag(ApiInfoFlags.UseDefaultResolver))
+ return NativeLibrary.LoadedDirectory;
+
+ var name = NativeLibrary.LoadedLibraryHandles.First().Key;
+ foreach (var obj in Process.GetCurrentProcess().Modules)
+ {
+ if (obj is not ProcessModule module || Path.GetFileNameWithoutExtension(module.ModuleName) != name)
+ continue;
+ return Path.GetDirectoryName(module.FileName) ?? string.Empty;
+ }
+ return string.Empty;
+ }
+ }
+
+ ///
+ /// Gets the API information, which provides details about the current API context.
+ ///
+ public static ApiInfoFlags ApiInfo => NativeLibrary.ApiInfo;
+
+ ///
+ /// Gets a value indicating whether the current API is interoperating in the stateless mode.
+ /// Stateless mode is typically used in server contexts.
+ ///
+ public static bool IsStatelessMode => ApiInfo.HasFlag(ApiInfoFlags.InAgentServerContext);
+
+ ///
+ /// Switches the context to the MaaAgentServer context.
+ ///
+ /// NativeLibrary is already loaded.
+ public static void SwitchToAgentServerContext()
+ {
+ ThrowIfLoaded();
+ NativeLibrary.ApiInfo = ApiInfoFlags.InAgentServerContext;
+ }
+
+ ///
+ /// Switches the context to the MaaFramework context.
+ ///
+ /// NativeLibrary is already loaded.
+ public static void SwitchToFrameworkContext()
+ {
+ ThrowIfLoaded();
+ NativeLibrary.ApiInfo = ApiInfoFlags.InFrameworkContext;
+ }
+
+ ///
+ /// Appends the specified paths to the native library search paths.
+ ///
+ ///
+ /// NativeLibrary is already loaded.
+ public static void AppendNativeLibrarySearchPaths(params IEnumerable paths)
+ {
+ ThrowIfLoaded();
+ NativeLibrary.SearchPath.AddRange(paths);
+ }
+
+ /// NativeLibrary is already loaded.
+ internal static void ThrowIfLoaded()
+ {
+ if (NativeLibrary.IsLoaded)
+ throw new InvalidOperationException("NativeLibrary is already loaded.");
+ }
+}
diff --git a/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj b/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj
index 1078b2b..e116e79 100644
--- a/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj
+++ b/src/MaaFramework.Binding.UnitTests/MaaFramework.Binding.UnitTests.csproj
@@ -5,25 +5,20 @@
-
-
+
+
+
PreserveNewest
-
-
-
-
-
+ false
$(NoWarn);S1121,S1656,S1199,IDE0022,IDE0060,IDE0072
-
-
$(DefineConstants);MAA_NATIVE;
diff --git a/src/MaaFramework.Binding.UnitTests/Properties/launchSettings.json b/src/MaaFramework.Binding.UnitTests/Properties/launchSettings.json
new file mode 100644
index 0000000..6b50930
--- /dev/null
+++ b/src/MaaFramework.Binding.UnitTests/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "MaaFramework.Binding.UnitTests": {
+ "commandName": "Project",
+ "commandLineArgs": "CurrentDirectory CurrentDirectory 6CDC213A-085C-40C8-8665-635820D10425"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs b/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs
index 6fa19f1..29b7f4c 100644
--- a/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs
+++ b/src/MaaFramework.Binding.UnitTests/Test_Buffers.cs
@@ -22,9 +22,9 @@ public class Test_Buffers
new MaaStringBuffer(),
new MaaImageListBuffer(),
new MaaStringListBuffer(),
- (AdbDeviceListBuffer)new MaaToolkit().AdbDevice.Find(),
+ (AdbDeviceListBuffer)MaaToolkit.Shared.AdbDevice.Find(),
#if MAA_WIN32
- (DesktopWindowListBuffer)new MaaToolkit().Desktop.Window.Find(),
+ (DesktopWindowListBuffer)MaaToolkit.Shared.Desktop.Window.Find(),
#endif
]
},
@@ -1101,6 +1101,7 @@ public bool ThrowOnInvalid
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
+ public bool IsStateless => throw new NotImplementedException();
public bool IsEmpty => throw new NotImplementedException();
public bool TryClear() => throw new NotImplementedException();
public ImageInfo GetInfo() => throw new NotImplementedException();
@@ -1126,6 +1127,7 @@ public bool ThrowOnInvalid
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
+ public bool IsStateless => throw new NotImplementedException();
public bool IsEmpty => throw new NotImplementedException();
public ulong Size => throw new NotImplementedException();
public bool TryClear() => throw new NotImplementedException();
@@ -1143,6 +1145,7 @@ public bool ThrowOnInvalid
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
+ public bool IsStateless => throw new NotImplementedException();
public int X => throw new NotImplementedException();
public int Y => throw new NotImplementedException();
public int Width => throw new NotImplementedException();
diff --git a/src/MaaFramework.Binding.UnitTests/Test_Common.cs b/src/MaaFramework.Binding.UnitTests/Test_Common.cs
index 8594752..4305396 100644
--- a/src/MaaFramework.Binding.UnitTests/Test_Common.cs
+++ b/src/MaaFramework.Binding.UnitTests/Test_Common.cs
@@ -27,7 +27,7 @@ private static void InitializeInfo(TestContext testContext)
#if GITHUB_ACTIONS // use environment "AdbPath"
AdbDeviceInfo[] devices = [];
#else
- var devices = new MaaToolkit().AdbDevice.Find();
+ var devices = MaaToolkit.Shared.AdbDevice.Find();
#endif
// 请修改 TestParam.runsettings,并在测试资源管理器——设置——配置运行设置
@@ -52,8 +52,8 @@ public static void InitializeAssembly(TestContext testContext)
{
ArgumentNullException.ThrowIfNull(testContext);
- _ = new MaaUtility().SetOption(GlobalOption.LogDir, DebugPath);
- _ = new MaaUtility().SetOption(GlobalOption.StdoutLevel, LoggingLevel.Off);
+ _ = MaaUtility.Shared.SetOption_LogDir(DebugPath);
+ _ = MaaUtility.Shared.SetOption_StdoutLevel(LoggingLevel.Off);
InitializeInfo(testContext);
}
diff --git a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs
index f5822bb..9354d3a 100644
--- a/src/MaaFramework.Binding.UnitTests/Test_Custom.cs
+++ b/src/MaaFramework.Binding.UnitTests/Test_Custom.cs
@@ -35,7 +35,10 @@ public bool Analyze(in IMaaContext context, in AnalyzeArgs args, in AnalyzeResul
Assert.AreEqual(NodeName, args.NodeName);
Assert.AreEqual(RecognitionParam, args.RecognitionParam);
- _ = Assert.ThrowsException(() => new MaaContext(IntPtr.Zero));
+ _ = Assert.ThrowsException(() =>
+#if MAA_NATIVE
+ new MaaContext(IntPtr.Zero));
+#endif
var cloneContext = (context as ICloneable).Clone() as IMaaContext;
cloneContext = cloneContext?.Clone();
#if MAA_NATIVE
@@ -43,14 +46,18 @@ public bool Analyze(in IMaaContext context, in AnalyzeArgs args, in AnalyzeResul
#endif
Assert.IsNotNull(cloneContext);
Assert.IsNull(
- cloneContext.RunRecognition(DiffEntry, "{}", (IMaaImageBuffer)args.Image));
- Assert.AreSame(
- context.Tasker, cloneContext.Tasker);
+ cloneContext.RunRecognition(DiffEntry, args.Image));
+ if (!context.Tasker.IsStateless)
+ {
+ Assert.AreSame(
+ context.Tasker, cloneContext.Tasker);
+ }
+
Assert.AreEqual(
context.TaskJob.Id, cloneContext.TaskJob.Id);
var recognitionDetail =
- context.RunRecognition(DiffEntry, DiffParam, args.Image);
+ context.RunRecognition(DiffEntry, args.Image, DiffParam);
Assert.IsNotNull(
recognitionDetail?.HitBox);
@@ -91,7 +98,7 @@ internal sealed class TestAction : IMaaCustomAction
{
public string Name { get; set; } = nameof(TestAction);
- public bool Run(in IMaaContext context, in RunArgs args)
+ public bool Run(in IMaaContext context, in RunArgs args, in RunResults results)
{
Assert.AreEqual(NodeName, args.NodeName);
Assert.AreEqual(ActionParam, args.ActionParam);
@@ -99,13 +106,13 @@ public bool Run(in IMaaContext context, in RunArgs args)
Assert.AreNotEqual(Detail, args.RecognitionDetail.Detail);
Assert.AreEqual(Box, $"{args.RecognitionBox.X}{args.RecognitionBox.Y}{args.RecognitionBox.Width}{args.RecognitionBox.Height}");
- var nodeDetail = context.RunAction(DiffEntry, DiffParam, args.RecognitionBox, args.RecognitionDetail.Detail);
+ var nodeDetail = context.RunAction(DiffEntry, args.RecognitionBox, args.RecognitionDetail.Detail, DiffParam);
Assert.IsNotNull(nodeDetail);
return true;
}
}
- internal sealed class TestController(MaaController c) : IMaaCustomController, IMaaDisposable
+ internal sealed class TestController(IMaaController c) : IMaaCustomController, IMaaDisposable
{
#region Test_IMaaDisposable
@@ -117,6 +124,8 @@ public bool ThrowOnInvalid
set => c.ThrowOnInvalid = value;
}
+ public bool IsStateless => c.IsStateless;
+
public void Dispose() => c.Dispose();
#endregion
@@ -137,7 +146,9 @@ public bool PressKey(int keycode)
public bool RequestResolution(out int width, out int height)
{
+#if MAA_NATIVE
using var image = new MaaImageBuffer();
+#endif
if (Screencap(image))
{
width = image.Width;
diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs
new file mode 100644
index 0000000..240a311
--- /dev/null
+++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentClient.cs
@@ -0,0 +1,121 @@
+using MaaFramework.Binding.Abstractions;
+using System.Diagnostics;
+
+namespace MaaFramework.Binding.UnitTests;
+
+///
+/// Test and .
+///
+[TestClass]
+// ReSharper disable InconsistentNaming
+public class Test_IMaaAgentClient
+{
+ public static Dictionary NewData => new()
+ {
+#if MAA_NATIVE
+ { MaaTypes.Native, MaaAgentClient.Create(new MaaResource()) },
+#endif
+ };
+ public static Dictionary Data { get; private set; } = default!;
+
+ [ClassInitialize]
+ public static void InitializeClass(TestContext context)
+ {
+ Data = NewData;
+ foreach (var data in Data.Values.Cast())
+ {
+ Assert.IsFalse(data.IsInvalid);
+ }
+ }
+
+ [ClassCleanup]
+ public static void CleanUpClass()
+ {
+ Common.DisposeData(Data.Values.Cast());
+ }
+
+ [TestMethod]
+ public void CreateInstances()
+ {
+#if MAA_NATIVE
+ var newId = Guid.NewGuid().ToString();
+ using var native1 = MaaAgentClient.Create(new MaaResource());
+ using var native2 = MaaAgentClient.Create(new MaaResource());
+ using var native3 = MaaAgentClient.Create(newId, new MaaResource());
+
+ Assert.AreNotEqual(native1.Id, native2.Id);
+ Assert.AreEqual(newId, native3.Id);
+#endif
+ }
+
+ [TestMethod]
+ [MaaData(MaaTypes.All, nameof(Data))]
+ public void Interface_Id_Resource(MaaTypes type, IMaaAgentClient maaAgentClient)
+ {
+ Assert.AreNotEqual(string.Empty, maaAgentClient.Id);
+ Assert.IsNotNull(maaAgentClient.Resource);
+ }
+
+ [TestMethod]
+ [MaaData(MaaTypes.All, nameof(Data))]
+ public void Interface_LinkStart_LinkStop_AgentServerProcess(MaaTypes type, IMaaAgentClient maaAgentClient)
+ {
+ _ = Assert.ThrowsException(() =>
+ maaAgentClient.AgentServerProcess);
+ var ret = maaAgentClient.LinkStart(StartupAgentServer);
+ Assert.IsTrue(
+ ret);
+ Assert.IsTrue( // double start
+ maaAgentClient.LinkStart());
+ Assert.IsFalse(
+ maaAgentClient.AgentServerProcess.HasExited);
+
+ Assert.IsTrue(
+ maaAgentClient.LinkStop());
+ Assert.IsTrue( // double stop
+ maaAgentClient.LinkStop());
+ Task.Delay(100).Wait(); // wait for process exit
+ Assert.IsTrue(
+ maaAgentClient.AgentServerProcess.HasExited);
+ }
+
+ [TestMethod]
+ [MaaData(MaaTypes.All, nameof(Data))]
+ public void RunTask(MaaTypes type, IMaaAgentClient maaAgentClient)
+ {
+ using var maa = new MaaTasker
+ {
+ Controller = new MaaAdbController(Common.AdbPath, Common.Address, AdbScreencapMethods.Encode, AdbInputMethods.AdbShell, Common.AdbConfig, Common.AgentPath),
+ Resource = new MaaResource(),
+ DisposeOptions = DisposeOptions.All,
+ };
+ Assert.IsTrue(
+ maa.IsInitialized);
+
+ using var agent = MaaAgentClient.Create("6CDC213A-085C-40C8-8665-635820D10425", maa.Resource);
+ using (var cts = new CancellationTokenSource(10000))
+ {
+ Assert.IsTrue(
+ // agent.LinkStart());
+ agent.LinkStart(StartupAgentServer, cts.Token));
+ }
+ var status = maa
+ .AppendTask(Custom.NodeName, Custom.Param)
+ .Wait();
+
+ Assert.AreEqual(MaaJobStatus.Succeeded,
+ status);
+ Assert.IsTrue(
+ agent.LinkStop());
+
+ }
+
+ private static Process? StartupAgentServer(string identifier, string nativeAssemblyDirectory)
+ {
+ return Process.Start(new ProcessStartInfo(
+ "dotnet", $"{typeof(Test_IMaaAgentServer).Assembly.Location} {nativeAssemblyDirectory} {Environment.CurrentDirectory} {identifier}")
+ {
+ UseShellExecute = false,
+ });
+ }
+}
diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs
new file mode 100644
index 0000000..de61aa6
--- /dev/null
+++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaAgentServer.cs
@@ -0,0 +1,34 @@
+namespace MaaFramework.Binding.UnitTests;
+
+internal static class Test_IMaaAgentServer
+{
+ public static int Main()
+ {
+ var commandLineArgs = Environment.GetCommandLineArgs();
+ if (commandLineArgs.Length < 4)
+ {
+ Console.WriteLine("Call AgentMain.cs instead of this file.");
+ return 1;
+ }
+
+ var socketId = commandLineArgs[^1];
+ var userPath = commandLineArgs[^2];
+ var dllPath = commandLineArgs[^3];
+ Test(socketId, userPath, dllPath);
+
+ return 0;
+ }
+
+ public static void Test(string id, string userPath, string dllPath)
+ {
+ _ = MaaAgentServer.Current // test double call
+ .WithIdentifier(id).WithIdentifier(id)
+ .WithNativeLibrary(dllPath).WithNativeLibrary(dllPath)
+ .WithToolkitConfig_InitOption(userPath).WithToolkitConfig_InitOption(userPath)
+ .Register(Custom.Recognition) // cat not double call from here
+ .Register(Custom.Action)
+ .StartUp()
+ .Join().Join() // test double call
+ .ShutDown().ShutDown();
+ }
+}
diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs
index 72b2ea1..901a9db 100644
--- a/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs
+++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaController.cs
@@ -63,7 +63,7 @@ static void InitializeData(AdbInputMethods inputPreset)
.Wait()
.ThrowIfNot(MaaJobStatus.Succeeded);
Assert.IsTrue(
- data.SetOption(ControllerOption.ScreenshotTargetShortSide, 720));
+ data.SetOption_ScreenshotTargetShortSide(720));
}
}
}
@@ -112,8 +112,7 @@ public void CreateInstances()
#if !GITHUB_ACTIONS
#region MaaWin32Controller
- var toolkit = new MaaToolkit();
- var windowInfo = toolkit.Desktop.Window.Find().First(static x
+ var windowInfo = MaaToolkit.Shared.Desktop.Window.Find().First(static x
=> x.Name.Contains("Visual Studio", StringComparison.OrdinalIgnoreCase)
|| x.Name.Contains("Maa", StringComparison.OrdinalIgnoreCase));
diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaResource.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaResource.cs
index 0036dcc..c371e46 100644
--- a/src/MaaFramework.Binding.UnitTests/Test_IMaaResource.cs
+++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaResource.cs
@@ -32,7 +32,7 @@ public static void InitializeClass(TestContext context)
data.Callback += Common.NotificationHandlerRegistry.OnCallback;
data.Callback += Common.OnResourceLoading.ToCallback();
- _ = data.SetOption(ResourceOption.InferenceDevice, InferenceDevice.CPU);
+ _ = data.SetOption_InferenceDevice(InferenceDevice.CPU);
}
}
diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaTasker.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaTasker.cs
index 1e76282..84d66ab 100644
--- a/src/MaaFramework.Binding.UnitTests/Test_IMaaTasker.cs
+++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaTasker.cs
@@ -52,15 +52,15 @@ public static void InitializeClass(TestContext context)
.Wait()
.ThrowIfNot(MaaJobStatus.Succeeded);
_ = data.Resource
- .SetOption(ResourceOption.InferenceDevice, InferenceDevice.CPU);
+ .SetOption_InferenceDevice(InferenceDevice.CPU);
_ = data.Resource
- .SetOption(ResourceOption.InferenceExecutionProvider, InferenceExecutionProvider.CPU);
+ .SetOption_InferenceExecutionProvider(InferenceExecutionProvider.CPU);
_ = data.Controller
.LinkStart()
.Wait()
.ThrowIfNot(MaaJobStatus.Succeeded);
_ = data.Controller
- .SetOption(ControllerOption.ScreenshotTargetShortSide, 720);
+ .SetOption_ScreenshotTargetShortSide(720);
Assert.IsTrue(data.IsInitialized);
}
}
@@ -328,7 +328,7 @@ public void MaaTaskJob_Query(MaaTypes type, IMaaTasker maaTasker, string taskEnt
{
Assert.IsNotNull(maaTasker);
Assert.IsTrue(
- maaTasker.Utility.SetOption(GlobalOption.DebugMode, debugMode));
+ maaTasker.Utility.SetOption_DebugMode(debugMode));
var job =
maaTasker.AppendTask(taskEntryName, diff);
@@ -358,7 +358,7 @@ public void MaaTaskJob_Query(MaaTypes type, IMaaTasker maaTasker, string taskEnt
}
Assert.IsTrue(
- maaTasker.Utility.SetOption(GlobalOption.DebugMode, false));
+ maaTasker.Utility.SetOption_DebugMode(false));
}
[TestMethod]
diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs
index a6de2da..33bbb88 100644
--- a/src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs
+++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaToolkit.cs
@@ -12,7 +12,7 @@ public class Test_IMaaToolkit
public static Dictionary NewData => new()
{
#if MAA_NATIVE
- { MaaTypes.Native, new MaaToolkit() },
+ { MaaTypes.Native, MaaToolkit.Shared },
#endif
};
public static Dictionary Data { get; private set; } = default!;
diff --git a/src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs b/src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs
index d50a234..3cadf96 100644
--- a/src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs
+++ b/src/MaaFramework.Binding.UnitTests/Test_IMaaUtility.cs
@@ -10,7 +10,7 @@ public class Test_IMaaMaaUtility
public static Dictionary NewData => new()
{
#if MAA_NATIVE
- { MaaTypes.Native, new MaaUtility() },
+ { MaaTypes.Native, MaaUtility.Shared },
#endif
};
public static Dictionary Data { get; private set; } = default!;
diff --git a/src/MaaFramework.Binding.UnitTests/packages.lock.json b/src/MaaFramework.Binding.UnitTests/packages.lock.json
index feecfca..5e33f5c 100644
--- a/src/MaaFramework.Binding.UnitTests/packages.lock.json
+++ b/src/MaaFramework.Binding.UnitTests/packages.lock.json
@@ -42,33 +42,33 @@
},
"Maa.Framework.Runtime.linux-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "xobEEAtdwH/TCtVVeqzyZwWpgmBj9/G+YKDhw8otumXFo6gzTFwwm917sc4La6LZfztolbhEP3HjHdkBXZAlnA=="
+ "resolved": "4.0.0",
+ "contentHash": "UO0WrFCkSAzW3CGj0N6ah+Znn4MTkd7CeWrdxJiSyRkRtjgIl2lPf67ZbbdYdCpXMF7PxQdwJaqo1A895kI/Qg=="
},
"Maa.Framework.Runtime.linux-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "lezMomm+OM83YIIRMeuH4kxp1dkGzi9ufRhI8HNAmWd5mQGVScR75Gz55dZlGQ0UPmtwPP2PT26/4D92zN8Tow=="
+ "resolved": "4.0.0",
+ "contentHash": "DAhsxoDXPJN7A96gZTUAWnub92xDKtd8MXLx0mxipvBPRJ/Qw4wes2IKYididKkMNBj1XNiqQ3ENQAvjgm05Bw=="
},
"Maa.Framework.Runtime.osx-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "fmsehoumgpe5Il2BGl8FrHczUiWr42qUJXYM/GagSW5MlB/hDlxGwA+Apmks8KerNpDAqv1NsuPXRQxnOuvMpw=="
+ "resolved": "4.0.0",
+ "contentHash": "yYMbg9TRzlpe9YB96fT1oTpWcX7Yg7dNGjmk7mfSZ+nm3ZRY95Uy7GJ3apk+kgCim89KEt8LsV5mmLgCNXAhpg=="
},
"Maa.Framework.Runtime.osx-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "afAeMiMiBOvuQfgXaiOiF3NAtWWKpigybM6pdxpAXupK5lyBrwyMgxh6hgzpduD3D+ohzdf4a5sdWdxLbyrdxw=="
+ "resolved": "4.0.0",
+ "contentHash": "5SvGaS2CMXECIC0QVzzr9hPUZQscIHIn1nddr8d7zyISTokkXMwQvaZRSu6hAibECXIg44yiowFYPPnS7SvOnQ=="
},
"Maa.Framework.Runtime.win-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "tl9tEzViM5vwmuLE1ozMP+YdVBK2A8E1RdXCqk4zXRoaYgLqH24V9NVjACDIKDE9MArv4Yqask0s3JMJick1dg=="
+ "resolved": "4.0.0",
+ "contentHash": "h2QLNIn2m7w2uzN1696n0v5HPyK+aR0DexyiCBbjYQTdOvfXC1V5QcP9boCf0VTrQMhN7Wr+qtCbbwb5qOiXJA=="
},
"Maa.Framework.Runtime.win-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "RxHzvnyVer26AMJfyME+EWFf2TKK8Ka8a3fBJQMJGZgvK+g06MTxW5LbumQp4F5ylMJvHr/+SVwo9sxZObsUBg=="
+ "resolved": "4.0.0",
+ "contentHash": "KkP36yP1s03GOcTDnm9HscOzZa5U2IeMcfV4gEZ1cg/Yji7ynKKtV2mRIsZnpPs2deFuTL3Qn22uPr9C8mHIqw=="
},
"Microsoft.ApplicationInsights": {
"type": "Transitive",
@@ -161,7 +161,7 @@
"type": "Project",
"dependencies": {
"Maa.Framework.Native": "[1.0.0-dev, )",
- "Maa.Framework.Runtimes": "[3.0.4, )"
+ "Maa.Framework.Runtimes": "[4.0.0, )"
}
},
"Maa.Framework.Binding": {
@@ -182,28 +182,28 @@
"Maa.Framework.Native": {
"type": "Project",
"dependencies": {
- "Maa.AgentBinary": "[1.0.0, )",
+ "Maa.AgentBinary": "[1.1.0, )",
"Maa.Framework.Binding.Native": "[1.0.0-dev, )"
}
},
"Maa.AgentBinary": {
"type": "CentralTransitive",
- "requested": "[1.0.0, )",
- "resolved": "1.0.0",
- "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA=="
+ "requested": "[1.1.0, )",
+ "resolved": "1.1.0",
+ "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A=="
},
"Maa.Framework.Runtimes": {
"type": "CentralTransitive",
- "requested": "[3.0.4, )",
- "resolved": "3.0.4",
- "contentHash": "F9As/LIRRxXvrkubxgHxExoxCqVzYkfo1Ph3kHYxCYXrdUAZVXESr3o99L+NPv2rUZ9VkupKeL+Xomk4hXCcVw==",
+ "requested": "[4.0.0, )",
+ "resolved": "4.0.0",
+ "contentHash": "hHYtVnknsGZJj22TO4gEfrqUlhq6BfF9DBIM1w0U0dv0mz4fzijawmZJ9QpRX0UldOXq7HN8TBC2lSLyz00FAA==",
"dependencies": {
- "Maa.Framework.Runtime.linux-arm64": "3.0.4",
- "Maa.Framework.Runtime.linux-x64": "3.0.4",
- "Maa.Framework.Runtime.osx-arm64": "3.0.4",
- "Maa.Framework.Runtime.osx-x64": "3.0.4",
- "Maa.Framework.Runtime.win-arm64": "3.0.4",
- "Maa.Framework.Runtime.win-x64": "3.0.4"
+ "Maa.Framework.Runtime.linux-arm64": "4.0.0",
+ "Maa.Framework.Runtime.linux-x64": "4.0.0",
+ "Maa.Framework.Runtime.osx-arm64": "4.0.0",
+ "Maa.Framework.Runtime.osx-x64": "4.0.0",
+ "Maa.Framework.Runtime.win-arm64": "4.0.0",
+ "Maa.Framework.Runtime.win-x64": "4.0.0"
}
}
},
@@ -248,33 +248,33 @@
},
"Maa.Framework.Runtime.linux-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "xobEEAtdwH/TCtVVeqzyZwWpgmBj9/G+YKDhw8otumXFo6gzTFwwm917sc4La6LZfztolbhEP3HjHdkBXZAlnA=="
+ "resolved": "4.0.0",
+ "contentHash": "UO0WrFCkSAzW3CGj0N6ah+Znn4MTkd7CeWrdxJiSyRkRtjgIl2lPf67ZbbdYdCpXMF7PxQdwJaqo1A895kI/Qg=="
},
"Maa.Framework.Runtime.linux-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "lezMomm+OM83YIIRMeuH4kxp1dkGzi9ufRhI8HNAmWd5mQGVScR75Gz55dZlGQ0UPmtwPP2PT26/4D92zN8Tow=="
+ "resolved": "4.0.0",
+ "contentHash": "DAhsxoDXPJN7A96gZTUAWnub92xDKtd8MXLx0mxipvBPRJ/Qw4wes2IKYididKkMNBj1XNiqQ3ENQAvjgm05Bw=="
},
"Maa.Framework.Runtime.osx-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "fmsehoumgpe5Il2BGl8FrHczUiWr42qUJXYM/GagSW5MlB/hDlxGwA+Apmks8KerNpDAqv1NsuPXRQxnOuvMpw=="
+ "resolved": "4.0.0",
+ "contentHash": "yYMbg9TRzlpe9YB96fT1oTpWcX7Yg7dNGjmk7mfSZ+nm3ZRY95Uy7GJ3apk+kgCim89KEt8LsV5mmLgCNXAhpg=="
},
"Maa.Framework.Runtime.osx-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "afAeMiMiBOvuQfgXaiOiF3NAtWWKpigybM6pdxpAXupK5lyBrwyMgxh6hgzpduD3D+ohzdf4a5sdWdxLbyrdxw=="
+ "resolved": "4.0.0",
+ "contentHash": "5SvGaS2CMXECIC0QVzzr9hPUZQscIHIn1nddr8d7zyISTokkXMwQvaZRSu6hAibECXIg44yiowFYPPnS7SvOnQ=="
},
"Maa.Framework.Runtime.win-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "tl9tEzViM5vwmuLE1ozMP+YdVBK2A8E1RdXCqk4zXRoaYgLqH24V9NVjACDIKDE9MArv4Yqask0s3JMJick1dg=="
+ "resolved": "4.0.0",
+ "contentHash": "h2QLNIn2m7w2uzN1696n0v5HPyK+aR0DexyiCBbjYQTdOvfXC1V5QcP9boCf0VTrQMhN7Wr+qtCbbwb5qOiXJA=="
},
"Maa.Framework.Runtime.win-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "RxHzvnyVer26AMJfyME+EWFf2TKK8Ka8a3fBJQMJGZgvK+g06MTxW5LbumQp4F5ylMJvHr/+SVwo9sxZObsUBg=="
+ "resolved": "4.0.0",
+ "contentHash": "KkP36yP1s03GOcTDnm9HscOzZa5U2IeMcfV4gEZ1cg/Yji7ynKKtV2mRIsZnpPs2deFuTL3Qn22uPr9C8mHIqw=="
},
"Microsoft.ApplicationInsights": {
"type": "Transitive",
@@ -367,7 +367,7 @@
"type": "Project",
"dependencies": {
"Maa.Framework.Native": "[1.0.0-dev, )",
- "Maa.Framework.Runtimes": "[3.0.4, )"
+ "Maa.Framework.Runtimes": "[4.0.0, )"
}
},
"Maa.Framework.Binding": {
@@ -388,28 +388,28 @@
"Maa.Framework.Native": {
"type": "Project",
"dependencies": {
- "Maa.AgentBinary": "[1.0.0, )",
+ "Maa.AgentBinary": "[1.1.0, )",
"Maa.Framework.Binding.Native": "[1.0.0-dev, )"
}
},
"Maa.AgentBinary": {
"type": "CentralTransitive",
- "requested": "[1.0.0, )",
- "resolved": "1.0.0",
- "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA=="
+ "requested": "[1.1.0, )",
+ "resolved": "1.1.0",
+ "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A=="
},
"Maa.Framework.Runtimes": {
"type": "CentralTransitive",
- "requested": "[3.0.4, )",
- "resolved": "3.0.4",
- "contentHash": "F9As/LIRRxXvrkubxgHxExoxCqVzYkfo1Ph3kHYxCYXrdUAZVXESr3o99L+NPv2rUZ9VkupKeL+Xomk4hXCcVw==",
+ "requested": "[4.0.0, )",
+ "resolved": "4.0.0",
+ "contentHash": "hHYtVnknsGZJj22TO4gEfrqUlhq6BfF9DBIM1w0U0dv0mz4fzijawmZJ9QpRX0UldOXq7HN8TBC2lSLyz00FAA==",
"dependencies": {
- "Maa.Framework.Runtime.linux-arm64": "3.0.4",
- "Maa.Framework.Runtime.linux-x64": "3.0.4",
- "Maa.Framework.Runtime.osx-arm64": "3.0.4",
- "Maa.Framework.Runtime.osx-x64": "3.0.4",
- "Maa.Framework.Runtime.win-arm64": "3.0.4",
- "Maa.Framework.Runtime.win-x64": "3.0.4"
+ "Maa.Framework.Runtime.linux-arm64": "4.0.0",
+ "Maa.Framework.Runtime.linux-x64": "4.0.0",
+ "Maa.Framework.Runtime.osx-arm64": "4.0.0",
+ "Maa.Framework.Runtime.osx-x64": "4.0.0",
+ "Maa.Framework.Runtime.win-arm64": "4.0.0",
+ "Maa.Framework.Runtime.win-x64": "4.0.0"
}
}
}
diff --git a/src/MaaFramework.Binding/Abstractions/IMaaDisposable.cs b/src/MaaFramework.Binding/Abstractions/IMaaDisposable.cs
index 3f3720f..abde39f 100644
--- a/src/MaaFramework.Binding/Abstractions/IMaaDisposable.cs
+++ b/src/MaaFramework.Binding/Abstractions/IMaaDisposable.cs
@@ -11,9 +11,17 @@ public interface IMaaDisposable : IDisposable
bool IsInvalid { get; }
///
- /// Gets a value indicating whether an is thrown when current instance is invalid but still called.
+ /// Gets a value indicating whether an is thrown when the current instance is invalid but still called.
///
bool ThrowOnInvalid { get; set; }
+
+ ///
+ /// Gets a value indicating whether the current instance is stateless.
+ ///
+ ///
+ /// The lifetime of unmanaged resources is controlled by .
+ ///
+ bool IsStateless { get; }
}
// 设计思路:
diff --git a/src/MaaFramework.Binding/Abstractions/MaaDisposableHandle.cs b/src/MaaFramework.Binding/Abstractions/MaaDisposableHandle.cs
index 82e5bf8..71a96b2 100644
--- a/src/MaaFramework.Binding/Abstractions/MaaDisposableHandle.cs
+++ b/src/MaaFramework.Binding/Abstractions/MaaDisposableHandle.cs
@@ -45,6 +45,9 @@ public void SetHandleAsInvalid()
///
public bool ThrowOnInvalid { get; set; }
+ ///
+ public bool IsStateless => !_needReleased;
+
///
/// Creates a instance.
///
diff --git a/src/MaaFramework.Binding/Custom/IMaaCustomAction.cs b/src/MaaFramework.Binding/Custom/IMaaCustomAction.cs
index 38942e5..cc8cf72 100644
--- a/src/MaaFramework.Binding/Custom/IMaaCustomAction.cs
+++ b/src/MaaFramework.Binding/Custom/IMaaCustomAction.cs
@@ -13,8 +13,9 @@ public interface IMaaCustomAction : IMaaCustomResource
///
/// The context.
/// The run args.
+ /// The run results.
/// if the operation was executed successfully; otherwise, .
- bool Run(in IMaaContext context, in RunArgs args);
+ bool Run(in IMaaContext context, in RunArgs args, in RunResults results);
}
///
@@ -27,3 +28,10 @@ public interface IMaaCustomAction : IMaaCustomResource
/// Gets the recognition detail.
/// Gets the recognition box.
public sealed record RunArgs(string NodeName, TaskDetail TaskDetail, string ActionName, [StringSyntax("Json")] string ActionParam, RecognitionDetail RecognitionDetail, IMaaRectBuffer RecognitionBox);
+
+#pragma warning disable S2094 // Classes should not be empty
+///
+/// The action run results.
+///
+public sealed record RunResults;
+#pragma warning restore S2094 // Classes should not be empty
diff --git a/src/MaaFramework.Binding/Enums/Binding/ApiInfoFlags.cs b/src/MaaFramework.Binding/Enums/Binding/ApiInfoFlags.cs
new file mode 100644
index 0000000..aed55a0
--- /dev/null
+++ b/src/MaaFramework.Binding/Enums/Binding/ApiInfoFlags.cs
@@ -0,0 +1,44 @@
+namespace MaaFramework.Binding;
+
+#pragma warning disable S2344 // Enumeration type names should not have "Flags" or "Enum" suffixes
+
+///
+/// Represents information about binding interoperable API.
+///
+[Flags]
+public enum ApiInfoFlags
+{
+ ///
+ /// No flags.
+ ///
+ None = 0,
+
+ ///
+ /// Indicates that the API is in MaaFramework context.
+ ///
+ InFrameworkContext = 1,
+
+ ///
+ /// Indicates that the API is in MaaAgentServer context.
+ ///
+ InAgentServerContext = 2,
+
+ ///
+ /// Indicates that the API uses the default resolver.
+ ///
+ UseDefaultResolver = 1 << 8,
+
+ ///
+ /// Indicates that the API uses the resolver from binding.
+ ///
+ UseBindingResolver = 2 << 8,
+}
+
+internal static class ApiInfoFlagsExtensions
+{
+ internal const ApiInfoFlags ContextMask = ApiInfoFlags.InFrameworkContext | ApiInfoFlags.InAgentServerContext;
+ internal const ApiInfoFlags ResolverMask = ApiInfoFlags.UseDefaultResolver | ApiInfoFlags.UseBindingResolver;
+
+ internal static bool HasFlag_Context(this ApiInfoFlags flags) => (flags & ContextMask) != ApiInfoFlags.None;
+ internal static bool HasFlag_ResolverExcept(this ApiInfoFlags flags, ApiInfoFlags other) => (flags & ~other & ResolverMask) != ApiInfoFlags.None;
+}
diff --git a/src/MaaFramework.Binding/Exceptions/MaaOperationException.cs b/src/MaaFramework.Binding/Exceptions/MaaOperationException.cs
index d3d4710..6a0bd4e 100644
--- a/src/MaaFramework.Binding/Exceptions/MaaOperationException.cs
+++ b/src/MaaFramework.Binding/Exceptions/MaaOperationException.cs
@@ -11,12 +11,12 @@ public class MaaInteroperationException : MaaException
///
/// Resource binding failed message.
///
- public const string ResourceBindingFailedMessage = "MaaTasker failed to bind MaaResource.";
+ public const string ResourceBindingFailedMessage = "Failed to bind MaaResource.";
///
/// Controller binding failed message.
///
- public const string ControllerBindingFailedMessage = "MaaTasker failed to bind MaaController.";
+ public const string ControllerBindingFailedMessage = "Failed to bind MaaController.";
///
/// Resource modified message.
diff --git a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs
index e448004..f370ede 100644
--- a/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs
+++ b/src/MaaFramework.Binding/Extensions/MaaOptionExtensions.cs
@@ -10,54 +10,54 @@ namespace MaaFramework.Binding;
public static class MaaOptionExtensions
{
///
- public static bool SetOptionScreenshotTargetLongSide(this IMaaOption? opt, int value)
+ public static bool SetOption_ScreenshotTargetLongSide(this IMaaOption? opt, int value)
=> opt?.SetOption(ControllerOption.ScreenshotTargetLongSide, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionScreenshotTargetShortSide(this IMaaOption? opt, int value)
+ public static bool SetOption_ScreenshotTargetShortSide(this IMaaOption? opt, int value)
=> opt?.SetOption(ControllerOption.ScreenshotTargetShortSide, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionScreenshotUseRawSize(this IMaaOption? opt, bool value)
+ public static bool SetOption_ScreenshotUseRawSize(this IMaaOption? opt, bool value)
=> opt?.SetOption(ControllerOption.ScreenshotUseRawSize, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionRecording(this IMaaOption? opt, bool value)
+ public static bool SetOption_Recording(this IMaaOption? opt, bool value)
=> opt?.SetOption(ControllerOption.Recording, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionLogDir(this IMaaOption? opt, string value)
+ public static bool SetOption_LogDir(this IMaaOption? opt, string value)
=> opt?.SetOption(GlobalOption.LogDir, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionSaveDraw(this IMaaOption? opt, bool value)
+ public static bool SetOption_SaveDraw(this IMaaOption? opt, bool value)
=> opt?.SetOption(GlobalOption.SaveDraw, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionRecording(this IMaaOption? opt, bool value)
+ public static bool SetOption_Recording(this IMaaOption? opt, bool value)
=> opt?.SetOption(GlobalOption.Recording, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionStdoutLevel(this IMaaOption? opt, LoggingLevel value)
+ public static bool SetOption_StdoutLevel(this IMaaOption? opt, LoggingLevel value)
=> opt?.SetOption(GlobalOption.StdoutLevel, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionShowHitDraw(this IMaaOption? opt, bool value)
+ public static bool SetOption_ShowHitDraw(this IMaaOption? opt, bool value)
=> opt?.SetOption(GlobalOption.ShowHitDraw, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionDebugMode(this IMaaOption? opt, bool value)
+ public static bool SetOption_DebugMode(this IMaaOption? opt, bool value)
=> opt?.SetOption(GlobalOption.DebugMode, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionInferenceDevice(this IMaaOption? opt, InferenceDevice value)
+ public static bool SetOption_InferenceDevice(this IMaaOption? opt, InferenceDevice value)
=> opt?.SetOption(ResourceOption.InferenceDevice, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionInferenceDevice(this IMaaOption? opt, int value)
+ public static bool SetOption_InferenceDevice(this IMaaOption? opt, int value)
=> opt?.SetOption(ResourceOption.InferenceDevice, value) ?? throw new ArgumentNullException(nameof(opt));
///
- public static bool SetOptionInferenceDevice(this IMaaOption? opt, InferenceExecutionProvider value)
+ public static bool SetOption_InferenceExecutionProvider(this IMaaOption? opt, InferenceExecutionProvider value)
=> opt?.SetOption(ResourceOption.InferenceExecutionProvider, value) ?? throw new ArgumentNullException(nameof(opt));
}
diff --git a/src/MaaFramework.Binding/IMaaAgentClient.cs b/src/MaaFramework.Binding/IMaaAgentClient.cs
new file mode 100644
index 0000000..9f9595d
--- /dev/null
+++ b/src/MaaFramework.Binding/IMaaAgentClient.cs
@@ -0,0 +1,92 @@
+using MaaFramework.Binding.Abstractions;
+using System.Diagnostics;
+
+namespace MaaFramework.Binding;
+
+///
+/// An interface defining wrapped members for MaaAgentClient with generic handle.
+///
+/// The type of handle.
+public interface IMaaAgentClient : IMaaAgentClient, IMaaDisposableHandle;
+
+///
+/// An interface defining wrapped members for MaaAgentClient.
+///
+public interface IMaaAgentClient : IMaaDisposable
+{
+ ///
+ /// Gets the unique identifier used to communicate with the agent server.
+ ///
+ string Id { get; }
+
+ ///
+ /// Gets or sets a resource that binds to the .
+ ///
+ ///
+ ///
+ IMaaResource Resource { get; set; }
+
+ ///
+ /// Starts the connection.
+ ///
+ /// if the connection was started successfully; otherwise, .
+ bool LinkStart();
+
+ ///
+ /// Starts the agent server process using the specified and connects to the agent server.
+ /// To start a new process, the current must have exited first.
+ ///
+ /// The process start info.
+ /// An optional token to cancel the asynchronous operation waiting for the connection.
+ /// if the connection was started successfully; otherwise, .
+ /// The has had cancellation requested.
+ bool LinkStart(ProcessStartInfo info, CancellationToken cancellationToken = default);
+
+ ///
+ /// Starts the agent server process using the specified method and connects to the agent server.
+ /// To start a new process, the current must have exited first.
+ ///
+ /// The delegate method that defines how to start the agent server process.
+ /// An optional token to cancel the asynchronous operation waiting for the connection.
+ /// if the connection was started successfully; otherwise, .
+ /// The has had cancellation requested.
+ bool LinkStart(AgentServerStartupMethod method, CancellationToken cancellationToken = default);
+
+ ///
+ /// Starts the connection asynchronously unless the process has exited.
+ ///
+ /// The process to monitor for exit status.
+ /// An optional token to cancel the asynchronous operation waiting for the connection.
+ ///
+ /// A task that represents the asynchronous operation. The task result contains
+ /// if the connection was started successfully; otherwise, .
+ ///
+ /// The has had cancellation requested.
+ Task LinkStartUnlessProcessExit(Process process, CancellationToken cancellationToken);
+
+ ///
+ /// Stops the connection.
+ ///
+ /// if the connection was stopped successfully; otherwise, .
+ bool LinkStop();
+
+ ///
+ /// Represents a method that starts the agent server process.
+ ///
+ /// The unique identifier used to communicate with the agent server.
+ /// The directory path where the native assemblies are located.
+ ///
+ /// A new that is associated with the process resource, or if no process resource is started.
+ ///
+ ///
+ /// The implementation of this delegate is responsible for validating the provided parameters.
+ /// Ensure that and are valid and meet the requirements of the agent server process.
+ ///
+ delegate Process? AgentServerStartupMethod(string identifier, string nativeAssemblyDirectory);
+
+ ///
+ /// Gets the agent server process managed by from method LinkStart.
+ ///
+ /// The process is unavailable or not managed by .
+ Process AgentServerProcess { get; }
+}
diff --git a/src/MaaFramework.Binding/IMaaAgentServer.cs b/src/MaaFramework.Binding/IMaaAgentServer.cs
new file mode 100644
index 0000000..5f73052
--- /dev/null
+++ b/src/MaaFramework.Binding/IMaaAgentServer.cs
@@ -0,0 +1,48 @@
+using MaaFramework.Binding.Custom;
+
+namespace MaaFramework.Binding;
+
+///
+/// An interface defining wrapped members for MaaAgentServer.
+///
+public interface IMaaAgentServer
+{
+ ///
+ /// Configures the unique identifier used to communicate with the agent client.
+ ///
+ /// The unique identifier used to communicate with the agent client.
+ IMaaAgentServer WithIdentifier(string identifier);
+
+ ///
+ /// Registers a or in the .
+ ///
+ /// The or .
+ /// The new name that will be used to reference the custom resource.
+ /// The custom resource instance to register.
+ /// Thrown if the registration fails.
+ IMaaAgentServer Register(string name, T custom) where T : IMaaCustomResource;
+
+ ///
+ IMaaAgentServer Register(T custom) where T : IMaaCustomResource;
+
+ ///
+ /// Starts up the agent server to prepare for receiving client messages from the specified connection.
+ ///
+ /// Thrown if the registration fails.
+ IMaaAgentServer StartUp();
+
+ ///
+ /// Shuts down the agent server.
+ ///
+ IMaaAgentServer ShutDown();
+
+ ///
+ /// Blocks the calling thread until the thread for receiving client messages finishes its execution.
+ ///
+ IMaaAgentServer Join();
+
+ ///
+ /// Separates the thread for receiving client messages, allowing execution to continue independently.
+ ///
+ IMaaAgentServer Detach();
+}
diff --git a/src/MaaFramework.Binding/IMaaContext.cs b/src/MaaFramework.Binding/IMaaContext.cs
index 4f0779c..a413f9d 100644
--- a/src/MaaFramework.Binding/IMaaContext.cs
+++ b/src/MaaFramework.Binding/IMaaContext.cs
@@ -26,28 +26,28 @@ public interface IMaaContext : ICloneable
/// The entry name of the task.
/// The json used to override the pipeline.
/// if the operation was executed successfully; otherwise, .
- TaskDetail? RunTask(string entry, [StringSyntax("Json")] string pipelineOverride);
+ TaskDetail? RunTask(string entry, [StringSyntax("Json")] string pipelineOverride = "{}");
///
/// Run a recognition.
///
/// The recognition entry name.
- /// The json used to override the pipeline.
/// The image to be recognized.
+ /// The json used to override the pipeline.
/// if the operation was executed successfully; otherwise, .
///
- RecognitionDetail? RunRecognition(string entry, [StringSyntax("Json")] string pipelineOverride, IMaaImageBuffer image);
+ RecognitionDetail? RunRecognition(string entry, IMaaImageBuffer image, [StringSyntax("Json")] string pipelineOverride = "{}");
///
/// Run an action.
///
/// The action entry name.
- /// The json used to override the pipeline.
/// The rect buffer containing current rect in the recognition result.
/// The rect detail in the recognition result.
+ /// The json used to override the pipeline.
/// if the operation was executed successfully; otherwise, .
///
- NodeDetail? RunAction(string entry, [StringSyntax("Json")] string pipelineOverride, IMaaRectBuffer recognitionBox, string recognitionDetail);
+ NodeDetail? RunAction(string entry, IMaaRectBuffer recognitionBox, [StringSyntax("Json")] string recognitionDetail, [StringSyntax("Json")] string pipelineOverride = "{}");
///
/// Override a pipeline.
diff --git a/src/MaaFramework.Binding/IMaaResource.cs b/src/MaaFramework.Binding/IMaaResource.cs
index 3e21b3e..b784619 100644
--- a/src/MaaFramework.Binding/IMaaResource.cs
+++ b/src/MaaFramework.Binding/IMaaResource.cs
@@ -18,9 +18,9 @@ public interface IMaaResource : IMaaCommon, IMaaOption, IMaaPost
/// Registers a or in the .
///
/// The or .
- /// The new name that will be used to reference it.
- /// The or .
- /// if the custom action or recognition was registered successfully; otherwise, .
+ /// The new name that will be used to reference the custom resource.
+ /// The custom resource instance to register.
+ /// if the registration was successful; otherwise, .
bool Register(string name, T custom) where T : IMaaCustomResource;
///
@@ -30,24 +30,25 @@ public interface IMaaResource : IMaaCommon, IMaaOption, IMaaPost
/// Unregisters a or in the .
///
/// The or .
- /// The name of or when it was registered.
- /// if the custom action or recognition was unregistered successfully; otherwise, .
+ /// The name of the instance when it was registered.
+ /// if the unregistration was successful; otherwise, .
bool Unregister(string name) where T : IMaaCustomResource;
///
- /// The or .
+ /// The custom resource instance to unregister.
bool Unregister(T custom) where T : IMaaCustomResource;
///
/// Clears all or registered in the .
///
/// The or .
- /// if custom actions or recognitions were cleared successfully; otherwise, .
+ /// if custom resource instances were cleared successfully; otherwise, .
bool Clear() where T : IMaaCustomResource;
///
- /// Clear the loaded resource.
+ /// Clears the loaded resource.
///
+ /// Whether to include custom resources.
/// if the is cleared successfully; otherwise, .
bool Clear(bool includeCustomResource = false);
diff --git a/src/MaaFramework.Binding/MaaFramework.Binding.csproj b/src/MaaFramework.Binding/MaaFramework.Binding.csproj
index 8a40f6b..a32697b 100644
--- a/src/MaaFramework.Binding/MaaFramework.Binding.csproj
+++ b/src/MaaFramework.Binding/MaaFramework.Binding.csproj
@@ -12,4 +12,8 @@
snupkg
+
+
+
+
diff --git a/src/MaaFramework.Binding/MaaImage.cs b/src/MaaFramework.Binding/MaaImage.cs
index 17e287a..d3ea0fd 100644
--- a/src/MaaFramework.Binding/MaaImage.cs
+++ b/src/MaaFramework.Binding/MaaImage.cs
@@ -25,6 +25,9 @@ public bool ThrowOnInvalid
set => buffer.ThrowOnInvalid = value;
}
+ ///
+ public bool IsStateless => buffer.IsStateless;
+
///
/// Caches the image data from the .
///
diff --git a/src/MaaFramework.Binding/MaaMsg.cs b/src/MaaFramework.Binding/MaaMsg.cs
index 9642a42..3e0d8ca 100644
--- a/src/MaaFramework.Binding/MaaMsg.cs
+++ b/src/MaaFramework.Binding/MaaMsg.cs
@@ -12,7 +12,7 @@
namespace MaaFramework.Binding.Notification;
-//MaaApiDocument Version: (main) v3.0.4
+//MaaApiDocument Version: (main) v4.0.0
///
/// A callback consists of a message and a payload.
/// The message is a string that indicates the type of the message.
diff --git a/src/MaaFramework.Native/packages.lock.json b/src/MaaFramework.Native/packages.lock.json
index 40fc620..1932700 100644
--- a/src/MaaFramework.Native/packages.lock.json
+++ b/src/MaaFramework.Native/packages.lock.json
@@ -4,9 +4,9 @@
"net7.0": {
"Maa.AgentBinary": {
"type": "Direct",
- "requested": "[1.0.0, )",
- "resolved": "1.0.0",
- "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA=="
+ "requested": "[1.1.0, )",
+ "resolved": "1.1.0",
+ "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A=="
},
"SonarAnalyzer.CSharp": {
"type": "Direct",
@@ -27,9 +27,9 @@
"net9.0": {
"Maa.AgentBinary": {
"type": "Direct",
- "requested": "[1.0.0, )",
- "resolved": "1.0.0",
- "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA=="
+ "requested": "[1.1.0, )",
+ "resolved": "1.1.0",
+ "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A=="
},
"SonarAnalyzer.CSharp": {
"type": "Direct",
diff --git a/src/MaaFramework/packages.lock.json b/src/MaaFramework/packages.lock.json
index 38658bb..fb372b1 100644
--- a/src/MaaFramework/packages.lock.json
+++ b/src/MaaFramework/packages.lock.json
@@ -4,16 +4,16 @@
"net7.0": {
"Maa.Framework.Runtimes": {
"type": "Direct",
- "requested": "[3.0.4, )",
- "resolved": "3.0.4",
- "contentHash": "F9As/LIRRxXvrkubxgHxExoxCqVzYkfo1Ph3kHYxCYXrdUAZVXESr3o99L+NPv2rUZ9VkupKeL+Xomk4hXCcVw==",
+ "requested": "[4.0.0, )",
+ "resolved": "4.0.0",
+ "contentHash": "hHYtVnknsGZJj22TO4gEfrqUlhq6BfF9DBIM1w0U0dv0mz4fzijawmZJ9QpRX0UldOXq7HN8TBC2lSLyz00FAA==",
"dependencies": {
- "Maa.Framework.Runtime.linux-arm64": "3.0.4",
- "Maa.Framework.Runtime.linux-x64": "3.0.4",
- "Maa.Framework.Runtime.osx-arm64": "3.0.4",
- "Maa.Framework.Runtime.osx-x64": "3.0.4",
- "Maa.Framework.Runtime.win-arm64": "3.0.4",
- "Maa.Framework.Runtime.win-x64": "3.0.4"
+ "Maa.Framework.Runtime.linux-arm64": "4.0.0",
+ "Maa.Framework.Runtime.linux-x64": "4.0.0",
+ "Maa.Framework.Runtime.osx-arm64": "4.0.0",
+ "Maa.Framework.Runtime.osx-x64": "4.0.0",
+ "Maa.Framework.Runtime.win-arm64": "4.0.0",
+ "Maa.Framework.Runtime.win-x64": "4.0.0"
}
},
"SonarAnalyzer.CSharp": {
@@ -24,33 +24,33 @@
},
"Maa.Framework.Runtime.linux-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "xobEEAtdwH/TCtVVeqzyZwWpgmBj9/G+YKDhw8otumXFo6gzTFwwm917sc4La6LZfztolbhEP3HjHdkBXZAlnA=="
+ "resolved": "4.0.0",
+ "contentHash": "UO0WrFCkSAzW3CGj0N6ah+Znn4MTkd7CeWrdxJiSyRkRtjgIl2lPf67ZbbdYdCpXMF7PxQdwJaqo1A895kI/Qg=="
},
"Maa.Framework.Runtime.linux-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "lezMomm+OM83YIIRMeuH4kxp1dkGzi9ufRhI8HNAmWd5mQGVScR75Gz55dZlGQ0UPmtwPP2PT26/4D92zN8Tow=="
+ "resolved": "4.0.0",
+ "contentHash": "DAhsxoDXPJN7A96gZTUAWnub92xDKtd8MXLx0mxipvBPRJ/Qw4wes2IKYididKkMNBj1XNiqQ3ENQAvjgm05Bw=="
},
"Maa.Framework.Runtime.osx-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "fmsehoumgpe5Il2BGl8FrHczUiWr42qUJXYM/GagSW5MlB/hDlxGwA+Apmks8KerNpDAqv1NsuPXRQxnOuvMpw=="
+ "resolved": "4.0.0",
+ "contentHash": "yYMbg9TRzlpe9YB96fT1oTpWcX7Yg7dNGjmk7mfSZ+nm3ZRY95Uy7GJ3apk+kgCim89KEt8LsV5mmLgCNXAhpg=="
},
"Maa.Framework.Runtime.osx-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "afAeMiMiBOvuQfgXaiOiF3NAtWWKpigybM6pdxpAXupK5lyBrwyMgxh6hgzpduD3D+ohzdf4a5sdWdxLbyrdxw=="
+ "resolved": "4.0.0",
+ "contentHash": "5SvGaS2CMXECIC0QVzzr9hPUZQscIHIn1nddr8d7zyISTokkXMwQvaZRSu6hAibECXIg44yiowFYPPnS7SvOnQ=="
},
"Maa.Framework.Runtime.win-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "tl9tEzViM5vwmuLE1ozMP+YdVBK2A8E1RdXCqk4zXRoaYgLqH24V9NVjACDIKDE9MArv4Yqask0s3JMJick1dg=="
+ "resolved": "4.0.0",
+ "contentHash": "h2QLNIn2m7w2uzN1696n0v5HPyK+aR0DexyiCBbjYQTdOvfXC1V5QcP9boCf0VTrQMhN7Wr+qtCbbwb5qOiXJA=="
},
"Maa.Framework.Runtime.win-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "RxHzvnyVer26AMJfyME+EWFf2TKK8Ka8a3fBJQMJGZgvK+g06MTxW5LbumQp4F5ylMJvHr/+SVwo9sxZObsUBg=="
+ "resolved": "4.0.0",
+ "contentHash": "KkP36yP1s03GOcTDnm9HscOzZa5U2IeMcfV4gEZ1cg/Yji7ynKKtV2mRIsZnpPs2deFuTL3Qn22uPr9C8mHIqw=="
},
"Maa.Framework.Binding": {
"type": "Project"
@@ -64,30 +64,30 @@
"Maa.Framework.Native": {
"type": "Project",
"dependencies": {
- "Maa.AgentBinary": "[1.0.0, )",
+ "Maa.AgentBinary": "[1.1.0, )",
"Maa.Framework.Binding.Native": "[1.0.0-dev, )"
}
},
"Maa.AgentBinary": {
"type": "CentralTransitive",
- "requested": "[1.0.0, )",
- "resolved": "1.0.0",
- "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA=="
+ "requested": "[1.1.0, )",
+ "resolved": "1.1.0",
+ "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A=="
}
},
"net9.0": {
"Maa.Framework.Runtimes": {
"type": "Direct",
- "requested": "[3.0.4, )",
- "resolved": "3.0.4",
- "contentHash": "F9As/LIRRxXvrkubxgHxExoxCqVzYkfo1Ph3kHYxCYXrdUAZVXESr3o99L+NPv2rUZ9VkupKeL+Xomk4hXCcVw==",
+ "requested": "[4.0.0, )",
+ "resolved": "4.0.0",
+ "contentHash": "hHYtVnknsGZJj22TO4gEfrqUlhq6BfF9DBIM1w0U0dv0mz4fzijawmZJ9QpRX0UldOXq7HN8TBC2lSLyz00FAA==",
"dependencies": {
- "Maa.Framework.Runtime.linux-arm64": "3.0.4",
- "Maa.Framework.Runtime.linux-x64": "3.0.4",
- "Maa.Framework.Runtime.osx-arm64": "3.0.4",
- "Maa.Framework.Runtime.osx-x64": "3.0.4",
- "Maa.Framework.Runtime.win-arm64": "3.0.4",
- "Maa.Framework.Runtime.win-x64": "3.0.4"
+ "Maa.Framework.Runtime.linux-arm64": "4.0.0",
+ "Maa.Framework.Runtime.linux-x64": "4.0.0",
+ "Maa.Framework.Runtime.osx-arm64": "4.0.0",
+ "Maa.Framework.Runtime.osx-x64": "4.0.0",
+ "Maa.Framework.Runtime.win-arm64": "4.0.0",
+ "Maa.Framework.Runtime.win-x64": "4.0.0"
}
},
"SonarAnalyzer.CSharp": {
@@ -98,33 +98,33 @@
},
"Maa.Framework.Runtime.linux-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "xobEEAtdwH/TCtVVeqzyZwWpgmBj9/G+YKDhw8otumXFo6gzTFwwm917sc4La6LZfztolbhEP3HjHdkBXZAlnA=="
+ "resolved": "4.0.0",
+ "contentHash": "UO0WrFCkSAzW3CGj0N6ah+Znn4MTkd7CeWrdxJiSyRkRtjgIl2lPf67ZbbdYdCpXMF7PxQdwJaqo1A895kI/Qg=="
},
"Maa.Framework.Runtime.linux-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "lezMomm+OM83YIIRMeuH4kxp1dkGzi9ufRhI8HNAmWd5mQGVScR75Gz55dZlGQ0UPmtwPP2PT26/4D92zN8Tow=="
+ "resolved": "4.0.0",
+ "contentHash": "DAhsxoDXPJN7A96gZTUAWnub92xDKtd8MXLx0mxipvBPRJ/Qw4wes2IKYididKkMNBj1XNiqQ3ENQAvjgm05Bw=="
},
"Maa.Framework.Runtime.osx-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "fmsehoumgpe5Il2BGl8FrHczUiWr42qUJXYM/GagSW5MlB/hDlxGwA+Apmks8KerNpDAqv1NsuPXRQxnOuvMpw=="
+ "resolved": "4.0.0",
+ "contentHash": "yYMbg9TRzlpe9YB96fT1oTpWcX7Yg7dNGjmk7mfSZ+nm3ZRY95Uy7GJ3apk+kgCim89KEt8LsV5mmLgCNXAhpg=="
},
"Maa.Framework.Runtime.osx-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "afAeMiMiBOvuQfgXaiOiF3NAtWWKpigybM6pdxpAXupK5lyBrwyMgxh6hgzpduD3D+ohzdf4a5sdWdxLbyrdxw=="
+ "resolved": "4.0.0",
+ "contentHash": "5SvGaS2CMXECIC0QVzzr9hPUZQscIHIn1nddr8d7zyISTokkXMwQvaZRSu6hAibECXIg44yiowFYPPnS7SvOnQ=="
},
"Maa.Framework.Runtime.win-arm64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "tl9tEzViM5vwmuLE1ozMP+YdVBK2A8E1RdXCqk4zXRoaYgLqH24V9NVjACDIKDE9MArv4Yqask0s3JMJick1dg=="
+ "resolved": "4.0.0",
+ "contentHash": "h2QLNIn2m7w2uzN1696n0v5HPyK+aR0DexyiCBbjYQTdOvfXC1V5QcP9boCf0VTrQMhN7Wr+qtCbbwb5qOiXJA=="
},
"Maa.Framework.Runtime.win-x64": {
"type": "Transitive",
- "resolved": "3.0.4",
- "contentHash": "RxHzvnyVer26AMJfyME+EWFf2TKK8Ka8a3fBJQMJGZgvK+g06MTxW5LbumQp4F5ylMJvHr/+SVwo9sxZObsUBg=="
+ "resolved": "4.0.0",
+ "contentHash": "KkP36yP1s03GOcTDnm9HscOzZa5U2IeMcfV4gEZ1cg/Yji7ynKKtV2mRIsZnpPs2deFuTL3Qn22uPr9C8mHIqw=="
},
"Maa.Framework.Binding": {
"type": "Project"
@@ -138,15 +138,15 @@
"Maa.Framework.Native": {
"type": "Project",
"dependencies": {
- "Maa.AgentBinary": "[1.0.0, )",
+ "Maa.AgentBinary": "[1.1.0, )",
"Maa.Framework.Binding.Native": "[1.0.0-dev, )"
}
},
"Maa.AgentBinary": {
"type": "CentralTransitive",
- "requested": "[1.0.0, )",
- "resolved": "1.0.0",
- "contentHash": "09TsLd4LbaxoK68AZAg4MZFle/kkX8JZTbjRbkJYptyUCPCtdbVr8odukKG1CUzL4Wumbu+Fk7ppNX/OSlRMOA=="
+ "requested": "[1.1.0, )",
+ "resolved": "1.1.0",
+ "contentHash": "SITxxo/h53R/WlhaaG6CK4qWGwmc6/fcPykCnXG3tIs2Mx4iS2Y4XfRWlCtwpMoP0lyanjl+ZvqMUQ4vEV9U7A=="
}
}
}
diff --git a/tools/Builder/Build.csx b/tools/Builder/Build.csx
index 163158b..cf035f3 100644
--- a/tools/Builder/Build.csx
+++ b/tools/Builder/Build.csx
@@ -31,7 +31,7 @@ var version = tags.Count switch
};
var runtimes = NuGetVersion.Parse(
- XDocument.Load("Directory.Packages.props")
+ XDocument.Load("./src/Directory.Packages.props")
.Descendants("PackageVersion")
.FirstOrDefault(e => e.Attribute("Include")?.Value == "Maa.Framework.Runtimes")
!.Attribute("Version")