提交新的改动

This commit is contained in:
Songyang.Huo
2023-10-13 08:11:16 +08:00
parent 5549ab6aeb
commit b785d36769
3 changed files with 178 additions and 12 deletions

View File

@@ -33,6 +33,7 @@ MAA 会以 1 秒的间隔持续轮询这个端点,尝试获取他要执行的
{
"tasks": // 需要让MAA执行的Task的列表目前可以支持的类型如示例中所示如果不存在tasks则视为连接无效。
[
// 顺序执行的任务:下面这些任务会按照下发的顺序排队执行
{
"id": "b353c469-b902-4357-bd8f-d133199eea31", //任务的唯一id字符串类型在汇报任务时会使用
"type": "CaptureImage", //截图任务会截取一张当前模拟器的截图并以Base64字符串的形式放在汇报任务的payload里。如果你需要下发这种类型的任务请务必注意你的端点可接受的最大请求大小因为截图会有数十MB会超过一般网关的默认大小限制。
@@ -54,6 +55,19 @@ MAA 会以 1 秒的间隔持续轮询这个端点,尝试获取他要执行的
"type": "Settings-ConnectionAddress", //修改配置项的任务等同于执行ConfigurationHelper.SetValue("ConnectionAddress", params); 为了安全起见,不是每个配置都可以修改,能修改的配置详见下述。
"params": "value" //要修改的值
},
// 立即执行任务下面这些任务可以在顺序执行任务运行中执行并且MAA保证下面的任何一个任务都会尽快返回结果通常用于对远程控制功能本身的控制。
{
"id": "b353c469-b902-4357-bd8f-d133199eea31", //任务的唯一id字符串类型在汇报任务时会使用
"type": "CaptureImageNow", //立刻截图任务,和上面的截图任务是基本一样的,唯一的区别是这个任务会立刻被运行,而不会等待其他任务。
},
{
"id": "b353c469-b902-4357-bd8f-d133199eea31", //任务的唯一id字符串类型在汇报任务时会使用
"type": "StopTask", //"结束当前任务"任务,将会尝试结束当前运行的任务。如果任务列表还有其他任务会继续开始执行下一个。该任务不会等待并确认当前任务已停止才会返回,因此请使用心跳任务来确认停止命令是否已生效。
},
{
"id": "b353c469-b902-4357-bd8f-d133199eea31", //任务的唯一id字符串类型在汇报任务时会使用
"type": "HeartBeat", //心跳任务该任务会立即返回并且将当前“顺序执行的任务”队列中正在执行的任务的Id作为Payload返回如果当前没有任务执行返回空字符串。
},
],
... // 如果你的这个端点还有其他用途你可以自行添加可选的返回值但是MAA只会读取tasks
}
@@ -64,8 +78,9 @@ MAA 会以 1 秒的间隔持续轮询这个端点,尝试获取他要执行的
::: note
- LinkStart-[TaskName] 型的任务 type 的可选值为 LinkStart-BaseLinkStart-WakeUpLinkStart-CombatLinkStart-RecruitingLinkStart-MallLinkStart-MissionLinkStart-AutoRoguelikeLinkStart-ReclamationAlgorithm
- Settings-[SettingsName] 型的任务的 type 的可选值为 Settings-ConnectionAddress
- Settings-[SettingsName] 型的任务的 type 的可选值为 Settings-ConnectionAddress, Settings-Stage1
- Settings 系列任务仍然是要按顺序执行的,并不会在收到任务的时候立刻执行,而是排在上一个任务的后面
- 多个立即执行的任务也会按下发顺序执行,只不过这些任务的执行速度都很快,通常来说,并不需要关注他们的顺序。
:::
## 汇报任务端点

View File

@@ -26,11 +26,15 @@ namespace MaaWpfGui.Services.RemoteControl
public class RemoteControlService
{
private readonly Task _pollJobTask = Task.CompletedTask;
private readonly Task _executeJobTask = Task.CompletedTask;
private readonly List<string> _executedTaskIds = new List<string>();
private readonly ConcurrentQueue<JObject> _taskQueue = new ConcurrentQueue<JObject>();
private readonly List<string> _enqueueTaskIds = new List<string>();
private readonly ConcurrentQueue<JObject> _sequentialTaskQueue = new ConcurrentQueue<JObject>();
private readonly ConcurrentQueue<JObject> _instantTaskQueue = new ConcurrentQueue<JObject>();
private readonly Task _executeSequentialJobTask = Task.CompletedTask;
private readonly Task _executeInstantJobTask = Task.CompletedTask;
private readonly RunningState _runningState;
private string _currentSequentialTaskId = string.Empty;
public RemoteControlService()
{
_pollJobTask = _pollJobTask.ContinueWith(async _ =>
@@ -51,14 +55,32 @@ namespace MaaWpfGui.Services.RemoteControl
// ReSharper disable once FunctionNeverReturns
});
_executeJobTask = _executeJobTask.ContinueWith(async _ =>
_executeSequentialJobTask = _executeSequentialJobTask.ContinueWith(async _ =>
{
while (true)
{
await Task.Delay(1000);
try
{
await ExecuteJobLoop();
await ExecuteSequentialJobLoop();
}
catch (Exception ex)
{
Log.Logger.Error(ex, "RemoteControl service raises unknown error.");
}
}
// ReSharper disable once FunctionNeverReturns
});
_executeInstantJobTask = _executeInstantJobTask.ContinueWith(async _ =>
{
while (true)
{
await Task.Delay(1000);
try
{
await ExecuteInstantJobLoop();
}
catch (Exception ex)
{
@@ -193,6 +215,8 @@ namespace MaaWpfGui.Services.RemoteControl
#endregion
#region JobLoops
private async Task PollJobTaskLoop()
{
var endpoint = Instances.SettingsViewModel.RemoteControlGetTaskEndpointUri;
@@ -226,20 +250,44 @@ namespace MaaWpfGui.Services.RemoteControl
// It is a valid task
var id = task.GetValue("id")?.Value<string>();
if (_executedTaskIds.Contains(id))
if (_enqueueTaskIds.Contains(id))
{
continue;
}
_executedTaskIds.Add(id);
_taskQueue.Enqueue(task);
_enqueueTaskIds.Add(id);
switch (type)
{
case "LinkStart":
case "LinkStart-Base":
case "LinkStart-WakeUp":
case "LinkStart-Combat":
case "LinkStart-Recruiting":
case "LinkStart-Mall":
case "LinkStart-Mission":
case "LinkStart-AutoRoguelike":
case "LinkStart-ReclamationAlgorithm":
case "Toolbox-GachaOnce":
case "Toolbox-GachaTenTimes":
case "CaptureImage":
case "Settings-ConnectAddress":
case "Settings-Stage1":
_sequentialTaskQueue.Enqueue(task);
break;
case "CaptureImageNow":
case "HeartBeat":
case "StopTask":
_instantTaskQueue.Enqueue(task);
break;
}
}
}
}
private async Task ExecuteJobLoop()
private async Task ExecuteSequentialJobLoop()
{
if (_taskQueue.TryDequeue(out var task))
if (_sequentialTaskQueue.TryDequeue(out var task))
{
var type = task.GetValue("type")?.Value<string>();
var id = task.GetValue("id")?.Value<string>();
@@ -247,6 +295,8 @@ namespace MaaWpfGui.Services.RemoteControl
var payload = string.Empty;
var status = "SUCCESS";
_currentSequentialTaskId = id;
switch (type)
{
case "LinkStart":
@@ -343,8 +393,106 @@ namespace MaaWpfGui.Services.RemoteControl
{
Instances.SettingsViewModel.ConnectAddress = data;
});
break;
case "Settings-Stage1":
Application.Current.Dispatcher.Invoke(() =>
{
Instances.TaskQueueViewModel.Stage1 = data;
});
break;
// ReSharper disable once RedundantEmptySwitchSection
default:
// 未知的Type统一直接发给MAACore
// No! 未知的任务一概不处理
break;
}
var endpoint = Instances.SettingsViewModel.RemoteControlReportStatusUri;
if (!string.IsNullOrWhiteSpace(endpoint) && endpoint.ToLower().StartsWith("https://"))
{
var uid = Instances.SettingsViewModel.RemoteControlUserIdentity;
var did = Instances.SettingsViewModel.RemoteControlDeviceIdentity;
var response = await Instances.HttpService.PostAsJsonAsync(new Uri(endpoint), new
{
user = uid,
device = did,
status,
task = id,
payload,
});
if (response == null)
{
Log.Logger.Error("RemoteControlService report task failed.");
}
}
_currentSequentialTaskId = string.Empty;
}
}
private async Task ExecuteInstantJobLoop()
{
if (_instantTaskQueue.TryDequeue(out var task))
{
var type = task.GetValue("type")?.Value<string>();
var id = task.GetValue("id")?.Value<string>();
var data = task.GetValue("params")?.Value<string>();
var payload = string.Empty;
var status = "SUCCESS";
switch (type)
{
case "HeartBeat":
{
payload = _currentSequentialTaskId;
break;
}
case "StopTask":
{
await Task.Run(() =>
{
if (!Instances.AsstProxy.AsstStop())
{
// 无法确定当前的界面找不到借用的UI位置因此只能Log
Log.Logger.Error("Failed to stop Asst.");
}
});
// 无需等待,甩出任务即可返回,远端应该用心跳来确认界面卡死和取消是否成功。
break;
}
case "CaptureImageNow":
{
string errMsg = string.Empty;
bool connected = await Task.Run(() => Instances.AsstProxy.AsstConnect(ref errMsg));
if (connected)
{
var image = Instances.AsstProxy.AsstGetImage();
if (image == null)
{
status = "FAILED";
break;
}
byte[] bytes;
using (MemoryStream stream = new MemoryStream())
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);
bytes = stream.ToArray();
}
payload = Convert.ToBase64String(bytes);
break;
}
status = "FAILED";
break;
}
// ReSharper disable once RedundantEmptySwitchSection
default:
@@ -374,6 +522,8 @@ namespace MaaWpfGui.Services.RemoteControl
}
}
#endregion
/// <summary>
/// 根据"一键长草"功能进行修改的方法。
/// </summary>

View File

@@ -33,6 +33,7 @@
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="10"
HorizontalAlignment="Center"
Block.TextAlignment="Center"
Text="{DynamicResource RemoteControlTooltips}"
TextWrapping="Wrap" />