Compare commits

..

1 Commits

Author SHA1 Message Date
zzyyyl
02961e60e9 feat: 增加从 GitHub 直接获取资源文件的方式,并优先尝试从 GitHub 获取
github raw 没有 dynamic_list.txt
2024-07-14 17:34:23 +08:00
9039 changed files with 218012 additions and 1265946 deletions

View File

@@ -1,9 +1,12 @@
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
# for clang-format 20.0.0
# for clang-format 17.0.1
Language: Json
IndentWidth: 4
---
Language: Cpp
BasedOnStyle: "WebKit"
BasedOnStyle: 'WebKit'
# AccessModifierOffset: 2
AlignAfterOpenBracket: "AlwaysBreak"
AlignAfterOpenBracket: 'AlwaysBreak'
AlignArrayOfStructures: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
@@ -26,9 +29,9 @@ AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: Inline
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
BreakAfterReturnType: Automatic
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
BreakTemplateDeclarations: Yes
AlwaysBreakTemplateDeclarations: Yes
# AttributeMacros:
# - __pragma
# - _Pragma
@@ -60,15 +63,15 @@ BraceWrapping:
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: true
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeConceptDeclarations: Always
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakStringLiterals: true
ColumnLimit: 120
ColumnLimit: 100
# CommentPragmas: '^ MEO pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
@@ -84,12 +87,12 @@ FixNamespaceComments: true
# - foreach
# - Q_FOREACH
# - BOOST_FOREACH
# IfMacros:
# IfMacros:
# - 'KJ_IF_MAYBE'
IncludeBlocks: Preserve
# IncludeCategories:
# IncludeIsMainRegex:
# IncludeIsMainSourceRegex:
# IncludeIsMainRegex:
# IncludeIsMainSourceRegex:
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
@@ -127,7 +130,7 @@ NamespaceIndentation: None
# ObjCSpaceAfterProperty:
# ObjCSpaceBeforeProtocolList:
PackConstructorInitializers: Never
# PenaltyBreakAssignment:
# PenaltyBreakAssignment:
# PenaltyBreakBeforeFirstCallParameter:
# PenaltyBreakComment:
# PenaltyBreakFirstLessLess:
@@ -145,7 +148,7 @@ PointerAlignment: Left
# - constexpr
# - type
ReferenceAlignment: Left
ReflowComments: Always
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
@@ -180,7 +183,7 @@ SpacesInLineCommentPrefix:
SpacesInParens: Never
SpacesInSquareBrackets: false
Standard: c++20
# StatementAttributeLikeMacros:
# StatementAttributeLikeMacros:
# StatementMacros:
TabWidth: 4
# TypeNames:
@@ -188,4 +191,3 @@ TabWidth: 4
UseTab: Never
# VerilogBreakBetweenInstancePorts:
# WhitespaceSensitiveMacros:
RemoveEmptyLinesInUnwrappedLines: true

View File

@@ -1,2 +0,0 @@
CompileFlags:
CompilationDatabase: build

View File

@@ -1,26 +0,0 @@
FROM mcr.microsoft.com/devcontainers/base:ubuntu
USER vscode
ENV PATH="/home/vscode/.local/bin:${PATH}"
ARG PYTHON_VERSION=3.12.11
ARG NODEJS_VERSION=24
# Install and setup mise and uv
RUN curl https://mise.run | sh
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
# Activate mise
RUN echo 'eval "$(mise activate bash)"' >> ~/.bashrc
# Setup Python and Node.js
RUN mise use -g node@$NODEJS_VERSION
RUN uv python install $PYTHON_VERSION
# Create and activate venv
RUN uv venv --clear ~/.venv/maa
RUN echo "source ~/.venv/maa/bin/activate" >> ~/.bashrc
# Install tools
RUN uv tool install prek

View File

@@ -1,48 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
{
"name": "MAA Docs Env (Light)",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
// "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"build": {
"dockerfile": "Dockerfile"
},
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [3001],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "bash .devcontainer/0/post-create.sh",
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"nekosu.maa-support",
"esbenp.prettier-vscode",
"DavidAnson.vscode-markdownlint",
"yzhang.markdown-all-in-one",
"vue.volar",
"mkxml.vscode-filesize"
],
"settings": {
// Color theme
"workbench.colorTheme": "Default Dark Modern",
// Editor formatting
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// Performance optimizations
"search.exclude": {
"**/node_modules": true,
"**/3rdparty": true
}
}
}
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@@ -1,19 +0,0 @@
#!/bin/bash
WORKSPACE=$(pwd)
echo "===================="
cd "$WORKSPACE"
echo "Setting up git safe.directory..."
git config --global --add safe.directory "$WORKSPACE"
git submodule foreach --recursive 'git config --global --add safe.directory "$toplevel/$path"'
echo "===================="
cd "$WORKSPACE"
echo "Updating submodules..."
git submodule update --init --recursive
echo "===================="
cd "$WORKSPACE"/docs
echo "Installing node modules..."
npm install -g pnpm
pnpm install --frozen-lockfile

View File

@@ -1,52 +0,0 @@
FROM mcr.microsoft.com/devcontainers/base:ubuntu
USER vscode
ENV PATH="/home/vscode/.local/bin:${PATH}"
ARG CLANGD_VERSION=20
ARG PYTHON_VERSION=3.12.11
ARG NODEJS_VERSION=24
# Add kitware's apt repository (for cmake)
RUN curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc | sudo gpg --dearmor -o /usr/share/keyrings/kitware-archive-keyring.gpg
RUN cat <<EOF | sudo tee /etc/apt/sources.list.d/kitware.sources
Types: deb
URIs: https://apt.kitware.com/ubuntu
Suites: $(lsb_release -cs)
Components: main
Architectures: $(dpkg --print-architecture)
Signed-By: /usr/share/keyrings/kitware-archive-keyring.gpg
EOF
# Install system dependencies
RUN sudo apt update \
&& sudo apt upgrade -y \
&& sudo apt install -y \
cmake \
fish \
&& sudo rm -rf /var/lib/apt/lists/*
# Initialize fish
RUN fish -c exit
# Install and setup mise and uv
RUN curl https://mise.run | sh
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
# Activate mise
RUN echo 'eval "$(mise activate bash)"' >> ~/.bashrc
RUN echo 'mise activate fish | source' >> ~/.config/fish/config.fish
# Setup Python and Node.js
RUN mise use -g node@$NODEJS_VERSION
RUN uv python install $PYTHON_VERSION
# Create and activate venv
RUN uv venv --clear ~/.venv/maa
RUN echo "source ~/.venv/maa/bin/activate" >> ~/.bashrc
RUN echo "source ~/.venv/maa/bin/activate.fish" >> ~/.config/fish/config.fish
# Install tools
RUN uv tool install prek
RUN uv tool install ruff

View File

@@ -1,78 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
{
"name": "MAA Core Env (Full)",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
// "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"build": {
"dockerfile": "Dockerfile"
},
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [3001],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "bash .devcontainer/1/post-create.sh",
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"nekosu.maa-support",
"ms-vscode.cmake-tools",
"xaver.clang-format",
"llvm-vs-code-extensions.vscode-clangd",
"ms-python.python",
"charliermarsh.ruff",
"esbenp.prettier-vscode",
"DavidAnson.vscode-markdownlint",
"yzhang.markdown-all-in-one",
"vue.volar",
"mkxml.vscode-filesize"
],
"settings": {
// Color theme
"workbench.colorTheme": "Default Dark Modern",
// Editor formatting
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// Language-specific formatting
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
},
"[cpp]": {
"editor.defaultFormatter": "xaver.clang-format"
},
"[c]": {
"editor.defaultFormatter": "xaver.clang-format"
},
// Python runtime
"python.terminal.launchArgs": ["-u"],
"python.defaultInterpreterPath": "/home/vscode/.venv/maa/bin/python",
"python.terminal.activateEnvironment": false,
// CMake settings
"cmake.configureOnOpen": false,
// Performance optimizations
"search.exclude": {
"**/node_modules": true,
"**/build": true,
"**/install": true,
"**/MaaDeps": true,
"**/3rdparty": true
}
}
}
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@@ -1,45 +0,0 @@
#!/bin/bash
WORKSPACE=$(pwd)
echo "===================="
cd "$WORKSPACE"
echo "Setting up git safe.directory..."
git config --global --add safe.directory "$WORKSPACE"
git submodule foreach --recursive 'git config --global --add safe.directory "$toplevel/$path"'
echo "===================="
cd "$WORKSPACE"
echo "Updating submodules..."
git submodule update --init --recursive
echo "===================="
cd "$WORKSPACE"/docs
echo "Installing node modules..."
npm install -g pnpm
pnpm install --frozen-lockfile
echo "===================="
cd "$WORKSPACE"
echo "Installing Python dependencies..."
# Install Python dependencies from all tools
for req_file in tools/*/requirements.txt; do
if [ -f "$req_file" ]; then
echo "Installing from $req_file"
uv pip install -r "$req_file"
fi
done
for req_file in tools/*/requirements-dev.txt; do
if [ -f "$req_file" ]; then
echo "Installing from $req_file"
uv pip install -r "$req_file"
fi
done
echo "===================="
cd "$WORKSPACE"
echo "Installing MaaDeps..."
python tools/maadeps-download.py
# Link clang-format & clangd to /usr/local/bin for easy access
sudo ln -s $WORKSPACE/src/MaaUtils/MaaDeps/x-tools/llvm/bin/clang-format /usr/local/bin/clang-format
sudo ln -s $WORKSPACE/src/MaaUtils/MaaDeps/x-tools/llvm/bin/clangd /usr/local/bin/clangd

View File

@@ -1,13 +1,16 @@
{
"name": "Plain Env (Nothing Installed)",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"postCreateCommand": "bash .devcontainer/post-create.sh",
"image": "mcr.microsoft.com/devcontainers/cpp:ubuntu-22.04",
"features": {
"ghcr.io/devcontainers/features/python:1": {},
"ghcr.io/devcontainers/features/sshd:1": {}
},
"postCreateCommand": "sudo sh -ce 'apt update && apt install g++-12 -y && echo export CC=gcc-12 CXX=g++-12 >> /etc/profile.d/set-compiler.sh'",
"customizations": {
"vscode": {
"settings": {
// Color theme
"workbench.colorTheme": "Default Dark Modern"
}
"extensions": [
"llvm-vs-code-extensions.vscode-clangd",
"DavidAnson.vscode-markdownlint"
]
}
}
}

View File

@@ -1,13 +0,0 @@
#!/bin/bash
WORKSPACE=$(pwd)
echo "===================="
cd "$WORKSPACE"
echo "Setting up git safe.directory..."
git config --global --add safe.directory "$WORKSPACE"
git submodule foreach --recursive 'git config --global --add safe.directory "$toplevel/$path"'
echo "===================="
cd "$WORKSPACE"
echo "Updating submodules..."
git submodule update --init --recursive

View File

@@ -13,7 +13,6 @@
290cc30f153a0206bd870a0a58cb6338aa16ea34
db9afdc77e85d5fc462d923fca5318be021656cd
a5daac889a55baf8895918d4964f20dad1938470
9ed30c5658c88edf1ef839771a53c7d599183b8b
# incorrect format of 3rdparty/resource/Arknights-Tile-Pos/levels.json
65d654e54b9d04d1902c9ee9f9fb2679520adafd
@@ -23,7 +22,6 @@ a5daac889a55baf8895918d4964f20dad1938470
206df466fd5e3d8ef80387049b0c5011fb58cada
c9b5aa638a97397ab8666031ea646904527afc09
559c913fca0dd0966896396e251be13f76ea3e52
# Re-structure again.
4b3b84df8f487be0ac86506c28dbebc4635f1282
91abbb7f175b93a58368a136acf3799666f770d2
@@ -31,28 +29,3 @@ c9b5aa638a97397ab8666031ea646904527afc09
bae271c09bb79e5cc2a7f8491ce882d276ac97b4
3d83b80dd67d996a5f4f1732fe1eb36cacc7605e
21617270bcef2456cb7f89e54560ca6788450711
# resource/*.json prettier mass styling
d84da5d01a872540d520bc87408d4524e07cfa39
546bfc399cdd140a9812f44b5e6bc89fccfda5fa
f0bdccdf0da7fac29f475951f0f43d3ab56a7ffe
7a758bce844bc2778f04a789195efbd17681dac7
5f3730b76785b1168125eb3b5edbed399722f8f9
bc3ad67a02cb3b8e154c06955cf17091c4265000
# oxipng template optimization
e3d63894b28b2ef5e2405e144a32a6981de5e1b2
# refactor: divide tasks.json into multiple jsons
dce6e317c8e56836662b64ac4b3d1a69b4ff4dd8
# c# 统一使用文件范围限定的 namespace
76252d42e3febae1aef396e8ee5482fa46d565c6
# usage: add "[blame ignore]" to the commit message
# This file is managed by an automated workflow
# Do not add, remove or modify lines below this comment
# as they are automatically appended
# --------------------------------------------------- #
f7772bc814c52f9a5e65af02404cf54b70fe0a32
ca9807ce099e5cae133293df8dfcd2b56b0e06bd

3
.gitattributes vendored
View File

@@ -8,7 +8,4 @@
*.json text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.sh text eol=lf

View File

@@ -1 +0,0 @@
1944164d9c47549d71c3b25987e9299404cc16f3

View File

@@ -1,22 +1,20 @@
name: Bug 反馈(使用中文)
description: 识别错误、操作异常、连接错误等
labels: ["bug"]
labels: ['bug']
body:
- type: checkboxes
id: checks
attributes:
label: 请确认自己完成了下列必选项之后再进行勾选,若未完成必选项或勾选了"我未仔细阅读"选项将视为自愿接受被直接关闭 Issue
label: 在提问之前...
options:
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决
required: false
- label: 未仔细阅读这些内容,只是一键已读[所有内容](/MaaAssistantArknights/MaaAssistantArknights/issues),并相信这不会影响问题的处理
required: false
- label: 填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等
required: false
- label: 使用的是当前更新版本的最新版, 并已查看版本发布至今的 [更新内容](https://github.com/MaaAssistantArknights/MaaAssistantArknights/commits/dev/), [Pull Requests](https://github.com/MaaAssistantArknights/MaaAssistantArknights/pulls) 中尚未发布的更新内容并未提及该 Bug 已被修复的情况
required: false
- label: 我已检查了 [常见问题](https://docs.maa.plus/zh-cn/manual/faq.html), [公告](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues/7732), [活跃议题](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues), [已关闭议题](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues?q=is%3Aissue%20state%3Aclosed) 确认我的问题未被提及
required: false
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决
required: true
- label: 填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等
required: true
- label: 已查看最新测试版本的更新内容,并未提及该 Bug 已被修复的情况
required: true
- label: 已检查了置顶议题pinned issue确认我的问题未被提及
required: true
- type: textarea
id: describe
attributes:
@@ -28,45 +26,32 @@ body:
id: version
attributes:
label: Version
description: |
请提供 MAA 版本。可在 MAA -> 设置 -> 软件更新中找到点击图标可复制
请分别点击 `资源版本` 和 `MAA 版本` 并粘贴,最终应有 5 行版本信息。
description: >-
请提供 MAA 版本。可在 MAA -> 设置 -> 软件更新中找到点击图标可复制)。
placeholder: |
Resource Version:
MAA Version:
UI Version:
Core Version:
validations:
required: true
- type: textarea
id: logs
attributes:
label: 日志和配置文件
label: 日志文件
description: |
**请在 MAA -> 设置 -> 问题反馈中找到生成日志压缩包按钮,点击并上传压缩包**
**若待上传压缩包大于 25MB请转而上传该压缩包同日期文件夹中的若干小压缩包**
若正在使用 macOS请点击屏幕左上角的“文件”点击“打开日志文件夹”
placeholder: |
请确认上传文件前已关闭 MAA
validations:
required: true
- type: textarea
id: configuration
attributes:
label: 配置信息
description: |
请说明操作系统及版本、模拟器品牌、模拟器分辨率、DPI、帧率
若正在使用 MuMu 或雷电 9请说明截图增强是否开启
最后请说明 GPU 加速推理是否开启,若开启请提供 GPU 型号。
**请在关闭MAA后,上传 `debug` 文件夹中的 `asst.log` 和 `gui.log` 日志文件,并说明问题出现的大致时间点**
**请直接将完整的文件拖拽进来,而非自己裁切或复制的片段;若文件体积过大可压缩后再上传**
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: 截图或录屏
label: 截图
description: |
可上传屏幕截图或录制以帮助解释你的问题,包括但不限于 MAA 软件截图、游戏画面截图
若是**识别相关问题**,请尽可能提供模拟器自带的截图工具截取的无遮挡的**原图**(或通过 adb 截取原图)
用其他的工具如QQ/微信)截取的图片包含窗口边框且长宽比、分辨率不固定不利于我们排除bug
若文件体积过大可压缩后再上传
如果有,添加屏幕截图以帮助解释你的问题,包括但不限于 MAA 软件截图、游戏画面截图
若是**识别相关问题**,请尽可能提供模拟器自带的截图工具截取的无遮挡的**原图**(或通过 adb 截取原图)
用其他的工具如QQ截取的图片包含窗口边框不利于我们排除bug
`debug` 文件夹下有一些自动截图的错误图片,若有相关的,请一并打包上传
validations:
required: false
- type: textarea
@@ -74,6 +59,7 @@ body:
attributes:
label: 还有别的吗?
description: |
使用的模拟器?操作系统?相关的配置?链接?参考资料?
任何能让我们对你所遇到的问题有更多了解的东西
validations:
required: false

View File

@@ -1,22 +1,16 @@
name: 需求建议(使用中文)
description: 新功能、建议等
labels: ["enhancement"]
labels: ['enhancement']
body:
- type: checkboxes
id: checks
attributes:
label: 请确认自己完成了下列必选项之后再进行勾选,若未完成必选项或勾选了"我未仔细阅读"选项将视为自愿接受被直接关闭 Issue
label: 在提问之前...
options:
- label: 基本确定这是一个新功能/建议,而不是遇到了 Bug不确定的话请附上日志
required: false
- label: 未仔细阅读这些内容,只是一键已读[所有内容](/MaaAssistantArknights/MaaAssistantArknights/issues),并相信这不会影响问题的处理
required: false
- label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等
required: false
- label: 我使用的是当前更新版本的最新版, 并已查看版本发布至今的 [更新内容](https://github.com/MaaAssistantArknights/MaaAssistantArknights/commits/dev/), [Pull Requests](https://github.com/MaaAssistantArknights/MaaAssistantArknights/pulls) 中尚未发布的更新内容,确认我的建议未被提及
required: false
- label: 我已检查了 [常见问题](https://docs.maa.plus/zh-cn/manual/faq.html), [公告](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues/7732), [活跃议题](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues), [已关闭议题](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues?q=is%3Aissue%20state%3Aclosed) 确认我的建议未被提及
required: false
- label: 填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等
required: true
- label: 基本确定这是一个新功能/建议,而不是遇到了 Bug不确定的话请附上日志
required: true
- type: textarea
id: describe
attributes:

View File

@@ -1,85 +0,0 @@
name: 雷电截图增强反馈(使用中文)
description: 有关雷电 9 截图增强功能的集中反馈
labels: ["LD9 API"]
body:
- type: checkboxes
id: checks
attributes:
label: 在提问之前...
options:
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决
required: false
- label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等
required: false
- label: 我已检查了[公告](/MaaAssistantArknights/MaaAssistantArknights/issues/7732)、[活跃议题](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues)、[已关闭议题](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues?q=is%3Aissue%20state%3Aclosed),确认我的问题未被提及
required: false
- label: 我未仔细阅读这些内容,只是一键已读[所有内容](/MaaAssistantArknights/MaaAssistantArknights/issues),并相信这不会影响问题的处理
required: false
- label: 我正在使用最新的 MAA 公测版本和最新的官版雷电 9 模拟器
required: false
- label: 我已参考文档确定安装路径、实例编号填写正确,并确认在关闭截图增强模式后问题消失
required: false
- label: 我未开启高帧率,且模拟器帧数设置为 60
required: false
- type: textarea
id: describe
attributes:
label: 问题描述
description: 描述问题时请尽可能详细
validations:
required: true
- type: textarea
id: version
attributes:
label: Version
description: |
请提供 MAA 版本和雷电 9 版本。MAA 版本可在 MAA -> 设置 -> 软件更新中找到(点击图标可复制)。
雷电 9 版本可在右上角菜单按钮 -> 关于雷电 中找到。
placeholder: |
Resource Version:
MAA Version:
雷电 9 版本号:
validations:
required: true
- type: textarea
id: logs
attributes:
label: MAA 日志和配置文件
description: |
**请在 MAA -> 设置 -> 问题反馈中找到生成日志压缩包按钮,点击并上传压缩包**
**请直接将完整的文件拖拽进来,而非自己裁切或复制的片段;若文件体积过大可压缩后再上传**
placeholder: |
请确认上传文件前已关闭 MAA
validations:
required: true
- type: textarea
id: ldlogs
attributes:
label: 雷电 9 诊断信息
description: |
**在雷电 9 右上角菜单按钮 -> 诊断信息 中点击 复制信息,并将全部内容粘贴进来。**
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: 截图
description: |
`debug` 目录下按功能分类的文件夹内,有一些自动截图的错误图片,若有相关的,请一并打包上传
可上传屏幕截图或录制以帮助解释你的问题,包括但不限于 MAA 软件截图、游戏画面截图
若是**识别相关问题**,请尽可能提供模拟器自带的截图工具截取的无遮挡的**原图**(或通过 adb 截取原图)
用其他的工具如QQ/微信截取的图片包含窗口边框且长宽比、分辨率不固定不利于我们排除bug
若文件体积过大可压缩后再上传
validations:
required: false
- type: textarea
id: others
attributes:
label: 其他
description: |
GPU 加速推理是否开启,若开启请提供 GPU 型号。
亦或者任何能让我们对你所遇到的问题有更多了解的东西
validations:
required: false

View File

@@ -1,26 +1,26 @@
name: MuMu 截图增强反馈(使用中文)
description: 有关 MuMu 截图增强功能的集中反馈
labels: ["MuMu API"]
labels: ['mumu api']
body:
- type: checkboxes
id: checks
attributes:
label: 在提问之前...
options:
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决
required: false
- label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等
required: false
- label: 我已检查了[公告](/MaaAssistantArknights/MaaAssistantArknights/issues/7732)、[活跃议题](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues)、[已关闭议题](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues?q=is%3Aissue%20state%3Aclosed),确认我的问题未被提及
required: false
- label: 未仔细阅读这些内容,只是一键已读[所有内容](/MaaAssistantArknights/MaaAssistantArknights/issues),并相信这不会影响问题的处理
required: false
- label: 正在使用最新的 MAA 公测版本和最新的官版或方舟专版 MuMu 模拟器
required: false
- label: 已参考文档确定安装路径、实例编号、屏幕编号填写正确,并确认在关闭截图增强模式后问题消失
required: false
- label: 未开启动态帧率及后台保活,模拟器帧数为 60显存使用策略为“画面表现更好”
required: false
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决
required: true
- label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等
required: true
- label: 我已检查了置顶议题Pinned Issue,确认我的问题未被提及
required: true
- label: 正在使用最新的 MAA 内测版本和最新的**官版** MuMu 模拟器
required: true
- label: 已参考文档确定安装路径、实例编号、屏幕编号填写正确,并确认在关闭截图增强模式后问题消失
required: true
- label: 未开启高帧率模式,且显存使用策略为“画面表现更好”
required: false
- label: 正在使用后台保活功能,并确定该问题仅在开启后台保活时出现
required: false
- type: textarea
id: describe
attributes:
@@ -32,25 +32,22 @@ body:
id: version
attributes:
label: Version
description: |
请提供 MAA 版本和 MuMu 版本。MAA 版本可在 MAA -> 设置 -> 软件更新中找到(点击图标可复制)。
MuMu 版本可在右上角菜单按钮 -> 关于 MuMu 中找到。
description: >-
请提供 MAA 版本和 MuMu 版本。MAA 版本可在 MAA -> 设置 -> 软件更新中找到(点击图标可复制)。MuMu 版本可在右上角菜单按钮 -> 关于 MuMu 中找到。
placeholder: |
Resource Version:
MAA Version:
MuMu 版本号:
Resource Version:
UI Version:
Core Version:
MuMu 版本号:
validations:
required: true
- type: textarea
id: logs
attributes:
label: MAA 日志和配置文件
label: MAA 日志文件
description: |
**请在 MAA -> 设置 -> 问题反馈中找到生成日志压缩包按钮,点击并上传压缩包**
**上传 `debug` 文件夹中的 `asst.log` 和 `gui.log` 日志文件,并说明问题出现的大致时间点**
**请直接将完整的文件拖拽进来,而非自己裁切或复制的片段;若文件体积过大可压缩后再上传**
placeholder: |
请确认上传文件前已关闭 MAA
validations:
required: true
- type: textarea
@@ -67,20 +64,18 @@ body:
attributes:
label: 截图
description: |
`debug` 目录下按功能分类的文件夹内,有一些自动截图的错误图片,若有相关的,请一并打包上传
可上传屏幕截图或录制以帮助解释你的问题,包括但不限于 MAA 软件截图、游戏画面截图
如果有,添加屏幕截图以帮助解释你的问题,包括但不限于 MAA 软件截图、游戏画面截图
若是**识别相关问题**,请尽可能提供模拟器自带的截图工具截取的无遮挡的**原图**(或通过 adb 截取原图)
用其他的工具如QQ/微信)截取的图片包含窗口边框且长宽比、分辨率不固定不利于我们排除bug
若文件体积过大可压缩后再上传
用其他的工具如QQ截取的图片包含窗口边框不利于我们排除bug
`debug` 文件夹下有一些自动截图的错误图片,若有相关的,请一并打包上传
validations:
required: false
- type: textarea
id: others
attributes:
label: 其他
label: 还有别的吗?
description: |
GPU 加速推理是否开启,若开启请提供 GPU 型号。
亦或者任何能让我们对你所遇到的问题有更多了解的东西
操作系统?相关的配置?链接?参考资料?
任何能让我们对你所遇到的问题有更多了解的东西
validations:
required: false

View File

@@ -1,6 +1,6 @@
name: 其他议题(使用中文)
description: 提出问题,而不是 Bug 反馈或需求建议
labels: ["question"]
labels: ['question']
body:
- type: textarea
id: describe

View File

@@ -1,24 +1,33 @@
name: Bug Report (in English)
description: Recognition errors, operation abnormalities, connection errors, etc.
labels: ["bug"]
description: >-
Recognition errors, operation abnormalities,
connection errors, etc.
labels: ['bug']
body:
- type: checkboxes
id: checks
attributes:
label: |
Please ensure that you have completed the required items before checking the boxes.
Issues with incomplete required items or with the 'I did not read carefully' option checked will be considered voluntary acceptance of direct closure.
label: Before raising the issue...
options:
- label: I understand that Issues are for feedback and problem solving, not trolling the comments section, and will provide as much information as possible to help solve the problem.
required: true
- label: I filled in a short, clear title so that developers could quickly identify the general problem when going through the issue list. Instead of "Some suggestions", "Stuck", etc.
required: true
- label: I am using the latest version of the current update version, and have reviewed the [update content](https://github.com/MaaAssistantArknights/MaaAssistantArknights/commits/dev/) released to date, [Pull Requests](https://github.com/MaaAssistantArknights/MaaAssistantArknights/pulls) unreleased update content and there is no mention of the bug being fixed.
required: true
- label: I have checked all the options without carefully reading the content and believe this will not affect issue resolution.
required: false
- label: I have reviewed the [FAQs](https://docs.maa.plus/en-us/manual/faq.html), [Announcement](/MaaAssistantArknights/MaaAssistantArknights/issues/7732), [Open Issues](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues), [Closed Issues](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues?q=is%3Aissue%20state%3Aclosed) to ensure that my issue has not been mentioned.
required: true
- label: >-
I understand that Issues are for feedback and problem solving,
not trolling the comments section, and will provide as
much information as possible to help solve the problem.
required: true
- label: >-
I filled in a short, clear title
so that developers could quickly identify the general problem
when going through the issue list.
Instead of "Some suggestions", "Stuck", etc.
required: true
- label: >-
I have checked the latest beta update and there is no mention
of the bug being fixed.
required: true
- label: >-
I have reviewed the pinned issues and confirmed that my issue
is not mentioned.
required: true
- type: textarea
id: describe
attributes:
@@ -30,58 +39,56 @@ body:
id: version
attributes:
label: Version
description: |
Please provide the MAA version. Can be found in MAA -> Settings -> Update. Click to copy.
Click on `Resource Version` and `MAA Version` and paste them respectively. There should be 5 lines of version information in total.
description: >-
Please provide the MAA version. Can be found in MAA -> Settings -> Update. (Click to copy)
placeholder: |
Resource Version:
MAA Version:
UI Version:
Core Version:
validations:
required: true
- type: textarea
id: logs
attributes:
label: Log and config files
description: |
**Please locate the Generate Support Payload button under MAA Settings -> Issue Report, click the button, and upload the generated zip file**
**If the archive to upload is larger than 25MB, please upload smaller archives from the same date folder instead.**
If you are using macOS, please click the "File" option in the top-left corner of the screen, then click "Open Log Folder".
placeholder: |
Please confirm that MAA is not running before uploading files.
validations:
required: true
- type: textarea
id: configuration
attributes:
label: Configuration information
description: |
Please specify the operating system and version, emulator brand, emulator resolution, DPI, and frame rate.
If you are using MuMu or LDPlayer 9, please specify whether Screenshot Enhancement is enabled.
Finally, please specify whether GPU accelerated inference is enabled, and if so, provide the GPU model.
label: Log files
description: >-
**After closing the MAA, upload the `asst.log` and `gui.log` log file in the `debug` folder and**
**indicate the approximate point in time when the problem occurred.**
**Please drag and drop the full file in, not your own cuttings or copies;**
**compress it before uploading if too large.**
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots or recordings
description: |
If available, upload screenshots and recordings to help explain your problem,
label: Screenshots
description: >-
If available, add screenshots to help explain your problem,
including but not limited to screenshots of MAA software,
screenshots of game screens.
screenshots of game screens.
If the problem is **recognition related**, please help by providing
the **original image** taken by the emulator's own screenshot tool
(or via adb).
Screenshots taken with other tools may contain the frames of the window/emulator
and their aspect ratio and resolution are inconsistent,
which makes it harder for us to debug.
If the file size is too large, you may compress it before uploading.
(or via adb).
Screenshots taken with other tools may contain the frames of the window/emulator, which makes it harder for us to debug.
The `debug` folder has some automatic screenshots of the error images,
if you have any,
please upload them together.
validations:
required: false
- type: textarea
id: others
attributes:
label: Anything else?
description: |
Anything that will give us more insight into the problem you are having.
label: Anthing else?
description: >-
Emulator used, operating system, related configuration, links,
reference material, etc.
Anything that will give us more insight into the problem
you are having.
validations:
required: false

View File

@@ -1,24 +1,16 @@
name: Feature Request (in English)
description: New features, suggestions, etc.
labels: ["enhancement"]
labels: ['enhancement']
body:
- type: checkboxes
id: checks
attributes:
label: |
Please ensure that you have completed the required items before checking the boxes.
Issues with incomplete required items or with the 'I did not read carefully' option checked will be considered voluntary acceptance of direct closure.
label: Before raising the issue...
options:
- label: I'm probably sure this is a new feature/suggestion and not a bug encountered (please attach logs if you're not sure).
required: true
- label: I filled in a short, clear title so that developers could quickly identify the general problem when going through the issue list. Instead of "Some suggestions", "Stuck", etc.
required: true
- label: I am using the latest version of the current update version, and have reviewed the [update content](https://github.com/MaaAssistantArknights/MaaAssistantArknights/commits/dev/) released to date, [Pull Requests](https://github.com/MaaAssistantArknights/MaaAssistantArknights/pulls) unreleased update content and there is no mention of this feature/suggestion being added.
required: true
- label: I have checked all the options without carefully reading the content and believe this will not affect issue resolution.
required: false
- label: I have reviewed the [FAQs](https://docs.maa.plus/en-us/manual/faq.html), [Announcement](/MaaAssistantArknights/MaaAssistantArknights/issues/7732), [Open Issues](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues), [Closed Issues](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues?q=is%3Aissue%20state%3Aclosed) to ensure that my feature/suggestion has not been mentioned.
required: true
- label: I filled in a short, clear title so that developers could quickly identify the general problem when going through the issue list. Instead of "Some suggestions", "Stuck", etc.
required: true
- label: I'm probably sure this is a new feature/suggestion and not a bug encountered (please attach logs if you're not sure)
required: true
- type: textarea
id: describe
attributes:

View File

@@ -1,6 +1,6 @@
name: Others (in English)
description: Ask a question rather than a bug report or feature request.
labels: ["question"]
labels: ['question']
body:
- type: textarea
id: describe

View File

@@ -1,19 +0,0 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /.github/workflows
schedule:
interval: weekly
commit-message:
prefix: ci
assignees:
- AnnAngela
- Constrat
- lucienshawls
groups:
github-actions:
patterns:
- "*"
ignore:
- dependency-name: "Nerixyz/actionsx-prettier"
versions: ["3.0.0"]

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +0,0 @@
$totalClearedSize = 0
$cacheList = gh cache list --json id,ref,sizeInBytes | ConvertFrom-Json
$branchSet = @{}
git ls-remote --heads origin | ForEach-Object {
if ($_ -match 'refs/heads/(.+)$') { $branchSet[$matches[1]] = $true }
}
foreach ($cache in $cacheList) {
$shouldDelete = $false
if ($cache.ref -match "refs/pull/(\d+)/merge") {
$prNumber = $matches[1]
Write-Host "PR #${prNumber}: " -NoNewline
try {
$prStatus = (gh pr view $prNumber --json state | ConvertFrom-Json).state
Write-Host "$prStatus" -NoNewline
$shouldDelete = $prStatus -in @("MERGED", "CLOSED")
}
catch {
Write-Host "NOT FOUND" -NoNewline
$shouldDelete = $true
}
}
elseif ($cache.ref -match "refs/heads/(.+)" -and $matches[1] -ne "dev") {
$branchName = $matches[1]
$exists = $branchSet.ContainsKey($branchName)
Write-Host "Branch '$branchName': $(if ($exists) { 'EXISTS' } else { 'DELETED' })" -NoNewline
$shouldDelete = -not $exists
}
else {
continue
}
if ($shouldDelete) {
Write-Host " -> DELETING"
gh cache delete $cache.id
$totalClearedSize += $cache.sizeInBytes
}
else {
Write-Host ""
}
}
Write-Output "Cleared size: $("{0:N2}" -f ($totalClearedSize / 1MB)) MB"

View File

@@ -11,6 +11,7 @@ reviewers:
# A list of assignees to be added to pull requests (GitHub user name)
assignees:
- AnnAngela
# A list of keywords to be skipped the process that add reviewers if pull requests include it
# skipKeywords:
# - wip

View File

@@ -1,76 +0,0 @@
$totalClearedSize = 0
$cacheList = gh cache list --json id,key,ref,sizeInBytes,createdAt | ConvertFrom-Json
# List of keys to process
$keyPatterns = @(
"Windows-x64-nuget",
"Windows-x64-maadeps",
"Windows-arm64-nuget",
"Windows-arm64-maadeps",
"macOS-x64-maadeps",
"macOS-arm64-maadeps",
"Linux-x64-maadeps",
"Linux-arm64-maadeps",
"Smoke-testing",
"prek-v1|Linux|X64"
)
# Filter caches matching any of our key patterns
$matchingCaches = $cacheList | Where-Object {
$cache = $_
($keyPatterns | Where-Object { $cache.key -like "*$_*" }).Count -gt 0
}
# Get unique branch references from the matching caches
$branches = $matchingCaches | Select-Object -Property ref -Unique | ForEach-Object { $_.ref }
Write-Output "Found caches across $($branches.Count) branches"
foreach ($branch in $branches) {
# Extract branch name from ref for display
$branchName = $branch -replace "refs/heads/", ""
if ($branch -match "refs/pull/(\d+)/merge") {
$branchName = "PR #$($matches[1])"
}
Write-Output "Processing caches for branch: $branchName"
# Filter for current branch caches
$branchCaches = $matchingCaches | Where-Object { $_.ref -eq $branch }
foreach ($pattern in $keyPatterns) {
Write-Output " Processing $branchName branch caches for pattern: $pattern"
# Filter for caches matching the current key pattern within branch
$patternCaches = $branchCaches | Where-Object { $_.key -like "*$pattern*" }
if (-not $patternCaches) {
Write-Output " No $branchName branch caches found for pattern: $pattern"
continue
}
# Sort by creation time (newest first)
$sortedCaches = $patternCaches | Sort-Object -Property createdAt -Descending
# Keep the first one (latest) and delete the rest
$latestCache = $sortedCaches[0]
Write-Output " Keeping latest cache for $branchName/${pattern}: '$($latestCache.key)' (ID: $($latestCache.id))"
# Delete all except the latest one
for ($i = 1; $i -lt $sortedCaches.Count; $i++) {
$cache = $sortedCaches[$i]
$cacheId = $cache.id
$cacheKey = $cache.key
Write-Output " Deleting cache for $branchName/${pattern}: '$cacheKey' (ID: $cacheId)"
gh cache delete $cacheId
$totalClearedSize += $cache.sizeInBytes
}
}
Write-Output ""
}
$formattedSize = "{0:N2}" -f ($totalClearedSize / 1MB)
Write-Output "Total cleared size: $formattedSize MB"

View File

@@ -1,60 +0,0 @@
$totalClearedSize = 0
$cacheList = gh cache list --json id,key,ref,sizeInBytes,createdAt | ConvertFrom-Json
# Filter for dev branch caches only
$devCaches = $cacheList | Where-Object { $_.ref -eq "refs/heads/dev" }
if (-not $devCaches) {
Write-Output "No caches found for dev branch."
exit
}
# List of keys to process
$keyPatterns = @(
"Windows-x64-nuget",
"Windows-x64-maadeps",
"Windows-arm64-nuget",
"Windows-arm64-maadeps",
"macOS-x64-maadeps",
"macOS-arm64-maadeps",
"Linux-x64-maadeps",
"Linux-arm64-maadeps",
"Smoke-testing",
"prek-v1|Linux|X64"
)
foreach ($pattern in $keyPatterns) {
Write-Output "Processing dev branch caches for pattern: $pattern"
# Filter for caches matching the current key pattern within dev branch
$matchingCaches = $devCaches | Where-Object { $_.key -like "*$pattern*" }
if (-not $matchingCaches) {
Write-Output " No dev branch caches found for pattern: $pattern"
continue
}
# Sort by creation time (newest first)
$sortedCaches = $matchingCaches | Sort-Object -Property createdAt -Descending
# Keep the first one (latest) and delete the rest
$latestCache = $sortedCaches[0]
Write-Output " Keeping latest dev branch cache: '$($latestCache.key)' (ID: $($latestCache.id))"
# Delete all except the latest one
for ($i = 1; $i -lt $sortedCaches.Count; $i++) {
$cache = $sortedCaches[$i]
$cacheId = $cache.id
$cacheKey = $cache.key
Write-Output " Deleting dev branch cache: '$cacheKey' (ID: $cacheId)"
gh cache delete $cacheId
$totalClearedSize += $cache.sizeInBytes
}
Write-Output ""
}
$formattedSize = "{0:N2}" -f ($totalClearedSize / 1MB)
Write-Output "Total cleared size: $formattedSize MB"

View File

@@ -1,51 +0,0 @@
name: Blame Ignore Update
on:
schedule:
- cron: "45 21 * * *" # Runs daily at 22:00 UTC
workflow_dispatch:
jobs:
blame-ignore:
name: Update Git Blame Ignore Revs
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false # Needed to bypass protection rules in Push changes
- name: Check blame
id: check_changes
continue-on-error: true
run: |
LAST_CHECKED_COMMIT=$(cat .github/.last-blame-ignore-commit 2>/dev/null || git rev-list --max-parents=0 HEAD)
git log $LAST_CHECKED_COMMIT..HEAD --pretty=format:"%H %s" --grep="\[blame ignore\]" | awk '{print $1}' >> .git-blame-ignore-revs
git diff --quiet .git-blame-ignore-revs
- name: Commit changes
if: ${{ steps.check_changes.outcome == 'failure' }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git rev-parse HEAD > .github/.last-blame-ignore-commit
git add .github/.last-blame-ignore-commit .git-blame-ignore-revs
git commit -m "chore: auto blame ignore" -m "[skip changelog]"
- name: Push changes
if: ${{ steps.check_changes.outcome == 'failure' }}
uses: ad-m/github-push-action@master
with:
branch: ${{ github.ref }}
github_token: ${{ secrets.MAA_RESOURCE_SYNC }}
- name: Stop if no changes
if: ${{ steps.check_changes.outcome == 'success' }}
uses: andymckay/cancel-action@0.5

View File

@@ -1,40 +0,0 @@
name: Cache Cleanup
on:
pull_request:
types: [closed]
workflow_dispatch:
inputs:
pr_number:
description: "PR Number"
required: true
jobs:
cache-delete:
name: Delete PR Cache
# Run only on organization branches with PRs
if: github.event_name == 'workflow_dispatch' ||
github.event.pull_request.head.repo.full_name ==
github.event.pull_request.base.repo.full_name
runs-on: ubuntu-latest
steps:
- name: Delete PR cache
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
PR_NUMBER=${{ github.event.inputs.pr_number }}
else
PR_NUMBER=${{ github.event.pull_request.number }}
fi
echo "PR Number: $PR_NUMBER"
curl -s -L https://api.github.com/repos/Maaassistantarknights/Maaassistantarknights/actions/caches |
jq -r --arg PR_NUMBER "refs/pull/$PR_NUMBER/merge" '.actions_caches[] | select(.ref == $PR_NUMBER) | .id' |
while read -r id; do
echo "Deleting cache with ID: $id"
curl -L -X DELETE \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/MaaAssistantArknights/MaaAssistantArknights/actions/caches/$id"
done

View File

@@ -1,80 +1,76 @@
name: Release Pipeline
name: ci
on:
push:
tags:
- "v*"
branches-ignore:
- "master"
paths:
- "3rdparty/include/**"
- "include/**"
- "src/**"
- "cmake/**"
- "CMakeLists.txt"
- "MAA.sln"
- ".github/workflows/ci.yml"
- "!**/*.md"
pull_request:
branches:
- "dev"
paths:
- ".github/workflows/ci.yml"
- "3rdparty/include/**"
- "cmake/**"
- "CMakeLists.txt"
- "CMakePresets.json"
- "include/**"
- "src/**"
- "tools/maadeps-download.py"
- "!**/*.md"
pull_request:
paths:
- ".github/workflows/ci.yml"
- "3rdparty/include/**"
- "cmake/**"
- "CMakeLists.txt"
- "CMakePresets.json"
- "include/**"
- "src/**"
- "tools/maadeps-download.py"
- "MAA.sln"
- "!**/*.md"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.head_ref || github.ref_name }}${{ github.ref == 'refs/heads/dev' && format('-{0}', github.sha) || '' }}
cancel-in-progress: true
jobs:
meta:
name: Gather Meta Information
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.set_tag.outputs.tag }}
prerelease: ${{ steps.set_pre.outputs.prerelease }}
steps:
- name: Show concurrency group
run: |
echo "Concurrency Group: ${{ github.workflow }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.head_ref || github.ref_name }}${{ github.ref == 'refs/heads/dev' && format('-{0}', github.sha) || '' }}"
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
path: temp
show-progress: false
- name: Fetch history
if: ${{ !startsWith(github.ref, 'refs/pull/') }}
run: |
git init
cp $GITHUB_WORKSPACE/temp/.git/config ./.git
rm -rf $GITHUB_WORKSPACE/temp
# git config remote.origin.fetch '+refs/*:refs/*'
git fetch --filter=tree:0 # --update-head-ok
git reset --hard origin/$(git branch --show-current) || true
git checkout ${{ github.ref_name }}
- name: Set tag
id: set_tag
run: |
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
# For tag pushes, use the tag name
tag="${{ github.ref_name }}"
else
# For PRs and branch pushes, use git describe or fallback
tag=$(git describe --tags --match "v*" HEAD 2>/dev/null || echo "v0.0.1-$(git rev-parse --short HEAD)")
fi
echo "tag=${tag}" | tee -a $GITHUB_OUTPUT
${{ startsWith(github.ref, 'refs/pull/') && 'cd temp' || '' }}
echo tag=$(git describe --tags --match "v*" ${{ github.ref }} || git rev-parse --short HEAD) | tee -a $GITHUB_OUTPUT
exit ${PIPESTATUS[0]}
- name: Check if it is a pre-release
- name: Judge pre-release
id: set_pre
if: startsWith(github.ref, 'refs/tags/v')
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
if [[ '${{ steps.set_tag.outputs.tag }}' =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "prerelease=false" | tee -a $GITHUB_OUTPUT
echo prerelease=false | tee -a $GITHUB_OUTPUT
else
echo "prerelease=true" | tee -a $GITHUB_OUTPUT
echo prerelease=true | tee -a $GITHUB_OUTPUT
fi
- name: Finalize changelog
if: startsWith(github.ref, 'refs/tags/v')
- name: Generate changelog
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
this_tag=${{ steps.set_tag.outputs.tag }}
if [[ '${{ steps.set_pre.outputs.prerelease }}' != 'false' ]]; then
@@ -84,88 +80,64 @@ jobs:
fi
echo >> CHANGELOG.md
echo "**Full Changelog**: [$last_tag -> $this_tag](https://github.com/MaaAssistantArknights/MaaAssistantArknights/compare/${last_tag}...${this_tag})" >> CHANGELOG.md
echo >> CHANGELOG.md
echo "[已有 Mirror酱 CDK前往 Mirror酱 高速下载](https://mirrorchyan.com/zh/projects?rid=MAA&source=maagh-release)" >> CHANGELOG.md
- name: Upload changelog to GitHub
uses: actions/upload-artifact@v6
if: startsWith(github.ref, 'refs/tags/v')
- name: Upload changelog to Github
uses: actions/upload-artifact@v4
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
with:
name: changelog
path: CHANGELOG.md
windows:
name: Build for Windows
needs: meta
strategy:
matrix:
arch: [arm64, x64]
fail-fast: false
include:
- msbuild_target: x64
lowercase_target: x64
- msbuild_target: ARM64
lowercase_target: arm64
env:
MAABUILDER_TARGET_PLATFORM: ${{ matrix.msbuild_target }}
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
show-progress: false
- name: Fetch submodules
run: |
git submodule update --init --depth 1 src/MaaUtils
git submodule update --init --depth 1 3rdparty/EmulatorExtras
- name: Cache MaaDeps
id: cache-maadeps
uses: actions/cache@v5
continue-on-error: true
with:
path: |
./src/MaaUtils/MaaDeps
key: ${{ runner.os }}-${{ matrix.arch }}-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- name: Bootstrap MaaDeps
if: steps.cache-maadeps.outputs.cache-hit != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 tools/maadeps-download.py ${{ matrix.arch }}-windows
- name: Configure, build and install
run: |
cmake -B build --preset ${{ matrix.arch == 'arm64' && 'windows-publish-arm64' || 'windows-publish-x64' }} -DMAA_HASH_VERSION='${{ needs.meta.outputs.tag }}'
cmake --build --preset ${{ matrix.arch == 'arm64' && 'windows-publish-arm64' || 'windows-publish-x64' }} --parallel $env:NUMBER_OF_PROCESSORS
cmake --install build --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/
submodules: recursive
- name: Cache .nuke/temp, ~/.nuget/packages
id: cache-nuget
uses: actions/cache@v5
continue-on-error: true
uses: actions/cache@v4
with:
path: |
.nuke/temp
~/.nuget/packages
key: ${{ runner.os }}-${{ matrix.arch }}-nuget-${{ hashFiles('**/*.csproj') }}
key: ${{ runner.os }}-${{ matrix.msbuild_target }}-${{ hashFiles('**/global.json', '**/*.csproj') }}
- name: Restore dependencies
if: steps.cache-nuget.outputs.cache-hit != 'true'
run: dotnet restore src/MaaWpfGui/MaaWpfGui.csproj
run: dotnet restore
- name: Taggify version for csproj
- name: Bootstrap MaaDeps
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 maadeps-download.py ${{ matrix.lowercase_target }}-windows
- name: Create fake event file
shell: bash
run: cp -v "$GITHUB_EVENT_PATH" ./event.json
- name: Merge Event Data with Inputs
if: startsWith(github.ref, 'refs/tags/v')
shell: bash
run: |
cat "$GITHUB_EVENT_PATH" | jq '. + { "inputs": {"ReleaseSimulation": "${{ needs.meta.outputs.tag }}"} }' | tee ./event.json
- name: Taggify Version
run: |
$csprojPath = "src/MaaWpfGui/MaaWpfGui.csproj"
$csprojPath = Resolve-Path -Path $csprojPath
@@ -182,99 +154,89 @@ jobs:
$node.FileVersion = $match
$node.AssemblyVersion = $match
$csproj.Save($csprojPath)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish WPF GUI
continue-on-error: true
- name: Build
run: |
dotnet publish src/MaaWpfGui/MaaWpfGui.csproj -c Release -p:Platform=${{ matrix.arch == 'arm64' && 'ARM64' || 'x64' }} -o install
$env:GITHUB_WORKFLOW = 'dev-build-win' # pretend this is a dev-build-win workflow
$env:GITHUB_EVENT_PATH = "$pwd\\event.json"
$env:GITHUB_REF = "refs/heads/not_master"
$env:MAA_BUILDER_MAA_VERSION = '${{ needs.meta.outputs.tag }}'
./build.cmd DevBuild
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Collect PDB files
- name: Cleanup checksum file
run: |
cp build/bin/RelWithDebInfo/*.pdb install/
Compress-Archive -Path install/*.pdb -DestinationPath install/MAAComponent-DebugSymbol-${{ needs.meta.outputs.tag }}-win-${{ matrix.arch }}.zip
continue-on-error: true
- name: Upload PDB files
uses: actions/upload-artifact@v6
with:
name: MAAComponent-DebugSymbol-win-${{ matrix.arch }}
path: install/MAAComponent-DebugSymbol-${{ needs.meta.outputs.tag }}-win-${{ matrix.arch }}.zip
- name: Organize install files
rm -vf ./artifacts/checksum.json
shell: bash
run: |
rm -rf install/MAAComponent-DebugSymbol-*.zip
rm -rf install/*.pdb
rm -rf install/msvc-debug
rm -rf install/*.h
cp tools/DependencySetup_依赖库安装.bat install
- name: Zip files
run: |
cd install
Compress-Archive -Destination MAA-${{ needs.meta.outputs.tag }}-win-${{ matrix.arch }}.zip -Path ./*
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v6
- name: Upload MAA to Github
uses: actions/upload-artifact@v4
with:
name: MAA-win-${{ matrix.arch }}
path: install/MAA-*.zip
name: MAA-win-${{ matrix.lowercase_target }}
path: artifacts
ubuntu:
name: Build for Ubuntu
needs: meta
runs-on: ubuntu-latest
strategy:
matrix:
arch: [aarch64, x86_64]
fail-fast: false
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
show-progress: false
submodules: recursive
- name: Fetch submodules
- name: Install cross compile toolchains
if: ${{ matrix.arch != 'x86_64' }}
run: |
git submodule update --init --depth 1 src/MaaUtils
git submodule update --init --depth 1 3rdparty/EmulatorExtras
git submodule update --init --depth 1 src/maa-cli
sudo apt-get update
sudo apt-get install g++-12-aarch64-linux-gnu g++-12-aarch64-linux-gnu
- name: Cache MaaDeps
id: cache-maadeps
uses: actions/cache@v5
continue-on-error: true
- name: Setup ccache
uses: Chocobo1/setup-ccache-action@v1
with:
path: ./src/MaaUtils/MaaDeps
key: ${{ runner.os }}-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
remove_stale_cache: false
- name: Bootstrap MaaDeps
if: steps.cache-maadeps.outputs.cache-hit != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 tools/maadeps-download.py ${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-linux
python3 maadeps-download.py ${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-linux
- name: Configure, build and install
- name: Build MAA
run: |
cmake --preset ${{ matrix.arch == 'x86_64' && 'linux-publish-x64' || 'linux-publish-arm64' }} -DMAA_HASH_VERSION='${{ needs.meta.outputs.tag }}'
cmake --build --preset ${{ matrix.arch == 'x86_64' && 'linux-publish-x64' || 'linux-publish-arm64' }} --parallel $(nproc)
cmake --install build --config RelWithDebInfo
env:
CLICOLOR_FORCE: 1
mkdir -p build
cmake -B build \
-DMAADEPS_TRIPLET='maa-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-linux' \
-DINSTALL_THIRD_LIBS=ON \
-DINSTALL_RESOURCE=ON \
-DINSTALL_PYTHON=ON \
-DMAA_VERSION='${{ needs.meta.outputs.tag }}'
cmake --build build --parallel $(nproc --all)
- name: Setup cross compile toolchains for CLI
mkdir -p install
cmake --install build --prefix install
env:
CC: ${{ matrix.arch == 'x86_64' && 'ccache gcc-12' || 'ccache aarch64-linux-gnu-gcc-12' }}
CXX: ${{ matrix.arch == 'x86_64' && 'ccache g++-12' || 'ccache aarch64-linux-gnu-g++-12' }}
- name: Setup Cross Compile Toolchains for CLI
uses: ./src/maa-cli/.github/actions/setup
with:
os: ubuntu-latest
target_arch: ${{ matrix.arch }}
- name: Build CLI
run: |
cargo build --release --locked --package maa-cli --features git2/vendored-openssl
cargo build --release --locked --package maa-cli --features vendored-openssl
cp -v target/$CARGO_BUILD_TARGET/release/maa $GITHUB_WORKSPACE/install/maa
cargo build --release --locked --package maa-cli --no-default-features \
--features git2,git2/vendored-openssl
--features git2,vendored-openssl
cp -v target/$CARGO_BUILD_TARGET/release/maa $GITHUB_WORKSPACE/appimage-maa
working-directory: src/maa-cli
@@ -306,11 +268,11 @@ jobs:
ln -sv usr/share/maa/maa Maa.AppDir/AppRun
mkdir -pv Maa.AppDir/usr/share/metainfo/
cp -v tools/AppImage/io.github.maaassistantarknights.maaassistantarknights.metainfo.xml Maa.AppDir/usr/share/metainfo/
wget "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
# appimagetool with embedded runtime does not support cross build, till AppImage/appimagetool 7a10b8
wget "https://github.com/AppImage/type2-runtime/releases/download/old/runtime-fuse3-${{ matrix.arch }}"
wget "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-${{ matrix.arch }}"
chmod a+x appimagetool-x86_64.AppImage
ARCH=${{ matrix.arch }} ./appimagetool-x86_64.AppImage --runtime-file runtime-fuse3-${{ matrix.arch }} Maa.AppDir
ARCH=${{ matrix.arch }} ./appimagetool-x86_64.AppImage --runtime-file runtime-${{ matrix.arch }} Maa.AppDir
chmod a+x MaaAssistantArknights-${{ matrix.arch }}.AppImage
mv -v MaaAssistantArknights-${{ matrix.arch }}.AppImage $GITHUB_WORKSPACE/release/MAA-${{ needs.meta.outputs.tag }}-linux-${{ matrix.arch }}.AppImage
@@ -320,8 +282,8 @@ jobs:
cd install
tar czvf $GITHUB_WORKSPACE/release/MAA-${{ needs.meta.outputs.tag }}-linux-${{ matrix.arch }}.tar.gz .
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v6
- name: Upload MAA to Github
uses: actions/upload-artifact@v4
with:
name: MAA-linux-${{ matrix.arch }}
path: |
@@ -329,142 +291,88 @@ jobs:
release/*.tar.gz
macOS-Core:
name: Build Core for macOS
needs: meta
runs-on: macos-26
runs-on: macos-14
strategy:
matrix:
arch: [arm64, x86_64]
fail-fast: false
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
show-progress: false
- name: Fetch submodules
- name: Install Dependencies
run: |
git submodule update --init --depth 1 src/MaaUtils
# ninja 1.13.1 is already installed and up-to-date.
# - name: Install dependencies
# run: |
# brew install ninja
- name: Cache MaaDeps
id: cache-maadeps
uses: actions/cache@v5
continue-on-error: true
with:
path: ./src/MaaUtils/MaaDeps
key: ${{ runner.os }}-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
brew install ninja
- name: Bootstrap MaaDeps
run: |
python3 tools/maadeps-download.py ${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-osx
[[ ${{ matrix.arch }} = "arm64" ]] && triplet="arm64-osx" || triplet="x64-osx"
python3 maadeps-download.py ${triplet}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Configure, build and install
- name: Configure MaaCore
run: |
cmake -B build --preset ${{ matrix.arch == 'x86_64' && 'macos-publish-x64' || 'macos-publish-arm64' }} -DMAA_HASH_VERSION='${{ needs.meta.outputs.tag }}'
cmake --build --preset ${{ matrix.arch == 'x86_64' && 'macos-publish-x64' || 'macos-publish-arm64' }} --parallel $(sysctl -n hw.logicalcpu)
cmake --install build --config RelWithDebInfo
cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES='${{ matrix.arch }}' -DMAA_VERSION='${{ needs.meta.outputs.tag }}'
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v6
- name: Build MaaCore
run: |
cmake --build build
cmake --install build --prefix install
- name: Upload MAA to Github
uses: actions/upload-artifact@v4
with:
name: MAACore-macos-${{ matrix.arch }}
path: "install/*.dylib"
macOS-GUI:
name: Build GUI for macOS
needs: [meta, macOS-Core]
runs-on: macos-26
runs-on: macos-14
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
show-progress: false
submodules: recursive
- name: Install dependencies
if: startsWith(github.ref, 'refs/tags/v')
run: |
brew install graphicsmagick imagemagick
npm install -g create-dmg
- name: Fetch submodules
run: |
git submodule update --init --depth 1 src/MaaMacGui
- name: Download MAA (arm64) from GitHub
uses: actions/download-artifact@v7
- name: Download Arm64 MAA from Github
uses: actions/download-artifact@v4
with:
name: MAACore-macos-arm64
path: install-arm64
- name: Download MAA (x64) from GitHub
uses: actions/download-artifact@v7
- name: Download x64 MAA from Github
uses: actions/download-artifact@v4
with:
name: MAACore-macos-x86_64
path: install-x86_64
- name: Install Developer ID certificate
- name: Install Developer ID Certificate
if: startsWith(github.ref, 'refs/tags/v')
uses: apple-actions/import-codesign-certs@v6
uses: ssrobins/import-codesign-certs@v2
with:
p12-file-base64: ${{ secrets.HGUANDL_SIGN_CERT_P12 }}
p12-password: ${{ secrets.HGUANDL_SIGN_CERT_PASSWD }}
- name: Download provisioning profiles
if: startsWith(github.ref, 'refs/tags/v')
uses: apple-actions/download-provisioning-profiles@v5
with:
bundle-id: "com.hguandl.MeoAsstMac"
issuer-id: ${{ secrets.HGUANDL_APPSTORE_ISSUER }}
api-key-id: ${{ secrets.HGUANDL_APPSTORE_KEYID }}
api-private-key: ${{ secrets.HGUANDL_APPSTORE_KEY }}
- name: Setup Xcode toolchain
if: false
run: |
sudo xcode-select -s /Applications/Xcode_16.1.app/Contents/Developer
- name: Build universal binaries
- name: Build Universal Binaries
run: |
mkdir build
for LIB_NAME in $(ls install-arm64); do
lipo -create install-arm64/$LIB_NAME install-x86_64/$LIB_NAME -output build/$LIB_NAME
done
- name: Archive runtime files
run: |
mkdir runtime && cd runtime
name='MAA-${{ needs.meta.outputs.tag }}-macos-runtime-universal'
mkdir "$name" && cd "$name"
cp -v ../../build/*.dylib .
ln -vs libonnxruntime*.dylib libonnxruntime.dylib
ln -vs libopencv_world4*.dylib libopencv_world4.dylib
cp -vr ../../resource .
cd .. || exit 1
zip -yrX9 "$name.zip" "$name"
- name: Upload MAA runtime to GitHub
uses: actions/upload-artifact@v6
with:
name: MAA-macos-runtime-universal
path: runtime/MAA-${{ needs.meta.outputs.tag }}-macos-runtime-universal.zip
- name: Build XCFramework
run: |
xcodebuild -create-xcframework -library libMaaCore.dylib -headers ../include -output MaaCore.xcframework
xcodebuild -create-xcframework -library libMaaUtils.dylib -output MaaUtils.xcframework
xcodebuild -create-xcframework -library libfastdeploy_ppocr.dylib -output fastdeploy_ppocr.xcframework
xcodebuild -create-xcframework -library libonnxruntime.*.dylib -output ONNXRuntime.xcframework
xcodebuild -create-xcframework -library libopencv*.dylib -output OpenCV.xcframework
working-directory: build
- name: Setup GUI version
- name: Setup GUI Version
run: |
RELEASE_COUNT=$(git ls-remote --tags origin | grep refs/tags/v | awk 'END{print NR}')
echo 'MARKETING_VERSION = ${{ needs.meta.outputs.tag }}' | tee src/MaaMacGui/Version.xcconfig
@@ -473,9 +381,9 @@ jobs:
- name: Build MAA
run: |
if ${{ startsWith(github.ref, 'refs/tags/v') }}; then
xcodebuild -scheme MAA archive -archivePath MAA.xcarchive
xcodebuild -project MeoAsstMac.xcodeproj -scheme MAA archive -archivePath MAA.xcarchive -configuration Release
else
xcodebuild CODE_SIGNING_ALLOWED=NO -scheme MAA archive -archivePath MAA.xcarchive
xcodebuild CODE_SIGN_IDENTITY="-" DEVELOPMENT_TEAM="-" ONLY_ACTIVE_ARCH=NO -project MeoAsstMac.xcodeproj -scheme MAA archive -archivePath MAA.xcarchive -configuration Debug
fi
working-directory: src/MaaMacGui
@@ -485,20 +393,22 @@ jobs:
xcodebuild -exportArchive -archivePath MAA.xcarchive -exportOptionsPlist ExportOptions.plist -exportPath Export
working-directory: src/MaaMacGui
- name: Create disk image
- name: Create Disk Image
if: startsWith(github.ref, 'refs/tags/v')
run: |
create-dmg Export/MAA.app
mv MAA*.dmg MAA.dmg
mkdir Image
mv Export/MAA.app Image/
ln -s /Applications Image/
hdiutil create -volname MAA -srcfolder Image -ov -fs APFS -format ULMO MAA.dmg
working-directory: src/MaaMacGui
- name: Archive debug symbols
- name: Archive Debug Symbols
if: startsWith(github.ref, 'refs/tags/v')
run: |
ditto -c -k --keepParent MAA.app.dSYM MAA.app.dSYM.zip
working-directory: src/MaaMacGui/MAA.xcarchive/dSYMs
- name: Place packages
- name: Place Packages
if: startsWith(github.ref, 'refs/tags/v')
run: |
GIT_TAG=${{ needs.meta.outputs.tag }}
@@ -508,47 +418,37 @@ jobs:
mv src/MaaMacGui/MAA.dmg release/${APP_DMG}
mv src/MaaMacGui/MAA.xcarchive/dSYMs/MAA.app.dSYM.zip release/${APP_SYM}
- name: Notarize image
if: startsWith(github.ref, 'refs/tags/v')
uses: nick-fields/retry@v3
with:
max_attempts: 3
timeout_minutes: 15
command: |
cd release
echo "$APPSTORE_KEY" > "AuthKey_${APPSTORE_KEYID}.p8"
dmg="MAA-${{ needs.meta.outputs.tag }}-macos-universal.dmg"
xcrun notarytool submit -k "AuthKey_${APPSTORE_KEYID}.p8" -d "$APPSTORE_KEYID" -i "$ISSUER_ID" --wait ${dmg}
env:
APPSTORE_KEYID: ${{ secrets.HGUANDL_APPSTORE_KEYID }}
APPSTORE_KEY: ${{ secrets.HGUANDL_APPSTORE_KEY }}
ISSUER_ID: ${{ secrets.HGUANDL_APPSTORE_ISSUER }}
- name: Attach notarization tickets
- name: Notarize Image
if: startsWith(github.ref, 'refs/tags/v')
run: |
dmg="MAA-${{ needs.meta.outputs.tag }}-macos-universal.dmg"
xcrun stapler staple ${dmg}
find . -name "*.dmg" | while read dmg; do
codesign -s "$NOTARY_TEAM" --timestamp ${dmg}
xcrun notarytool submit --apple-id "$NOTARY_USER" --password "$NOTARY_PASSWD" --team-id "$NOTARY_TEAM" --wait ${dmg}
xcrun stapler staple ${dmg}
done
env:
NOTARY_USER: ${{ secrets.HGUANDL_NOTARY_AAPL_ID }}
NOTARY_PASSWD: ${{ secrets.HGUANDL_NOTARY_PASSWD }}
NOTARY_TEAM: ${{ secrets.HGUANDL_SIGN_IDENTITY }}
working-directory: release
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v6
- name: Upload MAA to Github
uses: actions/upload-artifact@v4
with:
name: MAA-macos-universal
path: ${{ startsWith(github.ref, 'refs/tags/v') && 'release/MAA*' || 'src/MaaMacGui/MAA.xcarchive/**' }}
release:
name: Publish Release
if: startsWith(github.ref, 'refs/tags/v')
needs: [meta, windows, ubuntu, macOS-Core, macOS-GUI]
needs: [meta, windows, ubuntu, macOS-GUI]
runs-on: ubuntu-latest
steps:
- name: Download MAA from GitHub
uses: actions/download-artifact@v7
- name: Download MAA from Github
uses: actions/download-artifact@v4
with:
path: assets
- name: Clean up files
- name: Cleanup files
run: |
mv -vf assets/changelog/* .
rm -rf assets/MAACore-macos-*
@@ -556,35 +456,16 @@ jobs:
# find . -type f | xargs mv -fvt .
find . -type f | while read f; do mv -fvt . $f; done
- name: Publish release to GitHub
uses: softprops/action-gh-release@v2.5.0
- name: Release to Github
uses: softprops/action-gh-release@v1
with:
body_path: CHANGELOG.md
files: |
assets/*
prerelease: ${{ needs.meta.outputs.prerelease != 'false' }}
- name: Trigger release distribution workflows
- name: Trigger secondary workflows # ref: https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow
run: |
gh workflow run --repo $GITHUB_REPOSITORY release-package-distribution.yml \
-f release_tag="${{ needs.meta.outputs.tag }}" \
-f mirrorchyan=true \
-f winget=${{ needs.meta.outputs.prerelease == 'false' }}
gh workflow run --repo $GITHUB_REPOSITORY mirrorchyan_release_note.yml
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Trigger OTA release workflow # ref: https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow
run: |
gh workflow run --repo $GITHUB_REPOSITORY release-ota.yml
gh workflow run --repo $GITHUB_REPOSITORY release-ota
env:
GH_TOKEN: ${{ secrets.MISTEOWORKFLOW }}
- name: Create issue on failure
if: failure()
uses: actions-cool/issues-helper@v3
with:
actions: "create-issue"
title: "Errors occured during release ${{ needs.meta.outputs.tag }}"
body: |
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

98
.github/workflows/gen-changelog.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
name: gen-changelog
on:
pull_request:
types:
- opened
- reopened
- ready_for_review
workflow_dispatch:
jobs:
gen:
# startsWith 表达式不区分大小写
if: github.event.pull_request.draft == false && startsWith(github.event.pull_request.title, 'Release v')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
show-progress: false
- name: Extract tag name
id: extract_tag
env:
PR_BODY: ${{ format('{0}/{1}', runner.temp, 'output' ) }}
run: |
tag_name=$(echo "${{ github.event.pull_request.title }}" | sed -E 's/(Release|release)//' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
echo "tag_name=$tag_name" >> $GITHUB_OUTPUT
pr_title="docs: Auto Update Changelogs of "$tag_name
echo "pr_title=$pr_title" >> $GITHUB_OUTPUT
latest_stable_tag=$(git tag -l 'v*' | grep -v '-' | sort -V | tail -n 1) # 上一个 stable 版本
newest_tag=$(git describe --tags --match "v*" --abbrev=0) # 最新版本
echo "latest_stable_tag=$latest_stable_tag" >> $GITHUB_OUTPUT
echo "newest_tag=$newest_tag" >> $GITHUB_OUTPUT
if [[ $tag_name == *-* ]]; then # 判断新版本是否为 beta 版本
latest=$newest_tag # 若是,则将 latest 参数设置为最新版本
else
latest=$latest_stable_tag # 若否,则设置为上一个 stable 版本
fi
echo "latest=$latest" >> $GITHUB_OUTPUT
cat $GITHUB_OUTPUT
echo '======='
echo 'Target RP: ${{ github.event.pull_request.html_url }}' >> $PR_BODY
echo '' >> $PR_BODY
echo '<details><summary>Debug info</summary>' >> $PR_BODY
echo '' >> $PR_BODY
echo '```' >> $PR_BODY
sed 's/=/: /1' $GITHUB_OUTPUT >> $PR_BODY
echo '```' >> $PR_BODY
echo '' >> $PR_BODY
echo '</details>' >> $PR_BODY
cat $PR_BODY
- name: Generate Changelog
run: |
git switch dev
python3 tools/ChangelogGenerator/changelog_generator.py --tag "${{ steps.extract_tag.outputs.tag_name }}" --latest "${{ steps.extract_tag.outputs.latest }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Add files to git
run: |
git status
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
git add .
commit_msg="docs: Auto Generate Changelog of Release ""${{ steps.extract_tag.outputs.tag_name }}"
git commit -m "$commit_msg"
- name: Create PR
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
title: ${{ steps.extract_tag.outputs.pr_title }}
body-path: ${{ format('{0}/{1}', runner.temp, 'output' ) }}
base: "dev"
branch: "changelog"
delete-branch: true
reviewers: |
AnnAngela
assignees: |
AnnAngela
- name: Add reviewers to release PR
uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: ".github/release_reviewers.yaml"

View File

@@ -1,153 +0,0 @@
name: Issue Review
on:
issues:
types: [opened]
jobs:
check-then-close-or-fold:
name: Review and Modify Issues
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Check for duplicate issues in last hour
id: duplicate-check
uses: actions/github-script@v8
with:
script: |
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
const currentIssueCreatedAt = new Date(context.payload.issue.created_at);
const issueAuthor = context.payload.issue.user.login;
// 重新获取该用户最近的 issues
const { data: userIssues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
creator: issueAuthor,
state: 'all',
since: oneHourAgo.toISOString(),
per_page: 30
});
// 过滤出1小时内创建的其他 issues排除当前 issue
const recentIssues = userIssues.filter(issue => {
const issueCreatedAt = new Date(issue.created_at);
return issue.number !== context.issue.number &&
issueCreatedAt >= oneHourAgo &&
issueCreatedAt < currentIssueCreatedAt;
});
if (recentIssues.length > 0) {
// 给当前 issue 添加标签
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['rapid-submission']
});
// 给1小时内的其他 issues 也添加标签
for (const issue of recentIssues) {
try {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ['rapid-submission']
});
console.log(`Added rapid-submission label to issue #${issue.number}`);
} catch (error) {
console.log(`Failed to add label to issue #${issue.number}: ${error.message}`);
}
}
}
- name: Check for issue checkboxes
id: unread-checkbox-check
uses: actions/github-script@v8
with:
script: |
// 找不到###就算了
if (!/###([^#]+)###/.test(context.payload.issue.body)) {
return false;
}
var checkList = /###([^#]+)###/.exec(context.payload.issue.body)[1];
const UnreadRegexCn = /- \[x\]\s*.*?我.*未仔细阅读/i;
const UnreadRegexCn_ = /- \[ \]\s*.*?我.*未仔细阅读/i;
if (/- \[(x| )\]\s*.*?[\u4e00-\u9fa5]+/i.test(checkList)) { // 中英区分
if (UnreadRegexCn.test(checkList)) {
return true;
}
checkList = checkList.replace(UnreadRegexCn_, "");
return /- \[ \]\s*.*?[\u4e00-\u9fa5]+/i.test(checkList);
}
const texts = [
'I have checked all the options without carefully reading the content and believe this will not affect issue resolution.'];
return texts.some(text => new RegExp(`- \\[x\\]\\s*${text}`).test(context.payload.issue.body));
- name: Close low-quality issue
if: steps.unread-checkbox-check.outputs.result == 'true'
uses: actions/github-script@v8
with:
script: |
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
state: 'closed',
state_reason: 'not_planned'
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['low-quality-report']
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '因为未阅读模板提示并正确提交,触发了自动关闭规则\n\nBecause the template prompt was not read and submitted correctly, the automatic closing rule was triggered'
});
- name: Fold checkboxes
if: steps.unread-checkbox-check.outputs.result == 'false'
uses: actions/github-script@v8
with:
script: |
const originalBody = context.payload.issue.body;
if (originalBody.includes('<details><summary>Checkboxes</summary>\n\n')) {
return;
}
const checkboxSectionRegex_cn_bug = /([\s\S]*?)(### 问题描述\n\n)/;
const checkboxSectionRegex_cn_feat = /([\s\S]*?)(### 说说你遇到的问题?\n\n)/;
const checkboxSectionRegex_en_bug = /([\s\S]*?)(### Description\n\n)/;
const checkboxSectionRegex_en_feat = /([\s\S]*?)(### The problems you have encountered?\n\n)/;
const foldedBody_cn_bug = originalBody.replace(
checkboxSectionRegex_cn_bug,
`<details><summary>Checkboxes</summary>\n\n$1\n\n</details>\n\n$2`
);
const foldedBody_cn_feat = foldedBody_cn_bug.replace(
checkboxSectionRegex_cn_feat,
`<details><summary>Checkboxes</summary>\n\n$1\n\n</details>\n\n$2`
);
const foldedBody_en_bug = foldedBody_cn_feat.replace(
checkboxSectionRegex_en_bug,
`<details><summary>Checkboxes</summary>\n\n$1\n\n</details>\n\n$2`
);
const foldedBody = foldedBody_en_bug.replace(
checkboxSectionRegex_en_feat,
`<details><summary>Checkboxes</summary>\n\n$1\n\n</details>\n\n$2`
);
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: foldedBody
});

View File

@@ -1,5 +1,4 @@
name: Issue Triage
name: "Issue Checker"
on:
issues:
types: [opened, edited]
@@ -15,11 +14,9 @@ permissions:
jobs:
triage:
name: Triage Issues and PRs
runs-on: ubuntu-latest
steps:
- name: Label issues and PRs Automatically
uses: MaaAssistantArknights/issue-checker@v1.14
- uses: zzyyyl/issue-checker@v1.8
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/issue-checker.yml

View File

@@ -1,63 +1,49 @@
name: Markdown Link Check
name: Lint Markdown files
on:
schedule:
- cron: "0 0 * * 2" # Every Tuesday at 00:00 UTC
push:
branches:
- dev
paths:
- ".github/workflows/markdown-checker.yml"
- "docs/**"
- "**/*.md"
- ".github/workflows/lint-markdown.yml"
pull_request:
branches:
- dev
paths:
- ".github/workflows/markdown-checker.yml"
- "docs/**"
- "**/*.md"
- ".github/workflows/lint-markdown.yml"
schedule:
- cron: "44 7 * * 2"
workflow_dispatch:
jobs:
check-links:
name: Check Dead Links
if: github.repository_owner == 'MaaAssistantArknights' && github.event.head_commit.author.email != '41898282+github-actions[bot]@users.noreply.github.com'
lint:
name: Lint Markdown
runs-on: ubuntu-latest
permissions:
pull-requests: write
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Check out code
uses: actions/checkout@v4
with:
show-progress: false
- name: Cache lychee responses
uses: actions/cache@v5
continue-on-error: true
- name: Lint Markdown
uses: DavidAnson/markdownlint-cli2-action@v16
with:
path: .lycheecache
key: lychee-cache
config: docs/.markdownlint.yaml
globs: |
README.md
docs/**/*.md
- name: Check dead internal links
uses: lycheeverse/lychee-action@v2
with:
# 仅检查内部链接,排除所有外部链接
args: >
--verbose --no-progress
--cache --max-cache-age 1d --cache-exclude-status '429, 500..599'
--exclude 'https?://.*'
--exclude-path 'docs/zh-tw/manual/introduction/introduction_old.md'
--exclude-path 'docs/ja-jp/manual/introduction/introduction_old.md'
-- './docs/**/*.md' './README.md'
- name: Comment (only for PR)
if: failure() && github.event_name == 'pull_request'
uses: marocchino/sticky-pull-request-comment@v2
with:
message: |
Dead links found in the documentation. Please fix them.
Details: https://github.com/MaaAssistantArknights/MaaAssistantArknights/actions/runs/${{ github.run_id }}
# check-links:
# name: Check Dead Links
# runs-on: ubuntu-latest
# steps:
# - name: Check out code
# uses: actions/checkout@v4
# with:
# show-progress: false
#
# - name: Check Dead Link
# uses: umbrelladocs/action-linkspector@v1
# with:
# # Use github-pr-review when triggered by a pull request; otherwise, use github-check
# reporter: ${{ github.event_name == 'pull_request' && 'github-pr-review' || 'github-check' }}
# filter_mode: nofilter
# fail_on_error: true

View File

@@ -1,19 +0,0 @@
name: Release Note Distribution
on:
workflow_dispatch:
release:
types: [edited]
jobs:
mirrorchyan:
name: Upload to MirrorChyan
runs-on: macos-latest
steps:
- name: Upload release notes to MirrorChyan
uses: MirrorChyan/release-note-action@v1
with:
mirrorchyan_rid: MAA
upload_token: ${{ secrets.MirrorChyanUploadToken }}
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,110 +0,0 @@
name: PNG Image Optimization
on:
push:
branches:
- "dev"
paths:
- "docs/.vuepress/public/images/**"
- "resource/**/*.png"
workflow_dispatch:
inputs:
commit_message:
description: "Commit Message"
type: string
required: false
jobs:
optimize-png:
name: Optimize PNG Images
# Skip workflow to prevent double consecutive runs
if: github.repository_owner == 'MaaAssistantArknights' && github.event.head_commit.author.email != '41898282+github-actions[bot]@users.noreply.github.com'
runs-on: ubuntu-latest
steps:
# Skip workflow on PR merges
- name: Check if it is a direct push
id: check_push
run: |
if [[ "${{ github.event_name }}" != "workflow_dispatch" ]]; then
pr_count=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/commits/${{ github.event.after }}/pulls" \
| jq 'length')
if [[ "$pr_count" == "0" ]]; then
echo "Direct push detected. Proceeding..."
echo "is_pr=False" >> $GITHUB_OUTPUT
else
echo "PR merge detected. Exiting."
echo "is_pr=True" >> $GITHUB_OUTPUT
fi
else
echo "Manual trigger detected. Proceeding..."
echo "is_pr=False" >> $GITHUB_OUTPUT
fi
- name: Checkout repository
if: steps.check_push.outputs.is_pr != 'True'
uses: actions/checkout@v6
with:
show-progress: false
persist-credentials: false
- name: Setup python
if: steps.check_push.outputs.is_pr != 'True'
uses: actions/setup-python@v6
with:
python-version: "3.11"
# - name: Cache Python packages
# id: cache_python
# if: steps.check_push.outputs.is_pr != 'True' && always()
# uses: actions/cache@v4
# continue-on-error: true
# with:
# path: ${{ env.pythonLocation }}/lib/python3.11/site-packages
# key: ${{ runner.os }}-pip-optimize-templates-${{ hashFiles('./tools/OptimizeTemplates/requirements.txt') }}
- name: Install dependencies
# if: steps.check_push.outputs.is_pr != 'True' && steps.cache_python.outputs.cache-hit != 'true'
if: steps.check_push.outputs.is_pr != 'True'
run: |
pip install -r tools/OptimizeTemplates/requirements.txt
- name: Setup oxipng
if: steps.check_push.outputs.is_pr != 'True'
uses: baptiste0928/cargo-install@v3
with:
crate: oxipng
- name: Optimize png images
if: steps.check_push.outputs.is_pr != 'True'
run: |
python3 tools/OptimizeTemplates/optimize_templates.py
- name: Commit changes
id: commit_changes
if: steps.check_push.outputs.is_pr != 'True'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add .
if git diff-index --quiet HEAD --; then
echo "No changes to commit"
else
commit_msg="${{ github.event.inputs.commit_message }}"
if [ -z "$commit_msg" ]; then
commit_msg="chore: Auto Templates Optimization"
fi
git commit -m "$commit_msg" -m "Triggered by ${{github.sha}}" -m "[skip changelog]"
git pull origin $(git rev-parse --abbrev-ref HEAD) --unshallow --rebase
echo "have_commits=True" >> $GITHUB_OUTPUT
fi
- name: Push changes
if: steps.check_push.outputs.is_pr != 'True' && steps.commit_changes.outputs.have_commits == 'True' && github.repository_owner == 'MaaAssistantArknights'
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.MAA_RESOURCE_SYNC }}
branch: ${{ github.ref }}

View File

@@ -1,4 +1,4 @@
name: Release Version Tagging
name: Auto Tag Release PR
on:
pull_request:
@@ -9,29 +9,27 @@ on:
workflow_dispatch:
inputs:
tag:
description: "Name of the tag to create for the release"
type: string
required: true
jobs:
auto_tag_release:
name: Tag Release
if: github.event.pull_request.merged == true && (startsWith(github.event.pull_request.title, 'Release v') || startsWith(github.event.pull_request.title, 'release v')) || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.MAARELEASE_RELEASE }}
- name: Configure git
- name: Git config
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Determine tag name
- name: Extract tag name
id: extract_tag
run: |
if ${{ github.event_name != 'workflow_dispatch' }}; then
@@ -41,12 +39,12 @@ jobs:
echo "tag_name=${{ inputs.tag }}" >> $GITHUB_OUTPUT
fi
- name: Create and push release tag
- name: Create release tag and push
run: |
git tag -a "${{ steps.extract_tag.outputs.tag_name }}" -m "${{ steps.extract_tag.outputs.tag_name }}" -f
git push origin "${{ steps.extract_tag.outputs.tag_name }}"
- name: Merge tag into dev and push
- name: Merge into dev and push
run: |
git switch dev
git merge "${{ steps.extract_tag.outputs.tag_name }}"

View File

@@ -1,17 +1,17 @@
name: PR Commit Check
name: PR Checker
on:
pull_request_target:
types: [opened, edited, ready_for_review, reopened, synchronize]
jobs:
check_commit_name_in_pr:
name: Check Commits in PR
if: ${{ !github.event.pull_request.merged && github.base_ref != 'master' }}
runs-on: ubuntu-latest
steps:
- name: Clean up previous comment
uses: actions/github-script@v8
- name: Cleanup Previous Comment
uses: actions/github-script@v7
with:
script: |
const { data: comments } = await github.rest.issues.listComments({
@@ -28,8 +28,8 @@ jobs:
comment_id: previousComment.id
});
}
- name: Check commits
uses: actions/github-script@v8
- name: Check Commits
uses: actions/github-script@v7
with:
script: |
const { data: commits } = await github.rest.pulls.listCommits({
@@ -39,7 +39,7 @@ jobs:
per_page: 100
});
const regex = /^((build|chore|ci|docs?|feat!?|fix|perf|refactor|rft|style|test|i18n|typo|debug)[\:\.\(\,]|[Rr]evert|[Rr]elease|[Rr]eapply)/;
const regex = /^((build|chore|ci|docs?|feat!?|fix|perf|refactor|rft|style|test|i18n|typo)[\:\.\(\,]|[Rr]evert|[Rr]elease)/;
const invalidCommits = commits.filter(commit => !regex.test(commit.commit.message) || commit.parents.length > 1);
console.log(`Checked ${commits.length} commit(s)`);
@@ -52,8 +52,8 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: `## ⚠️ Found ${invalidCommits.length} invalid commit(s):\n\n${invalidCommitInfoList}\n\n---\nPlease follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format, and **DO NOT** use merge commits.\n请遵循 [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) 格式,以及**不要**使用Merge Commit修改 Commit Message 无法绕过检测)。`
body: `## ⚠️ Found ${invalidCommits.length} invalid commit(s):\n\n${invalidCommitInfoList}\n\n---\nPlease follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format.`
});
core.setFailed(`Found ${invalidCommits.length} invalid commit(s):\n${invalidCommitNames.join("\n-------------------\n")}`);
}
}

View File

@@ -1,36 +0,0 @@
name: Scheduled Pre-commit
on:
schedule:
# 每天 UTC 21:00 运行(北京时间 5:00
- cron: "0 21 * * *"
workflow_dispatch: # 允许手动触发
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
show-progress: false
- name: Setup and run Prek
uses: j178/prek-action@v1
id: pre-commit
continue-on-error: true
with:
extra-args: '--all-files'
- name: Commit and push changes
if: steps.pre-commit.outcome == 'failure' && github.repository_owner == 'MaaAssistantArknights'
uses: actions-js/push@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
message: "chore: Auto update by pre-commit hooks
https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
[skip changelog]"
branch: ${{ github.ref }}

View File

@@ -1,8 +1,6 @@
name: Release Pipeline (Nightly OTA)
name: release-nightly-ota
on:
schedule:
- cron: "0 22 * * *" # Runs daily at 22:00 UTC
workflow_dispatch:
inputs:
release_body:
@@ -13,263 +11,77 @@ on:
description: "Commit to build (git checkout)"
type: string
required: false
limit_maa:
limit:
description: "Number of releases to fetch from MaaAssistantArknights"
required: true
default: 10
default: 30
type: number
limit_maarelease:
limit_2:
description: "Number of releases to fetch from MaaRelease"
required: true
default: 10
default: 30
type: number
jobs:
build-win-nightly:
name: Build Nightly for Windows
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: windows-latest
strategy:
matrix:
arch: [x64]
include:
- msbuild_target: x64
lowercase_target: x64
fail-fast: false
env:
MAABUILDER_TARGET_PLATFORM: ${{ matrix.msbuild_target }}
outputs:
tag: ${{ steps.set_tag.outputs.tag }}
pre_version: ${{ steps.set_tag.outputs.pre_version }}
main_tag_name: ${{ steps.push_main_tag.outputs.main_tag_name }}
changelog: ${{ steps.read_changelog.outputs.content }}
changelog: ${{ steps.generate_changelog.outputs.changelog }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
# repository: 'MaaAssistantArknights/MaaAssistantArknights'
submodules: recursive
#ref: ${{ inputs.ref }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 250
- name: Fetch tags
- name: Checkout ref
run: |
git fetch --depth=250 --tags
if ("${{ inputs.ref }}" -ne "") {
git checkout --progress --recurse-submodules ${{ inputs.ref || 'dev' }}
}
- name: Fetch submodules
- name: Install semver
run: |
git submodule update --init --depth 1 src/MaaUtils
git submodule update --init --depth 1 3rdparty/EmulatorExtras
npm install --global --progress semver
- name: Checkout ref (if provided)
if: inputs.ref != ''
run: |
git checkout --progress --recurse-submodules ${{ inputs.ref }}
- name: Check for Changes and Set tag
- name: Set tag
id: set_tag
run: |
# Fetch the latest tag from the repository
$latest_tag = git describe --tags --abbrev=0
Write-Output "Previous Tag: $latest_tag"
Write-Output "latest_tag=$latest_tag" >> $env:GITHUB_OUTPUT
# Check for changes between the latest tag and HEAD
$recent_changes = git log "$latest_tag..HEAD" --oneline
if (-not $recent_changes) {
Write-Output "No commits. Cancelling workflow..."
Write-Output "cancel_run=true" >> $env:GITHUB_ENV
exit 0
} else {
Write-Output "Recent commits: $recent_changes"
Write-Output "Commits found. Continuing build..."
Write-Output "cancel_run=false" >> $env:GITHUB_ENV
}
# Get the full description of the current commit
$described = git describe --tags --long --match 'v*'
Write-Output "New Described: $described"
$ids = $described -split "-"
if ($ids.Length -eq 3) {
# Extract and parse the current version
$current_version = $ids[0].Substring(1)
$parts = $current_version -split '\.'
if ($parts.Length -ne 3) {
Write-Error "Invalid version format: $current_version. Expected 'major.minor.patch'."
exit 1
}
# Increment the patch version
$parts[2] = [int]$parts[2] + 1
$new_version = "$($parts[0]).$($parts[1]).$($parts[2])"
# Construct the new tag and pre_version
$ver = "v$new_version"
$pre_version = $ids[0]
$dist = "{0:D3}" -f [int]$ids[1]
Write-Output "pre_version=$pre_version" >> $env:GITHUB_OUTPUT
Write-Output "New Tag: $ver-alpha.1.d$dist.$($ids[2])"
Write-Output "tag=$ver-alpha.1.d$dist.$($ids[2])" >> $env:GITHUB_OUTPUT
# pre_version是上一个公版这里需要拉上一个tag避免堆积过多commit
$latest_tag=$(git describe --tags --abbrev=0)
echo "latest_tag=$latest_tag" >> $env:GITHUB_OUTPUT
$described = $(git describe --tags --long --match 'v*')
$ids = $($described -split "-")
if ($ids.length -eq 3) {
$ver = "v$(semver --increment $ids[0].Substring(1))"
$pre_version = "$($ids[0])"
$dist = `printf "%03d"` $ids[1]
echo "tag=$ver-alpha.1.d$($dist).$($ids[2])" >> $env:GITHUB_OUTPUT
echo "pre_version=$pre_version" >> $env:GITHUB_OUTPUT
exit 0
}
if ($ids.Length -eq 4) {
$dist = "{0:D3}" -f [int]$ids[2]
$pre_version = "$($ids[0])-$($ids[1])"
Write-Output "Pre Version: $pre_version"
Write-Output "pre_version=$pre_version" >> $env:GITHUB_OUTPUT
Write-Output "tag: $pre_version.d$dist.$($ids[3])"
Write-Output "tag=$pre_version.d$dist.$($ids[3])" >> $env:GITHUB_OUTPUT
if ($ids.length -eq 4) {
$dist = `printf "%03d"` $ids[2]
$pre_version = "$($ids[0])""-""$($ids[1])"
echo "tag=$($ids[0])-$($ids[1]).d$($dist).$($ids[3])" >> $env:GITHUB_OUTPUT
echo "pre_version=$pre_version" >> $env:GITHUB_OUTPUT
exit 0
}
exit 1
- name: Stop if no changes
if: env.cancel_run == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
curl -s -L -X POST `
-H "Accept: application/vnd.github+json" `
-H "Authorization: Bearer ${{ secrets.MAA_ACTION_READ_WRITE }}" `
-H "X-GitHub-Api-Version: 2022-11-28" `
"https://api.github.com/repos/MaaAssistantArknights/MaaAssistantArknights/actions/runs/${{ github.run_id }}/cancel" > $null
gh run watch ${{ github.run_id }}
- name: Generate changelog
id: generate_changelog
run: |
python3 tools/ChangelogGenerator/changelog_generator.py --latest "${{ steps.set_tag.outputs.latest_tag }}" --tag "${{ steps.set_tag.outputs.tag }}"
Get-Content .\changelog.md | Select-Object -Skip 2 | Out-File -FilePath changelog_notag.md -Encoding utf8
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PYTHONIOENCODING: "utf-8"
- name: Stop if no changes
if: steps.generate_changelog.outputs.cancel_run == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
curl -s -L -X POST `
-H "Accept: application/vnd.github+json" `
-H "Authorization: Bearer ${{ secrets.MAA_ACTION_READ_WRITE }}" `
-H "X-GitHub-Api-Version: 2022-11-28" `
"https://api.github.com/repos/MaaAssistantArknights/MaaAssistantArknights/actions/runs/${{ github.run_id }}/cancel" > $null
gh run watch ${{ github.run_id }}
- name: Read changelog to variable
id: read_changelog
uses: juliangruber/read-file-action@v1
with:
path: ./changelog_notag.md
- name: Cache MaaDeps
id: cache-maadeps
uses: actions/cache@v5
continue-on-error: true
with:
path: |
./src/MaaUtils/MaaDeps
key: ${{ runner.os }}-${{ matrix.arch }}-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- name: Bootstrap MaaDeps
if: steps.cache-maadeps.outputs.cache-hit != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 tools/maadeps-download.py ${{ matrix.arch }}-windows
- name: Configure, build and install
run: |
cmake -B build --preset ${{ matrix.arch == 'arm64' && 'windows-publish-arm64' || 'windows-publish-x64' }} -DMAA_HASH_VERSION='${{ steps.set_tag.outputs.tag }}'
cmake --build --preset ${{ matrix.arch == 'arm64' && 'windows-publish-arm64' || 'windows-publish-x64' }} --parallel $env:NUMBER_OF_PROCESSORS
cmake --install build --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
continue-on-error: true
with:
path: |
.nuke/temp
~/.nuget/packages
key: ${{ runner.os }}-${{ matrix.arch }}-nuget-${{ hashFiles('**/*.csproj') }}
- name: Restore dependencies
if: steps.cache-nuget.outputs.cache-hit != 'true'
run: dotnet restore src/MaaWpfGui/MaaWpfGui.csproj
- name: Taggify version for csproj
run: |
$csprojPath = "src/MaaWpfGui/MaaWpfGui.csproj"
$csprojPath = Resolve-Path -Path $csprojPath
$tag = '${{ steps.set_tag.outputs.tag }}' -replace '.*?/', ''
if ($tag -match '\d+(\.\d+){1,3}') {
$match = $Matches[0]
} else {
$match = "0.0.1"
}
[xml]$csproj = Get-Content -Path $csprojPath
$node = $csproj.Project.PropertyGroup | where {$_.ApplicationVersion -ne $null}
$node.InformationalVersion = $tag
$node.Version = $match
$node.FileVersion = $match
$node.AssemblyVersion = $match
$csproj.Save($csprojPath)
- name: Publish WPF GUI
run: |
dotnet publish src/MaaWpfGui/MaaWpfGui.csproj -c Release -p:Platform=${{ matrix.arch == 'arm64' && 'ARM64' || 'x64' }} -o install
- name: Collect PDB files
run: |
cp build/bin/RelWithDebInfo/*.pdb install/
Compress-Archive -Path install/*.pdb -DestinationPath install/MAAComponent-DebugSymbol-${{ steps.set_tag.outputs.tag }}-win-${{ matrix.arch }}.zip
continue-on-error: true
- name: Upload PDB files
uses: actions/upload-artifact@v6
with:
name: MAAComponent-DebugSymbol-win-${{ matrix.arch }}
path: install/MAAComponent-DebugSymbol-${{ steps.set_tag.outputs.tag }}-win-${{ matrix.arch }}.zip
- name: Organize install files
shell: bash
run: |
rm -rf install/MAAComponent-DebugSymbol-*.zip
rm -rf install/*.pdb
rm -rf install/msvc-debug
rm -rf install/*.h
cp tools/DependencySetup_依赖库安装.bat install
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v6
with:
name: MAA-win-${{ matrix.arch }}
path: install
- name: Push tag to main repo
id: push_main_tag
run: |
@@ -281,14 +93,81 @@ jobs:
git push --tags origin HEAD:refs/tags/$main_tag_name -f
echo "main_tag_name=$main_tag_name" >> $env:GITHUB_OUTPUT
- name: Generate Changelog
id: generate_changelog
run: |
python3 tools/ChangelogGenerator/changelog_generator.py --latest "${{ steps.set_tag.outputs.latest_tag }}" --tag "${{ steps.set_tag.outputs.tag }}"
$content=$(((Get-Content .\changelog.md) | Select-Object -Skip 2) -join "\n\n")
echo "changelog=$content" >> $env:GITHUB_OUTPUT
echo $content
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PYTHONIOENCODING: "utf-8"
- name: Cache .nuke/temp, ~/.nuget/packages
id: cache-nuget
uses: actions/cache@v4
with:
path: |
.nuke/temp
~/.nuget/packages
key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj') }}
- name: Restore dependencies
if: steps.cache-nuget.outputs.cache-hit != 'true'
run: dotnet restore
- name: Bootstrap MaaDeps
run: |
python3 maadeps-download.py ${{ matrix.lowercase_target }}-windows
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Taggify Version
run: |
$csprojPath = "src/MaaWpfGui/MaaWpfGui.csproj"
$csprojPath = Resolve-Path -Path $csprojPath
$tag = '${{ steps.set_tag.outputs.tag }}' -replace '.*?/', ''
if ($tag -match '\d+(\.\d+){1,3}') {
$match = $Matches[0]
} else {
$match = "0.0.1"
}
echo "Tag value: $tag"
echo "Version value: $match"
[xml]$csproj = Get-Content -Path $csprojPath
$node = $csproj.Project.PropertyGroup | where {$_.ApplicationVersion -ne $null}
$node.InformationalVersion = $tag
$node.Version = $match
$node.FileVersion = $match
$node.AssemblyVersion = $match
$csproj.Save($csprojPath)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run './build.cmd DevBuild'
run: |
$env:GITHUB_WORKFLOW = 'dev-build-win' # pretend this is a dev-build-win workflow
$env:MAA_BUILDER_MAA_VERSION = "${{ steps.set_tag.outputs.tag }}"
echo "tag: " $env:MAA_BUILDER_MAA_VERSION
./build.cmd DevBuild
env:
Reason: "Build nightly version"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload MAA to Github
uses: actions/upload-artifact@v4
with:
name: MAA-win-${{ matrix.lowercase_target }}
path: artifacts
push-tag:
name: Push Tag to MaaRelease
if: github.repository_owner == 'MaaAssistantArknights'
needs: build-win-nightly
runs-on: ubuntu-latest
steps:
- name: Checkout MaaRelease
uses: actions/checkout@v6
- name: Fetch MaaRelease
uses: actions/checkout@v4
with:
repository: ${{ format('{0}/{1}', github.repository_owner, 'MaaRelease') }}
fetch-depth: 0
@@ -306,46 +185,35 @@ jobs:
git push --tags origin HEAD:refs/tags/${{ needs.build-win-nightly.outputs.tag }}
make-ota:
name: Build and Upload Nightly OTA for Windows
if: github.repository_owner == 'MaaAssistantArknights'
needs: [build-win-nightly, push-tag]
strategy:
matrix:
target: [x64]
runs-on: ubuntu-latest
steps:
- name: Show tag version
- name: Echo tag version
run: |
echo ${{ needs.build-win-nightly.outputs.tag }}
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
path: MaaAssistantArknights
token: ${{ secrets.MAARELEASE_RELEASE }}
show-progress: false
- name: Download MAA from GitHub
uses: actions/download-artifact@v7
- name: Download MAA from Github
uses: actions/download-artifact@v4
with:
name: MAA-win-${{ matrix.target }}
path: ${{ format('{0}/{1}', 'build-ota', needs.build-win-nightly.outputs.tag) }}
- name: Fetch release information
- name: Fetch release info
run: |
mkdir -pv build-ota && cd build-ota
# Convert inputs to integers
limit_maa=${{ inputs.limit_maa || 10 }}
limit_maa=${limit_maa%.*}
echo "Parsed limit_maa: $limit_maa"
limit_maarelease=${{ inputs.limit_maarelease || 10 }}
limit_maarelease=${limit_maarelease%.*}
echo "Parsed limit_maarelease: $limit_maarelease"
gh release list --repo 'MaaAssistantArknights/MaaAssistantArknights' --limit $limit_maa | tee ./release_maa.txt
gh release list --repo "${{ github.repository_owner }}/MaaRelease" --limit $limit_maarelease | tee ./release_mr.txt
gh release list --repo 'MaaAssistantArknights/MaaAssistantArknights' --limit ${{ inputs.limit || 30 }} | tee ./release_maa.txt
gh release list --repo "${{ github.repository_owner }}/MaaRelease" --limit ${{ inputs.limit_2 || 30 }} | tee ./release_mr.txt
echo ${{ needs.build-win-nightly.outputs.tag }} > ./config
cat ./release_maa.txt | awk '{ print $1 }' > ./tags_maa.txt
@@ -365,7 +233,8 @@ jobs:
cd build-ota
pushd ${{ needs.build-win-nightly.outputs.tag }}
zip -r MAA-${{ env.release_tag }}-win-${{ matrix.target }}.zip ./*
zip -d *.zip '*.lib' '*.pdb' '*.exp' '*.config' '*.xml'
mv *.zip MAA-${{ env.release_tag }}-win-${{ matrix.target }}.zip
popd
$GITHUB_WORKSPACE/MaaAssistantArknights/tools/OTAPacker/build.sh 'MaaAssistantArknights/MaaAssistantArknights' ./config ${{ matrix.target }} "${{ github.repository_owner }}/MaaRelease"
@@ -373,6 +242,22 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Make release body
id: make_release_body
shell: bash
run: |
pre_version=${{ needs.build-win-nightly.outputs.pre_version }}
cur_version=${{ needs.build-win-nightly.outputs.main_tag_name }}
echo -e "${{ inputs.release_body || '' }}\n\n${{ needs.build-win-nightly.outputs.changelog }}\n\n**Full Changelog**: [$pre_version -> $cur_version](https://github.com/$GITHUB_REPOSITORY/compare/$pre_version...$cur_version)" > alpha_changelog.md
export r=$(cat alpha_changelog.md)
# https://github.com/svenstaro/upload-release-action
r="${r//'%'/'%25'}" # Multiline escape sequences for %
r="${r//$'\n'/'%0A'}" # Multiline escape sequences for '\n'
r="${r//$'\r'/'%0D'}" # Multiline escape sequences for '\r'
echo $r
echo "full_release_body=$r" >> $GITHUB_OUTPUT
- name: Upload to MaaRelease
uses: svenstaro/upload-release-action@v2
with:
@@ -383,17 +268,10 @@ jobs:
tag: ${{ env.release_tag }}
prerelease: true
overwrite: true
body: |
${{ inputs.release_body || '' }}
${{ needs.build-win-nightly.outputs.changelog }}
**Full Changelog**: [${{ needs.build-win-nightly.outputs.pre_version }} -> ${{ needs.build-win-nightly.outputs.main_tag_name }}](https://github.com/${{ github.repository }}/compare/${{ needs.build-win-nightly.outputs.pre_version }}...${{ needs.build-win-nightly.outputs.main_tag_name }})
body: ${{ steps.make_release_body.outputs.full_release_body }}
- name: Setup release mirror
run: |
gh workflow --repo MaaAssistantArknights/MaaRelease run release-mirrors.yml
gh workflow --repo MaaAssistantArknights/MaaRelease run mirrorchyan_alpha.yml
gh workflow --repo MaaAssistantArknights/MaaRelease run mirrorchyan_alpha_release_note.yml
env:
GH_TOKEN: ${{ secrets.MISTEOWORKFLOW }}

View File

@@ -1,4 +1,4 @@
name: Release Pipeline (OTA)
name: release-ota
on:
release:
@@ -9,12 +9,12 @@ on:
limit:
description: Number of releases to fetch from MaaAssistantArknights, 2 at least
required: false
default: 31
default: '31'
type: number
limit_2:
description: Number of releases to fetch from MaaRelease
required: false
default: 30
default: '30'
type: number
env:
@@ -22,11 +22,10 @@ env:
jobs:
create-tag:
name: Create OTA Release Tag in MaaRelease
runs-on: ubuntu-latest
steps:
- name: Checkout MaaRelease
uses: actions/checkout@v6
- name: Fetch MaaRelease
uses: actions/checkout@v4
with:
show-progress: false
repository: ${{ format('{0}/{1}', github.repository_owner, 'MaaRelease') }}
@@ -34,22 +33,12 @@ jobs:
fetch-depth: 0
token: ${{ secrets.MAARELEASE_RELEASE }}
- name: Fetch release information
- name: Fetch release info
id: fetchReleaseInfo
run: |
mkdir -pv build-ota && cd build-ota
# Convert inputs to integers
limit=${{ inputs.limit || 31 }}
limit=${limit%.*}
echo "Parsed limit: $limit"
limit_2=${{ inputs.limit_2 || 30 }}
limit_2=${limit_2%.*}
echo "Parsed limit_2: $limit_2"
gh release list --repo 'MaaAssistantArknights/MaaAssistantArknights' --limit $limit | tee ./release_maa.txt
gh release list --repo "MaaAssistantArknights/MaaRelease" --limit $limit_2 | tee ./release_mr.txt
gh release list --repo 'MaaAssistantArknights/MaaAssistantArknights' --limit ${{ inputs.limit || 31 }} | tee ./release_maa.txt
gh release list --repo "MaaAssistantArknights/MaaRelease" --limit ${{ inputs.limit_2 || 30 }} | tee ./release_mr.txt
head -n 1 ./release_maa.txt | awk '{ print $1 }' > ./config
tail -n +1 ./release_maa.txt | awk '{ print $1 }' > ./tags_maa.txt
@@ -77,7 +66,7 @@ jobs:
PUSH_REMOTE: https://github-actions[bot]:${{ secrets.MAARELEASE_RELEASE }}@github.com/${{ github.repository_owner }}/MaaRelease
- name: Upload release config to GitHub
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: MaaReleaseConfig
path: ./build-ota/config
@@ -87,7 +76,6 @@ jobs:
release_tag: ${{ steps.fetchReleaseInfo.outputs.release_tag }}
make-ota:
name: Build and Upload OTA for Windows
needs: create-tag
runs-on: ubuntu-latest
strategy:
@@ -96,13 +84,10 @@ jobs:
- x64
steps:
- name: Download release config
uses: actions/download-artifact@v7
with:
name: MaaReleaseConfig
path: ./MaaReleaseConfig
uses: actions/download-artifact@v4
- name: Checkout MaaRelease
uses: actions/checkout@v6
- name: Fetch MaaRelease
uses: actions/checkout@v4
with:
show-progress: false
repository: ${{ format('{0}/{1}', github.repository_owner, 'MaaRelease') }}
@@ -110,8 +95,8 @@ jobs:
fetch-depth: 0
token: ${{ secrets.MAARELEASE_RELEASE }}
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
show-progress: false
path: MaaAssistantArknights
@@ -139,21 +124,11 @@ jobs:
prerelease: ${{ fromJSON(needs.create-tag.outputs.prerelease) }}
overwrite: true
- name: Create issue on failure
if: failure()
uses: actions-cool/issues-helper@v3
with:
actions: "create-issue"
title: "Failed to make OTA release for Windows"
body: |
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
make-ota-mac:
name: Build and Upload OTA for macOS
needs: create-tag
runs-on: macos-14
steps:
- name: Fetch release information
- name: Fetch release info
run: gh release list --repo 'MaaAssistantArknights/MaaAssistantArknights' --limit ${{ inputs.limit || 31 }} | tee ./release_maa.txt
- name: Download release packages
@@ -169,7 +144,7 @@ jobs:
run: |
gh release download --repo 'sparkle-project/Sparkle' --clobber -O - -p 'Sparkle-*.tar.xz' | tar xf -
- name: Generate update packages
- name: Generate Update Packages
run: |
if [ $(head -n 1 release_maa.txt | awk '{ print $2 }') = 'Pre-release' ]; then
echo ${{ secrets.SPARKLE_PRIV_KEY }} | ./bin/generate_appcast --channel beta --ed-key-file - ./packages
@@ -177,10 +152,35 @@ jobs:
echo ${{ secrets.SPARKLE_PRIV_KEY }} | ./bin/generate_appcast --ed-key-file - ./packages
fi
- name: Clean up files
- name: Cleanup files
run: |
find ./packages -type f ! \( -name 'MAA-${{ needs.create-tag.outputs.release_tag }}-macos-universal.dmg' -o -name '*.delta' -o -name '*.xml' \) -print -delete
- name: Unpack the .dmg files for .dylib
run: |
cd ${{ runner.temp }}
mkdir -p macos-runtime-temp
echo ::group::Attaching release package...
hdiutil attach ${{ github.workspace }}/packages/MAA-${{ needs.create-tag.outputs.release_tag }}-macos-universal.dmg
echo ::endgroup::
echo ::group::Copying files...
cp -v /Volumes/MAA/MAA.app/Contents/Frameworks/*.dylib macos-runtime-temp/
cp -vr /Volumes/MAA/MAA.app/Contents/Resources/resource macos-runtime-temp/resource
echo ::endgroup::
echo ::group::Linking files...
libonnxruntime_file=$(basename macos-runtime-temp/libonnxruntime*.dylib)
ln -vs $libonnxruntime_file macos-runtime-temp/libonnxruntime.dylib
libopencv_world_file=$(basename macos-runtime-temp/libopencv_world*.dylib)
ln -vs $libopencv_world_file macos-runtime-temp/libopencv_world4.dylib
echo ::endgroup::
cd macos-runtime-temp
echo ::group::Compressing files...
zip -yrX9 ${{ github.workspace }}/packages/MAA-${{ needs.create-tag.outputs.release_tag }}-macos-runtime-universal.zip *
echo ::endgroup::
echo ::group::Detaching release package...
hdiutil detach /Volumes/MAA
echo ::endgroup::
- name: Upload to MaaRelease
uses: svenstaro/upload-release-action@v2
with:
@@ -192,17 +192,7 @@ jobs:
prerelease: ${{ fromJSON(needs.create-tag.outputs.prerelease) }}
overwrite: true
- name: Create issue on failure
if: failure()
uses: actions-cool/issues-helper@v3
with:
actions: "create-issue"
title: "Failed to make OTA release for macOS"
body: |
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
release:
name: Publish to Release Mirrors
needs:
- make-ota
- make-ota-mac

View File

@@ -1,154 +0,0 @@
name: Release Package Distribution
on:
workflow_dispatch:
inputs:
release_tag:
description: "Release Tag (empty for latest)"
type: string
required: false
default: ""
mirrorchyan:
description: "Upload to MirrorChyan"
type: boolean
required: false
default: true
winget:
description: "Upload to WinGet"
type: boolean
required: false
default: true
maa_cos:
description: "Upload to MAA COS"
type: boolean
required: false
default: true
env:
RELEASE_TAG_RAW: ${{ github.event.inputs.release_tag || 'latest' }}
jobs:
meta:
name: Define Release Tag
if: ${{ github.repository_owner == 'MaaAssistantArknights' }}
runs-on: ubuntu-latest
outputs:
RELEASE_TAG: ${{ steps.define_release_tag.outputs.RELEASE_TAG }}
steps:
- name: Get latest release tag
id: get_latest
if: ${{ env.RELEASE_TAG_RAW == 'latest' }}
uses: pozetroninc/github-action-get-latest-release@master
with:
owner: ${{ github.repository_owner }}
repo: ${{ github.event.repository.name }}
token: ${{ secrets.GITHUB_TOKEN }}
excludes: "draft"
- name: Define release tag
id: define_release_tag
run: |
if [ "${{ env.RELEASE_TAG_RAW }}" == "latest" ]; then
echo "RELEASE_TAG=${{ steps.get_latest.outputs.release }}" >> $GITHUB_OUTPUT
else
echo "RELEASE_TAG=${{ env.RELEASE_TAG_RAW }}" >> $GITHUB_OUTPUT
fi
mirrorchyan:
name: Upload to MirrorChyan
needs: meta
if: ${{ github.event.inputs.mirrorchyan == 'true' }}
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
include:
- os: win
arch: x64
filename: "*MAA-*-win-x64.zip"
extra_zip: false
- os: win
arch: arm64
filename: "*MAA-*-win-arm64.zip"
extra_zip: false
- os: macos
arch: arm64
filename: "MAA-*-macos-universal.dmg"
extra_zip: true
- os: macos
arch: x64
filename: "MAA-*-macos-universal.dmg"
extra_zip: true
env:
RELEASE_TAG: ${{ needs.meta.outputs.RELEASE_TAG }}
steps:
- name: Upload MAA ${{ matrix.os }} ${{ matrix.arch }}
uses: MirrorChyan/uploading-action@v1
continue-on-error: true
with:
filetype: latest-release
filename: ${{ matrix.filename }}
extra_zip: ${{ matrix.extra_zip }}
tag: ${{ env.RELEASE_TAG }}
mirrorchyan_rid: MAA
github_token: ${{ secrets.GITHUB_TOKEN }}
owner: ${{ github.repository_owner }}
repo: ${{ github.event.repository.name }}
upload_token: ${{ secrets.MirrorChyanUploadToken }}
os: ${{ matrix.os }}
arch: ${{ matrix.arch }}
winget:
name: Upload to WinGet
needs: meta
if: ${{ github.event.inputs.winget == 'true' }}
runs-on: windows-latest
continue-on-error: true
env:
RELEASE_TAG: ${{ needs.meta.outputs.RELEASE_TAG }}
steps:
- name: Upload MAA to WinGet
uses: vedantmgoyal9/winget-releaser@main
with:
identifier: MaaAssistantArknights.MaaAssistantArknights
version: ""
installers-regex: "-win-"
max-versions-to-keep: 0
release-tag: ${{ env.RELEASE_TAG }}
fork-user: MaaAssistantArknights
token: ${{ secrets.MAABOT_WINGET_TOKEN }}
maa_cos:
name: Upload to MAA COS
needs: meta
if: ${{ github.event.inputs.maa_cos == 'true' && !contains(needs.meta.outputs.RELEASE_TAG, '-') }}
runs-on: ubuntu-latest
continue-on-error: true
env:
FILENAME: MAA-${{ needs.meta.outputs.RELEASE_TAG }}-win-x64.zip
steps:
- uses: robinraju/release-downloader@v1
with:
repository: 'MaaAssistantArknights/MaaAssistantArknights'
latest: true
fileName: ${{ env.FILENAME }}
out-file-path: 'downloads'
token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload to COS
uses: zkqiang/tencent-cos-action@v0.1.0
with:
args: upload -rs downloads/${{ env.FILENAME }} MAA/${{ env.FILENAME }}
secret_id: ${{ secrets.MISTEO_MAA_COS_SECRET_ID }}
secret_key: ${{ secrets.MISTEO_MAA_COS_SECRET_KEY }}
bucket: ${{ secrets.MISTEO_MAA_COS_BUCKET }}
region: ${{ secrets.MISTEO_MAA_COS_REG }}

View File

@@ -1,111 +0,0 @@
name: Release Preparation
on:
pull_request:
types:
- opened
- reopened
- ready_for_review
workflow_dispatch:
jobs:
generate-changelog:
name: Generate Changelog
# startsWith 表达式不区分大小写
if: github.event.pull_request.draft == false && startsWith(github.event.pull_request.title, 'Release v')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
show-progress: false
- name: Extract release information
id: extract_tag
env:
PR_BODY: ${{ format('{0}/{1}', runner.temp, 'output' ) }}
run: |
tag_name=$(echo "${{ github.event.pull_request.title }}" | sed -E 's/(Release|release)//' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
echo "tag_name=$tag_name" >> $GITHUB_OUTPUT
pr_title="docs: Auto Update Changelogs of "$tag_name
echo "pr_title=$pr_title" >> $GITHUB_OUTPUT
latest_stable_tag=$(git tag -l 'v*' | grep -v '-' | sort -V | tail -n 1) # 上一个 stable 版本
newest_tag=$(git describe --tags --match "v*" --abbrev=0) # 最新版本
echo "latest_stable_tag=$latest_stable_tag" >> $GITHUB_OUTPUT
echo "newest_tag=$newest_tag" >> $GITHUB_OUTPUT
if [[ $tag_name == *-* ]]; then # 判断新版本是否为 beta 版本
latest=$newest_tag # 若是,则将 latest 参数设置为最新版本
else
latest=$latest_stable_tag # 若否,则设置为上一个 stable 版本
fi
echo "latest=$latest" >> $GITHUB_OUTPUT
cat $GITHUB_OUTPUT
echo '======='
echo 'Target PR: ${{ github.event.pull_request.html_url }}' >> $PR_BODY
echo '' >> $PR_BODY
echo '<details><summary>Debug information</summary>' >> $PR_BODY
echo '' >> $PR_BODY
echo '```' >> $PR_BODY
sed 's/=/: /1' $GITHUB_OUTPUT >> $PR_BODY
echo '```' >> $PR_BODY
echo '' >> $PR_BODY
echo '</details>' >> $PR_BODY
cat $PR_BODY
- name: Generate changelog
run: |
git switch dev
python3 tools/ChangelogGenerator/changelog_generator.py --tag "${{ steps.extract_tag.outputs.tag_name }}" --latest "${{ steps.extract_tag.outputs.latest }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Commit changes
run: |
git status
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
git add .
commit_msg="docs: Auto Generate Changelog of Release ""${{ steps.extract_tag.outputs.tag_name }}"
git commit -m "$commit_msg"
- name: Create changelog PR
uses: peter-evans/create-pull-request@v8
with:
sign-commits: true
token: ${{ secrets.GITHUB_TOKEN }}
title: ${{ steps.extract_tag.outputs.pr_title }}
body-path: ${{ format('{0}/{1}', runner.temp, 'output' ) }}
base: "dev"
branch: "changelog"
delete-branch: true
reviewers: |
AnnAngela
assignees: |
AnnAngela
- name: Assign reviewers to release PR
uses: kentaro-m/auto-assign-action@v2.0.1
with:
configuration-path: ".github/release_reviewers.yaml"
update-submodules:
name: Update Submodules
if: github.event.pull_request.draft == false && startsWith(github.event.pull_request.title, 'Release v')
runs-on: ubuntu-latest
steps:
- name: Trigger submodule update workflow
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh workflow run --repo $GITHUB_REPOSITORY update-submodules.yml

View File

@@ -1,8 +1,8 @@
name: Game Resource Update
name: res-update-game
on:
schedule:
- cron: "*/20 * * * *" # Runs every 20 minutes
- cron: "*/20 * * * *"
workflow_dispatch:
inputs:
commit_message:
@@ -11,19 +11,38 @@ on:
required: false
jobs:
clone-resources-official:
name: Download Official Resources
update-game-resources:
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
runs-on: windows-latest
env:
GH_TOKEN: ${{ secrets.MISTEOWORKFLOW }}
steps:
- name: Checkout MAA
uses: actions/checkout@v4
with:
show-progress: false
token: ${{ secrets.MISTEOWORKFLOW }}
- name: Bootstrap MaaDeps
run: |
python3 maadeps-download.py x64-windows
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1
- name: Build Resource Updater
run: |
MSBUILD tools/ResourceUpdater/ResourceUpdater.vcxproj /t:rebuild /p:Configuration="Release" /p:Platform="x64" /p:BuildProjectReferences=false /m
- name: Clone ArknightsGameResource for Official
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
show-progress: false
repository: yuanyan3060/ArknightsGameResource
ref: main
path: ./Official
sparse-checkout-cone-mode: false
path: .\tools\ResourceUpdater\x64\Release\Official
sparse-checkout: |
/levels.json
/item
@@ -35,333 +54,136 @@ jobs:
/gamedata/excel/gacha_table.json
/gamedata/excel/roguelike_topic_table.json
/gamedata/excel/activity_table.json
- name: Upload Official resources
uses: actions/upload-artifact@v6
with:
name: official
path: ./Official
compression-level: 0
clone-resources-overseas:
name: Download Overseas Resources
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
steps:
- name: Clone ArknightsGameResource_Yostar for Overseas
uses: actions/checkout@v6
with:
show-progress: false
repository: ArknightsAssets/ArknightsGamedata
ref: master
path: ./Overseas
sparse-checkout-cone-mode: false
sparse-checkout: |
/en/gamedata/excel/item_table.json
/en/gamedata/excel/building_data.json
/en/gamedata/excel/range_table.json
/en/gamedata/excel/character_table.json
/en/gamedata/excel/gacha_table.json
/en/gamedata/excel/roguelike_topic_table.json
/en/gamedata/excel/activity_table.json
/jp/gamedata/excel/item_table.json
/jp/gamedata/excel/building_data.json
/jp/gamedata/excel/range_table.json
/jp/gamedata/excel/character_table.json
/jp/gamedata/excel/gacha_table.json
/jp/gamedata/excel/roguelike_topic_table.json
/jp/gamedata/excel/activity_table.json
/kr/gamedata/excel/item_table.json
/kr/gamedata/excel/building_data.json
/kr/gamedata/excel/range_table.json
/kr/gamedata/excel/character_table.json
/kr/gamedata/excel/gacha_table.json
/kr/gamedata/excel/roguelike_topic_table.json
/kr/gamedata/excel/activity_table.json
- name: Upload Overseas resources
uses: actions/upload-artifact@v6
with:
name: overseas
path: ./Overseas
compression-level: 0
clone-resources-txwy:
name: Download Taiwan Resources
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
steps:
- name: Clone arknights-toolbox-update for Taiwan
uses: actions/checkout@v6
- name: Clone ArknightsGameResource_Yostar for Overseas
uses: actions/checkout@v4
with:
show-progress: false
repository: arkntools/arknights-data-tw-for-maa
repository: Kengxxiao/ArknightsGameData_Yostar
ref: main
path: ./excel
path: .\tools\ResourceUpdater\x64\Release\Overseas
sparse-checkout: |
/en_US/gamedata/excel/item_table.json
/en_US/gamedata/excel/building_data.json
/en_US/gamedata/excel/range_table.json
/en_US/gamedata/excel/character_table.json
/en_US/gamedata/excel/gacha_table.json
/en_US/gamedata/excel/roguelike_topic_table.json
/en_US/gamedata/excel/activity_table.json
/ja_JP/gamedata/excel/item_table.json
/ja_JP/gamedata/excel/building_data.json
/ja_JP/gamedata/excel/range_table.json
/ja_JP/gamedata/excel/character_table.json
/ja_JP/gamedata/excel/gacha_table.json
/ja_JP/gamedata/excel/roguelike_topic_table.json
/ja_JP/gamedata/excel/activity_table.json
/ko_KR/gamedata/excel/item_table.json
/ko_KR/gamedata/excel/building_data.json
/ko_KR/gamedata/excel/range_table.json
/ko_KR/gamedata/excel/character_table.json
/ko_KR/gamedata/excel/gacha_table.json
/ko_KR/gamedata/excel/roguelike_topic_table.json
/ko_KR/gamedata/excel/activity_table.json
sparse-checkout-cone-mode: false
- name: Download stages.json from Penguin Stats
run: |
cd "./excel"
baseUrl="https://penguin-stats.io/PenguinStats/api/v2/stages?server="
parameters=("CN" "US" "JP" "KR")
pids=()
for param in "${parameters[@]}"; do
{
if curl -f -s -o "stages_${param}.json" "${baseUrl}${param}"; then
echo "Successfully fetched data for ${param}"
else
echo "Failed to fetch data for ${param}" >&2
exit 1
fi
} &
pids+=($!)
done
failed=0
for pid in "${pids[@]}"; do
if ! wait "$pid"; then
failed=1
fi
done
if [ $failed -ne 0 ]; then
echo "One or more downloads failed" >&2
exit 1
fi
- name: Upload Taiwan resources
uses: actions/upload-artifact@v6
- name: Clone arknights-toolbox-update for Taiwan
uses: actions/checkout@v4
with:
name: txwy
path: ./excel
compression-level: 0
update-game-resources:
name: Update Game Resources
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
# TL;DR https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues/9872#issuecomment-2251378371
# actions/checkout uses ${{ secrets.GITHUB_TOKEN }} by default, meaning all steps will inherit it
persist-credentials: false # Needed to bypass protection rules in Push changes
show-progress: false
fetch-depth: 3
repository: arkntools/arknights-toolbox-update
ref: data-tw
path: .\tools\ResourceUpdater\x64\Release\Overseas\zh_TW\gamedata\excel
token: ${{ secrets.ARKNTOOLS_MAA_RESOURCE_UPDATER}}
- name: Fetch submodules
- name: Run Resource Updater
run: |
git submodule update --init --depth 1 src/MaaUtils
.\tools\ResourceUpdater\x64\Release\ResourceUpdater.exe
- name: Restore ResourceUpdater from cache
id: resupd-cache
uses: actions/cache/restore@v5
continue-on-error: true
with:
key: ResourceUpdater-${{ runner.os }}-${{ hashFiles('tools/ResourceUpdater/main.cpp') }}
path: |
./tools/ResourceUpdater/libopencv_world4.4.11.0.dylib
./tools/ResourceUpdater/res_updater
- name: Cache MaaDeps
if: steps.resupd-cache.outputs.cache-hit != 'true'
id: maadeps-cache
uses: actions/cache@v5
continue-on-error: true
with:
path: |
./src/MaaUtils/MaaDeps
key: ${{ runner.os }}-arm64-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- name: Bootstrap MaaDeps
if: steps.resupd-cache.outputs.cache-hit != 'true' && steps.maadeps-cache.outputs.cache-hit != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 tools/maadeps-download.py arm64-osx
- name: Config, Build & Install ResourceUpdater
if: steps.resupd-cache.outputs.cache-hit != 'true'
run: |
cd tools/ResourceUpdater
cmake --preset res-updater-macos-arm64
cmake --build --preset res-updater-macos-arm64 --parallel $(sysctl -n hw.logicalcpu)
cmake --install build --config Release
- name: Save ResourceUpdater to cache
if: always() && steps.resupd-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
continue-on-error: true
with:
key: ResourceUpdater-${{ runner.os }}-${{ hashFiles('tools/ResourceUpdater/main.cpp') }}
path: |
./tools/ResourceUpdater/libopencv_world4.4.11.0.dylib
./tools/ResourceUpdater/res_updater
- name: Wait for resource cloning
uses: yogeshlonkar/wait-for-jobs@v0
with:
ignore-skipped: 'false'
jobs: |
Download Official Resources
Download Overseas Resources
Download Taiwan Resources
ttl: 15
- name: Download txwy
uses: actions/download-artifact@v7
with:
name: txwy
path: ./tools/ResourceUpdater/Overseas/tw/gamedata/excel
- name: Download Overseas
uses: actions/download-artifact@v7
with:
name: overseas
path: ./tools/ResourceUpdater/Overseas
- name: Download Official
uses: actions/download-artifact@v7
with:
name: official
path: ./tools/ResourceUpdater/Official
- name: Update resources
run: |
./tools/ResourceUpdater/res_updater
- name: Sort tasks
id: task_sorting
- name: Overseas Tasks Ordering
run: |
python3 tools/TaskSorter/TaskSorter.py
echo "gitdiff=$(echo $(git diff --name-only --diff-filter=ACM 2>/dev/null | grep '\.json$'))" >> $GITHUB_OUTPUT
- name: Run prettier
uses: Nerixyz/actionsx-prettier@v3-adj
with:
args: -w ${{ steps.task_sorting.outputs.gitdiff }}
- name: Update version.json date if necessary
id: update_version
- name: Check if only sorted
id: check_only_sorted
run: |
sh ./tools/ResourceUpdater/version.sh
git status
- name: Setup python
if: steps.update_version.outputs.contains_png == 'True'
uses: actions/setup-python@v6
with:
python-version: "3.11"
$diff = $(git diff --numstat HEAD | findstr -i resource)
if ($LASTEXITCODE -ne 0) {
echo "no diff"
exit 0
}
- name: Cache python packages
if: always() && steps.update_version.outputs.contains_png == 'True'
id: cache_python
uses: actions/cache@v5
continue-on-error: true
with:
path: ${{ env.pythonLocation }}/Lib/site-packages
key: ${{ runner.os }}-pip-optimize-templates-${{ hashFiles('./tools/OptimizeTemplates/requirements.txt') }}
echo "diff: "$diff
$sp = $($diff -split "\s+")
$numbers = $($sp | Where-Object { $_ -match "^\d+$" })
echo "numbers: "$numbers
- name: Install dependencies
if: steps.cache_python.outputs.cache-hit != 'true' && steps.update_version.outputs.contains_png == 'True'
run: |
pip install -r tools/OptimizeTemplates/requirements.txt
$length = $numbers.Length
$noDiff = $true
for ($i = 0; $i -lt $length; $i += 2) {
$firstNumber = $numbers[$i]
$secondNumber = $numbers[$i+1]
- name: Setup oxipng
if: steps.update_version.outputs.contains_png == 'True'
uses: baptiste0928/cargo-install@v3
with:
crate: oxipng
if ($firstNumber -gt 1) {
$noDiff = $false
break
}
if ($firstNumber -ne $secondNumber) {
$noDiff = $false
break
}
}
echo "noDiff: "$noDiff
echo "only_sorted=$noDiff" >> $env:GITHUB_OUTPUT
- name: Optimize png images
if: steps.update_version.outputs.contains_png == 'True'
run: |
python3 tools/OptimizeTemplates/optimize_templates.py --no-cleanup -p resource/template/items/ resource/template/infrast/
- name: Cancelling
if: steps.check_only_sorted.outputs.only_sorted == 'True'
uses: andymckay/cancel-action@0.3
- name: Commit changes
if: steps.update_version.outputs.changes == 'True'
- name: Add files to git
if: steps.check_only_sorted.outputs.only_sorted != 'True'
id: add_files
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git status
git config user.name "$env:GITHUB_ACTOR"
git config user.email "$env:GITHUB_ACTOR@users.noreply.github.com"
git add .
commit_msg="${{ github.event.inputs.commit_message }}"
if [ -z "$commit_msg" ]; then
commit_msg="chore: Auto Update Game Resources - $(date '+%Y-%m-%d')"
else
commit_msg=$(echo "$commit_msg" | xargs) # Trim whitespace
fi
$commit_msg = "${{ github.event.inputs.commit_message }}"
if (-not [string]::IsNullOrWhiteSpace($commit_msg)) {
$commit_msg = $commit_msg.Trim()
} else {
$commit_msg = "chore: Auto Update Game Resources - $(Get-Date -Format 'yyyy-MM-dd')"
}
git commit -m "$commit_msg" -m "[skip changelog]"
if ($LASTEXITCODE -eq 0) {
echo "have_commits=True" >> $env:GITHUB_OUTPUT
}
run_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
git pull origin $(git rev-parse --abbrev-ref HEAD) --unshallow --rebase
git commit -m "$commit_msg" -m "$run_url" -m "[skip changelog]"
if [ $? -eq 0 ]; then
echo "have_commits=True" >> $GITHUB_OUTPUT
fi
- name: Cancelling
if: steps.add_files.outputs.have_commits != 'True'
uses: andymckay/cancel-action@0.3
git pull origin ${{ github.ref_name }} --rebase
- name: Push changes
- name: Push changes # push the output folder to your repo
if: steps.add_files.outputs.have_commits == 'True'
uses: ad-m/github-push-action@master
with:
branch: ${{ github.ref }}
github_token: ${{ secrets.MAA_RESOURCE_SYNC }}
github_token: ${{ secrets.MISTEOWORKFLOW }}
# - name: Update OTA resource
# if: steps.update_version.outputs.update_resources == 'True'
# env:
# GH_TOKEN: ${{ secrets.MISTEOWORKFLOW }}
# run: |
# gh workflow --repo MaaAssistantArknights/MaaRelease run update-resource.yml
- name: Add cancelled status
if: steps.update_version.outputs.changes != 'True' || steps.add_files.outputs.have_commits != 'True'
uses: andymckay/cancel-action@0.5
# - name: Release # ref: https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow
# if: steps.add_files.outputs.have_commits == 'True'
# run: |
# gh workflow run release-nightly-ota.yml -f release_body="Auto Release of Resource Updates"
- name: Upsert failure comment (single active)
if: failure() && github.ref == 'refs/heads/dev'
- name: Update OTA resource
if: steps.add_files.outputs.have_commits == 'True'
run: |
run_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
latest_id=$(gh api --paginate repos/${{ github.repository }}/issues/14493/comments | jq -r '.[] | select(.user.login=="github-actions[bot]") | .id' | tail -n 1)
if [ -n "$latest_id" ]; then
body=$(gh api repos/${{ github.repository }}/issues/comments/$latest_id --jq '.body')
if echo "$body" | grep -q '/actions/runs/'; then
# Count existing run URLs
count=$(echo "$body" | grep -o 'https://github.com/[^[:space:]]*' | wc -l)
# Collapse existing content
old_content=$(echo "$body" | sed 's|https://github.com/[^[:space:]]*|<details>\n<summary>⚠️ Previous failures ('"$count"')</summary>\n\n###### &\n\n</details>|g')
new_body="$run_url
gh workflow --repo MaaAssistantArknights/MaaRelease run update-resource.yml
@MistEO @ABA2396 @Constrat
$old_content"
gh api repos/${{ github.repository }}/issues/comments/$latest_id -X PATCH -f body="$new_body" || echo "Patch failed"
exit 0
fi
fi
# Create new failure comment if none suitable
gh issue comment 14493 -R ${{ github.repository }} --body "$run_url
@MistEO @ABA2396 @Constrat"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Minimize failure comment on success/cancelled
if: ${{ !failure() && github.ref == 'refs/heads/dev' }}
run: |
latest_node=$(gh api --paginate repos/${{ github.repository }}/issues/14493/comments | jq -r '.[] | select(.user.login=="github-actions[bot]") | .node_id' | tail -n 1)
if [ -n "$latest_node" ]; then
gh api graphql -f query='mutation { minimizeComment(input: {subjectId: "'"$latest_node"'", classifier: OUTDATED}) { minimizedComment { isMinimized } } }' || true
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - name: Release # ref: https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow
# if: steps.add_files.outputs.have_commits == 'True'
# run: |
# gh workflow run release-nightly-ota -f release_body="Auto Release of Resource Updates"
# env:
# GH_TOKEN: ${{ github.token }}

View File

@@ -1,127 +1,78 @@
name: Smoke Test
permissions:
contents: read
name: test
on:
push:
branches:
- "dev"
paths:
- ".github/workflows/smoke-testing.yml"
- "3rdparty/include/**"
- "include/**"
- "src/**"
- "cmake/**"
- "CMakeLists.txt"
- "include/**"
- "MAA.sln"
- "resource/**"
- "src/Cpp/**"
- "src/MaaCore/**"
- "tools/maadeps-download.py"
- "tools/SmokeTesting/**"
- "MaaDeps/**"
- "!**/*.md"
pull_request:
paths:
- ".github/workflows/smoke-testing.yml"
- "3rdparty/include/**"
- "include/**"
- "src/**"
- "cmake/**"
- "CMakeLists.txt"
- "include/**"
- "MAA.sln"
- "resource/**"
- "src/Cpp/**"
- "src/MaaCore/**"
- "tools/maadeps-download.py"
- "tools/SmokeTesting/**"
- "MaaDeps/**"
- "!**/*.md"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.head_ref || github.ref_name }}${{ github.ref == 'refs/heads/dev' && format('-{0}', github.sha) || '' }}
cancel-in-progress: true
jobs:
smoke-testing:
name: Run Smoke Test
runs-on: macos-latest
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout code
uses: actions/checkout@v4
with:
show-progress: false
- name: Generate cache key
id: cache_key
continue-on-error: true
run: |
echo "key=Smoke-testing-${{ hashFiles('src/Cpp/**', 'src/MaaCore/**', '3rdparty/include/**', 'include/**', 'cmake/**', 'CMakeLists.txt', 'tools/maadeps-download.py', 'tools/linux-toolchain-download.py') }}" >> $GITHUB_OUTPUT
- name: Restore cache for Smoke Test
if: ${{ steps.cache_key.outputs.key != '' }}
id: smoke-cache
uses: actions/cache/restore@v5
continue-on-error: true
with:
key: ${{ steps.cache_key.outputs.key }}
path: |
./install/libfastdeploy_ppocr.dylib
./install/libMaaCore.dylib
./install/libMaaUtils.dylib
./install/libonnxruntime.1.19.2.dylib
./install/libopencv_world4.4.11.0.dylib
./install/smoke_test
- name: Fetch submodules
if: steps.smoke-cache.outputs.cache-hit != 'true'
run: |
git submodule update --init --depth 1 src/MaaUtils
- name: Cache MaaDeps
if: steps.smoke-cache.outputs.cache-hit != 'true'
id: maadeps-cache
uses: actions/cache@v5
continue-on-error: true
with:
path: ./src/MaaUtils/MaaDeps
key: ${{ runner.os }}-arm64-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
submodules: recursive
- name: Restore dependencies
run: dotnet restore
- name: Bootstrap MaaDeps
if: steps.smoke-cache.outputs.cache-hit != 'true' && steps.maadeps-cache.outputs.cache-hit != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python3 tools/maadeps-download.py arm64-osx
python3 maadeps-download.py x64-windows
- name: Configure, build and install
if: steps.smoke-cache.outputs.cache-hit != 'true'
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1
- name: Build MaaSample
run: |
cmake -B build --preset smoke-test
cmake --build build --preset smoke-test --parallel $(sysctl -n hw.logicalcpu)
cmake --install build --config Debug
MSBUILD src/Cpp/MaaSample.slnf /t:rebuild /p:Configuration="Debug" /p:Platform="x64" /m
- name: Make link to Smoke Test cache
if: steps.smoke-cache.outputs.cache-hit == 'true'
- name: Run tests - Official #用来判断是不是国服资源出错导致外服全炸
run: |
ln -s "$(pwd)/resource" install/resource
.\x64\Debug\Sample.exe
- name: Run tests
- name: Run tests - YostarJP
run: |
sh ./tools/SmokeTesting/run_tests.sh
- name: Save cache for Smoke Test (only in dev)
if: steps.smoke-cache.outputs.cache-hit != 'true' && github.ref == 'refs/heads/dev'
uses: actions/cache/save@v5
continue-on-error: true
with:
key: ${{ steps.cache_key.outputs.key }}
path: |
./install/libfastdeploy_ppocr.dylib
./install/libMaaCore.dylib
./install/libMaaUtils.dylib
./install/libonnxruntime.1.19.2.dylib
./install/libopencv_world4.4.11.0.dylib
./install/smoke_test
.\x64\Debug\Sample.exe YostarJP
- name: Run tests - YostarEN
run: |
.\x64\Debug\Sample.exe YostarEN
- name: Run tests - YostarKR
run: |
.\x64\Debug\Sample.exe YostarKR
- name: Run tests - txwy
run: |
.\x64\Debug\Sample.exe txwy
- name: Upload logs
if: always()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: logs
path: ./install/debug
name: log
path: .\x64\Debug\debug

View File

@@ -1,40 +0,0 @@
name: Issue Staleness Management
on:
schedule:
- cron: "0 0 * * *" # Runs daily at UTC 00:00
workflow_dispatch: # Allows manual triggering
env: # config
daysBeforeStale: 90 # Number of days of inactivity before marking as stale
daysBeforeClose: 7 # Number of days to wait after marking as stale before closing
jobs:
stale:
name: Handle Stale Issues
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
permissions:
actions: write # Workaround for https://github.com/actions/stale/issues/1090
issues: write
# Completely disable stalling for PRs
pull-requests: none
contents: none
steps:
- name: Handle stale issues
uses: actions/stale@v10
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: ${{ env.daysBeforeStale }}
days-before-close: ${{ env.daysBeforeClose }}
stale-issue-label: "stale"
stale-issue-message: |
This issue has been inactive for a prolonged period and will be closed automatically in ${{ env.daysBeforeClose }} days.
该问题已长时间处于闲置状态,${{ env.daysBeforeClose }} 天后将自动关闭。
exempt-issue-labels: "keep-open, MAA Team, enhancement"
days-before-pr-stale: -1 # Completely disable stalling for PRs
days-before-pr-close: -1 # Completely disable closing for PRs
# Temporary to reduce the huge issues number
operations-per-run: 100
debug-only: false

View File

@@ -1,34 +1,32 @@
name: Resource Sync
name: sync-resource
on:
workflow_dispatch:
push:
branches:
- dev
paths:
- ".github/workflows/sync-resource.yml"
- "resource/**"
workflow_dispatch:
- ".github/workflows/sync-resource.yml"
jobs:
sync-resource:
name: Sync Resource to MaaResource
if: github.repository_owner == 'MaaAssistantArknights'
sync-reource:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Checkout MaaAssistantArknights
uses: actions/checkout@v4
with:
show-progress: false
- name: Configure git
- name: Setup Git
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git config --global user.name "$GITHUB_ACTOR"
git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com"
git show -s
- name: Checkout MaaResource
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
repository: MaaAssistantArknights/MaaResource
show-progress: false
@@ -50,3 +48,9 @@ jobs:
git status
git commit -m "$commit_msg" || exit 0
git push
- name: Setup resource update
env:
GH_TOKEN: ${{ secrets.MISTEOWORKFLOW }}
run: |
gh workflow --repo MaaAssistantArknights/MaaRelease run update-resource.yml

View File

@@ -1,33 +0,0 @@
name: Submodule Update
on:
schedule:
- cron: "50 21 * * *" # Runs daily at 21:50 UTC (before `Release Pipeline (Nightly OTA)`)
workflow_dispatch:
jobs:
update-submodules:
name: Update Submodules
runs-on: ubuntu-latest
if: github.repository_owner == 'MaaAssistantArknights'
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: true
- name: Update submodules
run: |
git submodule update --remote src/MaaMacGui
git submodule update --remote src/maa-cli
- name: Commit and push changes
uses: actions-js/push@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
message: "feat: Update Submodules MaaMacGui, maa-cli
https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
[skip changelog]"
branch: ${{ github.ref }}

View File

@@ -1,4 +1,4 @@
name: Documentation Site
name: MaaWebsite Workflow
on:
push:
@@ -8,68 +8,102 @@ on:
paths:
- ".github/workflows/website-workflow.yml"
- "docs/**"
- "website/**"
pull_request:
paths:
- ".github/workflows/website-workflow.yml"
- "docs/**"
- "website/**"
workflow_dispatch:
inputs:
deploy-to-prod:
description: "Deploy to prod"
deploy-to-prod-env:
description: "Deploy to production environment"
default: false
required: true
type: boolean
concurrency:
group: "pages"
cancel-in-progress: false
permissions:
# required for peaceiris/actions-gh-pages
contents: write
jobs:
build:
name: Build and Deploy
runs-on: ubuntu-latest
timeout-minutes: 20
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Check out code
uses: actions/checkout@v4
with:
show-progress: false
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
package_json_file: "./docs/package.json"
version: 9
run_install: false
- name: Setup node.js environment
uses: actions/setup-node@v6
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: 24
node-version: 20
cache: pnpm
cache-dependency-path: "./docs/pnpm-lock.yaml"
cache-dependency-path: './website/pnpm-lock.yaml'
- name: Install node modules
- name: Install dependencies
run: pnpm install --frozen-lockfile
working-directory: "./docs"
working-directory: "./website"
- name: Build
run: pnpm run build
working-directory: "./docs"
working-directory: "./website"
- name: Upload artifact to GitHub
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: dist
path: "./docs/.vuepress/dist"
path: "./website/dist"
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
if: github.repository_owner == 'MaaAssistantArknights' && (inputs.deploy-to-prod == true || github.ref == 'refs/heads/master')
publish:
runs-on: ubuntu-latest
needs: build
if: github.event_name != 'pull_request'
steps:
- name: Download website artifacts
uses: actions/download-artifact@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: "./docs/.vuepress/dist"
publish_branch: gh-pages
name: dist
- name: Set deployment environment
id: set-deployment-env
run: |
if [[ "${{ github.event_name }}" != "workflow_dispatch" && "${{ github.ref }}" == "refs/heads/master" ]] || [[ "${{ github.event.inputs.deploy-to-prod-env }}" == "true" ]]; then
echo "AZURE_TARGET_ENV=prod" >> $GITHUB_ENV
else
echo "AZURE_TARGET_ENV=dev" >> $GITHUB_ENV
fi
- name: Show environment
run: echo ${{ env.AZURE_TARGET_ENV }}
- name: Deploy to Azure (prod)
id: deploy-prod
uses: Azure/static-web-apps-deploy@v1
if: env.AZURE_TARGET_ENV == 'prod'
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_MAA_WEBSITE }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
app_location: "."
api_location: ""
skip_app_build: true
skip_api_build: true
- name: Deploy to Azure (dev)
id: deploy-dev
uses: Azure/static-web-apps-deploy@v1
if: env.AZURE_TARGET_ENV == 'dev'
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_MAA_WEBSITE }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
deployment_environment: dev
app_location: "."
api_location: ""
skip_app_build: true
skip_api_build: true

43
.gitignore vendored
View File

@@ -411,10 +411,10 @@ FodyWeavers.xsd
# VS Code files for those working on multiple tools
**/.vscode/*
.vscode/*
# !.vscode/settings.json
# !.vscode/tasks.json
# !.vscode/launch.json
# !.vscode/extensions.json
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
@@ -434,11 +434,9 @@ FodyWeavers.xsd
screen.png
adb_screen.png
tools/**/*.png
.vscode
enc_temp_folder/*
# RoguelikeRecruitmentTool 导出的 Excel 文件
tools/RoguelikeRecruitmentTool/output
# Nuke
.nuke/temp/*
@@ -450,34 +448,3 @@ tools/RoguelikeRecruitmentTool/output
# CF 活动关卡(异格夜刀),手动改的地图
/resource/Arknights-Tile-Pos/act24side_09-activities-act24side-level_act24side_09.json
/src/MaaWpfGui/FodyWeavers.xml
*.lnk
# 链接检查缓存
.lycheecache
# MaaDeps
MaaDeps
src/MaaUtils/*
# ResourceUpdater workflow
/original/*
# Windows Explorer Shits
desktop.ini
Thumbs.db
# macOS Finder Shit
.DS_Store
# MaaSupportExtension config
/config/maa_pi_config.json
# build & install
build
install
# pnpm cache
.pnpm-store
# CMake user presets
CMakeUserPresets.json

8
.gitmodules vendored
View File

@@ -4,12 +4,14 @@
[submodule "src/MaaMacGui"]
path = src/MaaMacGui
url = https://github.com/MaaAssistantArknights/MaaMacGui.git
[submodule "MaaDeps"]
path = MaaDeps
url = https://github.com/MaaAssistantArknights/MaaDeps
shallow = true
branch = master
[submodule "src/maa-cli"]
path = src/maa-cli
url = https://github.com/MaaAssistantArknights/maa-cli.git
[submodule "3rdparty/EmulatorExtras"]
path = 3rdparty/EmulatorExtras
url = https://github.com/MaaXYZ/EmulatorExtras.git
[submodule "src/MaaUtils"]
path = src/MaaUtils
url = https://github.com/MaaXYZ/MaaUtils

5
.linkspector.yml Normal file
View File

@@ -0,0 +1,5 @@
dirs:
- .
useGitIgnore: true
ignorePatterns:
- pattern: "^https?://127.0.0.1(:\\d+)?/"

132
.nuke/build.schema.json Normal file
View File

@@ -0,0 +1,132 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Build Schema",
"$ref": "#/definitions/build",
"definitions": {
"build": {
"type": "object",
"properties": {
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
},
"Help": {
"type": "boolean",
"description": "Shows the help text for this build assembly"
},
"Host": {
"type": "string",
"description": "Host for execution. Default is 'automatic'",
"enum": [
"AppVeyor",
"AzurePipelines",
"Bamboo",
"Bitbucket",
"Bitrise",
"GitHubActions",
"GitLab",
"Jenkins",
"Rider",
"SpaceAutomation",
"TeamCity",
"Terminal",
"TravisCI",
"VisualStudio",
"VSCode"
]
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
"Partition": {
"type": "string",
"description": "Partition to use on CI"
},
"Plan": {
"type": "boolean",
"description": "Shows the execution plan (HTML)"
},
"Profile": {
"type": "array",
"description": "Defines the profiles to load",
"items": {
"type": "string"
}
},
"Root": {
"type": "string",
"description": "Root directory during build execution"
},
"Skip": {
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
"type": "string",
"enum": [
"Default",
"DevBuild",
"DevBuildDefault",
"DevBuildReleaseSimulation",
"ReleaseMaa",
"SetMaaChangeLog",
"SetPackageBundled",
"SetVersion",
"UseClean",
"UseCommitVersion",
"UseMaaChangeLog",
"UseMaaDevBundle",
"UseMaaRelease",
"UsePublishArtifact",
"UsePublishRelease",
"UseRsVersion",
"UseTagVersion",
"WithCompileCoreRelease",
"WithCompileWpfRelease",
"WithSyncRes"
]
}
},
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
"type": "string",
"enum": [
"Default",
"DevBuild",
"DevBuildDefault",
"DevBuildReleaseSimulation",
"ReleaseMaa",
"SetMaaChangeLog",
"SetPackageBundled",
"SetVersion",
"UseClean",
"UseCommitVersion",
"UseMaaChangeLog",
"UseMaaDevBundle",
"UseMaaRelease",
"UsePublishArtifact",
"UsePublishRelease",
"UseRsVersion",
"UseTagVersion",
"WithCompileCoreRelease",
"WithCompileWpfRelease",
"WithSyncRes"
]
}
},
"Verbosity": {
"type": "string",
"description": "Logging verbosity during build execution. Default is 'Normal'",
"enum": [
"Minimal",
"Normal",
"Quiet",
"Verbose"
]
}
}
}
}
}

4
.nuke/parameters.json Normal file
View File

@@ -0,0 +1,4 @@
{
"$schema": "./build.schema.json",
"Solution": "tools/MaaBuilder.sln"
}

View File

@@ -1,45 +0,0 @@
default_install_hook_types: [pre-commit, prepare-commit-msg]
repos:
- repo: https://github.com/shssoichiro/oxipng
rev: v10.0.0
hooks:
- id: oxipng
name: PNG Image Compression
args: ["-q", "-o", "2", "-s", "--ng"]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v21.1.8
hooks:
- id: clang-format
name: Clang-Format (MaaCore)
files: ^src/MaaCore/.*
args: ["--assume-filename", ".clang-format"]
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.7.4
hooks:
- id: prettier
name: Prettier (Config Files)
files: ^((\.github/ISSUE_TEMPLATE|resource|src|tools)/.*|\.pre-commit-config\.yaml|package-definition\.json)
types_or:
- yaml
- json
- id: prettier
name: Prettier (Documentation)
files: ^docs/.*
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.10
hooks:
- id: ruff-format
name: Ruff format (Python)
- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.18.1
hooks:
- id: markdownlint-cli2
name: MarkdownLint (Documentation)
files: ^docs/.*|^README\.md$
types:
- markdown
args: ["--fix", "--config", "docs/.markdownlint.yaml"]

View File

@@ -1,12 +0,0 @@
**/pnpm-lock.yaml
3rdparty/
src/maa-cli
src/MaaMacGui
src/MaaUtils
resource/Arknights-Tile-Pos/
tools/OptimizeTemplates/optimize_templates.json
CITATION.cff
CHANGELOG.md

View File

@@ -1,23 +0,0 @@
{
"tabWidth": 4,
"printWidth": 120,
"useTabs": false,
"bracketSameLine": true,
"bracketSpacing": true,
"endOfLine": "auto",
"overrides": [
{
"files": [".clang-format", "**/*.yml", "**/*.yaml"],
"options": {
"parser": "yaml",
"tabWidth": 2
}
},
{
"files": ["**/*.md"],
"options": {
"tabWidth": 2
}
}
]
}

View File

@@ -2,8 +2,7 @@
"json.schemas": [
{
"fileMatch": [
"resource/tasks/**/*.json",
"resource/global/**/resource/tasks/**/*.json",
"resource/tasks.json"
],
"url": "./docs/maa_tasks_schema.json"
}
@@ -12,9 +11,5 @@
"MaaDeps/vcpkg/buildtrees": true,
"MaaDeps/vcpkg/packages": true
},
"files.associations": {
"**/resource/tasks/**/*.json": "jsonc"
},
"C_Cpp.exclusionPolicy": "checkFilesAndFolders",
"cmake.outputLogEncoding": "UTF-8"
"C_Cpp.exclusionPolicy": "checkFilesAndFolders"
}

View File

@@ -39,8 +39,8 @@ inline matrix4x4 camera_matrix_from_trans(
const vec3d& euler,
double ratio,
double fov_2_y = 20 * degree,
double far_c = 1000,
double near_c = 0.3)
double far = 1000,
double near = 0.3)
{
const double cos_y = std::cos(euler[0]);
const double sin_y = std::sin(euler[0]);
@@ -66,11 +66,11 @@ inline matrix4x4 camera_matrix_from_trans(
0, -sin_x, -cos_x, 0, //
0, 0, 0, 1,
};
const matrix4x4 proj = matrix4x4 {
const matrix4x4 proj = {
// clang-format off
ratio / tan_f, 0, 0, 0,
0, 1 / tan_f, 0, 0,
0, 0, -(far_c + near_c) / (far_c - near_c), -(far_c * near_c * 2) / (far_c - near_c),
0, 0, -(far + near) / (far - near), -(far * near * 2) / (far - near),
0, 0, -1, 0,
// clang-format on
};
@@ -78,13 +78,14 @@ inline matrix4x4 camera_matrix_from_trans(
return proj * matrixX * matrixY * translate;
}
inline cv::Point world_to_screen(const Level& level, const vec3d& world_pos, bool side, const vec3d& offset = {})
inline cv::Point
world_to_screen(const Level& level, const vec3d& world_pos, bool side, const vec3d& offset = {})
{
static constexpr int width = 1280;
static constexpr int height = 720;
static constexpr double width = 1280;
static constexpr double height = 720;
const vec3d pos_cam = camera_pos(level, side, width, height) + offset;
const vec3d euler = camera_euler_angles_yxz(level, side);
const matrix4x4 matrix = camera_matrix_from_trans(pos_cam, euler, static_cast<double>(height) / width);
const matrix4x4 matrix = camera_matrix_from_trans(pos_cam, euler, height / width);
auto result = matrix * cv::Point3d(world_pos);
result = result / result(3);
result = (result + cv::Vec4d::ones()) / 2.;
@@ -106,7 +107,12 @@ inline vec3d get_tile_world_pos(const Level& level, int tile_y, int tile_x)
};
}
inline auto get_tile_screen_pos(const Level& level, int tile_y, int tile_x, bool side = false, const vec3d& offset = {})
inline auto get_tile_screen_pos(
const Level& level,
int tile_y,
int tile_x,
bool side = false,
const vec3d& offset = {})
{
return world_to_screen(level, get_tile_world_pos(level, tile_y, tile_x), side, offset);
}
@@ -115,15 +121,15 @@ static constexpr double rel_pos_x = 1.3143386840820312;
static constexpr double rel_pos_y = 1.314337134361267;
static constexpr double rel_pos_z = -0.3967874050140381;
inline auto get_retreat_screen_pos(const Level& level, bool has_multi_stages = false)
inline auto get_retreat_screen_pos(const Level& level)
{
const vec3d relative_pos = { -rel_pos_x + (has_multi_stages ? level.view[0].x : 0), +rel_pos_y, rel_pos_z };
const vec3d relative_pos = { -rel_pos_x, +rel_pos_y, rel_pos_z };
return world_to_screen(level, relative_pos, true);
}
inline auto get_skill_screen_pos(const Level& level, bool has_multi_stages = false)
inline auto get_skill_screen_pos(const Level& level)
{
const vec3d relative_pos = { +rel_pos_x + (has_multi_stages ? level.view[0].x : 0), -rel_pos_y, rel_pos_z };
const vec3d relative_pos = { +rel_pos_x, -rel_pos_y, rel_pos_z };
return world_to_screen(level, relative_pos, true);
}

View File

@@ -0,0 +1,533 @@
#pragma once
#include <initializer_list>
#include <optional>
#include <ostream>
#include <string>
#include <tuple>
#include <vector>
#include "exception.hpp"
#include "utils.hpp"
namespace json
{
template <typename string_t>
class basic_array
{
friend class basic_value<string_t>;
friend class basic_object<string_t>;
public:
using raw_array = std::vector<basic_value<string_t>>;
using value_type = typename raw_array::value_type;
using iterator = typename raw_array::iterator;
using const_iterator = typename raw_array::const_iterator;
using reverse_iterator = typename raw_array::reverse_iterator;
using const_reverse_iterator = typename raw_array::const_reverse_iterator;
using char_t = typename string_t::value_type;
public:
basic_array() = default;
basic_array(const basic_array<string_t>& rhs) = default;
basic_array(basic_array<string_t>&& rhs) noexcept = default;
basic_array(std::initializer_list<value_type> init_list);
basic_array(typename raw_array::size_type size);
// explicit basic_array(const basic_value<string_t>& val);
// explicit basic_array(basic_value<string_t>&& val);
template <typename collection_t,
std::enable_if_t<_utils::is_collection<collection_t> &&
std::is_constructible_v<value_type, _utils::range_value_t<collection_t>>,
bool> = true>
basic_array(collection_t arr)
: _array_data(std::make_move_iterator(arr.begin()), std::make_move_iterator(arr.end()))
{}
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_member<jsonization_t>::value, bool> = true>
basic_array(const jsonization_t& value) : basic_array(value.to_json())
{}
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_templ_spec<jsonization_t>::value, bool> = true>
basic_array(const jsonization_t& value) : basic_array(ext::jsonization<jsonization_t>().to_json(value))
{}
~basic_array() noexcept = default;
bool empty() const noexcept { return _array_data.empty(); }
size_t size() const noexcept { return _array_data.size(); }
bool contains(size_t pos) const { return pos < _array_data.size(); }
bool exists(size_t pos) const { return contains(pos); }
const basic_value<string_t>& at(size_t pos) const;
string_t dumps(std::optional<size_t> indent = std::nullopt) const { return indent ? format(*indent) : to_string(); }
string_t to_string() const;
string_t format(size_t indent = 4) const { return format(indent, 0); }
template <typename value_t>
bool all() const;
template <typename value_t, template <typename...> typename collection_t = std::vector>
collection_t<value_t> as_collection() const;
// Usage: get(key_1, key_2, ..., default_value);
template <typename... key_then_default_value_t>
auto get(key_then_default_value_t&&... keys_then_default_value) const;
template <typename value_t = basic_value<string_t>>
std::optional<value_t> find(size_t pos) const;
template <typename... args_t>
decltype(auto) emplace_back(args_t&&... args);
template <typename... args_t>
decltype(auto) push_back(args_t&&... args);
void clear() noexcept;
bool erase(size_t pos);
bool erase(iterator iter);
iterator begin() noexcept;
iterator end() noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
reverse_iterator rbegin() noexcept;
reverse_iterator rend() noexcept;
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator rend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
const basic_value<string_t>& operator[](size_t pos) const;
basic_value<string_t>& operator[](size_t pos);
basic_array<string_t> operator+(const basic_array<string_t>& rhs) const&;
basic_array<string_t> operator+(basic_array<string_t>&& rhs) const&;
basic_array<string_t> operator+(const basic_array<string_t>& rhs) &&;
basic_array<string_t> operator+(basic_array<string_t>&& rhs) &&;
basic_array<string_t>& operator+=(const basic_array<string_t>& rhs);
basic_array<string_t>& operator+=(basic_array<string_t>&& rhs);
basic_array<string_t>& operator=(const basic_array<string_t>&) = default;
basic_array<string_t>& operator=(basic_array<string_t>&&) noexcept = default;
template <typename value_t, std::enable_if_t<std::is_convertible_v<value_t, basic_array<string_t>>, bool> = true>
basic_array<string_t>& operator=(value_t rhs)
{
return *this = basic_array<string_t>(std::move(rhs));
}
bool operator==(const basic_array<string_t>& rhs) const;
bool operator!=(const basic_array<string_t>& rhs) const { return !(*this == rhs); }
template <typename value_t, template <typename...> typename collection_t = std::vector,
std::enable_if_t<_utils::is_collection<collection_t<value_t>>, bool> = true>
explicit operator collection_t<value_t>() const
{
return as_collection<value_t, collection_t>();
}
template <typename jsonization_t,
std::enable_if_t<_utils::has_from_json_in_member<jsonization_t, string_t>::value, bool> = true>
explicit operator jsonization_t() const
{
jsonization_t dst {};
if (!dst.from_json(*this)) {
throw exception("Wrong JSON");
}
return dst;
}
template <typename jsonization_t,
std::enable_if_t<_utils::has_from_json_in_templ_spec<jsonization_t, string_t>::value, bool> = true>
explicit operator jsonization_t() const
{
jsonization_t dst {};
if (!ext::jsonization<jsonization_t>().from_json(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
}
private:
template <typename... key_then_default_value_t, size_t... keys_indexes_t>
auto get(std::tuple<key_then_default_value_t...> keys_then_default_value,
std::index_sequence<keys_indexes_t...>) const;
template <typename value_t, typename... rest_keys_t>
auto get_helper(const value_t& default_value, size_t pos, rest_keys_t&&... rest) const;
template <typename value_t>
auto get_helper(const value_t& default_value, size_t pos) const;
string_t format(size_t indent, size_t indent_times) const;
private:
raw_array _array_data;
};
template <typename string_t>
inline basic_array<string_t>::basic_array(std::initializer_list<value_type> init_list) : _array_data(init_list)
{}
template <typename string_t>
inline basic_array<string_t>::basic_array(typename raw_array::size_type size) : _array_data(size)
{}
// template <typename string_t>
// inline basic_array<string_t>::basic_array(const basic_value<string_t>& val) : basic_array<string_t>(val.as_array())
//{}
//
// template <typename string_t>
// inline basic_array<string_t>::basic_array(basic_value<string_t>&& val)
// : basic_array<string_t>(std::move(val.as_array()))
//{}
template <typename string_t>
inline void basic_array<string_t>::clear() noexcept
{
_array_data.clear();
}
template <typename string_t>
inline bool basic_array<string_t>::erase(size_t pos)
{
return erase(_array_data.begin() + pos);
}
template <typename string_t>
inline bool basic_array<string_t>::erase(iterator iter)
{
return _array_data.erase(iter) != _array_data.end();
}
template <typename string_t>
template <typename... args_t>
inline decltype(auto) basic_array<string_t>::emplace_back(args_t&&... args)
{
static_assert(std::is_constructible_v<value_type, args_t...>,
"Parameter can't be used to construct a raw_array::value_type");
return _array_data.emplace_back(std::forward<args_t>(args)...);
}
template <typename string_t>
template <typename... args_t>
inline decltype(auto) basic_array<string_t>::push_back(args_t&&... args)
{
return emplace_back(std::forward<args_t>(args)...);
}
template <typename string_t>
inline const basic_value<string_t>& basic_array<string_t>::at(size_t pos) const
{
return _array_data.at(pos);
}
template <typename string_t>
inline string_t basic_array<string_t>::to_string() const
{
string_t str { '[' };
for (auto iter = _array_data.cbegin(); iter != _array_data.cend();) {
str += iter->to_string();
if (++iter != _array_data.cend()) {
str += ',';
}
}
str += char_t(']');
return str;
}
template <typename string_t>
inline string_t basic_array<string_t>::format(size_t indent, size_t indent_times) const
{
const string_t tail_indent(indent * indent_times, ' ');
const string_t body_indent(indent * (indent_times + 1), ' ');
string_t str { '[', '\n' };
for (auto iter = _array_data.cbegin(); iter != _array_data.cend();) {
str += body_indent + iter->format(indent, indent_times + 1);
if (++iter != _array_data.cend()) {
str += ',';
}
str += '\n';
}
str += tail_indent + char_t(']');
return str;
}
template <typename string_t>
template <typename value_t>
inline bool basic_array<string_t>::all() const
{
for (const auto& elem : _array_data) {
if (!elem.template is<value_t>()) {
return false;
}
}
return true;
}
namespace _as_collection_helper
{
template <typename T>
class has_emplace_back
{
template <typename U>
static auto test(int) -> decltype(std::declval<U>().emplace_back(), std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
}
template <typename string_t>
template <typename value_t, template <typename...> typename collection_t>
inline collection_t<value_t> basic_array<string_t>::as_collection() const
{
collection_t<value_t> result;
if constexpr (_as_collection_helper::has_emplace_back<collection_t<value_t>>::value) {
for (const auto& elem : _array_data) {
result.emplace_back(elem.template as<value_t>());
}
}
else {
for (const auto& elem : _array_data) {
result.emplace(elem.template as<value_t>());
}
}
return result;
}
template <typename string_t>
template <typename... key_then_default_value_t>
inline auto basic_array<string_t>::get(key_then_default_value_t&&... keys_then_default_value) const
{
return get(std::forward_as_tuple(keys_then_default_value...),
std::make_index_sequence<sizeof...(keys_then_default_value) - 1> {});
}
template <typename string_t>
template <typename... key_then_default_value_t, size_t... keys_indexes_t>
inline auto basic_array<string_t>::get(std::tuple<key_then_default_value_t...> keys_then_default_value,
std::index_sequence<keys_indexes_t...>) const
{
constexpr unsigned long default_value_index = sizeof...(key_then_default_value_t) - 1;
return get_helper(std::get<default_value_index>(keys_then_default_value),
std::get<keys_indexes_t>(keys_then_default_value)...);
}
template <typename string_t>
template <typename value_t, typename... rest_keys_t>
inline auto basic_array<string_t>::get_helper(const value_t& default_value, size_t pos, rest_keys_t&&... rest) const
{
constexpr bool is_json = std::is_same_v<basic_value<string_t>, value_t> ||
std::is_same_v<basic_array<string_t>, value_t> ||
std::is_same_v<basic_object<string_t>, value_t>;
constexpr bool is_string = std::is_constructible_v<string_t, value_t> && !is_json;
if (!contains(pos)) {
if constexpr (is_string) {
return string_t(default_value);
}
else {
return value_t(default_value);
}
}
return at(pos).get_helper(default_value, std::forward<rest_keys_t>(rest)...);
}
template <typename string_t>
template <typename value_t>
inline auto basic_array<string_t>::get_helper(const value_t& default_value, size_t pos) const
{
constexpr bool is_json = std::is_same_v<basic_value<string_t>, value_t> ||
std::is_same_v<basic_array<string_t>, value_t> ||
std::is_same_v<basic_object<string_t>, value_t>;
constexpr bool is_string = std::is_constructible_v<string_t, value_t> && !is_json;
if (!contains(pos)) {
if constexpr (is_string) {
return string_t(default_value);
}
else {
return value_t(default_value);
}
}
auto val = _array_data.at(pos);
if (val.template is<value_t>()) {
if constexpr (is_string) {
return val.template as<string_t>();
}
else {
return val.template as<value_t>();
}
}
else {
if constexpr (is_string) {
return string_t(default_value);
}
else {
return value_t(default_value);
}
}
}
template <typename string_t>
template <typename value_t>
inline std::optional<value_t> basic_array<string_t>::find(size_t pos) const
{
if (!contains(pos)) {
return std::nullopt;
}
const auto& val = _array_data.at(pos);
return val.template is<value_t>() ? std::optional<value_t>(val.template as<value_t>()) : std::nullopt;
}
template <typename string_t>
inline typename basic_array<string_t>::iterator basic_array<string_t>::begin() noexcept
{
return _array_data.begin();
}
template <typename string_t>
inline typename basic_array<string_t>::iterator basic_array<string_t>::end() noexcept
{
return _array_data.end();
}
template <typename string_t>
inline typename basic_array<string_t>::const_iterator basic_array<string_t>::begin() const noexcept
{
return _array_data.begin();
}
template <typename string_t>
inline typename basic_array<string_t>::const_iterator basic_array<string_t>::end() const noexcept
{
return _array_data.end();
}
template <typename string_t>
inline typename basic_array<string_t>::const_iterator basic_array<string_t>::cbegin() const noexcept
{
return _array_data.cbegin();
}
template <typename string_t>
inline typename basic_array<string_t>::const_iterator basic_array<string_t>::cend() const noexcept
{
return _array_data.cend();
}
template <typename string_t>
inline typename basic_array<string_t>::reverse_iterator basic_array<string_t>::rbegin() noexcept
{
return _array_data.rbegin();
}
template <typename string_t>
inline typename basic_array<string_t>::reverse_iterator basic_array<string_t>::rend() noexcept
{
return _array_data.rend();
}
template <typename string_t>
inline typename basic_array<string_t>::const_reverse_iterator basic_array<string_t>::rbegin() const noexcept
{
return _array_data.rbegin();
}
template <typename string_t>
inline typename basic_array<string_t>::const_reverse_iterator basic_array<string_t>::rend() const noexcept
{
return _array_data.rend();
}
template <typename string_t>
inline typename basic_array<string_t>::const_reverse_iterator basic_array<string_t>::crbegin() const noexcept
{
return _array_data.crbegin();
}
template <typename string_t>
inline typename basic_array<string_t>::const_reverse_iterator basic_array<string_t>::crend() const noexcept
{
return _array_data.crend();
}
template <typename string_t>
inline basic_value<string_t>& basic_array<string_t>::operator[](size_t pos)
{
return _array_data[pos];
}
template <typename string_t>
inline const basic_value<string_t>& basic_array<string_t>::operator[](size_t pos) const
{
return _array_data[pos];
}
template <typename string_t>
inline basic_array<string_t> basic_array<string_t>::operator+(const basic_array<string_t>& rhs) const&
{
basic_array<string_t> temp = *this;
temp._array_data.insert(_array_data.end(), rhs.begin(), rhs.end());
return temp;
}
template <typename string_t>
inline basic_array<string_t> basic_array<string_t>::operator+(basic_array<string_t>&& rhs) const&
{
basic_array<string_t> temp = *this;
temp._array_data.insert(_array_data.end(), std::make_move_iterator(rhs.begin()),
std::make_move_iterator(rhs.end()));
return temp;
}
template <typename string_t>
inline basic_array<string_t> basic_array<string_t>::operator+(const basic_array<string_t>& rhs) &&
{
_array_data.insert(_array_data.end(), rhs.begin(), rhs.end());
return std::move(*this);
}
template <typename string_t>
inline basic_array<string_t> basic_array<string_t>::operator+(basic_array<string_t>&& rhs) &&
{
_array_data.insert(_array_data.end(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return std::move(*this);
}
template <typename string_t>
inline basic_array<string_t>& basic_array<string_t>::operator+=(const basic_array<string_t>& rhs)
{
_array_data.insert(_array_data.end(), rhs.begin(), rhs.end());
return *this;
}
template <typename string_t>
inline basic_array<string_t>& basic_array<string_t>::operator+=(basic_array<string_t>&& rhs)
{
_array_data.insert(_array_data.end(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return *this;
}
template <typename string_t>
inline bool basic_array<string_t>::operator==(const basic_array<string_t>& rhs) const
{
return _array_data == rhs._array_data;
}
template <typename ostream_t, typename string_t,
typename std_ostream_t =
std::basic_ostream<typename string_t::value_type, std::char_traits<typename string_t::value_type>>,
typename enable_t =
std::enable_if_t<std::is_same_v<std_ostream_t, ostream_t> || std::is_base_of_v<std_ostream_t, ostream_t>>>
ostream_t& operator<<(ostream_t& out, const basic_array<string_t>& arr)
{
out << arr.format();
return out;
}
} // namespace json

View File

@@ -0,0 +1,26 @@
#pragma once
#include <exception>
#include <string>
namespace json
{
class exception : public std::exception
{
public:
exception() = default;
exception(const std::string& msg) : _what(msg) {}
exception(const exception&) = default;
exception& operator=(const exception&) = default;
exception(exception&&) = default;
exception& operator=(exception&&) = default;
virtual ~exception() noexcept override = default;
virtual const char* what() const noexcept override { return _what.empty() ? "Unknown exception" : _what.c_str(); }
protected:
std::string _what;
};
}

View File

@@ -0,0 +1,474 @@
#pragma once
#include <initializer_list>
#include <map>
#include <optional>
#include <ostream>
#include <string>
#include <tuple>
#include "exception.hpp"
#include "utils.hpp"
namespace json
{
template <typename string_t>
class basic_object
{
friend class basic_value<string_t>;
friend class basic_array<string_t>;
public:
using raw_object = std::map<string_t, basic_value<string_t>>;
using key_type = typename raw_object::key_type;
using mapped_type = typename raw_object::mapped_type;
using value_type = typename raw_object::value_type;
using iterator = typename raw_object::iterator;
using const_iterator = typename raw_object::const_iterator;
using char_t = typename string_t::value_type;
public:
basic_object() = default;
basic_object(const basic_object<string_t>& rhs) = default;
basic_object(basic_object<string_t>&& rhs) noexcept = default;
basic_object(std::initializer_list<value_type> init_list);
// explicit basic_object(const basic_value<string_t>& val);
// explicit basic_object(basic_value<string_t>&& val);
template <typename map_t, std::enable_if_t<_utils::is_map<map_t> &&
std::is_constructible_v<value_type, _utils::range_value_t<map_t>>,
bool> = true>
basic_object(map_t map) : _object_data(std::make_move_iterator(map.begin()), std::make_move_iterator(map.end()))
{}
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_member<jsonization_t>::value, bool> = true>
basic_object(const jsonization_t& value) : basic_object(value.to_json())
{}
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_templ_spec<jsonization_t>::value, bool> = true>
basic_object(const jsonization_t& value) : basic_object(ext::jsonization<jsonization_t>().to_json(value))
{}
~basic_object() = default;
bool empty() const noexcept { return _object_data.empty(); }
size_t size() const noexcept { return _object_data.size(); }
bool contains(const string_t& key) const;
bool exists(const string_t& key) const { return contains(key); }
const basic_value<string_t>& at(const string_t& key) const;
string_t dumps(std::optional<size_t> indent = std::nullopt) const { return indent ? format(*indent) : to_string(); }
string_t to_string() const;
string_t format(size_t indent = 4) const { return format(indent, 0); }
template <typename value_t>
bool all() const;
template <typename value_t, template <typename...> typename map_t = std::map>
map_t<string_t, value_t> as_map() const;
// Usage: get(key_1, key_2, ..., default_value);
template <typename... key_then_default_value_t>
auto get(key_then_default_value_t&&... keys_then_default_value) const;
template <typename value_t = basic_value<string_t>>
std::optional<value_t> find(const string_t& key) const;
template <typename... args_t>
decltype(auto) emplace(args_t&&... args);
template <typename... args_t>
decltype(auto) insert(args_t&&... args);
void clear() noexcept;
bool erase(const string_t& key);
bool erase(iterator iter);
iterator begin() noexcept;
iterator end() noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
basic_value<string_t>& operator[](const string_t& key);
basic_value<string_t>& operator[](string_t&& key);
basic_object<string_t> operator|(const basic_object<string_t>& rhs) const&;
basic_object<string_t> operator|(basic_object<string_t>&& rhs) const&;
basic_object<string_t> operator|(const basic_object<string_t>& rhs) &&;
basic_object<string_t> operator|(basic_object<string_t>&& rhs) &&;
basic_object<string_t>& operator|=(const basic_object<string_t>& rhs);
basic_object<string_t>& operator|=(basic_object<string_t>&& rhs);
basic_object<string_t>& operator=(const basic_object<string_t>&) = default;
basic_object<string_t>& operator=(basic_object<string_t>&&) = default;
template <typename value_t, std::enable_if_t<std::is_convertible_v<value_t, basic_object<string_t>>, bool> = true>
basic_object<string_t>& operator=(value_t rhs)
{
return *this = basic_object<string_t>(std::move(rhs));
}
bool operator==(const basic_object<string_t>& rhs) const;
bool operator!=(const basic_object<string_t>& rhs) const { return !(*this == rhs); }
template <typename value_t, template <typename...> typename map_t = std::map,
std::enable_if_t<_utils::is_map<map_t<string_t, value_t>>, bool> = true>
explicit operator map_t<string_t, value_t>() const
{
return as_map<value_t, map_t>();
}
template <typename jsonization_t,
std::enable_if_t<_utils::has_from_json_in_member<jsonization_t, string_t>::value, bool> = true>
explicit operator jsonization_t() const
{
jsonization_t dst {};
if (!dst.from_json(*this)) {
throw exception("Wrong JSON");
}
return dst;
}
template <typename jsonization_t,
std::enable_if_t<_utils::has_from_json_in_templ_spec<jsonization_t, string_t>::value, bool> = true>
explicit operator jsonization_t() const
{
jsonization_t dst {};
if (!ext::jsonization<jsonization_t>().from_json(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
}
private:
template <typename... key_then_default_value_t, size_t... keys_indexes_t>
auto get(std::tuple<key_then_default_value_t...> keys_then_default_value,
std::index_sequence<keys_indexes_t...>) const;
template <typename value_t, typename... rest_keys_t>
auto get_helper(const value_t& default_value, const string_t& key, rest_keys_t&&... rest) const;
template <typename value_t>
auto get_helper(const value_t& default_value, const string_t& key) const;
string_t format(size_t indent, size_t indent_times) const;
private:
raw_object _object_data;
};
template <typename string_t>
inline basic_object<string_t>::basic_object(std::initializer_list<value_type> init_list)
: _object_data(std::make_move_iterator(init_list.begin()), std::make_move_iterator(init_list.end()))
{}
// template <typename string_t>
// inline basic_object<string_t>::basic_object(const basic_value<string_t>& val) :
// basic_object<string_t>(val.as_object())
//{}
//
// template <typename string_t>
// inline basic_object<string_t>::basic_object(basic_value<string_t>&& val)
// : basic_object<string_t>(std::move(val.as_object()))
//{}
template <typename string_t>
inline bool basic_object<string_t>::contains(const string_t& key) const
{
return _object_data.find(key) != _object_data.cend();
}
template <typename string_t>
inline const basic_value<string_t>& basic_object<string_t>::at(const string_t& key) const
{
return _object_data.at(key);
}
template <typename string_t>
inline void basic_object<string_t>::clear() noexcept
{
_object_data.clear();
}
template <typename string_t>
inline bool basic_object<string_t>::erase(const string_t& key)
{
return _object_data.erase(key) > 0 ? true : false;
}
template <typename string_t>
inline bool basic_object<string_t>::erase(iterator iter)
{
return _object_data.erase(iter) != _object_data.end();
}
template <typename string_t>
template <typename... args_t>
inline decltype(auto) basic_object<string_t>::emplace(args_t&&... args)
{
static_assert(std::is_constructible_v<value_type, args_t...>,
"Parameter can't be used to construct a raw_object::value_type");
return _object_data.emplace(std::forward<args_t>(args)...);
}
template <typename string_t>
template <typename... args_t>
inline decltype(auto) basic_object<string_t>::insert(args_t&&... args)
{
return emplace(std::forward<args_t>(args)...);
}
template <typename string_t>
inline string_t basic_object<string_t>::to_string() const
{
string_t str { '{' };
for (auto iter = _object_data.cbegin(); iter != _object_data.cend();) {
const auto& [key, val] = *iter;
str += char_t('"') + _utils::unescape_string(key) + string_t { '\"', ':' } + val.to_string();
if (++iter != _object_data.cend()) {
str += ',';
}
}
str += char_t('}');
return str;
}
template <typename string_t>
inline string_t basic_object<string_t>::format(size_t indent, size_t indent_times) const
{
const string_t tail_indent(indent * indent_times, ' ');
const string_t body_indent(indent * (indent_times + 1), ' ');
string_t str { '{', '\n' };
for (auto iter = _object_data.cbegin(); iter != _object_data.cend();) {
const auto& [key, val] = *iter;
str += body_indent + char_t('"') + _utils::unescape_string(key) + string_t { '\"', ':', ' ' } +
val.format(indent, indent_times + 1);
if (++iter != _object_data.cend()) {
str += ',';
}
str += '\n';
}
str += tail_indent + char_t('}');
return str;
}
template <typename string_t>
template <typename value_t>
inline bool basic_object<string_t>::all() const
{
for (const auto& [_, val] : _object_data) {
if (!val.template is<value_t>()) {
return false;
}
}
return true;
}
template <typename string_t>
template <typename value_t, template <typename...> typename map_t>
inline map_t<string_t, value_t> basic_object<string_t>::as_map() const
{
map_t<string_t, value_t> result;
for (const auto& [key, val] : _object_data) {
result.emplace(key, val.template as<value_t>());
}
return result;
}
template <typename string_t>
template <typename... key_then_default_value_t>
inline auto basic_object<string_t>::get(key_then_default_value_t&&... keys_then_default_value) const
{
return get(std::forward_as_tuple(keys_then_default_value...),
std::make_index_sequence<sizeof...(keys_then_default_value) - 1> {});
}
template <typename string_t>
template <typename... key_then_default_value_t, size_t... keys_indexes_t>
inline auto basic_object<string_t>::get(std::tuple<key_then_default_value_t...> keys_then_default_value,
std::index_sequence<keys_indexes_t...>) const
{
constexpr unsigned long default_value_index = sizeof...(key_then_default_value_t) - 1;
return get_helper(std::get<default_value_index>(keys_then_default_value),
std::get<keys_indexes_t>(keys_then_default_value)...);
}
template <typename string_t>
template <typename value_t, typename... rest_keys_t>
inline auto basic_object<string_t>::get_helper(const value_t& default_value, const string_t& key,
rest_keys_t&&... rest) const
{
constexpr bool is_json = std::is_same_v<basic_value<string_t>, value_t> ||
std::is_same_v<basic_array<string_t>, value_t> ||
std::is_same_v<basic_object<string_t>, value_t>;
constexpr bool is_string = std::is_constructible_v<string_t, value_t> && !is_json;
if (!contains(key)) {
if constexpr (is_string) {
return string_t(default_value);
}
else {
return value_t(default_value);
}
}
return at(key).get_helper(default_value, std::forward<rest_keys_t>(rest)...);
}
template <typename string_t>
template <typename value_t>
inline auto basic_object<string_t>::get_helper(const value_t& default_value, const string_t& key) const
{
constexpr bool is_json = std::is_same_v<basic_value<string_t>, value_t> ||
std::is_same_v<basic_array<string_t>, value_t> ||
std::is_same_v<basic_object<string_t>, value_t>;
constexpr bool is_string = std::is_constructible_v<string_t, value_t> && !is_json;
if (!contains(key)) {
if constexpr (is_string) {
return string_t(default_value);
}
else {
return value_t(default_value);
}
}
auto val = _object_data.at(key);
if (val.template is<value_t>()) {
if constexpr (is_string) {
return val.template as<string_t>();
}
else {
return val.template as<value_t>();
}
}
else {
if constexpr (is_string) {
return string_t(default_value);
}
else {
return value_t(default_value);
}
}
}
template <typename string_t>
template <typename value_t>
inline std::optional<value_t> basic_object<string_t>::find(const string_t& key) const
{
auto iter = _object_data.find(key);
if (iter == _object_data.end()) {
return std::nullopt;
}
const auto& val = iter->second;
return val.template is<value_t>() ? std::optional<value_t>(val.template as<value_t>()) : std::nullopt;
}
template <typename string_t>
inline typename basic_object<string_t>::iterator basic_object<string_t>::begin() noexcept
{
return _object_data.begin();
}
template <typename string_t>
inline typename basic_object<string_t>::iterator basic_object<string_t>::end() noexcept
{
return _object_data.end();
}
template <typename string_t>
inline typename basic_object<string_t>::const_iterator basic_object<string_t>::begin() const noexcept
{
return _object_data.begin();
}
template <typename string_t>
inline typename basic_object<string_t>::const_iterator basic_object<string_t>::end() const noexcept
{
return _object_data.end();
}
template <typename string_t>
inline typename basic_object<string_t>::const_iterator basic_object<string_t>::cbegin() const noexcept
{
return _object_data.cbegin();
}
template <typename string_t>
inline typename basic_object<string_t>::const_iterator basic_object<string_t>::cend() const noexcept
{
return _object_data.cend();
}
template <typename string_t>
inline basic_value<string_t>& basic_object<string_t>::operator[](const string_t& key)
{
return _object_data[key];
}
template <typename string_t>
inline basic_value<string_t>& basic_object<string_t>::operator[](string_t&& key)
{
return _object_data[std::move(key)];
}
template <typename string_t>
inline basic_object<string_t> basic_object<string_t>::operator|(const basic_object<string_t>& rhs) const&
{
basic_object<string_t> temp = *this;
temp._object_data.insert(rhs.begin(), rhs.end());
return temp;
}
template <typename string_t>
inline basic_object<string_t> basic_object<string_t>::operator|(basic_object<string_t>&& rhs) const&
{
basic_object<string_t> temp = *this;
// temp._object_data.merge(std::move(rhs._object_data));
temp._object_data.insert(std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return temp;
}
template <typename string_t>
inline basic_object<string_t> basic_object<string_t>::operator|(const basic_object<string_t>& rhs) &&
{
_object_data.insert(rhs.begin(), rhs.end());
return std::move(*this);
}
template <typename string_t>
inline basic_object<string_t> basic_object<string_t>::operator|(basic_object<string_t>&& rhs) &&
{
//_object_data.merge(std::move(rhs._object_data));
_object_data.insert(std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return std::move(*this);
}
template <typename string_t>
inline basic_object<string_t>& basic_object<string_t>::operator|=(const basic_object<string_t>& rhs)
{
_object_data.insert(rhs.begin(), rhs.end());
return *this;
}
template <typename string_t>
inline basic_object<string_t>& basic_object<string_t>::operator|=(basic_object<string_t>&& rhs)
{
_object_data.insert(std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return *this;
}
template <typename string_t>
inline bool basic_object<string_t>::operator==(const basic_object<string_t>& rhs) const
{
return _object_data == rhs._object_data;
}
template <typename ostream_t, typename string_t,
typename std_ostream_t =
std::basic_ostream<typename string_t::value_type, std::char_traits<typename string_t::value_type>>,
typename enable_t =
std::enable_if_t<std::is_same_v<std_ostream_t, ostream_t> || std::is_base_of_v<std_ostream_t, ostream_t>>>
ostream_t& operator<<(ostream_t& out, const basic_object<string_t>& obj)
{
out << obj.format();
return out;
}
} // namespace json

View File

@@ -0,0 +1,170 @@
#pragma once
#include <type_traits>
#include "types.hpp"
#include "utils.hpp"
namespace json
{
namespace _serialization_helper
{
template <typename in_t, typename serializer_t>
class is_serializable
{
template <typename U>
static auto test(int) -> decltype(std::declval<serializer_t>()(std::declval<U>()), std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<in_t>(0))::value;
};
struct empty_serializer
{
// sample:
// json::value operator()(const type_1&) const { return ...; }
// json::value operator()(const type_2&) const { return ...; }
// json::value operator()(const type_3&) const { return ...; }
};
template <typename T>
void unable_to_serialize()
{
static_assert(!sizeof(T), "Unable to serialize T. "
#ifdef _MSC_VER
"See T below: " __FUNCSIG__
#else
// "See T below: " __PRETTY_FUNCTION__
#endif
);
}
}
namespace _serialization_helper
{
template <typename out_t, typename deserializer_t, typename string_t = default_string_t>
class is_deserializable
{
template <typename U>
static auto test(int)
-> decltype(std::declval<deserializer_t>()(std::declval<basic_value<string_t>>(), std::declval<U&>()),
std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<out_t>(0))::value;
};
struct empty_deserializer
{
// sample:
// bool operator()(const json::value&, type_1&) const { return ...; }
// bool operator()(const json::value&, type_2&) const { return ...; }
// bool operator()(const json::value&, type_3&) const { return ...; }
};
template <typename T>
void unable_to_deserialize()
{
static_assert(!sizeof(T), "Unable to deserialize T. "
#ifdef _MSC_VER
"See T below: " __FUNCSIG__
#else
// "See T below: " __PRETTY_FUNCTION__
#endif
);
}
}
template <typename in_t, typename serializer_t = _serialization_helper::empty_serializer,
typename string_t = default_string_t>
basic_value<string_t> serialize(in_t&& in, const serializer_t& serializer = {})
{
if constexpr (_serialization_helper::is_serializable<in_t, serializer_t>::value) {
return serializer(std::forward<in_t>(in));
}
else if constexpr (std::is_constructible_v<basic_value<string_t>, in_t>) {
return basic_value<string_t>(std::forward<in_t>(in));
}
else if constexpr (_utils::is_collection<std::decay_t<in_t>>) {
basic_array<string_t> arr;
for (auto&& elem : in) {
using elem_t = decltype(elem);
auto j_elem = serialize<elem_t, serializer_t, string_t>(std::forward<elem_t>(elem), serializer);
arr.emplace_back(std::move(j_elem));
}
return arr;
}
else if constexpr (_utils::is_map<std::decay_t<in_t>>) {
basic_object<string_t> obj;
for (auto&& [key, elem] : in) {
using key_t = decltype(key);
using elem_t = decltype(elem);
auto j_elem = serialize<elem_t, serializer_t, string_t>(std::forward<elem_t>(elem), serializer);
obj.emplace(std::forward<key_t>(key), std::move(j_elem));
}
return obj;
}
else {
_serialization_helper::unable_to_serialize<in_t>();
}
}
template <typename out_t, typename deserializer_t = _serialization_helper::empty_deserializer,
typename string_t = default_string_t>
bool deserialize(const basic_value<string_t>& in, out_t& out, const deserializer_t& deserializer = {})
{
if constexpr (_serialization_helper::is_deserializable<out_t, deserializer_t>::value) {
return deserializer(in, out);
}
else if constexpr (std::is_constructible_v<out_t, basic_value<string_t>>) {
out = out_t(in);
return true;
}
else if constexpr (_utils::is_collection<std::decay_t<out_t>>) {
if (!in.is_array()) {
return false;
}
for (auto&& j_elem : in.as_array()) {
using elem_t = typename out_t::value_type;
elem_t elem {};
if (!deserialize<elem_t, deserializer_t, string_t>(j_elem, elem, deserializer)) {
return false;
}
if constexpr (_as_collection_helper::has_emplace_back<out_t>::value) {
out.emplace_back(std::move(elem));
}
else {
out.emplace(std::move(elem));
}
}
return true;
}
else if constexpr (_utils::is_map<std::decay_t<out_t>>) {
if (!in.is_object()) {
return false;
}
for (auto&& [key, j_elem] : in.as_object()) {
using elem_t = typename out_t::value_type;
elem_t elem {};
if (!deserialize<elem_t, deserializer_t, string_t>(j_elem, elem, deserializer)) {
return false;
}
out.emplace(std::move(elem));
}
return true;
}
else {
_serialization_helper::unable_to_deserialize<out_t>();
}
}
} // namespace json

View File

@@ -0,0 +1,5 @@
#pragma once
#include "array.hpp"
#include "object.hpp"
#include "value.hpp"

View File

@@ -0,0 +1,235 @@
#pragma once
#include <string>
#include <type_traits>
namespace json
{
template <typename string_t>
class basic_value;
template <typename string_t>
class basic_array;
template <typename string_t>
class basic_object;
using default_string_t = std::string;
using value = basic_value<default_string_t>;
using array = basic_array<default_string_t>;
using object = basic_object<default_string_t>;
using wvalue = basic_value<std::wstring>;
using warray = basic_array<std::wstring>;
using wobject = basic_object<std::wstring>;
}
namespace json::ext
{
template <typename T>
class jsonization
{
public:
// json::value to_json(const T&) const;
// bool check_json(const json::value&) const;
// bool from_json(const json::value&, T&) const;
};
}
namespace json::_utils
{
template <typename T>
using iterator_t = decltype(std::declval<T&>().begin());
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename T>
using iter_value_t = typename std::iterator_traits<remove_cvref_t<T>>::value_type;
template <typename R>
using range_value_t = iter_value_t<iterator_t<R>>;
template <typename T, typename = void>
constexpr bool is_string = false;
template <typename T>
constexpr bool is_string<T, std::void_t<typename T::traits_type>> =
std::is_same_v<typename T::traits_type, std::char_traits<typename T::value_type>>;
template <typename T, typename = void>
constexpr bool is_container = false;
template <typename T>
constexpr bool is_container<T, std::void_t<typename T::value_type, range_value_t<T>>> =
std::is_same_v<typename T::value_type, range_value_t<T>> && !is_string<T>;
template <typename T, typename = void>
constexpr bool is_map = false;
template <typename T>
constexpr bool is_map<T, std::void_t<typename T::key_type, typename T::mapped_type>> = is_container<T>;
template <typename T, typename = void>
constexpr bool is_collection = false;
template <typename T>
constexpr bool is_collection<T> = is_container<T> && !is_map<T>;
template <typename T>
class has_to_json_in_member
{
template <typename U>
static auto test(int) -> decltype(std::declval<U>().to_json(), std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename T>
class has_to_json_in_templ_spec
{
template <typename U>
static auto test(int) -> decltype(std::declval<ext::jsonization<U>>().to_json(std::declval<U>()), std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename T, typename string_t>
class has_check_json_in_member
{
template <typename U>
static auto test(int)
-> decltype(std::declval<U>().check_json(std::declval<json::basic_value<string_t>>()), std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename T, typename string_t>
class has_check_json_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<U>>().check_json(std::declval<json::basic_value<string_t>>()),
std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename T, typename string_t>
class has_from_json_in_member
{
template <typename U>
static auto test(int)
-> decltype(std::declval<U>().from_json(std::declval<json::basic_value<string_t>>()), std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename T, typename string_t>
class has_from_json_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<U>>().from_json(std::declval<json::basic_value<string_t>>(),
std::declval<U&>()),
std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename string_t>
static constexpr string_t unescape_string(const string_t& str)
{
using char_t = typename string_t::value_type;
string_t result {};
auto cur = str.cbegin();
auto end = str.cend();
auto no_escape_beg = cur;
char_t escape = 0;
for (; cur != end; ++cur) {
switch (*cur) {
case '"':
escape = '"';
break;
case '\\':
escape = '\\';
break;
case '\b':
escape = 'b';
break;
case '\f':
escape = 'f';
break;
case '\n':
escape = 'n';
break;
case '\r':
escape = 'r';
break;
case '\t':
escape = 't';
break;
default:
break;
}
if (escape) {
result += string_t(no_escape_beg, cur) + char_t('\\') + escape;
no_escape_beg = cur + 1;
escape = 0;
}
}
result += string_t(no_escape_beg, cur);
return result;
}
template <typename string_t>
static constexpr string_t true_string()
{
return { 't', 'r', 'u', 'e' };
}
template <typename string_t>
static constexpr string_t false_string()
{
return { 'f', 'a', 'l', 's', 'e' };
}
template <typename string_t>
static constexpr string_t null_string()
{
return { 'n', 'u', 'l', 'l' };
}
template <typename string_t, typename any_t>
string_t to_basic_string(any_t&& arg)
{
if constexpr (std::is_same_v<string_t, std::string>) {
return std::to_string(std::forward<any_t>(arg));
}
else if constexpr (std::is_same_v<string_t, std::wstring>) {
return std::to_wstring(std::forward<any_t>(arg));
}
else {
static_assert(!sizeof(any_t), "Unsupported type");
}
}
} // namespace json::_utils

View File

@@ -0,0 +1,994 @@
#pragma once
#include <cstddef>
#include <initializer_list>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <tuple>
#include <variant>
#include "exception.hpp"
#include "utils.hpp"
namespace json
{
template <typename string_t>
class basic_value
{
using array_ptr = std::unique_ptr<basic_array<string_t>>;
using object_ptr = std::unique_ptr<basic_object<string_t>>;
public:
enum class value_type : char
{
invalid,
null,
boolean,
string,
number,
array,
object
};
using var_t = std::variant<string_t, array_ptr, object_ptr>;
using char_t = typename string_t::value_type;
public:
basic_value();
basic_value(const basic_value<string_t>& rhs);
basic_value(basic_value<string_t>&& rhs) noexcept;
basic_value(bool b);
basic_value(int num);
basic_value(unsigned num);
basic_value(long num);
basic_value(unsigned long num);
basic_value(long long num);
basic_value(unsigned long long num);
basic_value(float num);
basic_value(double num);
basic_value(long double num);
basic_value(const char_t* str);
basic_value(string_t str);
basic_value(std::nullptr_t);
basic_value(basic_array<string_t> arr);
basic_value(basic_object<string_t> obj);
basic_value(std::initializer_list<typename basic_object<string_t>::value_type> init_list);
// Constructed from raw data
template <typename... args_t>
basic_value(value_type type, args_t&&... args);
template <typename collection_t,
std::enable_if_t<_utils::is_collection<collection_t> &&
std::is_constructible_v<typename basic_array<string_t>::value_type,
_utils::range_value_t<collection_t>>,
bool> = true>
basic_value(collection_t&& collection) : basic_value(basic_array<string_t>(std::forward<collection_t>(collection)))
{}
template <typename map_t, std::enable_if_t<_utils::is_map<map_t> &&
std::is_constructible_v<typename basic_object<string_t>::value_type,
_utils::range_value_t<map_t>>,
bool> = true>
basic_value(map_t&& map) : basic_value(basic_object<string_t>(std::forward<map_t>(map)))
{}
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_member<jsonization_t>::value, bool> = true>
basic_value(const jsonization_t& value) : basic_value(value.to_json())
{}
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_templ_spec<jsonization_t>::value, bool> = true>
basic_value(const jsonization_t& value) : basic_value(ext::jsonization<jsonization_t>().to_json(value))
{}
template <typename value_t, std::enable_if_t<!std::is_convertible_v<value_t, basic_value<string_t>>, bool> = true>
basic_value(value_t) = delete;
// I don't know if you want to convert char to string or number, so I delete these constructors.
basic_value(char) = delete;
basic_value(wchar_t) = delete;
basic_value(char16_t) = delete;
basic_value(char32_t) = delete;
~basic_value();
bool valid() const noexcept { return _type != value_type::invalid; }
bool empty() const noexcept { return is_null(); }
bool is_null() const noexcept { return _type == value_type::null; }
bool is_number() const noexcept { return _type == value_type::number; }
bool is_boolean() const noexcept { return _type == value_type::boolean; }
bool is_string() const noexcept { return _type == value_type::string; }
bool is_array() const noexcept { return _type == value_type::array; }
bool is_object() const noexcept { return _type == value_type::object; }
template <typename value_t>
bool is() const noexcept;
template <typename value_t>
bool all() const;
bool contains(const string_t& key) const;
bool contains(size_t pos) const;
bool exists(const string_t& key) const { return contains(key); }
bool exists(size_t pos) const { return contains(pos); }
value_type type() const noexcept { return _type; }
const basic_value<string_t>& at(size_t pos) const;
const basic_value<string_t>& at(const string_t& key) const;
bool erase(size_t pos);
bool erase(const string_t& key);
// Usage: get(key_1, key_2, ..., default_value);
template <typename... key_then_default_value_t>
auto get(key_then_default_value_t&&... keys_then_default_value) const;
template <typename value_t = basic_value<string_t>>
std::optional<value_t> find(size_t pos) const;
template <typename value_t = basic_value<string_t>>
std::optional<value_t> find(const string_t& key) const;
bool as_boolean() const;
int as_integer() const;
unsigned as_unsigned() const;
long as_long() const;
unsigned long as_unsigned_long() const;
long long as_long_long() const;
unsigned long long as_unsigned_long_long() const;
float as_float() const;
double as_double() const;
long double as_long_double() const;
string_t as_string() const;
const basic_array<string_t>& as_array() const;
const basic_object<string_t>& as_object() const;
template <typename value_t, template <typename...> typename collection_t = std::vector>
collection_t<value_t> as_collection() const;
template <typename value_t, template <typename...> typename map_t = std::map>
map_t<string_t, value_t> as_map() const;
template <typename value_t>
value_t as() const;
basic_array<string_t>& as_array();
basic_object<string_t>& as_object();
template <typename... args_t>
decltype(auto) emplace(args_t&&... args);
void clear() noexcept;
string_t dumps(std::optional<size_t> indent = std::nullopt) const { return indent ? format(*indent) : to_string(); }
// return raw string
string_t to_string() const;
string_t format(size_t indent = 4) const { return format(indent, 0); }
basic_value<string_t>& operator=(const basic_value<string_t>& rhs);
basic_value<string_t>& operator=(basic_value<string_t>&&) noexcept;
template <typename value_t, std::enable_if_t<std::is_convertible_v<value_t, basic_value<string_t>>, bool> = true>
basic_value<string_t>& operator=(value_t rhs)
{
return *this = basic_value<string_t>(std::move(rhs));
}
bool operator==(const basic_value<string_t>& rhs) const;
bool operator!=(const basic_value<string_t>& rhs) const { return !(*this == rhs); }
const basic_value<string_t>& operator[](size_t pos) const;
basic_value<string_t>& operator[](size_t pos);
basic_value<string_t>& operator[](const string_t& key);
basic_value<string_t>& operator[](string_t&& key);
basic_value<string_t> operator|(const basic_object<string_t>& rhs) const&;
basic_value<string_t> operator|(basic_object<string_t>&& rhs) const&;
basic_value<string_t> operator|(const basic_object<string_t>& rhs) &&;
basic_value<string_t> operator|(basic_object<string_t>&& rhs) &&;
basic_value<string_t>& operator|=(const basic_object<string_t>& rhs);
basic_value<string_t>& operator|=(basic_object<string_t>&& rhs);
basic_value<string_t> operator+(const basic_array<string_t>& rhs) const&;
basic_value<string_t> operator+(basic_array<string_t>&& rhs) const&;
basic_value<string_t> operator+(const basic_array<string_t>& rhs) &&;
basic_value<string_t> operator+(basic_array<string_t>&& rhs) &&;
basic_value<string_t>& operator+=(const basic_array<string_t>& rhs);
basic_value<string_t>& operator+=(basic_array<string_t>&& rhs);
explicit operator bool() const { return as_boolean(); }
explicit operator int() const { return as_integer(); }
explicit operator unsigned() const { return as_unsigned(); }
explicit operator long() const { return as_long(); }
explicit operator unsigned long() const { return as_unsigned_long(); }
explicit operator long long() const { return as_long_long(); }
explicit operator unsigned long long() const { return as_unsigned_long_long(); }
explicit operator float() const { return as_float(); }
explicit operator double() const { return as_double(); }
explicit operator long double() const { return as_long_double(); }
explicit operator string_t() const { return as_string(); }
explicit operator basic_array<string_t>() const { return as_array(); }
explicit operator basic_object<string_t>() const { return as_object(); }
template <typename value_t, template <typename...> typename collection_t = std::vector,
std::enable_if_t<_utils::is_collection<collection_t<value_t>>, bool> = true>
explicit operator collection_t<value_t>() const
{
return as_collection<value_t, collection_t>();
}
template <typename value_t, template <typename...> typename map_t = std::map,
std::enable_if_t<_utils::is_map<map_t<string_t, value_t>>, bool> = true>
explicit operator map_t<string_t, value_t>() const
{
return as_map<value_t, map_t>();
}
template <typename jsonization_t,
std::enable_if_t<_utils::has_from_json_in_member<jsonization_t, string_t>::value, bool> = true>
explicit operator jsonization_t() const
{
jsonization_t dst {};
if (!dst.from_json(*this)) {
throw exception("Wrong JSON");
}
return dst;
}
template <typename jsonization_t,
std::enable_if_t<_utils::has_from_json_in_templ_spec<jsonization_t, string_t>::value, bool> = true>
explicit operator jsonization_t() const
{
jsonization_t dst {};
if (!ext::jsonization<jsonization_t>().from_json(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
}
private:
friend class basic_array<string_t>;
friend class basic_object<string_t>;
string_t format(size_t indent, size_t indent_times) const;
static var_t deep_copy(const var_t& src);
template <typename... key_then_default_value_t, size_t... keys_indexes_t>
auto get(std::tuple<key_then_default_value_t...> keys_then_default_value,
std::index_sequence<keys_indexes_t...>) const;
template <typename value_t, typename first_key_t, typename... rest_keys_t>
auto get_helper(const value_t& default_value, first_key_t&& first, rest_keys_t&&... rest) const;
template <typename value_t, typename unique_key_t>
auto get_helper(const value_t& default_value, unique_key_t&& first) const;
const string_t& as_basic_type_str() const;
string_t& as_basic_type_str();
value_type _type = value_type::null;
var_t _raw_data;
};
template <typename string_t>
inline basic_value<string_t>::basic_value() = default;
template <typename string_t>
inline basic_value<string_t>::basic_value(const basic_value<string_t>& rhs)
: _type(rhs._type), _raw_data(deep_copy(rhs._raw_data))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(basic_value<string_t>&& rhs) noexcept = default;
template <typename string_t>
inline basic_value<string_t>::basic_value(bool b)
: _type(value_type::boolean),
_raw_data(string_t(b ? _utils::true_string<string_t>() : _utils::false_string<string_t>()))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(int num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(unsigned num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(long num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(unsigned long num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(long long num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(unsigned long long num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(float num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(double num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(long double num)
: _type(value_type::number), _raw_data(_utils::to_basic_string<string_t>(num))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(const char_t* str) : _type(value_type::string), _raw_data(string_t(str))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(string_t str) : _type(value_type::string), _raw_data(std::move(str))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(std::nullptr_t) : _type(value_type::null)
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(basic_array<string_t> arr)
: _type(value_type::array), _raw_data(std::make_unique<basic_array<string_t>>(std::move(arr)))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(basic_object<string_t> obj)
: _type(value_type::object), _raw_data(std::make_unique<basic_object<string_t>>(std::move(obj)))
{}
template <typename string_t>
inline basic_value<string_t>::basic_value(std::initializer_list<typename basic_object<string_t>::value_type> init_list)
: _type(value_type::object), _raw_data(std::make_unique<basic_object<string_t>>(init_list))
{}
// for Pimpl
template <typename string_t>
inline basic_value<string_t>::~basic_value() = default;
template <typename string_t>
template <typename value_t>
inline bool basic_value<string_t>::is() const noexcept
{
if constexpr (std::is_same_v<basic_value<string_t>, value_t>) {
return true;
}
else if constexpr (_utils::has_check_json_in_member<value_t, string_t>::value) {
return value_t().check_json(*this);
}
else if constexpr (_utils::has_check_json_in_templ_spec<value_t, string_t>::value) {
return ext::jsonization<value_t>().check_json(*this);
}
else if constexpr (std::is_same_v<bool, value_t>) {
return is_boolean();
}
else if constexpr (std::is_arithmetic_v<value_t>) {
return is_number();
}
else if constexpr (std::is_constructible_v<string_t, value_t>) {
return is_string();
}
else if constexpr (std::is_same_v<basic_array<string_t>, value_t>) {
return is_array();
}
else if constexpr (_utils::is_collection<value_t>) {
return is_array() && all<typename value_t::value_type>();
}
else if constexpr (std::is_same_v<basic_object<string_t>, value_t>) {
return is_object();
}
else if constexpr (_utils::is_map<value_t>) {
return is_object() && std::is_constructible_v<string_t, typename value_t::key_type> &&
all<typename value_t::mapped_type>();
}
else {
static_assert(!sizeof(value_t), "Unsupported type");
}
}
template <typename string_t>
inline bool basic_value<string_t>::contains(const string_t& key) const
{
return is_object() && as_object().contains(key);
}
template <typename string_t>
inline bool basic_value<string_t>::contains(size_t pos) const
{
return is_array() && as_array().contains(pos);
}
template <typename string_t>
inline const basic_value<string_t>& basic_value<string_t>::at(size_t pos) const
{
return as_array().at(pos);
}
template <typename string_t>
inline const basic_value<string_t>& basic_value<string_t>::at(const string_t& key) const
{
return as_object().at(key);
}
template <typename string_t>
inline bool basic_value<string_t>::erase(size_t pos)
{
return as_array().erase(pos);
}
template <typename string_t>
inline bool basic_value<string_t>::erase(const string_t& key)
{
return as_object().erase(key);
}
template <typename string_t>
template <typename... key_then_default_value_t>
inline auto basic_value<string_t>::get(key_then_default_value_t&&... keys_then_default_value) const
{
return get(std::forward_as_tuple(keys_then_default_value...),
std::make_index_sequence<sizeof...(keys_then_default_value) - 1> {});
}
template <typename string_t>
template <typename... key_then_default_value_t, size_t... keys_indexes_t>
inline auto basic_value<string_t>::get(std::tuple<key_then_default_value_t...> keys_then_default_value,
std::index_sequence<keys_indexes_t...>) const
{
constexpr unsigned long default_value_index = sizeof...(key_then_default_value_t) - 1;
return get_helper(std::get<default_value_index>(keys_then_default_value),
std::get<keys_indexes_t>(keys_then_default_value)...);
}
template <typename string_t>
template <typename value_t, typename first_key_t, typename... rest_keys_t>
inline auto basic_value<string_t>::get_helper(const value_t& default_value, first_key_t&& first,
rest_keys_t&&... rest) const
{
if constexpr (std::is_constructible_v<string_t, first_key_t>) {
return is_object() ? as_object().get_helper(default_value, std::forward<first_key_t>(first),
std::forward<rest_keys_t>(rest)...)
: default_value;
}
else if constexpr (std::is_integral_v<std::decay_t<first_key_t>>) {
return is_array() ? as_array().get_helper(default_value, std::forward<first_key_t>(first),
std::forward<rest_keys_t>(rest)...)
: default_value;
}
else {
static_assert(!sizeof(first_key_t), "Parameter must be integral or string_t constructible");
}
}
template <typename string_t>
template <typename value_t, typename unique_key_t>
inline auto basic_value<string_t>::get_helper(const value_t& default_value, unique_key_t&& first) const
{
if constexpr (std::is_constructible_v<string_t, unique_key_t>) {
return is_object() ? as_object().get_helper(default_value, std::forward<unique_key_t>(first)) : default_value;
}
else if constexpr (std::is_integral_v<std::decay_t<unique_key_t>>) {
return is_array() ? as_array().get_helper(default_value, std::forward<unique_key_t>(first)) : default_value;
}
else {
static_assert(!sizeof(unique_key_t), "Parameter must be integral or string_t constructible");
}
}
template <typename string_t>
template <typename value_t>
inline std::optional<value_t> basic_value<string_t>::find(size_t pos) const
{
return is_array() ? as_array().template find<value_t>(pos) : std::nullopt;
}
template <typename string_t>
template <typename value_t>
inline std::optional<value_t> basic_value<string_t>::find(const string_t& key) const
{
return is_object() ? as_object().template find<value_t>(key) : std::nullopt;
}
template <typename string_t>
inline bool basic_value<string_t>::as_boolean() const
{
if (is_boolean()) {
if (const string_t& b_str = as_basic_type_str(); b_str == _utils::true_string<string_t>()) {
return true;
}
else if (b_str == _utils::false_string<string_t>()) {
return false;
}
else {
throw exception("Unknown Parse Error");
}
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline int basic_value<string_t>::as_integer() const
{
if (is_number()) {
return std::stoi(as_basic_type_str());
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline unsigned basic_value<string_t>::as_unsigned() const
{
// I don't know why there is no std::stou.
return static_cast<unsigned>(as_unsigned_long());
}
template <typename string_t>
inline long basic_value<string_t>::as_long() const
{
if (is_number()) {
return std::stol(as_basic_type_str());
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline unsigned long basic_value<string_t>::as_unsigned_long() const
{
if (is_number()) {
return std::stoul(as_basic_type_str());
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline long long basic_value<string_t>::as_long_long() const
{
if (is_number()) {
return std::stoll(as_basic_type_str());
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline unsigned long long basic_value<string_t>::as_unsigned_long_long() const
{
if (is_number()) {
return std::stoull(as_basic_type_str());
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline float basic_value<string_t>::as_float() const
{
if (is_number()) {
return std::stof(as_basic_type_str());
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline double basic_value<string_t>::as_double() const
{
if (is_number()) {
return std::stod(as_basic_type_str());
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline long double basic_value<string_t>::as_long_double() const
{
if (is_number()) {
return std::stold(as_basic_type_str());
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline string_t basic_value<string_t>::as_string() const
{
if (is_string()) {
return as_basic_type_str();
}
else {
throw exception("Wrong Type");
}
}
template <typename string_t>
inline const basic_array<string_t>& basic_value<string_t>::as_array() const
{
if (is_array()) {
return *std::get<array_ptr>(_raw_data);
}
throw exception("Wrong Type");
}
template <typename string_t>
inline const basic_object<string_t>& basic_value<string_t>::as_object() const
{
if (is_object()) {
return *std::get<object_ptr>(_raw_data);
}
throw exception("Wrong Type or data empty");
}
template <typename string_t>
inline basic_array<string_t>& basic_value<string_t>::as_array()
{
if (empty()) {
*this = basic_array<string_t>();
}
if (is_array()) {
return *std::get<array_ptr>(_raw_data);
}
throw exception("Wrong Type");
}
template <typename string_t>
inline basic_object<string_t>& basic_value<string_t>::as_object()
{
if (empty()) {
*this = basic_object<string_t>();
}
if (is_object()) {
return *std::get<object_ptr>(_raw_data);
}
throw exception("Wrong Type or data empty");
}
template <typename string_t>
template <typename value_t>
inline value_t basic_value<string_t>::as() const
{
if constexpr (std::is_same_v<basic_value<string_t>, value_t>) {
return *this;
}
else if constexpr (_utils::has_from_json_in_member<value_t, string_t>::value) {
value_t dst {};
if (!dst.from_json(*this)) {
throw exception("Wrong JSON");
}
return dst;
}
else if constexpr (_utils::has_from_json_in_templ_spec<value_t, string_t>::value) {
value_t dst {};
if (!ext::jsonization<value_t>().from_json(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
}
else {
return static_cast<value_t>(*this);
}
}
template <typename string_t>
inline const string_t& basic_value<string_t>::as_basic_type_str() const
{
return std::get<string_t>(_raw_data);
}
template <typename string_t>
inline string_t& basic_value<string_t>::as_basic_type_str()
{
return std::get<string_t>(_raw_data);
}
template <typename string_t>
template <typename... args_t>
inline decltype(auto) basic_value<string_t>::emplace(args_t&&... args)
{
constexpr bool is_array_args = std::is_constructible_v<typename basic_array<string_t>::value_type, args_t...>;
constexpr bool is_object_args = std::is_constructible_v<typename basic_object<string_t>::value_type, args_t...>;
static_assert(is_array_args || is_object_args, "Args can not constructure a array or object value");
if constexpr (is_array_args) {
return as_array().emplace_back(std::forward<args_t>(args)...);
}
else if constexpr (is_object_args) {
return as_object().emplace(std::forward<args_t>(args)...);
}
}
template <typename string_t>
inline void basic_value<string_t>::clear() noexcept
{
*this = basic_value<string_t>();
}
template <typename string_t>
inline string_t basic_value<string_t>::to_string() const
{
switch (_type) {
case value_type::null:
return _utils::null_string<string_t>();
case value_type::boolean:
case value_type::number:
return as_basic_type_str();
case value_type::string:
return char_t('"') + _utils::unescape_string(as_basic_type_str()) + char_t('"');
case value_type::array:
return as_array().to_string();
case value_type::object:
return as_object().to_string();
default:
throw exception("Unknown basic_value Type");
}
}
template <typename string_t>
inline string_t basic_value<string_t>::format(size_t indent, size_t indent_times) const
{
switch (_type) {
case value_type::null:
case value_type::boolean:
case value_type::number:
case value_type::string:
return to_string();
case value_type::array:
return as_array().format(indent, indent_times);
case value_type::object:
return as_object().format(indent, indent_times);
default:
throw exception("Unknown basic_value Type");
}
}
template <typename string_t>
template <typename value_t>
inline bool basic_value<string_t>::all() const
{
if (is_array()) {
return as_array().template all<value_t>();
}
else if (is_object()) {
return as_object().template all<value_t>();
}
else {
return false;
}
}
template <typename string_t>
template <typename value_t, template <typename...> typename collection_t>
inline collection_t<value_t> basic_value<string_t>::as_collection() const
{
return as_array().template as_collection<value_t, collection_t>();
}
template <typename string_t>
template <typename value_t, template <typename...> typename map_t>
inline map_t<string_t, value_t> basic_value<string_t>::as_map() const
{
return as_object().template as_map<value_t, map_t>();
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator=(const basic_value<string_t>& rhs)
{
_type = rhs._type;
_raw_data = deep_copy(rhs._raw_data);
return *this;
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator=(basic_value<string_t>&& rhs) noexcept = default;
template <typename string_t>
inline bool basic_value<string_t>::operator==(const basic_value<string_t>& rhs) const
{
if (_type != rhs._type) return false;
switch (_type) {
case value_type::null:
return rhs.is_null();
case value_type::boolean:
case value_type::number:
case value_type::string:
return _raw_data == rhs._raw_data;
case value_type::array:
return as_array() == rhs.as_array();
case value_type::object:
return as_object() == rhs.as_object();
default:
throw exception("Unknown basic_value Type");
}
}
template <typename string_t>
inline const basic_value<string_t>& basic_value<string_t>::operator[](size_t pos) const
{
// basic_array not support to create by operator[]
return as_array()[pos];
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator[](size_t pos)
{
// basic_array not support to create by operator[]
return as_array()[pos];
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator[](const string_t& key)
{
if (empty()) {
*this = basic_object<string_t>();
}
return as_object()[key];
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator[](string_t&& key)
{
if (empty()) {
*this = basic_object<string_t>();
}
return as_object()[std::move(key)];
}
template <typename string_t>
inline basic_value<string_t> basic_value<string_t>::operator|(const basic_object<string_t>& rhs) const&
{
return as_object() | rhs;
}
template <typename string_t>
inline basic_value<string_t> basic_value<string_t>::operator|(basic_object<string_t>&& rhs) const&
{
return as_object() | std::move(rhs);
}
template <typename string_t>
inline basic_value<string_t> basic_value<string_t>::operator|(const basic_object<string_t>& rhs) &&
{
return std::move(as_object()) | rhs;
}
template <typename string_t>
inline basic_value<string_t> basic_value<string_t>::operator|(basic_object<string_t>&& rhs) &&
{
return std::move(as_object()) | std::move(rhs);
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator|=(const basic_object<string_t>& rhs)
{
as_object() |= rhs;
return *this;
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator|=(basic_object<string_t>&& rhs)
{
as_object() |= std::move(rhs);
return *this;
}
template <typename string_t>
inline basic_value<string_t> basic_value<string_t>::operator+(const basic_array<string_t>& rhs) const&
{
return as_array() + rhs;
}
template <typename string_t>
inline basic_value<string_t> basic_value<string_t>::operator+(basic_array<string_t>&& rhs) const&
{
return as_array() + std::move(rhs);
}
template <typename string_t>
inline basic_value<string_t> basic_value<string_t>::operator+(const basic_array<string_t>& rhs) &&
{
return std::move(as_array()) + rhs;
}
template <typename string_t>
inline basic_value<string_t> basic_value<string_t>::operator+(basic_array<string_t>&& rhs) &&
{
return std::move(as_array()) + std::move(rhs);
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator+=(const basic_array<string_t>& rhs)
{
as_array() += rhs;
return *this;
}
template <typename string_t>
inline basic_value<string_t>& basic_value<string_t>::operator+=(basic_array<string_t>&& rhs)
{
as_array() += std::move(rhs);
return *this;
}
template <typename string_t>
template <typename... args_t>
inline basic_value<string_t>::basic_value(value_type type, args_t&&... args)
: _type(type), _raw_data(std::forward<args_t>(args)...)
{
static_assert(std::is_constructible_v<var_t, args_t...>, "Parameter can't be used to construct a var_t");
}
template <typename string_t>
inline typename basic_value<string_t>::var_t basic_value<string_t>::deep_copy(const var_t& src)
{
var_t dst;
if (const auto string_ptr = std::get_if<string_t>(&src)) {
dst = *string_ptr;
}
else if (const auto arr_ptr = std::get_if<array_ptr>(&src)) {
dst = std::make_unique<basic_array<string_t>>(**arr_ptr);
}
else if (const auto obj_ptr = std::get_if<object_ptr>(&src)) {
dst = std::make_unique<basic_object<string_t>>(**obj_ptr);
}
else {
// maybe invalid_value
}
return dst;
}
template <typename ostream_t, typename string_t,
typename std_ostream_t =
std::basic_ostream<typename string_t::value_type, std::char_traits<typename string_t::value_type>>,
typename =
std::enable_if_t<std::is_same_v<std_ostream_t, ostream_t> || std::is_base_of_v<std_ostream_t, ostream_t>>>
ostream_t& operator<<(ostream_t& out, const basic_value<string_t>& val)
{
out << val.format();
return out;
}
} // namespace json

6
3rdparty/include/meojson/json.hpp vendored Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include "common/serialization.hpp"
#include "common/types.hpp"
#include "parser/parser.hpp"
#include "reflection/jsonization.hpp"

4
3rdparty/include/meojson/json5.hpp vendored Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
#include "common/types.hpp"
#include "parser5/parser5.hpp"

View File

@@ -0,0 +1,112 @@
#pragma once
#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
#include <bit>
namespace json::_bitops
{
using std::countl_one;
using std::countl_zero;
using std::countr_one;
using std::countr_zero;
inline constexpr bool is_little_endian()
{
return std::endian::native == std::endian::little;
}
}
#else
#include <cstdint>
namespace json::_bitops
{
#if defined(__GNUC__) || defined(__clang__)
inline constexpr int countl_zero(uint32_t x)
{
if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) return x == 0 ? 32 : __builtin_clz(x);
if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) return x == 0 ? 32 : __builtin_clzl(x);
return x == 0 ? 32 : __builtin_clzll(x);
}
inline constexpr int countr_zero(uint32_t x)
{
if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) return x == 0 ? 32 : __builtin_ctz(x);
if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) return x == 0 ? 32 : __builtin_ctzl(x);
return x == 0 ? 32 : __builtin_ctzll(x);
}
inline constexpr int countl_zero(uint64_t x)
{
return x == 0 ? 64 : __builtin_clzll(x);
}
inline constexpr int countr_zero(uint64_t x)
{
return x == 0 ? 64 : __builtin_ctzll(x);
}
#elif defined(_MSC_VER)
#ifdef __AVX2__
// lzcnt intrinsics is not constexpr
inline int countl_zero(uint32_t x)
{
return __lzcnt(x);
}
inline int countr_zero(uint32_t x)
{
return _tzcnt_u32(x);
}
inline int countl_zero(uint64_t x)
{
return (int)__lzcnt64(x);
}
inline int countr_zero(uint64_t x)
{
return (int)_tzcnt_u64(x);
}
#else
inline constexpr int countl_zero(uint32_t x)
{
unsigned long index = 0;
return _BitScanReverse(&index, x) ? 31 - index : 32;
}
inline constexpr int countr_zero(uint32_t x)
{
unsigned long index = 0;
return _BitScanForward(&index, x) ? index : 32;
}
inline constexpr int countl_zero(uint64_t x)
{
unsigned long index = 0;
return _BitScanReverse64(&index, x) ? 63 - index : 64;
}
inline constexpr int countr_zero(uint64_t x)
{
unsigned long index = 0;
return _BitScanForward64(&index, x) ? index : 64;
}
#endif // __AVX2__
#else // compiler
#error "bring your own bit counting implementation"
#endif
inline int countl_one(uint32_t x)
{
return countl_zero(~x);
}
inline int countr_one(uint32_t x)
{
return countr_zero(~x);
}
inline int countl_one(uint64_t x)
{
return countl_zero(~x);
}
inline int countr_one(uint64_t x)
{
return countr_zero(~x);
}
// no constexpr endian awareness before C++20
inline bool is_little_endian()
{
union {
uint32_t u32;
uint8_t u8;
} u = { 0x01020304 };
return u.u8 == 4;
}
} // namespace json::_bitops
#endif // C++20

View File

@@ -0,0 +1,141 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <type_traits>
#include "bitops.hpp"
#if defined(__GNUC__) || defined(__clang__)
#define __packed_bytes_strong_inline __attribute__((always_inline))
#elif defined(_MSC_VER)
#define __packed_bytes_strong_inline __forceinline
#else
#define __packed_bytes_strong_inline inline
#endif
namespace json::_packed_bytes
{
struct packed_bytes_trait_none
{
static constexpr bool available = false;
};
template <size_t N>
struct packed_bytes
{
using traits = packed_bytes_trait_none;
};
}
#if defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP)
#include "packed_bytes_x86.hpp"
#elif defined(__ARM_NEON) || defined(_M_ARM) || defined(_M_ARM64)
#include "packed_bytes_arm.hpp"
#endif
namespace json::_packed_bytes
{
struct packed_bytes_trait_uint64
{
static constexpr bool available = sizeof(void*) >= 8;
static constexpr auto step = 8;
using value_type = uint64_t;
__packed_bytes_strong_inline static value_type load_unaligned(const void* ptr)
{
value_type result;
memcpy((void*)&result, ptr, 8);
return result;
}
__packed_bytes_strong_inline static value_type less(value_type x, uint8_t n)
{
return (((x)-UINT64_C(0x0101010101010101) * (n)) & ~(x)&UINT64_C(0x8080808080808080));
}
__packed_bytes_strong_inline static value_type is_zero_memberwise(value_type v)
{
return (((v)-UINT64_C(0x0101010101010101)) & ~(v)&UINT64_C(0x8080808080808080));
}
__packed_bytes_strong_inline static bool is_all_zero(value_type v) { return v == UINT64_C(0); }
__packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n)
{
return is_zero_memberwise((x) ^ (UINT64_C(0x0101010101010101) * (n)));
}
__packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) { return a | b; }
__packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x)
{
if (_bitops::is_little_endian())
return _bitops::countr_zero(x) / 8;
else
return _bitops::countl_zero(x) / 8;
}
};
struct packed_bytes_trait_uint32
{
static constexpr bool available = true;
static constexpr auto step = 4;
using value_type = uint32_t;
__packed_bytes_strong_inline static value_type load_unaligned(const void* ptr)
{
value_type result;
memcpy((void*)&result, ptr, 4);
return result;
}
__packed_bytes_strong_inline static value_type less(value_type x, uint8_t n)
{
return (((x) - ~UINT32_C(0) / 255 * (n)) & ~(x) & ~UINT32_C(0) / 255 * 128);
}
__packed_bytes_strong_inline static value_type is_zero_memberwise(value_type v)
{
return (((v)-UINT32_C(0x01010101)) & ~(v)&UINT32_C(0x80808080));
;
}
__packed_bytes_strong_inline static bool is_all_zero(value_type v) { return v == UINT32_C(0); }
__packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n)
{
return is_zero_memberwise((x) ^ (~UINT32_C(0) / 255 * (n)));
}
__packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) { return a | b; }
__packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x)
{
if (_bitops::is_little_endian())
return _bitops::countr_zero(x) / 8;
else
return _bitops::countl_zero(x) / 8;
}
};
template <>
struct packed_bytes<8>
{
using traits = packed_bytes_trait_uint64;
};
template <>
struct packed_bytes<4>
{
using traits = packed_bytes_trait_uint32;
};
template <size_t N>
using packed_bytes_trait = typename packed_bytes<N>::traits;
using packed_bytes_trait_max =
std::conditional_t<packed_bytes_trait<32>::available, packed_bytes_trait<32>,
std::conditional_t<packed_bytes_trait<16>::available, packed_bytes_trait<16>,
std::conditional_t<packed_bytes_trait<8>::available, packed_bytes_trait<8>,
packed_bytes_trait<4>>>>;
} // namespace json::_packed_bytes

View File

@@ -0,0 +1,62 @@
#pragma once
// current NEON implementation doesn't outperform 64-bit scalar implementation
#ifdef MEOJSON_ENABLE_NEON
#include "packed_bytes.hpp"
#include <arm_neon.h>
#if defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64)
#define __packed_bytes_trait_arm64
#endif
namespace json::_packed_bytes
{
struct packed_bytes_trait_neon
{
static constexpr bool available = true;
static constexpr auto step = 16;
using value_type = uint8x16_t;
__packed_bytes_strong_inline static value_type load_unaligned(const void* ptr) { return vld1q_u8((uint8_t*)ptr); }
__packed_bytes_strong_inline static value_type less(value_type x, uint8_t n)
{
auto bcast = vdupq_n_u8(n);
auto is_less = vcltq_u8(x, bcast);
return is_less;
}
__packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n) { return vceqq_u8(x, vdupq_n_u8(n)); }
__packed_bytes_strong_inline static value_type equal(value_type x, value_type y) { return vceqq_u8(x, y); }
__packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) { return vorrq_u8(a, b); }
__packed_bytes_strong_inline static bool is_all_zero(value_type x)
{
#ifdef __packed_bytes_trait_arm64
return vmaxvq_u8(x) == 0;
#else
auto fold64 = vorr_u64(vget_high_u64(vreinterpretq_u8_u64(x), 0), vget_low_u64(vreinterpretq_u8_u64(x), 1));
auto fold32 = vget_lane_u32(vreinterpret_u64_u32(fold64), 0) | vget_lane_u32(vreinterpret_u64_u32(fold64), 1);
return fold32 == 0;
#endif
}
__packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x)
{
// https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
auto cmp = equal(x, 0);
auto res = vshrn_n_u16(cmp, 4);
auto mask64 = vget_lane_u64(vreinterpret_u64_u8(res), 0);
return _bitops::countr_one(mask64) >> 2;
}
};
template <>
struct packed_bytes<16>
{
using traits = packed_bytes_trait_neon;
};
}
#endif

View File

@@ -0,0 +1,125 @@
#pragma once
#include "packed_bytes.hpp"
#include <emmintrin.h>
#if defined(__SSE4_1__) || defined(__AVX2__) || defined(_MSC_VER)
// MSVC enables all SSE4.1 intrinsics by default
#include <smmintrin.h>
#endif
namespace json::_packed_bytes
{
struct packed_bytes_trait_sse
{
static constexpr bool available = true;
static constexpr auto step = 16;
using value_type = __m128i;
__packed_bytes_strong_inline static value_type load_unaligned(const void* ptr)
{
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(ptr));
}
__packed_bytes_strong_inline static value_type less(value_type x, uint8_t n)
{
auto bcast = _mm_set1_epi8(static_cast<char>(n));
auto all1 = _mm_set1_epi8(-1);
auto max_with_n = _mm_max_epu8(x, bcast);
auto is_greater_or_equal = _mm_cmpeq_epi8(max_with_n, x);
auto is_less = _mm_andnot_si128(is_greater_or_equal, all1);
return is_less;
}
__packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n)
{
return _mm_cmpeq_epi8(x, _mm_set1_epi8(static_cast<char>(n)));
}
__packed_bytes_strong_inline static value_type equal(value_type x, value_type y) { return _mm_cmpeq_epi8(x, y); }
__packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) { return _mm_or_si128(a, b); }
__packed_bytes_strong_inline static bool is_all_zero(value_type x)
{
#if defined(__SSE4_1__) || defined(__AVX2__) || defined(_MSC_VER)
// SSE4.1 path
return !!_mm_testz_si128(x, x);
#else
// SSE2 path
auto cmp = _mm_cmpeq_epi8(x, _mm_set1_epi8(0));
auto mask = (uint16_t)_mm_movemask_epi8(cmp);
return mask == UINT16_C(0xFFFF);
#endif
}
__packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x)
{
auto cmp = _mm_cmpeq_epi8(x, _mm_set1_epi8(0));
auto mask = (uint16_t)_mm_movemask_epi8(cmp);
return _bitops::countr_one((uint32_t)mask);
}
};
template <>
struct packed_bytes<16>
{
using traits = packed_bytes_trait_sse;
};
}
#ifdef __AVX2__
#include <immintrin.h>
namespace json::_packed_bytes
{
struct packed_bytes_trait_avx2
{
static constexpr bool available = true;
static constexpr auto step = 32;
using value_type = __m256i;
__packed_bytes_strong_inline static value_type load_unaligned(const void* ptr)
{
return _mm256_loadu_si256(reinterpret_cast<const __m256i*>(ptr));
}
__packed_bytes_strong_inline static value_type less(value_type x, uint8_t n)
{
auto bcast = _mm256_set1_epi8(static_cast<char>(n));
auto all1 = _mm256_set1_epi8(-1);
auto max_with_n = _mm256_max_epu8(x, bcast);
auto is_greater_or_equal = _mm256_cmpeq_epi8(max_with_n, x);
auto is_less = _mm256_andnot_si256(is_greater_or_equal, all1);
return is_less;
}
__packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n)
{
return _mm256_cmpeq_epi8(x, _mm256_set1_epi8(static_cast<char>(n)));
}
__packed_bytes_strong_inline static value_type equal(value_type x, value_type y) { return _mm256_cmpeq_epi8(x, y); }
__packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b)
{
return _mm256_or_si256(a, b);
}
__packed_bytes_strong_inline static bool is_all_zero(value_type x) { return (bool)_mm256_testz_si256(x, x); }
__packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x)
{
auto cmp = _mm256_cmpeq_epi8(x, _mm256_set1_epi8(0));
auto mask = (uint32_t)_mm256_movemask_epi8(cmp);
// AVX512 alternative: _mm_cmpeq_epi8_mask
return _bitops::countr_one(mask);
}
};
template <>
struct packed_bytes<32>
{
using traits = packed_bytes_trait_avx2;
};
}
#endif

View File

@@ -0,0 +1,627 @@
#pragma once
#include <fstream>
#include <optional>
#include <ostream>
#include <string>
#include <tuple>
#include "../common/types.hpp"
#include "packed_bytes.hpp"
namespace json
{
// ****************************
// * parser declare *
// ****************************
template <typename string_t = default_string_t, typename parsing_t = void,
typename accel_traits = _packed_bytes::packed_bytes_trait_max>
class parser
{
public:
using parsing_iter_t = typename parsing_t::const_iterator;
public:
~parser() noexcept = default;
static std::optional<basic_value<string_t>> parse(const parsing_t& content);
private:
parser(parsing_iter_t cbegin, parsing_iter_t cend) noexcept : _cur(cbegin), _end(cend) { ; }
std::optional<basic_value<string_t>> parse();
basic_value<string_t> parse_value();
basic_value<string_t> parse_null();
basic_value<string_t> parse_boolean();
basic_value<string_t> parse_number();
// parse and return a basic_value<string_t> whose type is value_type::string
basic_value<string_t> parse_string();
basic_value<string_t> parse_array();
basic_value<string_t> parse_object();
// parse and return a string_t
std::optional<string_t> parse_stdstring();
bool skip_string_literal_with_accel();
bool skip_whitespace() noexcept;
bool skip_digit();
private:
parsing_iter_t _cur;
parsing_iter_t _end;
};
// ***************************
// * utils declare *
// ***************************
template <typename parsing_t>
auto parse(const parsing_t& content);
template <typename char_t>
auto parse(char_t* content);
template <typename istream_t,
typename = std::enable_if_t<std::is_base_of_v<std::basic_istream<typename istream_t::char_type>, istream_t>>>
auto parse(istream_t& istream, bool check_bom);
template <typename ifstream_t = std::ifstream, typename path_t = void>
auto open(const path_t& path, bool check_bom = false);
namespace literals
{
value operator""_json(const char* str, size_t len);
wvalue operator""_json(const wchar_t* str, size_t len);
value operator""_jvalue(const char* str, size_t len);
wvalue operator""_jvalue(const wchar_t* str, size_t len);
array operator""_jarray(const char* str, size_t len);
warray operator""_jarray(const wchar_t* str, size_t len);
object operator""_jobject(const char* str, size_t len);
wobject operator""_jobject(const wchar_t* str, size_t len);
}
template <typename string_t = default_string_t>
const basic_value<string_t> invalid_value();
// *************************
// * parser impl *
// *************************
template <typename string_t, typename parsing_t, typename accel_traits>
inline std::optional<basic_value<string_t>> parser<string_t, parsing_t, accel_traits>::parse(const parsing_t& content)
{
return parser<string_t, parsing_t, accel_traits>(content.cbegin(), content.cend()).parse();
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline std::optional<basic_value<string_t>> parser<string_t, parsing_t, accel_traits>::parse()
{
if (!skip_whitespace()) {
return std::nullopt;
}
basic_value<string_t> result_value;
switch (*_cur) {
case '[':
result_value = parse_array();
break;
case '{':
result_value = parse_object();
break;
default: // A JSON payload should be an basic_object or basic_array
return std::nullopt;
}
if (!result_value.valid()) {
return std::nullopt;
}
// After the parsing is complete, there should be no more content other than
// spaces behind
if (skip_whitespace()) {
return std::nullopt;
}
return result_value;
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_value()
{
switch (*_cur) {
case 'n':
return parse_null();
case 't':
case 'f':
return parse_boolean();
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return parse_number();
case '"':
return parse_string();
case '[':
return parse_array();
case '{':
return parse_object();
default:
return invalid_value<string_t>();
}
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_null()
{
for (const auto& ch : _utils::null_string<string_t>()) {
if (_cur != _end && *_cur == ch) {
++_cur;
}
else {
return invalid_value<string_t>();
}
}
return basic_value<string_t>();
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_boolean()
{
switch (*_cur) {
case 't':
for (const auto& ch : _utils::true_string<string_t>()) {
if (_cur != _end && *_cur == ch) {
++_cur;
}
else {
return invalid_value<string_t>();
}
}
return true;
case 'f':
for (const auto& ch : _utils::false_string<string_t>()) {
if (_cur != _end && *_cur == ch) {
++_cur;
}
else {
return invalid_value<string_t>();
}
}
return false;
default:
return invalid_value<string_t>();
}
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_number()
{
const auto first = _cur;
if (*_cur == '-') {
++_cur;
}
// numbers cannot have leading zeroes
if (_cur != _end && *_cur == '0' && _cur + 1 != _end && std::isdigit(*(_cur + 1))) {
return invalid_value<string_t>();
}
if (!skip_digit()) {
return invalid_value<string_t>();
}
if (*_cur == '.') {
++_cur;
if (!skip_digit()) {
return invalid_value<string_t>();
}
}
if (*_cur == 'e' || *_cur == 'E') {
if (++_cur == _end) {
return invalid_value<string_t>();
}
if (*_cur == '+' || *_cur == '-') {
++_cur;
}
if (!skip_digit()) {
return invalid_value<string_t>();
}
}
return basic_value<string_t>(basic_value<string_t>::value_type::number, string_t(first, _cur));
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_string()
{
auto string_opt = parse_stdstring();
if (!string_opt) {
return invalid_value<string_t>();
}
return basic_value<string_t>(basic_value<string_t>::value_type::string, std::move(string_opt).value());
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_array()
{
if (*_cur == '[') {
++_cur;
}
else {
return invalid_value<string_t>();
}
if (!skip_whitespace()) {
return invalid_value<string_t>();
}
else if (*_cur == ']') {
++_cur;
// empty basic_array
return basic_array<string_t>();
}
typename basic_array<string_t>::raw_array result;
while (true) {
if (!skip_whitespace()) {
return invalid_value<string_t>();
}
basic_value<string_t> val = parse_value();
if (!val.valid() || !skip_whitespace()) {
return invalid_value<string_t>();
}
result.emplace_back(std::move(val));
if (*_cur == ',') {
++_cur;
}
else {
break;
}
}
if (skip_whitespace() && *_cur == ']') {
++_cur;
}
else {
return invalid_value<string_t>();
}
return basic_array<string_t>(std::move(result));
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_object()
{
if (*_cur == '{') {
++_cur;
}
else {
return invalid_value<string_t>();
}
if (!skip_whitespace()) {
return invalid_value<string_t>();
}
else if (*_cur == '}') {
++_cur;
// empty basic_object
return basic_object<string_t>();
}
typename basic_object<string_t>::raw_object result;
while (true) {
if (!skip_whitespace()) {
return invalid_value<string_t>();
}
auto key_opt = parse_stdstring();
if (key_opt && skip_whitespace() && *_cur == ':') {
++_cur;
}
else {
return invalid_value<string_t>();
}
if (!skip_whitespace()) {
return invalid_value<string_t>();
}
basic_value<string_t> val = parse_value();
if (!val.valid() || !skip_whitespace()) {
return invalid_value<string_t>();
}
result.emplace(std::move(*key_opt), std::move(val));
if (*_cur == ',') {
++_cur;
}
else {
break;
}
}
if (skip_whitespace() && *_cur == '}') {
++_cur;
}
else {
return invalid_value<string_t>();
}
return basic_object<string_t>(std::move(result));
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline std::optional<string_t> parser<string_t, parsing_t, accel_traits>::parse_stdstring()
{
if (*_cur == '"') {
++_cur;
}
else {
return std::nullopt;
}
string_t result;
auto no_escape_beg = _cur;
while (_cur != _end) {
if constexpr (sizeof(*_cur) == 1 && accel_traits::available) {
if (!skip_string_literal_with_accel()) {
return std::nullopt;
}
}
switch (*_cur) {
case '\t':
case '\r':
case '\n':
return std::nullopt;
case '\\': {
result += string_t(no_escape_beg, _cur++);
if (_cur == _end) {
return std::nullopt;
}
switch (*_cur) {
case '"':
result.push_back('"');
break;
case '\\':
result.push_back('\\');
break;
case '/':
result.push_back('/');
break;
case 'b':
result.push_back('\b');
break;
case 'f':
result.push_back('\f');
break;
case 'n':
result.push_back('\n');
break;
case 'r':
result.push_back('\r');
break;
case 't':
result.push_back('\t');
break;
// case 'u':
// result.push_back('\u');
// break;
default:
// Illegal backslash escape
return std::nullopt;
}
no_escape_beg = ++_cur;
break;
}
case '"': {
result += string_t(no_escape_beg, _cur++);
return result;
}
default:
++_cur;
break;
}
}
return std::nullopt;
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline bool parser<string_t, parsing_t, accel_traits>::skip_string_literal_with_accel()
{
if constexpr (sizeof(*_cur) != 1) {
return false;
}
while (_end - _cur >= accel_traits::step) {
auto pack = accel_traits::load_unaligned(&(*_cur));
auto result = accel_traits::less(pack, 32);
result = accel_traits::bitwise_or(result, accel_traits::equal(pack, static_cast<uint8_t>('"')));
result = accel_traits::bitwise_or(result, accel_traits::equal(pack, static_cast<uint8_t>('\\')));
if (accel_traits::is_all_zero(result)) {
_cur += accel_traits::step;
}
else {
auto index = accel_traits::first_nonzero_byte(result);
_cur += index;
break;
}
}
return _cur != _end;
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline bool parser<string_t, parsing_t, accel_traits>::skip_whitespace() noexcept
{
while (_cur != _end) {
switch (*_cur) {
case ' ':
case '\t':
case '\r':
case '\n':
++_cur;
break;
case '\0':
return false;
default:
return true;
}
}
return false;
}
template <typename string_t, typename parsing_t, typename accel_traits>
inline bool parser<string_t, parsing_t, accel_traits>::skip_digit()
{
// At least one digit
if (_cur != _end && std::isdigit(*_cur)) {
++_cur;
}
else {
return false;
}
while (_cur != _end && std::isdigit(*_cur)) {
++_cur;
}
if (_cur != _end) {
return true;
}
else {
return false;
}
}
// *************************
// * utils impl *
// *************************
template <typename parsing_t>
auto parse(const parsing_t& content)
{
using string_t = std::basic_string<typename parsing_t::value_type>;
return parser<string_t, parsing_t>::parse(content);
}
template <typename char_t>
auto parse(char_t* content)
{
return parse(std::basic_string_view<std::decay_t<char_t>> { content });
}
template <typename istream_t, typename _>
auto parse(istream_t& ifs, bool check_bom)
{
using string_t = std::basic_string<typename istream_t::char_type>;
ifs.seekg(0, std::ios::end);
auto file_size = ifs.tellg();
ifs.seekg(0, std::ios::beg);
string_t str(file_size, '\0');
ifs.read(str.data(), file_size);
if (check_bom) {
using uchar = unsigned char;
static constexpr uchar Bom_0 = 0xEF;
static constexpr uchar Bom_1 = 0xBB;
static constexpr uchar Bom_2 = 0xBF;
if (str.size() >= 3 && static_cast<uchar>(str.at(0)) == Bom_0 && static_cast<uchar>(str.at(1)) == Bom_1 &&
static_cast<uchar>(str.at(2)) == Bom_2) {
str.assign(str.begin() + 3, str.end());
}
}
return parse(str);
}
template <typename ifstream_t, typename path_t>
auto open(const path_t& filepath, bool check_bom)
{
using char_t = typename ifstream_t::char_type;
using string_t = std::basic_string<char_t>;
using json_t = json::basic_value<string_t>;
using return_t = std::optional<json_t>;
ifstream_t ifs(filepath, std::ios::in);
if (!ifs.is_open()) {
return return_t(std::nullopt);
}
auto opt = parse(ifs, check_bom);
ifs.close();
return opt;
}
namespace literals
{
inline value operator""_json(const char* str, size_t len)
{
return operator""_jvalue(str, len);
}
inline wvalue operator""_json(const wchar_t* str, size_t len)
{
return operator""_jvalue(str, len);
}
inline value operator""_jvalue(const char* str, size_t len)
{
return parse(std::string_view(str, len)).value_or(value());
}
inline wvalue operator""_jvalue(const wchar_t* str, size_t len)
{
return parse(std::wstring_view(str, len)).value_or(wvalue());
}
inline array operator""_jarray(const char* str, size_t len)
{
auto val = parse(std::string_view(str, len)).value_or(value());
return val.is_array() ? val.as_array() : array();
}
inline warray operator""_jarray(const wchar_t* str, size_t len)
{
auto val = parse(std::wstring_view(str, len)).value_or(wvalue());
return val.is_array() ? val.as_array() : warray();
}
inline object operator""_jobject(const char* str, size_t len)
{
auto val = parse(std::string_view(str, len)).value_or(value());
return val.is_object() ? val.as_object() : object();
}
inline wobject operator""_jobject(const wchar_t* str, size_t len)
{
auto val = parse(std::wstring_view(str, len)).value_or(wvalue());
return val.is_object() ? val.as_object() : wobject();
}
} // namespace literals
template <typename string_t>
const basic_value<string_t> invalid_value()
{
return basic_value<string_t>(basic_value<string_t>::value_type::invalid, typename basic_value<string_t>::var_t());
}
} // namespace json

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,241 @@
#pragma once
#include <string>
#include <type_traits>
#include "../common/types.hpp"
namespace json::_jsonization_helper
{
struct next_is_optional_t
{};
struct va_arg_end
{};
struct dumper
{
template <typename var_t, typename... rest_t>
json::value _to_json(const char* key, const var_t& var, rest_t&&... rest) const
{
json::value result = _to_json(std::forward<rest_t>(rest)...);
result.emplace(key, var);
return result;
}
template <typename... rest_t>
json::value _to_json(const char*, next_is_optional_t, rest_t&&... rest) const
{
return _to_json(std::forward<rest_t>(rest)...);
}
json::value _to_json(va_arg_end) const { return {}; }
};
struct checker
{
template <typename var_t, typename... rest_t>
bool _check_json(const json::value& in, std::string& error_key, const char* key, const var_t&,
rest_t&&... rest) const
{
auto opt = in.find(key);
if (!opt || !opt->is<var_t>()) {
error_key = key;
return false;
}
return _check_json(in, error_key, std::forward<rest_t>(rest)...);
}
template <typename var_t, typename... rest_t>
bool _check_json(const json::value& in, std::string& error_key, const char*, next_is_optional_t, const char* key,
const var_t&, rest_t&&... rest) const
{
auto opt = in.find(key);
if (opt) {
if (!opt->is<var_t>()) {
error_key = key;
return false;
}
} // next_is_optional_t, ignore key not found
return _check_json(in, error_key, std::forward<rest_t>(rest)...);
}
bool _check_json(const json::value&, std::string&, va_arg_end) const { return true; }
};
struct loader
{
template <typename var_t, typename... rest_t>
bool _from_json(const json::value& in, std::string& error_key, const char* key, var_t& var, rest_t&&... rest) const
{
auto opt = in.find(key);
if (!opt || !opt->is<var_t>()) {
error_key = key;
return false;
}
var = std::move(opt)->as<var_t>();
return _from_json(in, error_key, std::forward<rest_t>(rest)...);
}
template <typename var_t, typename... rest_t>
bool _from_json(const json::value& in, std::string& error_key, const char*, next_is_optional_t, const char* key,
var_t& var, rest_t&&... rest) const
{
auto opt = in.find(key);
if (opt) {
if (!opt->is<var_t>()) {
error_key = key;
return false;
}
var = std::move(opt)->as<var_t>();
} // next_is_optional_t, ignore key not found
return _from_json(in, error_key, std::forward<rest_t>(rest)...);
}
bool _from_json(const json::value&, std::string&, va_arg_end) const { return true; }
};
} // namespace json::_jsonization_helper
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif
namespace json::_private_macro
{
#define _MEOJSON_STRINGIZE(arg) _MEOJSON_STRINGIZE1(arg)
#define _MEOJSON_STRINGIZE1(arg) _MEOJSON_STRINGIZE2(arg)
#define _MEOJSON_STRINGIZE2(arg) #arg
#define _MEOJSON_CONCATENATE(arg1, arg2) _MEOJSON_CONCATENATE1(arg1, arg2)
#define _MEOJSON_CONCATENATE1(arg1, arg2) _MEOJSON_CONCATENATE2(arg1, arg2)
#define _MEOJSON_CONCATENATE2(arg1, arg2) arg1##arg2
#define _MEOJSON_EXPAND(x) x
#define _MEOJSON_FOR_EACH_0(pred, ...)
#define _MEOJSON_FOR_EACH_1(pred, x, ...) pred(x)
#define _MEOJSON_FOR_EACH_2(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_1(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_3(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_2(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_4(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_3(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_5(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_4(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_6(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_5(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_7(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_6(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_8(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_7(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_9(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_8(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_10(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_9(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_11(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_10(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_12(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_11(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_13(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_12(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_14(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_13(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_15(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_14(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_16(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_15(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_17(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_16(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_18(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_17(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_19(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_18(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_20(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_19(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_21(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_20(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_22(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_21(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_23(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_22(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_24(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_23(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_25(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_24(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_26(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_25(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_27(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_26(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_28(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_27(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_29(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_28(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_30(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_29(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_31(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_30(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_32(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_31(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_33(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_32(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_34(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_33(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_35(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_34(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_36(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_35(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_37(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_36(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_38(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_37(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_39(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_38(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_40(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_39(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_41(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_40(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_42(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_41(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_43(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_42(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_44(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_43(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_45(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_44(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_46(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_45(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_47(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_46(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_48(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_47(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_49(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_48(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_50(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_49(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_51(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_50(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_52(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_51(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_53(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_52(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_54(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_53(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_55(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_54(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_56(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_55(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_57(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_56(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_58(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_57(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_59(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_58(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_60(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_59(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_61(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_60(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_62(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_61(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_63(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_62(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH_64(pred, x, ...) pred(x) _MEOJSON_EXPAND(_MEOJSON_FOR_EACH_63(pred, __VA_ARGS__))
#define _MEOJSON_ARG_COUNT(...) \
_MEOJSON_EXPAND(_MEOJSON_ARG_COUNT1(0, ##__VA_ARGS__, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \
49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \
30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, \
11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define _MEOJSON_ARG_COUNT1(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, \
_20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, \
_38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, \
_56, _57, _58, _59, _60, _61, _62, _63, _64, N, ...) \
N
#define _MEOJSON_FOR_EACH_(N, pred, ...) _MEOJSON_EXPAND(_MEOJSON_CONCATENATE(_MEOJSON_FOR_EACH_, N)(pred, __VA_ARGS__))
#define _MEOJSON_FOR_EACH(pred, ...) \
_MEOJSON_EXPAND(_MEOJSON_FOR_EACH_(_MEOJSON_EXPAND(_MEOJSON_ARG_COUNT(__VA_ARGS__)), pred, __VA_ARGS__))
#define _MEOJSON_VARNAME(x) _MEOJSON_CONCATENATE(_meojson_jsonization_, x)
#define _MEOJSON_KEY_VALUE(x) _MEOJSON_STRINGIZE(x), x,
} // namespace json::_private_macro
#define MEO_TOJSON(...) \
json::value to_json() const \
{ \
return json::_jsonization_helper::dumper()._to_json(_MEOJSON_EXPAND( \
_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__)) json::_jsonization_helper::va_arg_end {}); \
}
#define MEO_CHECKJSON(...) \
bool check_json(const json::value& _MEOJSON_VARNAME(in)) const \
{ \
std::string _MEOJSON_VARNAME(error_key); \
return check_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key)); \
} \
bool check_json(const json::value& _MEOJSON_VARNAME(in), std::string& _MEOJSON_VARNAME(error_key)) const \
{ \
return json::_jsonization_helper::checker()._check_json( \
_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key), \
_MEOJSON_EXPAND(_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__)) \
json::_jsonization_helper::va_arg_end {}); \
}
#define MEO_FROMJSON(...) \
bool from_json(const json::value& _MEOJSON_VARNAME(in)) \
{ \
std::string _MEOJSON_VARNAME(error_key); \
return from_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key)); \
} \
bool from_json(const json::value& _MEOJSON_VARNAME(in), std::string& _MEOJSON_VARNAME(error_key)) \
{ \
return json::_jsonization_helper::loader()._from_json( \
_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key), \
_MEOJSON_EXPAND(_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__)) \
json::_jsonization_helper::va_arg_end {}); \
}
#define MEO_JSONIZATION(...) \
_MEOJSON_EXPAND(MEO_TOJSON(__VA_ARGS__)) \
_MEOJSON_EXPAND(MEO_CHECKJSON(__VA_ARGS__)) \
_MEOJSON_EXPAND(MEO_FROMJSON(__VA_ARGS__))
#define MEO_OPT json::_jsonization_helper::next_is_optional_t {},
#if defined(__clang__)
#pragma clang diagnostic pop // -Wgnu-zero-variadic-macro-arguments
#endif

View File

@@ -1,268 +1,15 @@
## v6.3.2
### 拉电线不知天地为何物 | Highlight
本次版本更新真的是千呼万唤始出来,也恰逢新年版本和 PC 端的发布,我们在这个版本提供了对 PC 端的初步支持,也合并了 SideStory 「辞岁行」 的数据。
#### Windows 端一键长草任务配置重构
本次更新我们对 Windows 端 「一键长草」 任务配置进行了重构,解决了一些痛点。
现在牛牛支持添加多个重复类型任务,并支持重命名。现在你可以通过 「一键长草」 任务左下角的加号按钮添加多个同类型任务,并可以通过拖拽任务来调整任务执行顺序,以及右键点击任务右侧的齿轮图标重命名和删除任务,这样你就可以更灵活地管理任务。
牛牛也新增了理智作战任务的周计划设置,可按星期数(如星期三、星期二、星期五等)设置任务是否执行。这样你就可以结合上面提到的添加任务,<u>在理智作战任务前添加另一个理智作战任务,并将其设置为仅打当期剿灭、仅在星期一运行</u>,更多用法等你探索~
另外,在牛牛执行一键长草流程时,各个任务的勾选框会根据其运行状态以不同颜色进行区分,直观反馈任务被跳过、已完成、运行中或执行失败等状态。
注:由于牛牛现已支持 「理智作战」 周计划并可添加多个同类型任务,本次更新**移除了原有 「剿灭作战」 任务失败后自动尝试下一个已开放的备选逻辑**,如有需要请参照上方的示例添加对应任务。
#### PC 端初步支持
本次更新,牛牛已支持控制 PC 端明日方舟了~不过有以下限制:
* 由于 Windows 的限制PC 端在被控制时**不可以处于最小化窗口状态**,否则游戏画面不会被渲染,也就无法被牛牛识别;
* 由于 Win32 API 和某反作弊软件的限制,牛牛在控制 PC 端时**必须要直接控制鼠标(不能模拟点击)**,期间不建议你使用鼠标。
另外由于维护人手有限PC 端的适配可能长期处于不稳定状态,部分功能可能出现异常或暂时无法使用。
我们非常欢迎社区开发者协助适配并提交改进,共同完善对 PC 端的支持。
#### 其他方面
* 我们继续对界园肉鸽进行适配,包括 DLC 2 的更新支持;
* 我们优化了自动编队的识别,现在支持对作业要求的精英化、等级和模组的识别,如果发现你的干员不满足要求将会有对应提示;
* 相应的,干员识别功能也支持显示精英化和等级了。
----
#### Restructuring of *Farming* Configuration for Windows
In this update, we have restructured the *Farming* configuration for Windows, addressing several pain points.
MAA now supports adding multiple tasks of the same type and renaming them. You can add multiple tasks of the same type using the plus button at the bottom left of the *Farming* panel, adjust the execution order by dragging tasks, and rename or delete tasks by right-clicking the gear icon on the right side of each task. This allows for more flexible task management.
MAA also supports weekly schedule settings for *Combat* tasks, allowing you to specify on which days of the week (e.g., Monday, Thursday) a task should run. For example, you can add another *Combat* task before your existing one and set it to only run Current Annihilation on Mondays. More usage scenarios await your exploration!
Additionally, when MAA executes the *Farming* process, the checkboxes for each task will be color-coded to indicate their status: skipped, completed, running, or failed, providing intuitive feedback.
Note: Since MAA now supports weekly schedules for *Combat* tasks and allows adding multiple tasks of the same type, this update **removes the previous logic that automatically attempted the next available alternative stage after a "Current Annihilation" task failure**. If needed, please refer to the example above to add corresponding tasks.
#### Preliminary PC Client Support (**[CN ONLY]**)
In this update, MAA now supports controlling the PC client of Arknights.
However, due to limited maintenance resources, PC client adaptation may remain unstable for a long period, and some features may experience issues or be temporarily unavailable.
We warmly welcome community developers to assist with adaptation and submit improvements to collectively enhance PC client support.
#### Other Updates
* Continued adaptation for *Sui's Garden of Grotesqueries*, including support for DLC 2 updates (**[CN ONLY]**)
* Optimized recognition in *Auto Squad*, which now supports identifying elite, level, and module requirements. If your operators do not meet the requirements, you will receive corresponding prompts.
* The operator recognition feature now also supports displaying elite and level information.
----
以下是详细内容:
## v6.3.2
### 新增 | New
* 睡眠休眠前增加倒计时弹窗 @ABA2396
* 生息演算支援道具名称改为全字匹配 @ABA2396
### 改进 | Improved
* 干员识别按照 稀有度->精英化->等级->潜能->Id 排序 @ABA2396
* 优化识别工具中列表小于 5 时的显示效果 @ABA2396
* optimize templates @Constrat
* increase score for AddOperatorTradeAggressive EN @Constrat
### 修复 | Fix
* 4 星公招时间显示错误 @ABA2396
* 刷理智自定义剿灭关卡在特定情况的绑定错误 @status102
* 在禁用刷理智-自定义剿灭关卡后需要手动切换任务以刷新列表中显示的剿灭关卡, 尽管不影响实际运行 @status102
* 会客室任务失败重试时尝试关闭赠送线索界面 @ABA2396
* 肉鸽开局种子无法通过 Wpf 设置两位数难度 @status102
* 小工具-公招识别 3~5 星选择时间无法修改 @status102
* 小工具-干员识别 开始识别按钮启用状态绑定失效 @status102
* 自动公招任务 3~5 星确认时间在修改后未能刷新 UI @status102
* EN AveMujica terminal new template @Constrat
* EN IS tentative fix increasing roi for encounter and more replace regex @Constrat
* English text overflow on long strings in middle column @Constrat
## v5.4.1
### 文档 | Docs
* 删除一些过时内容 (#15700) @JasonHuang79
* website docs (#9287) @ABA2396 @Rbqwow @Constrat @wangl-cc @HX3N @SherkeyXD
* 修改win7相关问题描述&更新运行库 @Rbqwow
* fix a bad link @Rbqwow
* make i18n warnings show only once @Rbqwow
* 修复字体和评论区分类 @Rbqwow
### 其他 | Other
### For Overseas
* 增加 id 格式验证 @ABA2396
* 明确周计划和定时更新使用游戏时间而非现实时间 @ABA2396
* 移除老主线导航逻辑 (#15644) @SherkeyXD
* 繁中服「夢鄉」主題 (#15699) @momomochi987
* Yostar servers SSS#9 and ShamareThumbnail (#15732) @Manicsteiner @Constrat
#### YostarEN
## v6.3.1
### 新增 | New
* OperNameAnalyzer 支持左对齐检测 (#15682) @ABA2396
### 改进 | Improved
* 配置迁移自动为启用备选且首个关卡为剿灭模式的刷理智任务自动添加额外的刷理智任务 @status102
* 忽略干员属性要求 不再允许跳过精英化要求 (#15697) @status102 @Constrat @HX3N
### 修复 | Fix
* JP AveMujica event ocr @Manicsteiner
* EN IS StageRefresh @Constrat
* i'm kinda stupid @Constrat
* update refresh node EN IS5 @Constrat
* EN IS fix trader store templates AGAIN @Constrat
* EN Yu OCR for Yutenji @Constrat
* 自动编队选择技能时点击到技能描述/技能范围 @status102
* 自动编队期间如有干员属性要求被忽略, 则禁用自动化投票 @status102
### 文档 | Docs
* 更新 api 链接 @ABA2396
### 其他 | Other
* UserAdditional 非 json 格式返回原始字符串 @status102
* 使用屏幕宽高常量代替硬编码 @status102
* 屏幕外的干员跳过技能就绪检测 @status102
* 調整繁中服界園肉鴿 OCR (2/?) @momomochi987
* color [INF] for smoke testing as well @Constrat
* 调整单字干员正则 @ABA2396
* 調整繁中服界園肉鴿 OCR (#15678) @momomochi987
* 删除干员名开头/末尾的_<> @ABA2396
* 添加响石、赤刃明霄陈基建技能加成 (#15674) @drway
* 保全派驻自动战斗使用二值化结果识别干员名 @ABA2396
* 删除无用的正则替换 @ABA2396
* 自动战斗使用二值化结果识别干员名 @ABA2396
* 仅在 VS Code 中添加辅助项目 (#15669) @status102
* JP OperNameAnalyzer specialParams @Manicsteiner
## v6.3.0
### 新增 | New
* 支持 PC 端明日方舟 (#15407) @MistEO @ABA2396
* SideStory「辞岁行」导航及地图数据更新 @SherkeyXD @status102
* 界园肉鸽 DLC 2 分队更新、通宝数据更新、可选难度提高至 18、支持指定种子开局 (#15588) @SherkeyXD @status102 @HX3N @Manicsteiner
* 繁中服界園肉鴿初步適配 (#15605) @momomochi987
* 干员识别支持显示精英化、等级与潜能 @ABA2396
* 自动编队识别精英化、等级及模组要求 (#15161) @status102 @Manicsteiner @Constrat @HX3N
* 芯片本支持显示库存数量 @ABA2396
* 新增注入弹窗不再提醒的勾选框,勾选后使用软件渲染 @ABA2396
* WpfGui 清空缓存按钮 (#15582) @soundofautumn @Constrat @HX3N @momomochi987
* 自定干员名称无效时的错误处理及本地化支持 (#15556) @yali-hzy @HX3N
* 设置指引增加右键重命名/删除提示 @ABA2396
* 新 Config 加载时移除旧 Config 中不存在的配置 @status102
* 日志中额外记录 TaskChain 与 taskId ~~免得有人把 Fight 改成开始唤醒~~ @ABA2396
* YostarEN/JP/KR Dreamland、JieGarden 和 AveMujica 主题支持 @Constrat @Manicsteiner @HX3N
### 改进 | Improved
* Wpf 一键长草任务配置重构,支持添加多个同类型任务、重命名和周计划设置 (#15385) @status102 @ABA2396 @momomochi987 @HX3N
* 刷理智过期关卡逻辑/样式优化 @status102 @ABA2396
* 关卡候选列表刷新及关卡选择下拉列表刷新 (#15562) @status102 @HX3N @Constrat @Manicsteiner @momomochi987
* 优化自动战斗界面布局 (#15512) @yali-hzy
* 优化任务设置按钮悬浮提示 @ABA2396
* 优化设置右键菜单布局 @ABA2396
* 优化干员识别、仓库识别显示 @ABA2396
* 配置迁移检查优化与简化 @status102 @ABA2396
* 开始唤醒任务未设置账号切换时,禁用手动切换按钮 @status102
* 自动战斗掉线重连、自动肉鸽在战斗结束前延迟「停止」动作添加多任务共用提示 @ABA2396 @status102
* 剿灭卡使用到上限时不报错停止 @ABA2396
* 剿灭关卡通过 ends_with 判断 @ABA2396
* 刷理智任务高级设置 UI 调整选项顺序和显示优化 @status102
* 自动战斗自动编队检查`干员等级&精英化``技能等级`拆分 @status102
* 自动战斗不支持技能重置说明中,干员名遵循干员名语言设置 @status102
* 自动战斗作业列表使用相对路径代替绝对路径 @status102
* TaskQueue 重命名与移除时显示任务序号 @status102
* TaskQueue 任务开始&完成显示修改后的任务名 @status102
* 追加自定干员允许不切换技能 @status102
* 存在 crash.log 时Wpf 尝试获取 dumps 文件 (#15432) @status102
* 移除过时的参数兼容 @status102
* 消除部分编译警告 (#15578) @yali-hzy
* NumberOcrReplace 新增规则 (#14186) @Manicsteiner
### 修复 | Fix
* 配置迁移相关问题修复:迁移后切换回原配置、删除配置时未删除 .new、多配置用户删除 Default 配置时迁移异常、剩余理智启用状态和关卡选择迁移问题 @status102 @ABA2396
* 刷理智任务相关修复:切换刷理智任务时读取错误关卡列表、运行时不允许添加关卡 @status102
* 自动编队相关修复:识别技能等级匹配失败、干员技能描述过长时点击位置错误、禁用快速选中以修复外服干员技能描述过长的错误选中、干员等级跨精英化时判断出错、干员等级不足 i18n 未启用、干员组干员未解析精英化及等级属性、先兼容旧作业中不合理的技能选择 @status102
* 公招加速券识别问题修复 @status102
* LoadApiCache 路径拼接错误 @ABA2396
* 主线导航及外服主线导航问题修复 @SherkeyXD @ABA2396
* 在赠送线索时弹出上次线索交流结束的提示时无法返回 @ABA2396
* 粘贴作业集代码后下方的链接未重置为作业站链接 @ABA2396
* 修复移动已打开设置的任务后,当前的设置面板无法继续修改的问题 @ABA2396
* 萨米肉鸽刷开局功能异常 @ABA2396
* 肉鸽烧水分队切换界面后错误重置 @status102
* 保证通宝优先级未定义时不会加载崩溃 fallback 到默认值 @SherkeyXD
* SEH 错误终止运行 @status102
* 启动 MAA 时若没有任何任务,则追加一套默认任务 @status102
* 启动客户端绑定失效问题修复 @status102
* 关卡列表显示不刷新 @status102
* 选中完成后动作时添加新任务未能隐藏完成后动作设置 UI @status102
* 刷理智使用源石 CheckBox 勾选后不生效 @status102
* 退出 MAA 时重置变量不再刷新 UI @status102
* 初始化 StartEnabled 属性为 true (#15596) @yali-hzy
* 自动战斗切换活动类型未清空解析缓存 @status102
* 修复任务出错日志可能晚于任务完成日志显示的问题 @ABA2396
* 开始干员识别前重置潜能状态 @ABA2396
* 修复生息演算商店无法正常购买皮肤的问题 (#15585) @drway
* 手动输入关卡名时,不移除过期关卡 @status102
* 过期关卡重置模式补充自动迁移 @status102
* OR 关卡掉落界面关卡名识别问题 @ABA2396
* EX 关符合时 1 被识别为 | @ABA2396
* NumberOcrReplace 移除`|``/` (#15625) @status102
* 移除单字干员 ocr 替换中的 +*?避免误判 @Saratoga-Official
* 涤火杰西卡识别 @ABA2396
* 日服酒神、見字祠识别 @Saratoga-Official
* YostarKR Roguelike@ChooseOperConfirm @HX3N
* YostarKR use ' ' in ocrReplace to preserve '\n' for InfrastTrainingTask @HX3N
* EN Greyy Alter regex @Constrat
* EN IS6 encounter @Constrat
* EN IS TradeInvest templates text font change @Constrat
* EN IS ShoppingConfirm text font change @Constrat
* EN refresh node template @Constrat
* JP AT minigame confirm (#15427) @Manicsteiner
* JP JieGardenStrategyChange @Saratoga-Official
* add MaaWin32ControlUnit to nightly build (#15447) @Manicsteiner
### 文档 | Docs
* 集成文档统一格式,同时显示 field-group 和示例代码 (#15409) @ABA2396 @Manicsteiner @Constrat @momomochi987
* 繁中文件大更新 (#15480) @momomochi987
* 修正开发文档中的格式错误及笔误 (#15516) @yali-hzy
* 自动战斗作业文档干员技能值范围补上 0 @status102
### 其他 | Other
* 作业集 Parse 后不删除历史记录,避免错过作业抛出的错误 @ABA2396
* 辞岁行地图 2026-02-10 Map 更新 @status102
* 繁中服不上报企鹅物流 @ABA2396
* devcontainer 适配 CMakePresets.json (#15606) @lucienshawls
* 移除不再使用的 VirtualizingWrapPanel 与 NoAutomationDataGrid @ABA2396
* 调整清理图片缓存样式,增加提示 @ABA2396
* 补全缺少的翻译 @ABA2396
* 优化 emoji @ABA2396
* 自动战斗-自动编队干员不支持技能说明国际化 (#15609) @status102 @Constrat @HX3N @Manicsteiner
* 自动战斗编队技能等级不足 i18nCN 使用理智药及碎石文案 (#15435) @status102 @HX3N
* 增加借助战 OF-1 在后续刷理智选择`当前/上次`导致禁用时的输出 (#15478) @status102 @Manicsteiner @Constrat @HX3N @ABA2396 @momomochi987
* remove regex from `text` field in EN Sui IS @Constrat
* KR AnnouncementNotFinishedConfirm、ReceptionOptionsRequireInfrast、CreditFightWhenOF-1Warning、MiniGame ConversationRoom and HoneyFruit 支持 @HX3N
* EN minigame honeyfruit、IS6 tip 支持 @Constrat
* JP MiniGame HoneyFruit 支持 @Manicsteiner
* YostarJP ocr fix for roguelike @Manicsteiner
* manual data for txwy @Constrat
* optimize templates @Constrat
* fix casing typo and related context (#15656) @ittuann @Daydreamer114
* YoStarEN resolution warning on client switch (#9539) @Constrat @ABA2396

View File

@@ -1,57 +1,191 @@
cmake_minimum_required(VERSION 3.28)
cmake_minimum_required(VERSION 3.21)
project(MaaAssistantArknights)
project(MAA)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
endif ()
option(BUILD_WPF_GUI "build MaaWpfGui" ${WIN32})
option(BUILD_DEBUG_DEMO "build debug demo" OFF)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
option(BUILD_TEST "build a demo" OFF)
option(BUILD_XCFRAMEWORK "build xcframework for macOS app" OFF)
option(BUILD_SMOKE_TEST "build smoke_test" OFF)
option(BUILD_UNIVERSAL "build both arm64 and x86_64 on macOS" OFF)
option(INSTALL_PYTHON "install python ffi" OFF)
option(INSTALL_RESOURCE "install resource" OFF)
option(INSTALL_DEVEL "install development files" OFF)
option(INSTALL_FLATTEN "do not use bin lib include directory" ON)
option(INSTALL_THIRD_LIBS "install third party libraries" ON)
option(USE_MAADEPS "use third-party libraries built by MaaDeps" ON)
option(USE_RANGE_V3 "use range-v3" OFF)
option(WITH_EMULATOR_EXTRAS "build with emulator extras" ${WIN32})
option(WITH_HASH_VERSION "generate version from git hash" OFF)
option(WITH_THRIFT "build with thrift" OFF)
option(WITH_ASST_DEBUG "build with ASST_DEBUG defined" OFF)
include(${PROJECT_SOURCE_DIR}/cmake/utils.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/thrift-gen.cmake)
if(USE_MAADEPS)
include(${PROJECT_SOURCE_DIR}/MaaDeps/maadeps.cmake)
endif()
add_library(header_only_libraries INTERFACE)
target_include_directories(header_only_libraries INTERFACE 3rdparty/include 3rdparty/EmulatorExtras)
file(GLOB_RECURSE maa_src src/MaaCore/*.cpp)
add_library(MaaCore SHARED ${maa_src})
if (MSVC)
target_compile_options(MaaCore PRIVATE "/utf-8")
target_compile_options(MaaCore PRIVATE "/MP")
target_compile_options(MaaCore PRIVATE "/W4;/WX")
target_compile_options(MaaCore PRIVATE "/wd4127") # conditional expression is constant
target_compile_options(MaaCore PRIVATE "/Wv:19.35.32217") # disable warning introduced after this version
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else ()
target_compile_options(MaaCore PRIVATE "-Wall;-Werror;-Wextra;-Wpedantic;-Wno-missing-field-initializers")
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13)
target_compile_options(MaaCore PRIVATE "-Wno-restrict")
endif()
endif ()
if (WIN32)
#注意相比VS版本缺少了 -D_CONSOLE -D_WINDLL 两项
target_compile_definitions(MaaCore PRIVATE ASST_DLL_EXPORTS _UNICODE UNICODE)
endif ()
target_include_directories(MaaCore PUBLIC include PRIVATE src/MaaCore)
set(MaaCore_PUBLIC_HEADERS include/AsstCaller.h include/AsstPort.h)
target_sources(MaaCore PUBLIC ${MaaCore_PUBLIC_HEADERS})
set_target_properties(MaaCore PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
PUBLIC_HEADER "${MaaCore_PUBLIC_HEADERS}"
)
if(APPLE)
set_target_properties(MaaCore PROPERTIES INSTALL_RPATH "@loader_path/")
elseif(UNIX)
set_target_properties(MaaCore PROPERTIES INSTALL_RPATH "$ORIGIN/")
endif()
if (BUILD_TEST)
add_executable(test src/Cpp/main.cpp)
set_target_properties(test PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
)
target_link_libraries(test MaaCore)
endif (BUILD_TEST)
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs videoio)
find_package(ZLIB REQUIRED)
find_package(cpr CONFIG REQUIRED)
list(APPEND maa_libs ${OpenCV_LIBS} ZLIB::ZLIB cpr::cpr header_only_libraries)
if(USE_MAADEPS)
find_package(asio REQUIRED)
list(APPEND maa_libs asio::asio)
endif()
find_package(onnxruntime CONFIG REQUIRED) # provided by onnxruntime>=1.16
list(APPEND maa_libs onnxruntime::onnxruntime)
if(DEFINED fastdeploy_SOURCE_DIR)
# TODO: FetchContent github.com/MaaAssistantArknights/FastDeploy
if(NOT DEFINED fastdeploy_BINARY_DIR)
set(fastdeploy_BINARY_DIR ${CMAKE_BINARY_DIR}/fastdeploy)
endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${fastdeploy_SOURCE_DIR} ${fastdeploy_BINARY_DIR} EXCLUDE_FROM_ALL SYSTEM)
if(NOT DEFINED fastdeploy_INCLUDE_DIRS)
set(fastdeploy_INCLUDE_DIRS ${fastdeploy_SOURCE_DIR})
endif()
target_include_directories(MaaCore SYSTEM PRIVATE ${fastdeploy_INCLUDE_DIRS})
get_target_property(fastdeploy_type fastdeploy_ppocr TYPE)
if(fastdeploy_type STREQUAL "SHARED_LIBRARY")
install(TARGETS fastdeploy_ppocr)
endif()
else()
find_package(fastdeploy_ppocr REQUIRED)
endif()
list(APPEND maa_libs fastdeploy_ppocr)
target_link_libraries(MaaCore ${maa_libs})
if(WITH_EMULATOR_EXTRAS AND NOT EXISTS ${PROJECT_SOURCE_DIR}/3rdparty/EmulatorExtras/Mumu)
message(WARNING "EmulatorExtras not found, please run `git submodule update --init 3rdparty/EmulatorExtras`")
set(WITH_EMULATOR_EXTRAS OFF)
endif()
target_compile_definitions(MaaCore PRIVATE ASST_WITH_EMULATOR_EXTRAS=$<BOOL:${WITH_EMULATOR_EXTRAS}>)
if(WITH_THRIFT)
find_package(Thrift CONFIG REQUIRED)
add_subdirectory(src/MaaThriftController)
target_link_libraries(MaaCore MaaThriftController)
endif(WITH_THRIFT)
if (WITH_ASST_DEBUG)
target_compile_definitions(MaaCore PRIVATE ASST_DEBUG)
endif ()
if(WIN32)
target_link_libraries(MaaCore ws2_32)
endif()
if (USE_RANGE_V3)
target_compile_definitions(MaaCore PRIVATE ASST_USE_RANGES_RANGE_V3)
find_package(range-v3 REQUIRED)
target_link_libraries(MaaCore range-v3::range-v3)
endif ()
if(INSTALL_DEVEL)
set(MaaCore_install_extra_args PUBLIC_HEADER DESTINATION devel/include ARCHIVE DESTINATION devel/lib)
endif()
if(INSTALL_FLATTEN)
set(RPATH_LIBRARY_INSTALL_DIR .)
set(MaaCore_install_flatten_args RUNTIME DESTINATION . LIBRARY DESTINATION . PUBLIC_HEADER DESTINATION .)
endif()
include(src/MaaUtils/MaaUtils.cmake)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules")
include(${PROJECT_SOURCE_DIR}/cmake/config.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/utils.cmake)
install(TARGETS MaaCore ${MaaCore_install_flatten_args} ${MaaCore_install_extra_args})
if(APPLE)
include(${PROJECT_SOURCE_DIR}/cmake/macos.cmake)
if(INSTALL_THIRD_LIBS AND USE_MAADEPS)
maadeps_install()
endif()
add_library(HeaderOnlyLibraries INTERFACE)
target_include_directories(HeaderOnlyLibraries INTERFACE 3rdparty/include)
add_subdirectory(src/MaaCore)
if(BUILD_WPF_GUI)
include_external_msproject(MaaWpfGui ${PROJECT_SOURCE_DIR}/src/MaaWpfGui/MaaWpfGui.csproj)
add_dependencies(MaaWpfGui MaaCore)
if(DEFINED ENV{VSCODE_PID})
add_custom_target(run-MaaWpfGui
COMMAND "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/MAA.exe"
DEPENDS MaaWpfGui
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/bin/$<CONFIG>"
)
endif()
endif()
if(INSTALL_PYTHON)
if (INSTALL_PYTHON)
install(DIRECTORY src/Python DESTINATION .)
endif()
if(INSTALL_RESOURCE)
endif (INSTALL_PYTHON)
if (INSTALL_RESOURCE)
install(DIRECTORY resource DESTINATION .)
endif()
endif (INSTALL_RESOURCE)
if(BUILD_DEBUG_DEMO OR BUILD_SMOKE_TEST)
add_subdirectory(src/Cpp)
endif()
if (APPLE)
include(${PROJECT_SOURCE_DIR}/cmake/macos.cmake)
endif (APPLE)
# define MAA_VERSION from git
if (NOT DEFINED MAA_VERSION)
find_package(Git)
endif ()
if (NOT DEFINED MAA_VERSION AND GIT_FOUND)
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE result
OUTPUT_VARIABLE output
ERROR_VARIABLE err
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (result EQUAL 0)
set(MAA_VERSION "${output}")
else ()
message(WARNING "git rev-parse returning ${result}, output:\n${err}")
endif ()
endif ()
if (NOT MAA_VERSION)
set(MAA_VERSION "DEBUG VERSION")
endif ()
message(STATUS "MAA_VERSION=${MAA_VERSION}")
add_compile_definitions(MAA_VERSION="${MAA_VERSION}")

View File

@@ -1,348 +0,0 @@
{
"version": 10,
"cmakeMinimumRequired": {
"major": 3,
"minor": 23,
"patch": 0
},
"configurePresets": [
{
"name": "windows-base",
"hidden": true,
"generator": "Visual Studio 18 2026",
"binaryDir": "${sourceDir}/build",
"$comment": [
"Base for Windows presets; Visual Studio is multi-config (Debug/Release/RelWithDebInfo in same build dir)",
"see https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2018%202026.html"
],
"cacheVariables": {
"BUILD_WPF_GUI": "ON",
"BUILD_DEBUG_DEMO": "ON",
"INSTALL_RESOURCE": "OFF",
"INSTALL_PYTHON": "OFF"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "linux-base",
"hidden": true,
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/build",
"$comment": [
"Base for Linux presets; Ninja is multi-config (Debug/Release/RelWithDebInfo in same build dir)",
"see https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html",
"clangd uses compile_commands.json to provide code completion and navigation",
"so CMAKE_EXPORT_COMPILE_COMMANDS is set to ON",
"see https://clangd.llvm.org/installation.html"
],
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "macos-base",
"hidden": true,
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/build",
"$comment": [
"Base for macOS presets; Ninja is multi-config (Debug/Release/RelWithDebInfo in same build dir)",
"see https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html",
"clangd uses compile_commands.json to provide code completion and navigation",
"so CMAKE_EXPORT_COMPILE_COMMANDS is set to ON",
"see https://clangd.llvm.org/installation.html"
],
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "windows-x64",
"inherits": "windows-base",
"displayName": "Windows x64",
"architecture": "x64",
"cacheVariables": {
"MAADEPS_TRIPLET": "maa-x64-windows"
}
},
{
"name": "windows-arm64",
"inherits": "windows-base",
"displayName": "Windows arm64",
"architecture": "ARM64",
"cacheVariables": {
"MAADEPS_TRIPLET": "maa-arm64-windows"
}
},
{
"name": "linux-x64",
"inherits": "linux-base",
"displayName": "Linux x64",
"toolchainFile": "src/MaaUtils/MaaDeps/cmake/maa-x64-linux-toolchain.cmake",
"cacheVariables": {
"MAADEPS_TRIPLET": "maa-x64-linux"
}
},
{
"name": "linux-arm64",
"inherits": "linux-base",
"displayName": "Linux arm64",
"toolchainFile": "src/MaaUtils/MaaDeps/cmake/maa-arm64-linux-toolchain.cmake",
"cacheVariables": {
"MAADEPS_TRIPLET": "maa-arm64-linux"
}
},
{
"name": "macos-arm64",
"inherits": "macos-base",
"displayName": "macOS arm64",
"cacheVariables": {
"CMAKE_OSX_ARCHITECTURES": "arm64"
}
},
{
"name": "macos-x64",
"inherits": "macos-base",
"displayName": "macOS x64",
"cacheVariables": {
"CMAKE_OSX_ARCHITECTURES": "x86_64"
}
},
{
"name": "publish-base",
"$comment":[
"All the name contains 'publish' are used for github actions",
"see .github/workflows/ci.yml"
],
"hidden": true,
"installDir": "install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"INSTALL_RESOURCE": "ON",
"INSTALL_PYTHON": "ON",
"BUILD_WPF_GUI": "OFF",
"BUILD_DEBUG_DEMO": "OFF"
}
},
{
"name": "windows-publish-x64",
"inherits": ["publish-base", "windows-x64"],
"$comment": [
"github actions only support Visual Studio 17 2022",
"see https://github.com/actions/runner-images/issues/13291"
],
"generator": "Visual Studio 17 2022",
"displayName": "Windows x64 Publish"
},
{
"name": "windows-publish-arm64",
"inherits": ["publish-base", "windows-arm64"],
"$comment": [
"github actions only support Visual Studio 17 2022",
"see https://github.com/actions/runner-images/issues/13291"
],
"generator": "Visual Studio 17 2022",
"displayName": "Windows arm64 Publish"
},
{
"name": "linux-publish-x64",
"inherits": ["publish-base", "linux-x64"],
"displayName": "Linux x64 Publish"
},
{
"name": "linux-publish-arm64",
"inherits": ["publish-base", "linux-arm64"],
"displayName": "Linux arm64 Publish"
},
{
"name": "macos-publish-x64",
"inherits": ["publish-base", "macos-x64"],
"displayName": "macOS x64 Publish"
},
{
"name": "macos-publish-arm64",
"inherits": ["publish-base", "macos-arm64"],
"displayName": "macOS arm64 Publish"
},
{
"name": "smoke-test",
"$comment":[
"this preset is used for smoke testing",
"see .github/workflows/smoke-testing.yml"
],
"inherits": "macos-arm64",
"displayName": "macOS arm64 Smoke Test",
"installDir": "install",
"cacheVariables": {
"BUILD_SMOKE_TEST": "ON",
"INSTALL_RESOURCE": "ON",
"WITH_HASH_VERSION": "ON"
}
}
],
"buildPresets": [
{
"name": "windows-x64-Debug",
"displayName": "Build Windows x64 Debug",
"configurePreset": "windows-x64",
"configuration": "Debug"
},
{
"name": "windows-x64-Release",
"displayName": "Build Windows x64 Release",
"configurePreset": "windows-x64",
"configuration": "Release"
},
{
"name": "windows-x64-RelWithDebInfo",
"displayName": "Build Windows x64 RelWithDebInfo",
"configurePreset": "windows-x64",
"configuration": "RelWithDebInfo"
},
{
"name": "windows-arm64-Debug",
"displayName": "Build Windows arm64 Debug",
"configurePreset": "windows-arm64",
"configuration": "Debug"
},
{
"name": "windows-arm64-Release",
"displayName": "Build Windows arm64 Release",
"configurePreset": "windows-arm64",
"configuration": "Release"
},
{
"name": "windows-arm64-RelWithDebInfo",
"displayName": "Build Windows arm64 RelWithDebInfo",
"configurePreset": "windows-arm64",
"configuration": "RelWithDebInfo"
},
{
"name": "linux-x64-Debug",
"displayName": "Build Linux x64 Debug",
"configurePreset": "linux-x64",
"configuration": "Debug"
},
{
"name": "linux-x64-Release",
"displayName": "Build Linux x64 Release",
"configurePreset": "linux-x64",
"configuration": "Release"
},
{
"name": "linux-x64-RelWithDebInfo",
"displayName": "Build Linux x64 RelWithDebInfo",
"configurePreset": "linux-x64",
"configuration": "RelWithDebInfo"
},
{
"name": "linux-arm64-Debug",
"displayName": "Build Linux arm64 Debug",
"configurePreset": "linux-arm64",
"configuration": "Debug"
},
{
"name": "linux-arm64-Release",
"displayName": "Build Linux arm64 Release",
"configurePreset": "linux-arm64",
"configuration": "Release"
},
{
"name": "linux-arm64-RelWithDebInfo",
"displayName": "Build Linux arm64 RelWithDebInfo",
"configurePreset": "linux-arm64",
"configuration": "RelWithDebInfo"
},
{
"name": "macos-x64-Debug",
"displayName": "Build macOS x64 Debug",
"configurePreset": "macos-x64",
"configuration": "Debug"
},
{
"name": "macos-x64-Release",
"displayName": "Build macOS x64 Release",
"configurePreset": "macos-x64",
"configuration": "Release"
},
{
"name": "macos-x64-RelWithDebInfo",
"displayName": "Build macOS x64 RelWithDebInfo",
"configurePreset": "macos-x64",
"configuration": "RelWithDebInfo"
},
{
"name": "macos-arm64-Debug",
"displayName": "Build macOS arm64 Debug",
"configurePreset": "macos-arm64",
"configuration": "Debug"
},
{
"name": "macos-arm64-Release",
"displayName": "Build macOS arm64 Release",
"configurePreset": "macos-arm64",
"configuration": "Release"
},
{
"name": "macos-arm64-RelWithDebInfo",
"displayName": "Build macOS arm64 RelWithDebInfo",
"configurePreset": "macos-arm64",
"configuration": "RelWithDebInfo"
},
{
"name": "windows-publish-x64",
"displayName": "Build Windows x64 Publish",
"configurePreset": "windows-publish-x64",
"configuration": "RelWithDebInfo"
},
{
"name": "windows-publish-arm64",
"displayName": "Build Windows arm64 Publish",
"configurePreset": "windows-publish-arm64",
"configuration": "RelWithDebInfo"
},
{
"name": "linux-publish-x64",
"displayName": "Build Linux x64 Publish",
"configurePreset": "linux-publish-x64",
"configuration": "RelWithDebInfo"
},
{
"name": "linux-publish-arm64",
"displayName": "Build Linux arm64 Publish",
"configurePreset": "linux-publish-arm64",
"configuration": "RelWithDebInfo"
},
{
"name": "macos-publish-x64",
"displayName": "Build macOS x64 Publish",
"configurePreset": "macos-publish-x64",
"configuration": "RelWithDebInfo"
},
{
"name": "macos-publish-arm64",
"displayName": "Build macOS arm64 Publish",
"configurePreset": "macos-publish-arm64",
"configuration": "RelWithDebInfo"
},
{
"name": "smoke-test",
"displayName": "Build macOS arm64 Smoke Test",
"configurePreset": "smoke-test",
"configuration": "Debug"
}
]
}

15
MAA.DotSettings Normal file
View File

@@ -0,0 +1,15 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Arknights/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bilibili/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Infrast/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=oper/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=opers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pallas/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pormpt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Rougelike/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=templ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=thres/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Todays/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=txwy/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=vuvm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=XYAZ/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

106
MAA.sln Normal file
View File

@@ -0,0 +1,106 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MaaWpfGui", "src\MaaWpfGui\MaaWpfGui.csproj", "{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}"
ProjectSection(ProjectDependencies) = postProject
{362D1E30-F5AE-4279-9985-65C27B3BA300} = {362D1E30-F5AE-4279-9985-65C27B3BA300}
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC} = {F860C043-4D86-41B6-A97E-4A75C9A6C4EC}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaaCore", "src\MaaCore\MaaCore.vcxproj", "{362D1E30-F5AE-4279-9985-65C27B3BA300}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaaThriftController", "src\MaaThriftController\MaaThriftController.vcxproj", "{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{11F02235-5785-408B-9651-8A4B41FF36F4}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyncRes", "src\SyncRes\SyncRes.csproj", "{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sample", "src\Cpp\MaaSample.vcxproj", "{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}"
ProjectSection(ProjectDependencies) = postProject
{362D1E30-F5AE-4279-9985-65C27B3BA300} = {362D1E30-F5AE-4279-9985-65C27B3BA300}
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC} = {F860C043-4D86-41B6-A97E-4A75C9A6C4EC}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
RelWithDebInfo|ARM64 = RelWithDebInfo|ARM64
RelWithDebInfo|x64 = RelWithDebInfo|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Debug|ARM64.Build.0 = Debug|ARM64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Debug|x64.ActiveCfg = Debug|x64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Debug|x64.Build.0 = Debug|x64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Release|ARM64.ActiveCfg = Release|ARM64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Release|ARM64.Build.0 = Release|ARM64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Release|x64.ActiveCfg = Release|x64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.Release|x64.Build.0 = Release|x64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|ARM64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{FFDC8F49-8EAF-45BE-B0A8-7EF0DB9875A2}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.Debug|ARM64.ActiveCfg = Debug|ARM64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.Debug|ARM64.Build.0 = Debug|ARM64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.Debug|x64.ActiveCfg = Debug|x64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.Debug|x64.Build.0 = Debug|x64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.Release|ARM64.ActiveCfg = Release|ARM64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.Release|ARM64.Build.0 = Release|ARM64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.Release|x64.ActiveCfg = Release|x64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.Release|x64.Build.0 = Release|x64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|ARM64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{362D1E30-F5AE-4279-9985-65C27B3BA300}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Debug|ARM64.ActiveCfg = Debug|ARM64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Debug|ARM64.Build.0 = Debug|ARM64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Debug|x64.ActiveCfg = Debug|x64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Debug|x64.Build.0 = Debug|x64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Release|ARM64.ActiveCfg = Release|ARM64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Release|ARM64.Build.0 = Release|ARM64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Release|x64.ActiveCfg = Release|x64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.Release|x64.Build.0 = Release|x64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|ARM64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{8238EDB1-19BF-4E3B-AF5B-016120D59D7B}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Debug|ARM64.Build.0 = Debug|ARM64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Debug|x64.ActiveCfg = Debug|x64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Debug|x64.Build.0 = Debug|x64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Release|ARM64.ActiveCfg = Release|ARM64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Release|ARM64.Build.0 = Release|ARM64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Release|x64.ActiveCfg = Release|x64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.Release|x64.Build.0 = Release|x64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|ARM64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{F860C043-4D86-41B6-A97E-4A75C9A6C4EC}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.Debug|ARM64.ActiveCfg = Debug|ARM64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.Debug|ARM64.Build.0 = Debug|ARM64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.Debug|x64.ActiveCfg = Debug|x64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.Debug|x64.Build.0 = Debug|x64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.Release|ARM64.ActiveCfg = Release|ARM64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.Release|ARM64.Build.0 = Release|ARM64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.Release|x64.ActiveCfg = Release|x64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.Release|x64.Build.0 = Release|x64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.RelWithDebInfo|ARM64.ActiveCfg = RelWithDebInfo|ARM64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.RelWithDebInfo|ARM64.Build.0 = RelWithDebInfo|ARM64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{63B4F1A2-291C-4C85-91E1-A1F6DAE30D64}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4F2C0E4B-4FE9-47C6-A878-6BD2FAD8B9B2}
EndGlobalSection
EndGlobal

122
MAA.sln.DotSettings Normal file
View File

@@ -0,0 +1,122 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GUI/@EntryIndexedValue">GUI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SWMINIMIZE/@EntryIndexedValue">SWMINIMIZE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=AP/@EntryIndexedValue">AP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=AP_002D/@EntryIndexedValue">AP-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=CA/@EntryIndexedValue">CA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=CA_002D/@EntryIndexedValue">CA-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=CE/@EntryIndexedValue">CE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=CE_002D/@EntryIndexedValue">CE-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=EN/@EntryIndexedValue">EN</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=IS/@EntryIndexedValue">IS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=JP/@EntryIndexedValue">JP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=KR/@EntryIndexedValue">KR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=LD/@EntryIndexedValue">LD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=LS/@EntryIndexedValue">LS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=LS_002D/@EntryIndexedValue">LS-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=PR_002DA/@EntryIndexedValue">PR-A</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=PR_002DA_002D/@EntryIndexedValue">PR-A-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=PR_002DB/@EntryIndexedValue">PR-B</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=PR_002DB_002D/@EntryIndexedValue">PR-B-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=PR_002DC/@EntryIndexedValue">PR-C</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=PR_002DC_002D/@EntryIndexedValue">PR-C-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=PR_002DD/@EntryIndexedValue">PR-D</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=PR_002DD_002D/@EntryIndexedValue">PR-D-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=SK/@EntryIndexedValue">SK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=SK_002D/@EntryIndexedValue">SK-</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=SSS/@EntryIndexedValue">SSS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=WSA/@EntryIndexedValue">WSA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/Abbreviations/=XYAZ/@EntryIndexedValue">XYAZ</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=acast/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Aero/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=aguard/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=amedic/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=amiya/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=apionr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=arget/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Arknights/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=arkplanner/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=asnipe/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=autofill/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bilibili/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bluestacks/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=bsconsole/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ctrler/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=desp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=dnplayer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ehem/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=esource/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Favourite/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=findstr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=FLASHW/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=FLASHWINFO/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=foldartal/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Foldartals/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=framerate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=furni/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Gacha/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Gavial/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Grandet/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Hotkey/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=hwnd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Infrast/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Inited/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ldconsole/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lolicon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=lpwndpl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ls_005B_0022sub/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=luestacks/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=maatouch/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=memuc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Minitouch/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mizuki/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mumu/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nemu/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Netease/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=netstat/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nonfriend/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=notstationed/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=oper/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=opers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Originite/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Originium/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=originiums/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Orundum/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Orundums/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=o_FF9Fv_FF9F/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pallas/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pidl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pormpt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ppidl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pwfi/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rcast/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rdfend/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rguard/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rmean/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rmedic/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Roguelike/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rsnipe/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sami/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Screencap/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=serilog/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Showminimized/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Shownormal/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=specialaccess/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SSS/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=STGM/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=strlen/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stylet/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=subtask/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Suppressions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SWMINIMIZE/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=taskchain/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Todays/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=txwy/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ucrtbase/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=XYAZ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Yahei/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=yituliu/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=_0020_0020_0020wp_002ELength_0020/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=_0020_003D_0020stri/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

1
MaaDeps Submodule

Submodule MaaDeps added at 10d51f5832

128
README.md
View File

@@ -2,9 +2,9 @@
<div align="center">
<img alt="LOGO" src="./docs/.vuepress/public/images/maa-logo_512x512.png" width="256" height="256" />
<img alt="LOGO" src="https://cdn.jsdelivr.net/gh/MaaAssistantArknights/design@main/logo/maa-logo_512x512.png" width="256" height="256" />
# MAA
# MaaAssistantArknights
<br>
<div>
@@ -21,14 +21,11 @@
<img alt="stars" src="https://img.shields.io/github/stars/MaaAssistantArknights/MaaAssistantArknights?style=social">
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/MaaAssistantArknights/MaaAssistantArknights/total?style=social">
</div>
<div>
<a href="https://deepwiki.com/MaaAssistantArknights/MaaAssistantArknights"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
</div>
<br>
<!-- markdownlint-restore -->
[简体中文](https://docs.maa.plus/zh-cn/) | [繁體中文](https://docs.maa.plus/zh-tw/) | [English](https://docs.maa.plus/en-us/) | [日本語](https://docs.maa.plus/ja-jp/) | [한국어](https://docs.maa.plus/ko-kr/)
[简体中文](https://maa.plus/docs/zh-cn/) | [繁體中文](https://maa.plus/docs/zh-tw/) | [English](https://maa.plus/docs/en-us/) | [日本語](https://maa.plus/docs/ja-jp/) | [한국어](https://maa.plus/docs/ko-kr/)
MAA 的意思是 MAA Assistant Arknights
@@ -36,20 +33,16 @@ MAA 的意思是 MAA Assistant Arknights
基于图像识别技术,一键完成全部日常任务!
绝赞更新中 ✿✿ヽ(°▽°)ノ✿
绝赞更新中 ✿✿ヽ(°▽°)ノ✿
</div>
## 下载与安装
请阅读 [文档](https://docs.maa.plus/zh-cn/manual/newbie.html) 后前往 [官网](https://maa.plus) 或 [Releases](https://github.com/MaaAssistantArknights/MaaAssistantArknights/releases) 下载,并参考 [新手上路](https://docs.maa.plus/zh-cn/manual/newbie.html) 进行安装。
## 亮点功能
- 理智作战,掉落识别及上传 [企鹅物流](https://penguin-stats.cn/)[一图流](https://ark.yituliu.cn/)
- 智能基建换班,自动计算干员效率,单设施内最优解;同时也支持 [自定义排班](https://docs.maa.plus/zh-cn/protocol/base-scheduling-schema.html)
- 理智,掉落识别及上传 [企鹅物流](https://penguin-stats.cn/)[一图流](https://ark.yituliu.cn/)
- 智能基建换班,自动计算干员效率,单设施内最优解;同时也支持 [自定义排班](https://maa.plus/docs/zh-cn/protocol/base-scheduling-schema.html)
- 自动公招,可选使用加急许可,一次全部刷完!公招数据自动上传 [企鹅物流](https://penguin-stats.cn/result/stage/recruit/recruit)[一图流](https://ark.yituliu.cn/survey/maarecruitdata)
- 支持手动识别公招界面,方便对高星公招做出选择 ~~(你的这个高回费出的是推王呢还是推王呢)~~
- 支持手动识别公招界面,方便对高星公招做出选择 ~~(你的这个高姿回费出的是推王呢还是推王呢)~~
- 支持识别干员列表,统计已有和未有干员及潜能,并在公招识别显示
- 支持识别养成材料,并导出至 [企鹅物流刷图规划](https://penguin-stats.cn/planner)、[明日方舟工具箱](https://arkntools.app/#/material)、[ARK-NIGHTS 干员培养表](https://ark-nights.com/settings)
- 访问好友、收取信用及购物、领取日常奖励等,一键全日常自动长草
@@ -62,60 +55,92 @@ MAA 的意思是 MAA Assistant Arknights
<details><summary>话不多说,看图!</summary>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./docs/.vuepress/public/images/zh-cn/readme/1-dark.png">
<img alt="zh1" src="./docs/.vuepress/public/images/zh-cn/readme/1-light.png">
<source media="(prefers-color-scheme: dark)" srcset="./docs/.vuepress/public/image/zh-cn/readme/1-dark.png">
<img alt="zh1" src="./docs/.vuepress/public/image/zh-cn/readme/1-light.png">
</picture>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./docs/.vuepress/public/images/zh-cn/readme/2-dark.png">
<img alt="zh2" src="./docs/.vuepress/public/images/zh-cn/readme/2-light.png">
<source media="(prefers-color-scheme: dark)" srcset="./docs/.vuepress/public/image/zh-cn/readme/2-dark.png">
<img alt="zh2" src="./docs/.vuepress/public/image/zh-cn/readme/2-light.png">
</picture>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./docs/.vuepress/public/images/zh-cn/readme/3-dark.png">
<img alt="zh3" src="./docs/.vuepress/public/images/zh-cn/readme/3-light.png">
<source media="(prefers-color-scheme: dark)" srcset="./docs/.vuepress/public/image/zh-cn/readme/3-dark.png">
<img alt="zh3" src="./docs/.vuepress/public/image/zh-cn/readme/3-light.png">
</picture>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./docs/.vuepress/public/images/zh-cn/readme/4-dark.png">
<img alt="zh4" src="./docs/.vuepress/public/images/zh-cn/readme/4-light.png">
<source media="(prefers-color-scheme: dark)" srcset="./docs/.vuepress/public/image/zh-cn/readme/4-dark.png">
<img alt="zh4" src="./docs/.vuepress/public/image/zh-cn/readme/4-light.png">
</picture>
</details>
<!-- markdownlint-restore -->
## 下载地址
前往[官网](https://maa.plus)自动匹配镜像源并下载稳定版,或:
- [稳定版/公测版](https://github.com/MaaAssistantArknights/MaaAssistantArknights/releases)
- [内测版](https://github.com/MaaAssistantArknights/MaaRelease/releases)
## 使用说明
### 功能介绍
### 基本说明
请参阅 [用户手册](https://docs.maa.plus/zh-cn/manual/)。
请参阅 [新手上路](https://maa.plus/docs/zh-cn/manual/newbie.html) 与 [功能介绍](https://maa.plus/docs/zh-cn/manual/introduction/)。
### 常见问题
- 软件一打开就闪退;
- 连接错误、不知道 ADB 路径怎么填写;
- 连接成功了,但没反应;
- 如何连接自定义端口;
- 下载速度慢,且镜像站无法打开网页;
- 下载到一半提示“登陆”/“鉴权”;
- 连接正常,任务开始了,但是没反应。
请参阅 [常见问题](https://maa.plus/docs/zh-cn/manual/faq.html)
### 外服支持
目前国际服(美服)、日服、韩服、繁中服的绝大部分功能均已支持。但由于外服用户较少及项目人手不足,很多功能并没有进行全面的测试,所以请自行体验。
若您遇到了 Bug或对某个功能有强需求欢迎在 [Issues](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues) 和 [讨论区](https://github.com/MaaAssistantArknights/MaaAssistantArknights/discussions) 催更;或加入我们一起建设 MAA请参阅 [外服适配教程](https://docs.maa.plus/zh-cn/develop/overseas-client-adaptation.html)
若您遇到了 Bug或对某个功能有强需求欢迎在 [Issues](https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues) 和 [讨论区](https://github.com/MaaAssistantArknights/MaaAssistantArknights/discussions) 催更;或加入我们一起建设 MAA请参阅 [外服适配教程](https://maa.plus/docs/zh-cn/develop/overseas-client-adaptation.html)
### CLI 支持
MAA 支持命令行界面CLI操作支持 LinuxmacOS 和 Windows可用于自动化脚本或在无图形界面的服务器上使用。请参阅 [CLI 使用指南](https://docs.maa.plus/zh-cn/manual/cli/)
MAA 支持命令行界面CLI操作支持 LinuxmacOS 和 Windows可用于自动化脚本或在无图形界面的服务器上使用。请参阅 [CLI 使用指南](https://maa.plus/docs/zh-cn/manual/cli/intro.html)
## 加入我们
### 主要关联项目
**目前项目组非常缺前端大佬,若您有相关经验,欢迎加入我们!**
- 全新框架:[MaaFramework](https://github.com/MaaXYZ/MaaFramework)
- [作业站](https://prts.plus) 前端:[zoot-plus-frontend](https://github.com/ZOOT-Plus/zoot-plus-frontend)
- [作业站](https://prts.plus) 端:[ZootPlusBackend](https://github.com/ZOOT-Plus/ZootPlusBackend)
- 全新 GUI[MaaX](https://github.com/MaaAssistantArknights/MaaX)
- [作业站](https://prts.plus) 端:[maa-copilot-frontend](https://github.com/MaaAssistantArknights/maa-copilot-frontend)
- [作业站](https://prts.plus) 后端:[MaaBackendCenter](https://github.com/MaaAssistantArknights/MaaBackendCenter)
- [官网](https://maa.plus)[前端](https://github.com/MaaAssistantArknights/maa-website)
- 深度学习:[MaaAI](https://github.com/MaaAssistantArknights/MaaAI)
### 多语言 (i18n)
MAA 支持多国语言,并使用 Weblate 进行本地化管理。如果您通晓多门语言,欢迎前往 [MAA Weblate](https://weblate.maa-org.net) 帮助我们进行翻译。
MAA 以中文(简体)为第一语言,翻译词条均以中文(简体)为准。
[![Weblate](https://weblate.maa-org.net/widget/maa/wpf-gui/multi-auto.svg)](https://weblate.maa-org.net/engage/maa/)
### 参与开发
请参阅 [开发指南](https://docs.maa.plus/zh-cn/develop/development.html)。
#### Windows
### API
请参阅 [开始开发](https://maa.plus/docs/zh-cn/develop/development.html)。
#### Linux | macOS
请参阅 [Linux 编译教程](https://maa.plus/docs/zh-cn/develop/linux-tutorial.html)。
#### API
- [C 接口](include/AsstCaller.h)[集成示例](src/Cpp/main.cpp)
- [Python 接口](src/Python/asst/asst.py)[集成示例](src/Python/sample.py)
@@ -126,18 +151,22 @@ MAA 以中文(简体)为第一语言,翻译词条均以中文(简体)
- [Rust 接口](src/Rust/src/maa_sys)[HTTP 接口](src/Rust)
- [TypeScript 接口](https://github.com/MaaAssistantArknights/MaaX/tree/main/packages/main/coreLoader)
- [Woolang 接口](src/Woolang/maa.wo)[集成示例](src/Woolang/demo.wo)
- [集成文档](https://docs.maa.plus/zh-cn/protocol/integration.html)
- [回调消息协议](https://docs.maa.plus/zh-cn/protocol/callback-schema.html)
- [任务流程协议](https://docs.maa.plus/zh-cn/protocol/task-schema.html)
- [自动抄作业协议](https://docs.maa.plus/zh-cn/protocol/copilot-schema.html)
- [集成文档](https://maa.plus/docs/zh-cn/protocol/integration.html)
- [回调消息协议](https://maa.plus/docs/zh-cn/protocol/callback-schema.html)
- [任务流程协议](https://maa.plus/docs/zh-cn/protocol/task-schema.html)
- [自动抄作业协议](https://maa.plus/docs/zh-cn/protocol/copilot-schema.html)
### 外服适配
#### 外服适配
请参阅 [外服适配教程](https://docs.maa.plus/zh-cn/develop/overseas-client-adaptation.html),对于国服已支持的功能,绝大部分的外服适配工作仅需要截图 + 简单的 JSON 修改即可。
请参阅 [外服适配教程](https://maa.plus/docs/zh-cn/develop/overseas-client-adaptation.html),对于国服已支持的功能,绝大部分的外服适配工作仅需要截图 + 简单的 JSON 修改即可。
### Issue bot
#### 想参与开发,但不太会用 GitHub?
请参阅 [Issue Bot 使用方法](https://docs.maa.plus/zh-cn/develop/issue-bot-usage.html)
[GitHub Pull Request 流程简述](https://maa.plus/docs/zh-cn/develop/development.html#github-pull-request-流程简述)
#### Issue bot
请参阅 [Issue Bot 使用方法](https://maa.plus/docs/zh-cn/develop/issue-bot-usage.html)
## 致谢
@@ -169,33 +198,28 @@ MAA 以中文(简体)为第一语言,翻译词条均以中文(简体)
- ~~干员及基建数据:[PRTS Wiki](http://prts.wiki/)~~
- 关卡数据:[企鹅物流数据统计](https://penguin-stats.cn/)
- 游戏数据及资源:[明日方舟客户端素材](https://github.com/yuanyan3060/ArknightsGameResource)
- 游戏数据:[《明日方舟》Yostar游戏数据](https://github.com/ArknightsAssets/ArknightsGamedata)
- ~~游戏数据:[《明日方舟》游戏数据](https://github.com/Kengxxiao/ArknightsGameData)~~
### 贡献/参与者
感谢所有参与到开发/测试中的朋友们,是大家的帮助让 MAA 越来越好! (\*´▽`)
[![Contributors](https://contributors-img.web.app/image?repo=MaaAssistantArknights/MaaAssistantArknights&max=105&columns=15)](https://github.com/MaaAssistantArknights/MaaAssistantArknights/graphs/contributors)
[![Contributors](https://contributors-img.web.app/image?repo=MaaAssistantArknights/MaaAssistantArknights&max=114514&columns=15)](https://github.com/MaaAssistantArknights/MaaAssistantArknights/graphs/contributors)
## 声明
- 本软件使用 [GNU Affero General Public License v3.0 only](https://spdx.org/licenses/AGPL-3.0-only.html) 开源,并附带额外 [用户协议](https://github.com/MaaAssistantArknights/MaaAssistantArknights/blob/dev/terms-of-service.md)
- 本软件 logo 并非使用 AGPL 3.0 协议开源,[耗毛](https://weibo.com/u/3251357314)、vie 两位画师及软件全体开发者保留所有权利。不得以 AGPL 3.0 协议已授权为由在未经授权的情况下使用本软件 logo不得在未经授权的情况下将本软件 logo 用于任何商业用途。
- 本软件开源、免费,仅供学习交流使用。若您遇到商家使用本软件进行代练并收费,可能设备时间等费用,产生的问题及后果与本软件无关。
### DirectML 支持说明
本软件支持 GPU 加速功能,其在 Windows 平台上依赖于 Microsoft 提供的独立组件 [DirectML](https://learn.microsoft.com/en-us/windows/ai/directml/)。DirectML 并非本项目的开源部分,也不受 AGPL 3.0 的约束。为方便用户,我们随安装包附带了一个未经修改的 DirectML.dll 文件。如果您无需 GPU 加速功能,可安全删除该 DLL 文件,软件的核心功能仍可正常运行。
- 本软件使用 [GNU Affero General Public License v3.0 only](https://spdx.org/licenses/AGPL-3.0-only.html) 开源。
- 本软件 logo 并非使用 AGPL 3.0 协议开源,[耗毛](https://weibo.com/u/3251357314)、vie 两位画师及软件全体开发者保留所有权利。不得以 AGPL 3.0 协议已授权为由在未经授权的情况下使用本软件 logo不得在未经授权的情况下将本软件 logo 用于任何用途。
- 本软件开源、免费,仅供学习交流使用。若您遇到商家使用本软件进行代练并收费,可能由于设备时间等原因,产生的任何问题及后果与本软件无关。
## 广告
用户交流 QQ 群:[MAA 使用 & 粥游交流 QQ 群](https://api.maa.plus/MaaAssistantArknights/api/qqgroup/index.html)
Discord 服务器: [邀请链接](https://discord.gg/23DfZ9uA4V)
用户交流 QQ 群:[MAA 使用 & 粥游交流 QQ 群](https://ota.maa.plus/MaaAssistantArknights/api/qqgroup/index.html)
用户交流 TG 群:[Telegram 群](https://t.me/+Mgc2Zngr-hs3ZjU1)
自动战斗 JSON 作业分享:[prts.plus](https://prts.plus)
Bilibili 直播间:[MrEO 直播间](https://live.bilibili.com/2808861) 直播敲代码 & [MAA-Official 直播间](https://live.bilibili.com/27548877) 游戏/杂谈
自动战斗 JSON 作业分享:[prts.plus](https://prts.plus) 或 [抄作业.com](http://抄作业.com)
Bilibili 直播间:[直播间](https://live.bilibili.com/2808861) 每晚直播敲代码,近期很长一段时间应该都是在写本软件~
技术群(舟无关、禁水):[内卷地狱!(QQ 群)](https://jq.qq.com/?_wv=1027&k=ypbzXcA2)
开发者群:[QQ 群](https://jq.qq.com/?_wv=1027&k=JM9oCk3C)
开发者群:[QQ 群](https://jq.qq.com/?_wv=1027&k=JM9oCk3C)
如果觉得软件对你有帮助,帮忙点个 Star 吧!~(网页最上方右上角的小星星),这就是对我们最大的支持了!

View File

@@ -2,11 +2,11 @@
## 受支持的版本 / Supported Versions
我们仅对 v6 的最新稳定版提供安全支持,如有安全问题,我们将会尽可能快地发布新的稳定版解决。
我们仅对 v4 和 v5 两个大版本的最新稳定版提供安全支持,如有安全问题,我们将会尽可能快地发布新的稳定版解决。
内测版、公测版仅供测试,我们不会对其提供安全支持。
We only provide security support for the latest Stable Release versions of v6. If there are security issues, we will release a new stable version as soon as possible to solve them.
We only provide security support for the latest Stable Release versions of v4 and v5. If there are security issues, we will release a new stable version as soon as possible to solve them.
The Nightly Release version and the Beta Release version are for testing only, and we will not provide security support for them.

7
build.cmd Normal file
View File

@@ -0,0 +1,7 @@
:; set -eo pipefail
:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
:; ${SCRIPT_DIR}/build.sh "$@"
:; exit $?
@ECHO OFF
powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %*

69
build.ps1 Normal file
View File

@@ -0,0 +1,69 @@
[CmdletBinding()]
Param(
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$BuildArguments
)
Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)"
Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
###########################################################################
# CONFIGURATION
###########################################################################
$BuildProjectFile = "$PSScriptRoot\tools\MaaBuilder\MaaBuilder.csproj"
$TempDirectory = "$PSScriptRoot\\.nuke\temp"
$DotNetGlobalFile = "$PSScriptRoot\\global.json"
$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1"
$DotNetChannel = "Current"
$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
$env:DOTNET_MULTILEVEL_LOOKUP = 0
###########################################################################
# EXECUTION
###########################################################################
function ExecSafe([scriptblock] $cmd) {
& $cmd
if ($LASTEXITCODE) { exit $LASTEXITCODE }
}
# If dotnet CLI is installed globally and it matches requested version, use for execution
if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and `
$(dotnet --version) -and $LASTEXITCODE -eq 0) {
$env:DOTNET_EXE = (Get-Command "dotnet").Path
}
else {
# Download install script
$DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
# If global.json exists, load expected version
if (Test-Path $DotNetGlobalFile) {
$DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
$DotNetVersion = $DotNetGlobal.sdk.version
}
}
# Install by channel or version
$DotNetDirectory = "$TempDirectory\dotnet-win"
if (!(Test-Path variable:DotNetVersion)) {
ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
} else {
ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
}
$env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
}
Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)"
ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet }
ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }

62
build.sh Normal file
View File

@@ -0,0 +1,62 @@
#!/usr/bin/env bash
bash --version 2>&1 | head -n 1
set -eo pipefail
SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
###########################################################################
# CONFIGURATION
###########################################################################
BUILD_PROJECT_FILE="$SCRIPT_DIR/tools/MaaBuilder/MaaBuilder.csproj"
TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp"
DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh"
DOTNET_CHANNEL="Current"
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
export DOTNET_MULTILEVEL_LOOKUP=0
###########################################################################
# EXECUTION
###########################################################################
function FirstJsonValue {
perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}"
}
# If dotnet CLI is installed globally and it matches requested version, use for execution
if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then
export DOTNET_EXE="$(command -v dotnet)"
else
# Download install script
DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
mkdir -p "$TEMP_DIRECTORY"
curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
chmod +x "$DOTNET_INSTALL_FILE"
# If global.json exists, load expected version
if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then
DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")")
if [[ "$DOTNET_VERSION" == "" ]]; then
unset DOTNET_VERSION
fi
fi
# Install by channel or version
DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
if [[ -z ${DOTNET_VERSION+x} ]]; then
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
else
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
fi
export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
fi
echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)"
"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet
"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"

View File

@@ -1,2 +0,0 @@
set(debug_comp_defs "_DEBUG;ASST_DEBUG")
add_compile_definitions("$<$<CONFIG:Debug>:${debug_comp_defs}>")

View File

@@ -1,81 +1,30 @@
if (BUILD_XCFRAMEWORK)
set(XCFRAMEWORK_DIR "${CMAKE_BINARY_DIR}/xcframework")
file(MAKE_DIRECTORY ${XCFRAMEWORK_DIR})
# Macro to find a unique library file
macro(find_unique_library lib_name glob_pattern output_var)
file(GLOB _libs CONFIGURE_DEPENDS "${MAADEPS_DIR}/runtime/${MAADEPS_TRIPLET}/${glob_pattern}")
if(_libs)
list(LENGTH _libs _cnt)
if(_cnt EQUAL 1)
list(GET _libs 0 ${output_var})
message(STATUS "Found ${lib_name}: ${${output_var}}")
else()
message(FATAL_ERROR "Ambiguous ${lib_name} dylibs: ${_libs}")
endif()
else()
message(FATAL_ERROR "${lib_name} library not found in ${MAADEPS_DIR}/runtime/${MAADEPS_TRIPLET}/")
endif()
endmacro()
add_custom_command(OUTPUT ${XCFRAMEWORK_DIR}/MaaCore.xcframework
COMMAND ${CMAKE_COMMAND} -E rm -rf "${XCFRAMEWORK_DIR}/MaaCore.xcframework"
COMMAND xcodebuild -create-xcframework
-library $<TARGET_FILE:MaaCore>
-headers ${PROJECT_SOURCE_DIR}/include
-output MaaCore.xcframework
add_custom_command(OUTPUT MaaCore.xcframework
COMMAND rm -rf MaaCore.xcframework
COMMAND xcodebuild -create-xcframework -library libMaaCore.dylib -headers ${PROJECT_SOURCE_DIR}/include -output MaaCore.xcframework
DEPENDS MaaCore
WORKING_DIRECTORY ${XCFRAMEWORK_DIR}
COMMENT "Generating MaaCore.xcframework"
)
add_custom_command(OUTPUT ${XCFRAMEWORK_DIR}/MaaUtils.xcframework
COMMAND ${CMAKE_COMMAND} -E rm -rf "${XCFRAMEWORK_DIR}/MaaUtils.xcframework"
COMMAND xcodebuild -create-xcframework
-library $<TARGET_FILE:MaaUtils>
-output MaaUtils.xcframework
DEPENDS MaaUtils
WORKING_DIRECTORY ${XCFRAMEWORK_DIR}
COMMENT "Generating MaaUtils.xcframework"
)
find_unique_library("OpenCV" "libopencv_world*.dylib" OPENCV_LIB)
add_custom_command(OUTPUT ${XCFRAMEWORK_DIR}/OpenCV.xcframework
COMMAND ${CMAKE_COMMAND} -E rm -rf "${XCFRAMEWORK_DIR}/OpenCV.xcframework"
COMMAND xcodebuild -create-xcframework
-library "${OPENCV_LIB}"
-output OpenCV.xcframework
WORKING_DIRECTORY ${XCFRAMEWORK_DIR}
COMMENT "Generating OpenCV.xcframework"
add_custom_command(OUTPUT OpenCV.xcframework
COMMAND rm -rf OpenCV.xcframework
COMMAND xcodebuild -create-xcframework -library "${PROJECT_SOURCE_DIR}/MaaDeps/runtime/${MAADEPS_TRIPLET}/libopencv_world4.407.dylib" -output OpenCV.xcframework
)
find_unique_library("ONNXRuntime" "libonnxruntime*.dylib" ONNXRUNTIME_LIB)
add_custom_command(OUTPUT ${XCFRAMEWORK_DIR}/ONNXRuntime.xcframework
COMMAND ${CMAKE_COMMAND} -E rm -rf "${XCFRAMEWORK_DIR}/ONNXRuntime.xcframework"
COMMAND xcodebuild -create-xcframework
-library "${ONNXRUNTIME_LIB}"
-output ONNXRuntime.xcframework
WORKING_DIRECTORY ${XCFRAMEWORK_DIR}
COMMENT "Generating ONNXRuntime.xcframework"
add_custom_command(OUTPUT ONNXRuntime.xcframework
COMMAND rm -rf ONNXRuntime.xcframework
COMMAND xcodebuild -create-xcframework -library "${PROJECT_SOURCE_DIR}/MaaDeps/runtime/${MAADEPS_TRIPLET}/libonnxruntime.1.14.1.dylib" -output ONNXRuntime.xcframework
)
add_custom_command(OUTPUT ${XCFRAMEWORK_DIR}/fastdeploy_ppocr.xcframework
COMMAND ${CMAKE_COMMAND} -E rm -rf "${XCFRAMEWORK_DIR}/fastdeploy_ppocr.xcframework"
COMMAND xcodebuild -create-xcframework
-library "${MAADEPS_DIR}/runtime/${MAADEPS_TRIPLET}/libfastdeploy_ppocr.dylib"
-output fastdeploy_ppocr.xcframework
WORKING_DIRECTORY ${XCFRAMEWORK_DIR}
COMMENT "Generating fastdeploy_ppocr.xcframework"
add_custom_command(OUTPUT fastdeploy_ppocr.xcframework
COMMAND rm -rf fastdeploy_ppocr.xcframework
COMMAND xcodebuild -create-xcframework -library "${PROJECT_SOURCE_DIR}/MaaDeps/runtime/${MAADEPS_TRIPLET}/libfastdeploy_ppocr.dylib" -output fastdeploy_ppocr.xcframework
)
add_custom_target(MaaXCFramework ALL
DEPENDS
MaaCore
${XCFRAMEWORK_DIR}/MaaCore.xcframework
MaaUtils
${XCFRAMEWORK_DIR}/MaaUtils.xcframework
${XCFRAMEWORK_DIR}/OpenCV.xcframework
${XCFRAMEWORK_DIR}/ONNXRuntime.xcframework
${XCFRAMEWORK_DIR}/fastdeploy_ppocr.xcframework
DEPENDS MaaCore MaaCore.xcframework OpenCV.xcframework ONNXRuntime.xcframework fastdeploy_ppocr.xcframework
)
endif (BUILD_XCFRAMEWORK)
target_compile_options(MaaCore PRIVATE
-Wno-deprecated-declarations
-Wno-gnu-zero-variadic-macro-arguments)

54
cmake/opencv.cmake Normal file
View File

@@ -0,0 +1,54 @@
set(OPENCV_URL_PREFIX "https://github.com/MaaAssistantArknights/build-opencv/releases/download")
set(OPENCV_TAG "4.5.3")
set(COMPRESSED_SUFFIX ".tar.gz")
if(WIN32)
set(OPENCV_FILENAME "OpenCV-Windows")
set(OPENCV_CHECKSUM "bf736b243bbdaa020f139e4dfa1e4f15633f4ce7a8ad885524645e660de47a8b")
elseif(APPLE)
if (CURRENT_OSX_ARCH STREQUAL "arm64")
set(OPENCV_FILENAME "OpenCV-macOS-arm64")
set(OPENCV_CHECKSUM "31beb633c033dd4ee789ffa50911c29c9580860f9a91f334f03d8aa9c85e9700")
else()
set(OPENCV_FILENAME "OpenCV-macOS-x86_64")
set(OPENCV_CHECKSUM "249c5c97cc52257b68d35acf499b1cf1037f5e6f3b40752f82ad5abe7884bea9")
endif()
else(UNIX)
set(OPENCV_FILENAME "OpenCV-Linux")
set(OPENCV_CHECKSUM "edc4138456189c9e8bdf29114ad2be8ec152e8e31087d98e633f6cda59b141ea")
endif(WIN32)
set(OPENCV_URL ${OPENCV_URL_PREFIX}/${OPENCV_TAG}/${OPENCV_FILENAME}${COMPRESSED_SUFFIX})
if(OPENCV_DIRECTORY)
set(OpenCV_DIR ${OPENCV_DIRECTORY})
find_package(OpenCV REQUIRED PATHS ${OpenCV_DIR})
include_directories(${OpenCV_INCLUDE_DIRS})
list(APPEND DEPEND_LIBS ${OpenCV_LIBS})
else()
download_and_decompress(${OPENCV_URL}
${CMAKE_CURRENT_BINARY_DIR}/${OPENCV_FILENAME}${COMPRESSED_SUFFIX}
${OPENCV_CHECKSUM}
${THIRD_PARTY_PATH}/install/)
set(OPENCV_FILENAME opencv)
set(OpenCV_DIR ${THIRD_PARTY_PATH}/install/${OPENCV_FILENAME})
set(OPENCV_DIRECTORY ${OpenCV_DIR})
if (WIN32)
set(OpenCV_DIR ${OpenCV_DIR}/lib)
endif()
find_package(OpenCV REQUIRED PATHS ${OpenCV_DIR} NO_DEFAULT_PATH)
include_directories(${OpenCV_INCLUDE_DIRS})
list(APPEND DEPEND_LIBS ${OpenCV_LIBS})
endif(OPENCV_DIRECTORY)
if (INSTALL_THIRD_LIBS)
if (OpenCV_SHARED)
install(DIRECTORY ${OpenCV_INSTALL_PATH}/lib/
DESTINATION .
USE_SOURCE_PERMISSIONS PATTERN "cmake" EXCLUDE)
endif (OpenCV_SHARED)
endif (INSTALL_THIRD_LIBS)

44
cmake/thrift-gen.cmake Normal file
View File

@@ -0,0 +1,44 @@
macro(GENERATE_THRIFT_LIB LIB_NAME FILENAME OUTPUTDIR SOURCES)
file(MAKE_DIRECTORY ${OUTPUTDIR})
detect_host_triplet(HOST_TRIPLET)
if (CMAKE_CROSSCOMPILING)
if (CMAKE_HOST_WIN32)
set(_host_executable_suffix ".exe")
else()
set(_host_executable_suffix "")
endif()
else()
set(_host_executable_suffix ${CMAKE_EXECUTABLE_SUFFIX})
endif()
if(EXISTS ${PROJECT_SOURCE_DIR}/MaaDeps/vcpkg/installed/maa-${HOST_TRIPLET}/tools/thrift/thrift${_host_executable_suffix})
set(THRIFT_COMPILER ${PROJECT_SOURCE_DIR}/MaaDeps/vcpkg/installed/maa-${HOST_TRIPLET}/tools/thrift/thrift${_host_executable_suffix})
else()
find_program(THRIFT_COMPILER thrift)
endif()
if(NOT THRIFT_COMPILER)
message(FATAL_ERROR "Thrift compiler not found")
endif()
get_filename_component(THRIFT_IDL_NAME ${FILENAME} NAME_WE)
set(THRIFT_IDL_TARGET "${LIB_NAME}_${THRIFT_IDL_NAME}_idl")
set("${THRIFT_IDL_NAME}-gen-cpp"
${OUTPUTDIR}/${THRIFT_IDL_NAME}.cpp
${OUTPUTDIR}/${THRIFT_IDL_NAME}.h
${OUTPUTDIR}/${THRIFT_IDL_NAME}_types.cpp
${OUTPUTDIR}/${THRIFT_IDL_NAME}_types.h)
add_custom_command(OUTPUT ${${THRIFT_IDL_NAME}-gen-cpp}
DEPENDS ${FILENAME}
COMMAND ${THRIFT_COMPILER} --gen cpp:no_skeleton -out ${OUTPUTDIR} ${FILENAME}
VERBATIM)
add_custom_target(${THRIFT_IDL_TARGET} DEPENDS ${${THRIFT_IDL_NAME}-gen-cpp})
add_library(${LIB_NAME} STATIC ${${THRIFT_IDL_NAME}-gen-cpp})
add_dependencies(${LIB_NAME} ${THRIFT_IDL_TARGET})
set_target_properties(${LIB_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_link_libraries(${LIB_NAME} PUBLIC thrift::thrift)
target_include_directories(${LIB_NAME} PUBLIC ${OUTPUTDIR})
set(${SOURCES} ${${SOURCES}} ${GENERATED_SOURCES} PARENT_SCOPE)
endmacro(GENERATE_THRIFT_LIB)
if (WITH_THRIFT)
add_compile_definitions(WITH_THRIFT)
endif (WITH_THRIFT)

View File

@@ -1,26 +1,50 @@
# 创建资源目录链接的函数
function(create_resource_link TARGET_NAME OUTPUT_DIR)
if(WIN32)
# Windows 使用 mklink /J 创建目录链接(不需要管理员权限)
add_custom_command(
TARGET ${TARGET_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Creating resource directory link for ${TARGET_NAME}..."
COMMAND ${CMAKE_COMMAND} -E remove_directory "${OUTPUT_DIR}/resource"
COMMAND cmd /c "mklink /J \"${OUTPUT_DIR}/resource\" \"${PROJECT_SOURCE_DIR}/resource\""
COMMAND ${CMAKE_COMMAND} -E echo "Resource directory link created successfully"
COMMENT "Creating junction for resource directory for ${TARGET_NAME}"
)
else()
# UNIX/Linux/macOS 使用符号链接
add_custom_command(
TARGET ${TARGET_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Creating resource directory link for ${TARGET_NAME}..."
COMMAND ${CMAKE_COMMAND} -E remove "${OUTPUT_DIR}/resource"
COMMAND ${CMAKE_COMMAND} -E create_symlink "${PROJECT_SOURCE_DIR}/resource" "${OUTPUT_DIR}/resource"
COMMAND ${CMAKE_COMMAND} -E echo "Resource directory link created successfully"
COMMENT "Creating symlink for resource directory for ${TARGET_NAME}"
)
endif()
function(download_and_decompress url filename sha256_checksum decompress_dir)
if(EXISTS ${filename})
file(SHA256 ${filename} CHECKSUM_VARIABLE)
endif()
if(NOT EXISTS ${filename} OR NOT CHECKSUM_VARIABLE STREQUAL sha256_checksum)
message("Downloading file from ${url} to ${filename} ...")
file(DOWNLOAD ${url} "${filename}.tmp" SHOW_PROGRESS EXPECTED_HASH SHA256=${sha256_checksum})
file(RENAME "${filename}.tmp" ${filename})
endif()
if(NOT EXISTS ${decompress_dir})
file(MAKE_DIRECTORY ${decompress_dir})
endif()
message("Decompress file ${filename} ...")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar -xf ${filename} WORKING_DIRECTORY ${decompress_dir})
endfunction()
function(get_osx_architecture)
if (CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
set(CURRENT_OSX_ARCH "arm64" PARENT_SCOPE)
elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64")
set(CURRENT_OSX_ARCH "x86_64" PARENT_SCOPE)
else()
set(CURRENT_OSX_ARCH ${CMAKE_HOST_SYSTEM_PROCESSOR} PARENT_SCOPE)
endif()
endfunction()
function(detect_host_triplet outvar)
string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" host_triplet_system)
string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" host_triplet_arch)
if(host_triplet_system STREQUAL "darwin")
set(host_triplet_system "osx")
endif()
message("host_triplet_system: ${host_triplet_system}")
message("host_triplet_arch: ${host_triplet_arch}")
if(host_triplet_arch MATCHES "(amd64|x86_64)")
set(host_triplet_arch "x64")
elseif(host_triplet_arch MATCHES "i[3456]86")
set(host_triplet_arch "x86")
elseif(host_triplet_arch MATCHES "(aarch64|armv8l|arm64)")
set(host_triplet_arch "arm64")
else()
message(FATAL_ERROR "Unrecognized CMAKE_HOST_SYSTEM_PROCESSOR: ${CMAKE_HOST_SYSTEM_PROCESSOR}")
endif()
set(${outvar} "${host_triplet_arch}-${host_triplet_system}" PARENT_SCOPE)
endfunction()
if (APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
get_osx_architecture()
endif (APPLE)

View File

@@ -223,39 +223,6 @@ MD052: true
# MD053/link-image-reference-definitions - Link and image reference definitions should be needed
MD053:
# Ignored definitions
ignored_definitions: ["//"]
# MD054/link-image-style - Link and image style
MD054:
# Allow autolinks
autolink: true
# Allow collapsed reference links and images
collapsed: true
# Allow full reference links and images
full: true
# Allow inline links and images
inline: true
# Allow shortcut reference links and images
shortcut: true
# Allow URLs as inline links
url_inline: true
# MD055/table-pipe-style - Table pipe style
MD055:
# Table pipe style
style: "consistent"
# MD056/table-column-count - Table column count
MD056: true
# MD057 - Table column count inconsistencies with table header column count
# (Not documented in official rules list - MD057 appears to be missing or deprecated)
# MD058/blanks-around-tables - Tables should be surrounded by blank lines
MD058: true
# MD059/descriptive-link-text - Link text should be descriptive
MD059:
false
# Prohibited link texts
#prohibited_texts: ["click here", "here", "link", "more"]
ignored_definitions: [
"//"
]

View File

@@ -1 +0,0 @@
**/pnpm-lock.yaml

View File

@@ -1,40 +0,0 @@
module.exports = {
printWidth: 120,
tabWidth: 2,
useTabs: false,
bracketSpacing: true,
bracketSameLine: false,
endOfLine: 'auto',
semi: false,
singleQuote: true,
trailingComma: 'all',
arrowParens: 'always',
overrides: [
{
files: ['**/*.*css'],
options: {
singleQuote: false,
},
},
{
files: ['**/*.yml', '**/*.yaml'],
options: {
parser: 'yaml',
singleQuote: false,
},
},
{
files: ['**/*.json'],
options: {
tabWidth: 4,
},
},
{
files: ['**/*.md'],
options: {
embeddedLanguageFormatting: 'off',
},
},
],
}

View File

@@ -1,21 +1,8 @@
import { defineClientConfig } from 'vuepress/client'
import { getAsciiArt } from './plugins/asciiArt'
import AsciiArt from './components/AsciiArt.vue'
import ImageGrid from './components/ImageGrid.vue'
import Redirect from './components/Redirect.vue'
import './styles/index.scss'
import { defineClientConfig } from "vuepress/client";
import ImageGrid from "../components/ImageGrid.vue";
export default defineClientConfig({
enhance: ({ app }) => {
app.component('AsciiArt', AsciiArt)
app.component('ImageGrid', ImageGrid)
app.component('Redirect', Redirect)
// 输出一个随机的字符画
const asciiArtData = getAsciiArt(undefined, 'auto', 'console')
console.log('%c' + asciiArtData.text, 'white-space: pre;')
app.component("ImageGrid", ImageGrid);
},
})
});

View File

@@ -1,106 +0,0 @@
<template>
<div ref="asciiArtWrapperElement" class="ascii-art-wrapper">
<pre ref="asciiArtContentElement" class="ascii-art-content">{{ asciiArtText }}</pre>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { getAsciiArt, ThemeType, AsciiArtScope } from '../plugins/asciiArt'
// -------- Props --------
interface AsciiArtProps {
name?: string
theme?: ThemeType
scope?: AsciiArtScope
}
const props = defineProps<AsciiArtProps>()
// -------- Refs --------
const asciiArtWrapperElement = ref<HTMLElement>()
const asciiArtContentElement = ref<HTMLElement>()
// -------- Data --------
let asciiArtNameInUse = props.name
const asciiArtText = ref('')
let layoutObserver: ResizeObserver
let themeObserver: MutationObserver
let isScaleUpLocked = false
let lastScaleRatio = 1
function refreshAsciiArt() {
const asciiArtData = getAsciiArt(asciiArtNameInUse, props.theme, props.scope)
asciiArtNameInUse = asciiArtData.name
asciiArtText.value = asciiArtData.text
}
function scaleAsciiArt() {
if (!asciiArtWrapperElement.value || !asciiArtContentElement.value) return
// 原始高度和宽度无视scale
const contentWidth = asciiArtContentElement.value.scrollWidth
const contentHeight = asciiArtContentElement.value.scrollHeight
if (contentWidth === 0 || contentHeight === 0) return
const targetWidth = asciiArtWrapperElement.value.clientWidth
const targetHeight = window.innerHeight
const scaleRatio = Math.min(targetWidth / contentWidth, targetHeight / contentHeight)
// 锁定状态不允许放大
if (scaleRatio > lastScaleRatio && isScaleUpLocked) return
lastScaleRatio = scaleRatio
isScaleUpLocked = true
asciiArtContentElement.value.style.transform = `scale(${scaleRatio})`
asciiArtWrapperElement.value.style.height = `${contentHeight * scaleRatio}px`
}
function forceScaleAsciiArt() {
isScaleUpLocked = false
scaleAsciiArt()
}
onMounted(() => {
if (!asciiArtWrapperElement.value || !asciiArtContentElement.value) return
layoutObserver = new ResizeObserver(scaleAsciiArt)
layoutObserver.observe(asciiArtContentElement.value)
layoutObserver.observe(asciiArtWrapperElement.value)
themeObserver = new MutationObserver(refreshAsciiArt)
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })
window.addEventListener('resize', forceScaleAsciiArt)
refreshAsciiArt()
})
onBeforeUnmount(() => {
if (layoutObserver) {
layoutObserver.disconnect()
}
if (themeObserver) {
themeObserver.disconnect()
}
window.removeEventListener('resize', forceScaleAsciiArt)
})
</script>
<style scoped>
.ascii-art-wrapper {
display: flex;
justify-content: center;
align-items: flex-start;
}
.ascii-art-content {
display: block;
white-space: pre;
margin: 0 auto;
transform-origin: top;
line-height: 1;
font-family: 'JetBrains Mono', monospace;
}
</style>

Some files were not shown because too many files have changed in this diff Show More