Release v6.3.0-beta.4 (#15462)

## Summary by Sourcery

提升配置迁移的健壮性和任务队列行为,同时扩展与 PC 相关组件的夜间 OTA 打包流程。

Bug 修复:
- 在配置转换和任务队列迁移过程中,更安全地处理缺失或无效的配置部分。
- 当在任务队列 UI 中重新排序任务项时,保持任务设置的可见性选择同步。
- 在任务序列化过程中,通过对每个任务单独捕获并上报错误来防止崩溃。

增强:
- 在迁移过程中裁剪多余的配置项,使其与现有配置列表一致,并移除未使用的配置。
- 在配置转换中对基础设施方案选择索引进行限制,并改进日志记录的一致性。
- 明确 CHANGELOG 中关于《明日方舟》PC 版本支持状态和预期的相关表述。

CI:
- 扩展夜间 OTA 发布工作流,以下载 MaaFramework x64 构件,并将 MaaWin32ControlUnit
打包到安装输出中。

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Improve configuration migration robustness and task queue behavior while
extending nightly OTA packaging for PC-related components.

Bug Fixes:
- Handle missing or invalid configuration sections more safely during
config conversion and task queue migration.
- Keep task settings visibility selection in sync when task items are
reordered in the task queue UI.
- Prevent crashes during task serialization by catching and reporting
errors per task.

Enhancements:
- Prune extra configuration entries during migration to match the
existing configuration list and remove unused configurations.
- Clamp infrastructure plan selection indices and improve logging
consistency in configuration conversion.
- Clarify CHANGELOG wording around support status and expectations for
the PC version of Arknights.

CI:
- Extend the nightly OTA release workflow to download MaaFramework x64
artifacts and bundle MaaWin32ControlUnit into the install output.

</details>

Bug 修复:
- 确保在配置转换时,更稳健地处理缺失或无效的 `"Configurations"`,并使配置键与上一版本保持同步。
- 在任务队列 UI 中,当任务条目重新排序时,使任务设置的可见性选择保持同步。

增强:
- 在配置迁移过程中清理多余的配置项,使其与旧配置中的条目保持一致。

CI:
- 扩展夜间 OTA 发布工作流,以下载用于 x64 构建的 MaaFramework 构件,并在安装输出中包含
`MaaWin32ControlUnit`。

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

提升配置迁移的健壮性和任务队列行为,同时扩展与 PC 相关组件的夜间 OTA 打包流程。

Bug 修复:
- 在配置转换和任务队列迁移过程中,更安全地处理缺失或无效的配置部分。
- 当在任务队列 UI 中重新排序任务项时,保持任务设置的可见性选择同步。
- 在任务序列化过程中,通过对每个任务单独捕获并上报错误来防止崩溃。

增强:
- 在迁移过程中裁剪多余的配置项,使其与现有配置列表一致,并移除未使用的配置。
- 在配置转换中对基础设施方案选择索引进行限制,并改进日志记录的一致性。
- 明确 CHANGELOG 中关于《明日方舟》PC 版本支持状态和预期的相关表述。

CI:
- 扩展夜间 OTA 发布工作流,以下载 MaaFramework x64 构件,并将 MaaWin32ControlUnit
打包到安装输出中。

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Improve configuration migration robustness and task queue behavior while
extending nightly OTA packaging for PC-related components.

Bug Fixes:
- Handle missing or invalid configuration sections more safely during
config conversion and task queue migration.
- Keep task settings visibility selection in sync when task items are
reordered in the task queue UI.
- Prevent crashes during task serialization by catching and reporting
errors per task.

Enhancements:
- Prune extra configuration entries during migration to match the
existing configuration list and remove unused configurations.
- Clamp infrastructure plan selection indices and improve logging
consistency in configuration conversion.
- Clarify CHANGELOG wording around support status and expectations for
the PC version of Arknights.

CI:
- Extend the nightly OTA release workflow to download MaaFramework x64
artifacts and bundle MaaWin32ControlUnit into the install output.

</details>

</details>
This commit is contained in:
uye
2026-01-19 20:21:16 +08:00
committed by GitHub
14 changed files with 149 additions and 44 deletions

View File

@@ -199,6 +199,21 @@ jobs:
mkdir -p install
cmake --install build --prefix install --config RelWithDebInfo
- name: Download MaaFramework
if: matrix.arch == 'x64'
uses: robinraju/release-downloader@v1
with:
repository: MaaXYZ/MaaFramework
latest: true
fileName: '*win-x86_64*.zip'
extract: true
out-file-path: MaaFramework-temp
- name: Copy MaaWin32ControlUnit
if: matrix.arch == 'x64'
run: |
cp MaaFramework-temp/bin/*Win32ControlUnit* install/
- name: Cache .nuke/temp, ~/.nuget/packages
id: cache-nuget
uses: actions/cache@v5

View File

@@ -1,4 +1,4 @@
## v6.3.0-beta.3
## v6.3.0-beta.4
### Highlights
@@ -10,12 +10,9 @@
由于现已支持 「理智作战」 周计划并可添加多个同类型任务,本次更新**移除了原有 「剿灭作战」 任务失败后自动尝试下一个已开放的备选逻辑**。如有相关需要,请手动新增一个 「理智作战」 任务,并**在关卡中选择“剿灭作战”**。
#### 支持连接 PC 端
#### PC 端明日方舟
MAA 现已支持连接 PC 端运行,但后续不会对 PC 端运行情况提供维护或适配支持
- 功能可正常使用的情况下,可自行选择继续使用
- **若无法运行或存在问题,不受理 PC 端相关的 Issue 与反馈**
- 欢迎社区自行适配并提交 Pull Request
MAA 现已支持 PC 端明日方舟的运行。但由于维护人手有限PC 端版本可能长期处于不稳定状态,部分功能可能出现异常或暂时无法使用。我们非常欢迎社区开发者协助适配并提交改进,共同完善对 PC 端的支持
----
@@ -27,15 +24,36 @@ Now supports **adding repeating task types** and renaming them; introduces **Com
Since the "Combat" weekly schedule is now supported and multiple similar tasks can be added, this update **removes the original automatic attempt to use the next available alternate stage after "Annihilation" task failure**. If you need this, please manually add a "Combat" task and **select "Annihilation" in the stage**.
#### Supports Connection to PC (CN only)
#### PC version of Arknights (CN only)
MAA now supports running on PC, but will not provide maintenance or adaptation support for PC-side situations in the future:
- If the functionality works properly, you can choose to continue using it
- **If it cannot run or there are issues, PC-related issues and feedback will not be accepted**
- Community-contributed adaptations and pull requests are welcome
MAA now supports the PC version of Arknights. However, due to limited maintenance resources, the PC version may remain unstable for an extended period, with some features potentially malfunctioning or temporarily unavailable. We warmly welcome community developers to assist in adaptation and submit improvements to collectively enhance PC support.
----
## v6.3.0-beta.4
### 改进 | Improved
* 自动战斗掉线重连、自动肉鸽在战斗结束前延迟 「停止」 动作 添加多任务共用提示 @ABA2396
### 修复 | Fix
* 配置迁移后移除 gui.new 中多余的 config @status102
* 任务序列化 Catch @status102
* 基建计划转换期增加检查 @status102
* 多配置用户在删除 Default 配置时迁移异常 @ABA2396
* EX 关符合时 1 被识别为 | @ABA2396
* 修复移动已打开设置的任务后,当前的设置面板无法继续修改的问题 @ABA2396
* add MaaWin32ControlUnit to nightly build (#15447) @Manicsteiner
### 文档 | Docs
* changelog for PC arknights @MistEO
### 其他 | Other
* 调整删旧配置时机 @ABA2396
## v6.3.0-beta.3
### 新增 | New

View File

@@ -1277,7 +1277,8 @@
"templThreshold": 0.9,
"action": "ClickSelf",
"roi": [900, 70, 380, 400],
"maskRange": [1, 255]
"maskRange": [1, 255],
"onErrorNext": ["Stop"]
},
"StageBegin": {
"algorithm": "JustReturn",
@@ -4000,6 +4001,10 @@
"useRaw": false,
"ocrReplace": [
["—{1,2}", "-"],
["[Oo]", "0"],
["[Ii]", "1"],
["[Ll]", "1"],
["\\|", "1"],
["^.+-EX-?\\d+.*$", "_INVALID_"],
["^.+-S-?\\d+.*$", "_INVALID_"],
["^.+-MO-?\\d+.*$", "_INVALID_"],

View File

@@ -50,16 +50,40 @@ public class ConfigConverter
}
var root = ParseJsonFile(ConfigurationNewFile);
bool ret = true;
var needConvert = root?["Configurations"] is not JObject jObj || jObj.Count == 0 // 有神秘小配置Config里没东西
|| jObj["Default"]?["TaskQueueOrder"] is not null; // 常规撤
JObject? configurations = null;
JObject? oldConfigurations = null;
if (root?["Configurations"] is JObject jObj && parsedOld["Configurations"] is JObject oldObj)
{
configurations = jObj;
oldConfigurations = oldObj;
// 删除多余配置
var extraKeys = configurations.Properties()
.Select(p => p.Name)
.Except(oldConfigurations.Properties().Select(p => p.Name))
.ToList();
foreach (var key in extraKeys)
{
ConfigFactory.DeleteConfiguration(key);
configurations.Remove(key);
}
}
bool needConvert = configurations == null || configurations.Count == 0
|| configurations["Default"]?["TaskQueueOrder"] is not null;
if (needConvert)
{
ret &= ConvertTaskQueue();
}
else
{ // for 6.3.0-beta 出错用户
bool needConvert2 = root?["Configurations"] is JObject configurations && configurations.Properties()
else if (configurations != null) // 保证 configurations 可用
{
// 6.3.0-beta 出错用户,检查 TaskQueue
bool needConvert2 = configurations.Properties()
.Where(p => p.Value is JObject config && config.ContainsKey("TaskQueue"))
.Any(p => {
var taskQueue = ((JObject)p.Value)["TaskQueue"];
@@ -71,6 +95,7 @@ public class ConfigConverter
ret &= ConvertTaskQueue();
}
}
return ret;
}
@@ -90,6 +115,10 @@ public class ConfigConverter
ConfigurationKeys.VersionUpdateDoNotShowUpdate, ConfigurationKeys.CustomInfrastEnabled, ConfigurationKeys.CustomInfrastPlanShowInFightSettings,
];
foreach (var name in ConfigFactory.ConfigList.ToList())
{
ConfigFactory.DeleteConfiguration(name);
}
var currentConfigName = ConfigurationHelper.GetCurrentConfiguration();
foreach (var configName in ConfigurationHelper.GetConfigurationList())
{
@@ -99,13 +128,13 @@ public class ConfigConverter
}
else if (ConfigFactory.AddConfiguration(configName) is false)
{
Log.Error("配置迁移失败,无法添加配置: {ConfigName}", configName);
_logger.Error("配置迁移失败,无法添加配置: {ConfigName}", configName);
throw new Exception($"配置迁移失败,无法添加配置{configName}");
}
if (!ConfigFactory.SwitchConfig(configName))
{
Log.Error("配置迁移失败,无法切换到配置: {ConfigName}", configName);
_logger.Error("配置迁移失败,无法切换到配置: {ConfigName}", configName);
throw new Exception($"配置迁移失败,无法切换到配置{configName}");
}
@@ -250,6 +279,7 @@ public class ConfigConverter
catch
{
}
infrastTask.PlanSelect = Math.Clamp(infrastTask.PlanSelect, -1, infrastTask.InfrastPlan.Count - 1);
infrastTask.RoomList = [];
var roomTypes = Enum.GetNames<InfrastRoomType>();
@@ -272,7 +302,7 @@ public class ConfigConverter
}
else
{
Log.Error("Enum.TryParse<InfrastRoomType> 失败room: {Room}", room);
_logger.Error("Enum.TryParse<InfrastRoomType> 失败room: {Room}", room);
}
}
@@ -522,7 +552,7 @@ public class ConfigConverter
}
catch (Exception ex)
{
Log.Error(ex, "Failed to deserialize json file: {FilePath}", filePath);
_logger.Error(ex, "Failed to deserialize json file: {FilePath}", filePath);
}
return null;

View File

@@ -163,6 +163,7 @@ To customize rotation schedules, please use 「{key=InfrastModeCustom}」.</syst
<system:String x:Key="CustomInfrastTimeSchedule">Rotation time ({0})</system:String>
<system:String x:Key="CustomInfrastFileParseFailed">Base Configuration Parsing Error!</system:String>
<system:String x:Key="CustomInfrastFileHasPlanNoPeriod">Some rotations in the {Key=InfrastModeCustom} configuration have time periods set, which may lead to unexpected behavior. To set shift change time periods, ensure all rotations have time periods configured; otherwise, leave them all blank.</system:String>
<system:String x:Key="CustomInfrastPlanSelectOutOfIndex">Custom infrast plan selection is out of index</system:String>
<system:String x:Key="CustomInfrastTitle" xml:space="preserve">Base Configuration:&#160;</system:String>
<system:String x:Key="CustomInfrastPlan">Base Plan</system:String>
<system:String x:Key="CustomInfrastPlanToolTip">

View File

@@ -163,6 +163,7 @@
<system:String x:Key="CustomInfrastTimeSchedule">时间轮换 ({0})</system:String>
<system:String x:Key="CustomInfrastFileParseFailed">基地構成ファイルのパースエラー!</system:String>
<system:String x:Key="CustomInfrastFileHasPlanNoPeriod">カスタムインフラストラクチャ設定では、一部のプランにのみ時間帯が設定されている場合があり、予期しない動作が発生する可能性があります。シフト交代時間帯を設定する必要がある場合は、すべてのプランに時間帯が設定されていることを確認してください。設定されていない場合は、すべてのプランを空白のままにしてください。</system:String>
<system:String x:Key="CustomInfrastPlanSelectOutOfIndex">カスタムインフラストラクチャ構成の選択がインデックスを超えています</system:String>
<system:String x:Key="CustomInfrastTitle" xml:space="preserve">基地構成ファイルの設定: </system:String>
<system:String x:Key="CustomInfrastPlan">基地計画</system:String>
<system:String x:Key="CustomInfrastPlanToolTip">

View File

@@ -163,6 +163,7 @@
<system:String x:Key="CustomInfrastTimeSchedule">로테이션 시간 ({0})</system:String>
<system:String x:Key="CustomInfrastFileParseFailed">기반시설 구성 인식 실패!</system:String>
<system:String x:Key="CustomInfrastFileHasPlanNoPeriod">{Key=InfrastModeCustom} 설정 중 일부 계획에만 시간대가 설정되어 있어, 예기치 않은 동작이 발생할 수 있습니다. 교대 시간대를 설정하려면 모든 계획에 시간대를 설정하거나, 그렇지 않으면 모두 비워 두세요.</system:String>
<system:String x:Key="CustomInfrastPlanSelectOutOfIndex">사용자 지정 인프라 구성 선택 항목이 인덱스를 초과했습니다.</system:String>
<system:String x:Key="CustomInfrastTitle" xml:space="preserve">기반시설 구성: </system:String>
<system:String x:Key="CustomInfrastPlan">기반시설 계획</system:String>
<system:String x:Key="CustomInfrastPlanToolTip">

View File

@@ -163,6 +163,7 @@
<system:String x:Key="CustomInfrastTimeSchedule">时间轮换 ({0})</system:String>
<system:String x:Key="CustomInfrastFileParseFailed">自定义基建配置文件解析错误!</system:String>
<system:String x:Key="CustomInfrastFileHasPlanNoPeriod">自定义基建配置仅有部分计划存在时间段,可能会存在预期外的行为。如需设置换班时间段请保证所有计划均设置时间段,否则请全部留空。</system:String>
<system:String x:Key="CustomInfrastPlanSelectOutOfIndex">自定义基建配置选择超出索引</system:String>
<system:String x:Key="CustomInfrastTitle" xml:space="preserve">自定义基建配置: </system:String>
<system:String x:Key="CustomInfrastPlan">基建计划</system:String>
<system:String x:Key="CustomInfrastPlanToolTip">

View File

@@ -163,6 +163,7 @@
<system:String x:Key="CustomInfrastTimeSchedule">時間輪換 ({0})</system:String>
<system:String x:Key="CustomInfrastFileParseFailed">自定義基建配置檔案解析錯誤!</system:String>
<system:String x:Key="CustomInfrastFileHasPlanNoPeriod">自訂基建配置僅有部分計畫存在時間段,可能會存在預期外的行為。如需設定換班時段請確保所有方案均設定時段,否則請全部留白。</system:String>
<system:String x:Key="CustomInfrastPlanSelectOutOfIndex">自定義基建配置選擇超出索引</system:String>
<system:String x:Key="CustomInfrastTitle" xml:space="preserve">自定義基建配置: </system:String>
<system:String x:Key="CustomInfrastPlan">基建計劃</system:String>
<system:String x:Key="CustomInfrastPlanToolTip">

View File

@@ -498,10 +498,16 @@ public class SettingsViewModel : Screen
{
get => _currentConfiguration;
set {
SetAndNotify(ref _currentConfiguration, value);
ConfigurationHelper.SwitchConfiguration(value);
ConfigFactory.SwitchConfig(value);
bool ret = ConfigurationHelper.SwitchConfiguration(value);
ret &= ConfigFactory.SwitchConfig(value);
if (!ret)
{
ConfigurationHelper.SwitchConfiguration(_currentConfiguration);
ConfigFactory.SwitchConfig(_currentConfiguration);
return;
}
SetAndNotify(ref _currentConfiguration, value);
Bootstrapper.ShutdownAndRestartWithoutArgs();
}
}
@@ -540,6 +546,8 @@ public class SettingsViewModel : Screen
}
else
{
ConfigurationHelper.DeleteConfiguration(NewConfigurationName);
ConfigFactory.DeleteConfiguration(NewConfigurationName);
var growlInfo = new GrowlInfo {
IsCustom = true,
Message = string.Format(LocalizationHelper.GetString("ConfigExists"), NewConfigurationName),

View File

@@ -155,6 +155,11 @@ public class TaskQueueViewModel : Screen
{
TaskItemViewModels[i].Index = i;
}
if (TaskSettingVisibilities.CurrentIndex == oldIndex)
{
TaskSettingVisibilities.CurrentIndex = newIndex;
}
}
else if (e.Action == NotifyCollectionChangedAction.Add)
{
@@ -1696,21 +1701,29 @@ public class TaskQueueViewModel : Screen
continue;
}
switch (SerializeTask(item))
try
{
case true:
++count;
Instances.TaskQueueViewModel.TaskItemViewModels[index].TaskId = Instances.AsstProxy.TasksStatus.Last().Key;
break;
case false:
taskRet = false;
AddLog(LocalizationHelper.GetStringFormat("TaskAppend.Error", LocalizationHelper.GetString(item.TaskType.ToString()), item.Name), UiLogColor.Error);
Instances.TaskQueueViewModel.TaskItemViewModels[index].Status = (int)Main.TaskStatus.Error;
break;
case null:
AddLog(LocalizationHelper.GetStringFormat("TaskAppend.Skip", LocalizationHelper.GetString(item.TaskType.ToString()), item.Name), UiLogColor.Info);
Instances.TaskQueueViewModel.TaskItemViewModels[index].Status = 4;
break;
switch (SerializeTask(item))
{
case true:
++count;
Instances.TaskQueueViewModel.TaskItemViewModels[index].TaskId = Instances.AsstProxy.TasksStatus.Last().Key;
break;
case false:
taskRet = false;
AddLog(LocalizationHelper.GetStringFormat("TaskAppend.Error", LocalizationHelper.GetString(item.TaskType.ToString()), item.Name), UiLogColor.Error);
Instances.TaskQueueViewModel.TaskItemViewModels[index].Status = (int)Main.TaskStatus.Error;
break;
case null:
AddLog(LocalizationHelper.GetStringFormat("TaskAppend.Skip", LocalizationHelper.GetString(item.TaskType.ToString()), item.Name), UiLogColor.Info);
Instances.TaskQueueViewModel.TaskItemViewModels[index].Status = 4;
break;
}
}
catch (Exception ex)
{
taskRet = false;
AddLog(LocalizationHelper.GetStringFormat("TaskAppend.Error", LocalizationHelper.GetString(item.TaskType.ToString()), item.Name) + "\n" + ex.Message, UiLogColor.Error);
}
}
@@ -2072,7 +2085,6 @@ public class TaskQueueViewModel : Screen
{
ret ??= instance.SerializeTask(task, taskId);
}
return ret;
}
}

View File

@@ -608,7 +608,7 @@ public class InfrastSettingsUserControlModel : TaskSettingsViewModel
}
else if (infrast.PlanSelect != -1 && infrast.InfrastPlan.Count <= infrast.PlanSelect)
{
throw new InvalidOperationException("CustomInfrastPlanSelect is out of range");
throw new InvalidOperationException(LocalizationHelper.GetString("CustomInfrastPlanSelectOutOfIndex"));
}
else if (infrast.PlanSelect >= 0)
{

View File

@@ -403,10 +403,6 @@
Margin="0,10"
Content="{DynamicResource HideSeries}"
IsChecked="{Binding HideSeries}" />
<CheckBox
Margin="0,10"
Content="{DynamicResource AutoRestartOption}"
IsChecked="{Binding AutoRestartOnDrop}" />
<CheckBox
Margin="0,10"
Content="{DynamicResource UseWeeklySchedule}"
@@ -432,6 +428,16 @@
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<controls:TextBlock
Margin="0,5"
HorizontalAlignment="Center"
FontWeight="Bold"
Text="{DynamicResource MultiTasksShareTip}"
TextAlignment="Center" />
<CheckBox
Margin="0,10"
Content="{DynamicResource AutoRestartOption}"
IsChecked="{Binding AutoRestartOnDrop}" />
</StackPanel>
</StackPanel>
</UserControl>

View File

@@ -392,6 +392,12 @@
Text="{DynamicResource RoguelikeStartWithSeed}"
TextWrapping="Wrap" />
</CheckBox>-->
<controls:TextBlock
Margin="0,5"
HorizontalAlignment="Center"
FontWeight="Bold"
Text="{DynamicResource MultiTasksShareTip}"
TextAlignment="Center" />
<CheckBox Margin="0,10" IsChecked="{Binding RoguelikeDelayAbortUntilCombatComplete}">
<TextBlock
Block.TextAlignment="Left"