Compare commits

..

3 Commits

1044 changed files with 36721 additions and 201217 deletions

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,21 +0,0 @@
#!/bin/bash
WORKSPACE=$(pwd)
# conda activate maa
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,37 +0,0 @@
FROM mcr.microsoft.com/devcontainers/base:ubuntu
USER vscode
ENV CONDA_DIR=/home/vscode/miniconda
ENV PATH="$CONDA_DIR/bin:$PATH"
ARG CLANGD_VERSION=20
ARG PYTHON_VERSION=3.12
ARG NODEJS_VERSION=24
# Install system dependencies
RUN sudo apt update \
&& sudo apt upgrade -y \
&& sudo apt install -y \
cmake
# Install Miniconda
RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda.sh \
&& bash ~/miniconda.sh -b -p ${CONDA_DIR} \
&& rm ~/miniconda.sh \
&& conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/main \
&& conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/r
# Configure conda environment
RUN eval "$(conda shell.bash hook)" \
&& conda create -n maa \
&& conda activate maa \
&& conda install -y \
python=${PYTHON_VERSION} \
conda-forge::nodejs=${NODEJS_VERSION} \
&& pip install pre-commit black isort
# Finalize conda setup
RUN conda init \
&& conda config --set auto_activate false \
&& echo "conda activate maa" >> ~/.bashrc

View File

@@ -1,86 +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",
"ms-python.black-formatter",
"ms-python.isort",
"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": "ms-python.black-formatter",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
},
"[cpp]": {
"editor.defaultFormatter": "xaver.clang-format"
},
"[c]": {
"editor.defaultFormatter": "xaver.clang-format"
},
// Python formatting and linting
"isort.args": ["--profile", "black"],
// Python runtime
"python.terminal.launchArgs": ["-u"],
"python.defaultInterpreterPath": "/home/vscode/miniconda/envs/maa/bin/python",
"python.terminal.activateEnvironment": false,
// CMake settings
"cmake.configureSettings": {
"BUILD_DEBUG_DEMO": "ON",
"CMAKE_TOOLCHAIN_FILE": "src/MaaUtils/MaaDeps/cmake/maa-x64-linux-toolchain.cmake"
},
"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,47 +0,0 @@
#!/bin/bash
WORKSPACE=$(pwd)
# conda activate maa
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"
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"
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

@@ -17,12 +17,11 @@ RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -
# Configure conda environment
RUN eval "$(conda shell.bash hook)" \
&& conda create -n maa \
&& conda create -n maa python=$PYTHON_VERSION -y \
&& conda activate maa \
&& conda install -y \
python=$PYTHON_VERSION \
conda-forge::nodejs=$NODEJS_VERSION \
&& pip install pre-commit
&& conda install -y conda-forge::nodejs=$NODEJS_VERSION \
&& pip install pre-commit black \
&& npm install -g pnpm
# Finalize conda setup
RUN conda init \

View File

@@ -1,13 +1,50 @@
// 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": "Plain Env (Nothing Installed)",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"name": "MAA",
// 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/post-create.sh",
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"mkxml.vscode-filesize",
"nekosu.maa-support",
"DavidAnson.vscode-markdownlint",
"esbenp.prettier-vscode",
"vue.volar",
"ms-python.python",
"ms-python.black-formatter"
],
"settings": {
// Color theme
"workbench.colorTheme": "Default Dark Modern"
// format
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[markdown]": {
"editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
},
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
// python
"python.terminal.launchArgs": ["-u"],
"python.defaultInterpreterPath": "/home/vscode/miniconda/envs/maa/bin/python",
"python.terminal.activateEnvironment": false
}
}
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@@ -1,13 +1,21 @@
#!/bin/bash
WORKSPACE=$(pwd)
# conda activate maa
echo "===================="
echo "Setting up git safe.directory for $WORKSPACE and its submodules..."
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 "Installing dependencies for python..."
# pip install -r tools/.../requirements.txt
# pip install -r tools/.../requirements-dev.txt
echo "===================="
echo "Installing dependencies for nodejs..."
cd "$WORKSPACE"/docs
pnpm install --frozen-lockfile

View File

@@ -46,9 +46,6 @@ 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

2
.gitattributes vendored
View File

@@ -10,5 +10,3 @@
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.sh text eol=lf

View File

@@ -9,7 +9,6 @@ updates:
assignees:
- AnnAngela
- Constrat
- lucienshawls
groups:
github-actions:
patterns:

View File

@@ -1,4 +1,4 @@
name: Blame Ignore Update
name: Git Blame Ignore
on:
schedule:
@@ -7,7 +7,6 @@ on:
jobs:
blame-ignore:
name: Update Git Blame Ignore Revs
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
@@ -27,7 +26,7 @@ jobs:
git diff --quiet .git-blame-ignore-revs
- name: Commit changes
- name: Commit and Push changes
if: ${{ steps.check_changes.outcome == 'failure' }}
run: |
git config user.name "github-actions[bot]"

View File

@@ -1,4 +1,4 @@
name: Cache Cleanup
name: Delete Cache
on:
pull_request:
@@ -11,14 +11,13 @@ on:
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
- name: Delete cache on PR merged
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
PR_NUMBER=${{ github.event.inputs.pr_number }}

View File

@@ -1,4 +1,4 @@
name: Release Pipeline
name: ci
on:
push:
@@ -7,70 +7,79 @@ on:
branches-ignore:
- "master"
paths:
- ".github/workflows/ci.yml"
- "3rdparty/include/**"
- "cmake/**"
- "CMakeLists.txt"
- "include/**"
- "src/**"
- "tools/maadeps-download.py"
- "cmake/**"
- "CMakeLists.txt"
- ".github/workflows/ci.yml"
- "!**/*.md"
- "tools/maadeps-download.py"
pull_request:
branches:
- "dev"
paths:
- ".github/workflows/ci.yml"
- "3rdparty/include/**"
- "cmake/**"
- "CMakeLists.txt"
- "include/**"
- "src/**"
- "tools/maadeps-download.py"
- "cmake/**"
- "CMakeLists.txt"
- "!**/*.md"
- "tools/maadeps-download.py"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.ref == 'refs/heads/dev' && github.sha }}
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
meta:
name: Gather Meta Information
# Prevent duplicate runs on organization branches with PRs
if: github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name !=
github.event.pull_request.base.repo.full_name
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.set_tag.outputs.tag }}
prerelease: ${{ steps.set_pre.outputs.prerelease }}
steps:
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5
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
@@ -83,15 +92,19 @@ jobs:
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@v5
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
# Prevent duplicate runs on organization branches with PRs
if: github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name !=
github.event.pull_request.base.repo.full_name
needs: meta
strategy:
matrix:
@@ -100,14 +113,13 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5
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
@@ -115,7 +127,7 @@ jobs:
uses: actions/cache@v4
with:
path: |
./src/MaaUtils/MaaDeps
./MaaDeps
key: ${{ runner.os }}-${{ matrix.arch }}-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- name: Bootstrap MaaDeps
@@ -128,16 +140,16 @@ jobs:
- name: Config cmake
run: |
mkdir -p build
cmake -B build ${{ matrix.arch == 'arm64' && '-A ARM64' }} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMAADEPS_TRIPLET='maa-${{ matrix.arch }}-windows' -DINSTALL_RESOURCE=ON -DINSTALL_PYTHON=ON -DMAA_HASH_VERSION='${{ needs.meta.outputs.tag }}' -DBUILD_WPF_GUI=OFF
cmake -B build ${{ matrix.arch == 'arm64' && '-A ARM64' }} -DCMAKE_BUILD_TYPE=Release -DMAADEPS_TRIPLET='maa-${{ matrix.arch }}-windows' -DINSTALL_RESOURCE=ON -DINSTALL_PYTHON=ON -DMAA_HASH_VERSION='${{ needs.meta.outputs.tag }}' -DBUILD_WPF_GUI=OFF
- name: Build
run: |
cmake --build build --config RelWithDebInfo --parallel $env:NUMBER_OF_PROCESSORS
cmake --build build --config Release --parallel $env:NUMBER_OF_PROCESSORS
- name: Install
run: |
mkdir -p install
cmake --install build --prefix install --config RelWithDebInfo
cmake --install build --prefix install --config Release
- name: Cache .nuke/temp, ~/.nuget/packages
id: cache-nuget
@@ -152,7 +164,7 @@ jobs:
if: steps.cache-nuget.outputs.cache-hit != 'true'
run: dotnet restore src/MaaWpfGui/MaaWpfGui.csproj
- name: Taggify version for csproj
- name: Taggify Version for csproj
run: |
$csprojPath = "src/MaaWpfGui/MaaWpfGui.csproj"
$csprojPath = Resolve-Path -Path $csprojPath
@@ -173,15 +185,9 @@ jobs:
- name: Publish WPF GUI
continue-on-error: true
run: |
dotnet publish src/MaaWpfGui/MaaWpfGui.csproj -c Release -p:Platform=${{ matrix.arch == 'arm64' && 'ARM64' || 'x64' }} -o install
dotnet publish src/MaaWpfGui/MaaWpfGui.csproj -c Release -o install
- name: Upload PDB files
uses: actions/upload-artifact@v5
with:
name: MAAComponent-DebugSymbol-win-${{ matrix.arch }}
path: install/*.pdb
- name: Organize install files
- name: Organize Install Files
shell: bash
run: |
rm -rf install/*.pdb
@@ -195,14 +201,18 @@ jobs:
cd install
Compress-Archive -Destination MAA-${{ needs.meta.outputs.tag }}-win-${{ matrix.arch }}.zip -Path ./*
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v5
- name: Upload MAA to Github
uses: actions/upload-artifact@v4
with:
name: MAA-win-${{ matrix.arch }}
path: install/*.zip
ubuntu:
name: Build for Ubuntu
# Prevent duplicate runs on organization branches with PRs
if: github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name !=
github.event.pull_request.base.repo.full_name
needs: meta
runs-on: ubuntu-latest
strategy:
@@ -210,14 +220,13 @@ jobs:
arch: [aarch64, x86_64]
fail-fast: false
steps:
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5
with:
show-progress: false
- name: Fetch submodules
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
@@ -225,7 +234,7 @@ jobs:
id: cache-maadeps
uses: actions/cache@v4
with:
path: ./src/MaaUtils/MaaDeps
path: ./MaaDeps
key: ${{ runner.os }}-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- name: Bootstrap MaaDeps
@@ -239,27 +248,28 @@ jobs:
run: |
mkdir -p build
cmake -B build \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_BUILD_TYPE=Release \
-DMAADEPS_TRIPLET='maa-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-linux' \
-DINSTALL_RESOURCE=ON \
-DINSTALL_PYTHON=ON \
-DMAA_HASH_VERSION='${{ needs.meta.outputs.tag }}' \
-DCMAKE_TOOLCHAIN_FILE=src/MaaUtils/MaaDeps/cmake/maa-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-linux-toolchain.cmake
-DCMAKE_TOOLCHAIN_FILE=MaaDeps/cmake/maa-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-linux-toolchain.cmake
- name: Build
run: |
cmake --build build --config RelWithDebInfo --parallel $(nproc)
cmake --build build --config Release --parallel $(nproc)
env:
CLICOLOR_FORCE: 1
- name: Install
run: |
mkdir -p install
cmake --install build --prefix install --config RelWithDebInfo
cmake --install build --prefix install --config Release
- name: Setup cross compile toolchains for CLI
- 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
@@ -313,8 +323,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@v5
- name: Upload MAA to Github
uses: actions/upload-artifact@v4
with:
name: MAA-linux-${{ matrix.arch }}
path: |
@@ -322,25 +332,25 @@ jobs:
release/*.tar.gz
macOS-Core:
name: Build Core for macOS
# Prevent duplicate runs on organization branches with PRs
if: github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name !=
github.event.pull_request.base.repo.full_name
needs: meta
runs-on: macos-26
runs-on: macos-latest
strategy:
matrix:
arch: [arm64, x86_64]
fail-fast: false
steps:
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5
with:
show-progress: false
- name: Fetch submodules
run: |
git submodule update --init --depth 1 src/MaaUtils
# ninja 1.13.1 is already installed and up-to-date.
# - name: Install dependencies
# - name: Install Dependencies
# run: |
# brew install ninja
@@ -348,47 +358,52 @@ jobs:
id: cache-maadeps
uses: actions/cache@v4
with:
path: ./src/MaaUtils/MaaDeps
path: ./MaaDeps
key: ${{ runner.os }}-${{ matrix.arch == 'x86_64' && 'x64' || 'arm64' }}-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- 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 tools/maadeps-download.py ${triplet}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Config cmake
run: |
cmake -B build -GNinja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES='${{ matrix.arch }}' \
-DMAA_HASH_VERSION='${{ needs.meta.outputs.tag }}'
- name: Build
run: |
cmake --build build --config RelWithDebInfo --parallel $(sysctl -n hw.logicalcpu)
cmake --build build --config Release --parallel $(sysctl -n hw.logicalcpu)
- name: Install
run: |
cmake --install build --prefix install --config RelWithDebInfo
cmake --install build --prefix install --config Release
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v5
- 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
# Prevent duplicate runs on organization branches with PRs
if: github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name !=
github.event.pull_request.base.repo.full_name
needs: [meta, macOS-Core]
runs-on: macos-26
runs-on: macos-latest
steps:
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5
with:
show-progress: false
- name: Install dependencies
- name: Install Dependencies
if: startsWith(github.ref, 'refs/tags/v')
run: |
brew install graphicsmagick imagemagick
@@ -398,26 +413,26 @@ jobs:
run: |
git submodule update --init --depth 1 src/MaaMacGui
- name: Download MAA (arm64) from GitHub
uses: actions/download-artifact@v6
- name: Download Arm64 MAA from Github
uses: actions/download-artifact@v5
with:
name: MAACore-macos-arm64
path: install-arm64
- name: Download MAA (x64) from GitHub
uses: actions/download-artifact@v6
- name: Download x64 MAA from Github
uses: actions/download-artifact@v5
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@v5
with:
p12-file-base64: ${{ secrets.HGUANDL_SIGN_CERT_P12 }}
p12-password: ${{ secrets.HGUANDL_SIGN_CERT_PASSWD }}
- name: Download provisioning profiles
- name: Download Provisioning Profiles
if: startsWith(github.ref, 'refs/tags/v')
uses: apple-actions/download-provisioning-profiles@v4
with:
@@ -426,19 +441,19 @@ jobs:
api-key-id: ${{ secrets.HGUANDL_APPSTORE_KEYID }}
api-private-key: ${{ secrets.HGUANDL_APPSTORE_KEY }}
- name: Setup Xcode toolchain
if: false
- name: Setup Xcode Toolchain
if: true
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
- name: Archive Runtime Files
run: |
mkdir runtime && cd runtime
name='MAA-${{ needs.meta.outputs.tag }}-macos-runtime-universal'
@@ -450,8 +465,8 @@ jobs:
cd .. || exit 1
zip -yrX9 "$name.zip" "$name"
- name: Upload MAA runtime to GitHub
uses: actions/upload-artifact@v5
- name: Upload MAA runtime to Github
uses: actions/upload-artifact@v4
with:
name: MAA-macos-runtime-universal
path: runtime/MAA-${{ needs.meta.outputs.tag }}-macos-runtime-universal.zip
@@ -459,13 +474,12 @@ jobs:
- 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
@@ -486,20 +500,20 @@ 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
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 }}
@@ -509,7 +523,7 @@ 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
- name: Notarize Image
if: startsWith(github.ref, 'refs/tags/v')
uses: nick-fields/retry@v3
with:
@@ -525,31 +539,34 @@ jobs:
APPSTORE_KEY: ${{ secrets.HGUANDL_APPSTORE_KEY }}
ISSUER_ID: ${{ secrets.HGUANDL_APPSTORE_ISSUER }}
- name: Attach notarization tickets
- name: Attach Notarization Tickets
if: startsWith(github.ref, 'refs/tags/v')
run: |
dmg="MAA-${{ needs.meta.outputs.tag }}-macos-universal.dmg"
xcrun stapler staple ${dmg}
working-directory: release
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v5
- 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')
# Prevent duplicate runs on organization branches with PRs
if: (github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name !=
github.event.pull_request.base.repo.full_name) && startsWith(github.ref, 'refs/tags/v')
needs: [meta, windows, ubuntu, macOS-Core, macOS-GUI]
runs-on: ubuntu-latest
steps:
- name: Download MAA from GitHub
uses: actions/download-artifact@v6
- name: Download MAA from Github
uses: actions/download-artifact@v5
with:
path: assets
- name: Clean up files
- name: Cleanup files
run: |
mv -vf assets/changelog/* .
rm -rf assets/MAACore-macos-*
@@ -557,35 +574,32 @@ 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.4.2
- name: Release to Github
uses: softprops/action-gh-release@v2.3.3
with:
body_path: CHANGELOG.md
files: |
assets/*
prerelease: ${{ needs.meta.outputs.prerelease != 'false' }}
- name: Trigger release distribution workflows
- name: Trigger MirrorChyan
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
gh workflow run --repo $GITHUB_REPOSITORY mirrorchyan
gh workflow run --repo $GITHUB_REPOSITORY mirrorchyan_release_note
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
- 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-ota.yml
gh workflow run --repo $GITHUB_REPOSITORY release-ota
env:
GH_TOKEN: ${{ secrets.MISTEOWORKFLOW }}
- name: Create issue on failure
- name: Create issue if failed
if: failure()
uses: actions-cool/issues-helper@v3
with:
actions: "create-issue"
title: "Errors occured during release ${{ needs.meta.outputs.tag }}"
title: "Failed Release"
body: |
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

View File

@@ -1,15 +1,15 @@
name: CodeQL Code Analysis
name: "CodeQL MaaCore and MaaWpfGui Analysis"
on:
pull_request:
branches: ["dev"]
paths:
- ".github/workflows/codeql-core.yml"
- "3rdparty/include/**"
- "cmake/**"
- "CMakeLists.txt"
- "include/**"
- "src/**"
- "cmake/**"
- "CMakeLists.txt"
- ".github/workflows/codeql-core.yml"
- "!**/*.md"
- "!**/*.xaml"
schedule:
@@ -25,7 +25,6 @@ permissions:
jobs:
analyze-manual:
name: Analyze MaaCore and MaaWpfGui
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: windows-latest
steps:
- name: Checkout repository
@@ -34,13 +33,15 @@ jobs:
show-progress: false
- name: Setup CodeQL
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@v3
with:
languages: c-cpp,csharp
build-mode: manual
- name: Run CodeQL
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@v3
with:
category: "/language:multi-manual"
- name: Delete old caches
shell: pwsh

View File

@@ -1,4 +1,4 @@
name: CodeQL Workflow Analysis
name: "CodeQL Workflows Analysis"
on:
pull_request:
@@ -17,7 +17,6 @@ permissions:
jobs:
analyze-workflows:
name: Analyze GitHub Workflows
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
@@ -26,12 +25,12 @@ jobs:
show-progress: false
- name: Setup CodeQL for GitHub Actions
uses: github/codeql-action/init@v4
uses: github/codeql-action/init@v3
with:
languages: actions
build-mode: none
- name: Run CodeQL Analysis
uses: github/codeql-action/analyze@v4
uses: github/codeql-action/analyze@v3
with:
category: "/language:multi-none"

View File

@@ -1,4 +1,4 @@
name: Release Preparation
name: gen-changelog
on:
pull_request:
@@ -9,19 +9,18 @@ on:
workflow_dispatch:
jobs:
generate-changelog:
name: Generate Changelog
gen:
# startsWith 表达式不区分大小写
if: github.event.pull_request.draft == false && startsWith(github.event.pull_request.title, 'Release v')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0
show-progress: false
- name: Extract release information
- name: Extract tag name
id: extract_tag
env:
PR_BODY: ${{ format('{0}/{1}', runner.temp, 'output' ) }}
@@ -51,7 +50,7 @@ jobs:
echo 'Target PR: ${{ github.event.pull_request.html_url }}' >> $PR_BODY
echo '' >> $PR_BODY
echo '<details><summary>Debug information</summary>' >> $PR_BODY
echo '<details><summary>Debug info</summary>' >> $PR_BODY
echo '' >> $PR_BODY
echo '```' >> $PR_BODY
sed 's/=/: /1' $GITHUB_OUTPUT >> $PR_BODY
@@ -61,14 +60,14 @@ jobs:
cat $PR_BODY
- name: Generate changelog
- 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
- name: Add files to git
run: |
git status
@@ -79,7 +78,7 @@ jobs:
commit_msg="docs: Auto Generate Changelog of Release ""${{ steps.extract_tag.outputs.tag_name }}"
git commit -m "$commit_msg"
- name: Create changelog PR
- name: Create PR
uses: peter-evans/create-pull-request@v7
with:
sign-commits: true
@@ -94,18 +93,7 @@ jobs:
assignees: |
AnnAngela
- name: Assign reviewers to release PR
- name: Add reviewers to release PR
uses: kentaro-m/auto-assign-action@v2.0.0
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,4 +1,4 @@
name: Issue Review
name: Auto Close not reading issues or Fold checkboxes
on:
issues:
@@ -6,7 +6,6 @@ on:
jobs:
check-then-close-or-fold:
name: Review and Modify Issues
runs-on: ubuntu-latest
permissions:
issues: write
@@ -64,7 +63,7 @@ jobs:
}
}
- name: Check for issue checkboxes
- name: Check checkbox status
id: unread-checkbox-check
uses: actions/github-script@v8
with:
@@ -87,7 +86,7 @@ jobs:
'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
- name: Close and lock issue
if: steps.unread-checkbox-check.outputs.result == 'true'
uses: actions/github-script@v8
with:

View File

@@ -1,4 +1,4 @@
name: Issue Triage
name: "Issue Checker"
on:
issues:
@@ -15,11 +15,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: MaaAssistantArknights/issue-checker@v1.14
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/issue-checker.yml

View File

@@ -1,4 +1,4 @@
name: Markdown Link Check
name: Check Dead Links
on:
schedule:
@@ -7,22 +7,22 @@ on:
branches:
- dev
paths:
- ".github/workflows/markdown-checker.yml"
- "docs/**"
- "**/*.md"
- "docs/**"
- ".github/workflows/markdown-checker.yml"
pull_request:
branches:
- dev
paths:
- ".github/workflows/markdown-checker.yml"
- "docs/**"
- "**/*.md"
- "docs/**"
- ".github/workflows/markdown-checker.yml"
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'
name: Check Dead Links
runs-on: ubuntu-latest
permissions:
pull-requests: write
@@ -30,18 +30,19 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout repository
- name: Check out code
uses: actions/checkout@v5
with:
show-progress: false
- name: Cache lychee responses
- name: Cache Primes
id: cache-primes
uses: actions/cache@v4
with:
path: .lycheecache
key: lychee-cache
- name: Check dead internal links
- name: Check dead links
uses: lycheeverse/lychee-action@v2
with:
# 仅检查内部链接,排除所有外部链接

71
.github/workflows/mirrorchyan.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
name: mirrorchyan
on:
workflow_dispatch:
jobs:
mirrorchyan:
runs-on: macos-latest
steps:
- name: Upload MAA win x64
uses: MirrorChyan/uploading-action@v1
if: always()
with:
filetype: latest-release
filename: "*MAA-*-win-x64.zip"
mirrorchyan_rid: MAA
github_token: ${{ secrets.GITHUB_TOKEN }}
owner: ${{ github.repository_owner }}
repo: ${{ github.event.repository.name }}
upload_token: ${{ secrets.MirrorChyanUploadToken }}
os: win
arch: x64
- name: Upload MAA win arm64
uses: MirrorChyan/uploading-action@v1
if: always()
with:
filetype: latest-release
filename: "*MAA-*-win-arm64.zip"
mirrorchyan_rid: MAA
github_token: ${{ secrets.GITHUB_TOKEN }}
owner: ${{ github.repository_owner }}
repo: ${{ github.event.repository.name }}
upload_token: ${{ secrets.MirrorChyanUploadToken }}
os: win
arch: arm64
- name: Upload MAA macos arm64
uses: MirrorChyan/uploading-action@v1
if: always()
with:
filetype: latest-release
filename: "MAA-*-macos-universal.dmg"
extra_zip: true
mirrorchyan_rid: MAA
github_token: ${{ secrets.GITHUB_TOKEN }}
owner: ${{ github.repository_owner }}
repo: ${{ github.event.repository.name }}
upload_token: ${{ secrets.MirrorChyanUploadToken }}
os: macos
arch: arm64
- name: Upload MAA macos x64
uses: MirrorChyan/uploading-action@v1
if: always()
with:
filetype: latest-release
filename: "MAA-*-macos-universal.dmg"
extra_zip: true
mirrorchyan_rid: MAA
github_token: ${{ secrets.GITHUB_TOKEN }}
owner: ${{ github.repository_owner }}
repo: ${{ github.event.repository.name }}
upload_token: ${{ secrets.MirrorChyanUploadToken }}
os: macos
arch: x64

View File

@@ -1,4 +1,5 @@
name: Release Note Distribution
name: mirrorchyan_release_note
on:
workflow_dispatch:
release:
@@ -6,11 +7,10 @@ on:
jobs:
mirrorchyan:
name: Upload to MirrorChyan
runs-on: macos-latest
steps:
- name: Upload release notes to MirrorChyan
- name: Release Note for MAA
uses: MirrorChyan/release-note-action@v1
with:
mirrorchyan_rid: MAA

View File

@@ -1,12 +1,14 @@
name: PNG Image Optimization
name: Optimize PNG Templates
on:
push:
branches:
- "dev"
paths:
- "docs/.vuepress/public/images/**"
- "resource/**/*.png"
- "docs/.vuepress/public/images/**"
- "website/apps/web/public/**"
- "website/apps/web/src/assets/links/**"
workflow_dispatch:
inputs:
commit_message:
@@ -16,13 +18,12 @@ on:
jobs:
optimize-png:
name: Optimize PNG Images
# Skip workflow to prevent double consecutive runs
# Skip workflow on PR merges
if: github.repository_owner == 'MaaAssistantArknights' && ${{ github.event.head_commit.author.email != '41898282+github-actions[bot]@users.noreply.github.com' }}
runs-on: ubuntu-latest
steps:
- name: Check if it is a direct push
- name: Check for direct push
id: check_push
run: |
if [[ "${{ github.event_name }}" != "workflow_dispatch" ]]; then
@@ -74,7 +75,7 @@ jobs:
with:
crate: oxipng
- name: Optimize png images
- name: Run optimize_templates
if: steps.check_push.outputs.is_pr != 'True'
run: |
python3 tools/OptimizeTemplates/optimize_templates.py

View File

@@ -1,4 +1,4 @@
name: Release Version Tagging
name: Auto Tag Release PR
on:
pull_request:
@@ -15,23 +15,22 @@ on:
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
- name: Checkout code
uses: actions/checkout@v5
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 +40,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,4 +1,4 @@
name: PR Commit Check
name: PR Checker
on:
pull_request_target:
@@ -6,11 +6,10 @@ on:
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
- name: Cleanup Previous Comment
uses: actions/github-script@v8
with:
script: |
@@ -28,7 +27,7 @@ jobs:
comment_id: previousComment.id
});
}
- name: Check commits
- name: Check Commits
uses: actions/github-script@v8
with:
script: |

View File

@@ -1,4 +1,4 @@
name: Release Pipeline (Nightly OTA)
name: release-nightly-ota
on:
schedule:
@@ -26,7 +26,6 @@ on:
jobs:
build-win-nightly:
name: Build Nightly for Windows
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: windows-latest
strategy:
@@ -39,8 +38,7 @@ jobs:
main_tag_name: ${{ steps.push_main_tag.outputs.main_tag_name }}
changelog: ${{ steps.read_changelog.outputs.content }}
steps:
- name: Checkout repository
uses: actions/checkout@v5
- uses: actions/checkout@v5
with:
# repository: 'MaaAssistantArknights/MaaAssistantArknights'
#ref: ${{ inputs.ref }}
@@ -53,7 +51,6 @@ jobs:
- name: Fetch submodules
run: |
git submodule update --init --depth 1 src/MaaUtils
git submodule update --init --depth 1 3rdparty/EmulatorExtras
- name: Checkout ref (if provided)
@@ -141,7 +138,7 @@ jobs:
gh run watch ${{ github.run_id }}
- name: Generate changelog
- 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 }}"
@@ -163,7 +160,7 @@ jobs:
gh run watch ${{ github.run_id }}
- name: Read changelog to variable
- name: Read Changelog to variable
id: read_changelog
uses: juliangruber/read-file-action@v1
with:
@@ -174,7 +171,7 @@ jobs:
uses: actions/cache@v4
with:
path: |
./src/MaaUtils/MaaDeps
./MaaDeps
key: ${{ runner.os }}-${{ matrix.arch }}-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- name: Bootstrap MaaDeps
@@ -211,7 +208,7 @@ jobs:
if: steps.cache-nuget.outputs.cache-hit != 'true'
run: dotnet restore src/MaaWpfGui/MaaWpfGui.csproj
- name: Taggify version for csproj
- name: Taggify Version for csproj
run: |
$csprojPath = "src/MaaWpfGui/MaaWpfGui.csproj"
$csprojPath = Resolve-Path -Path $csprojPath
@@ -233,7 +230,7 @@ jobs:
run: |
dotnet publish src/MaaWpfGui/MaaWpfGui.csproj -c Release -o install
- name: Organize install files
- name: Organize Install Files
shell: bash
run: |
rm -rf install/*.pdb
@@ -242,8 +239,8 @@ jobs:
cp tools/DependencySetup_依赖库安装.bat install
- name: Upload MAA to GitHub
uses: actions/upload-artifact@v5
- name: Upload MAA to Github
uses: actions/upload-artifact@v4
with:
name: MAA-win-${{ matrix.arch }}
path: install
@@ -260,12 +257,11 @@ jobs:
echo "main_tag_name=$main_tag_name" >> $env:GITHUB_OUTPUT
push-tag:
name: Push Tag to MaaRelease
if: github.repository_owner == 'MaaAssistantArknights'
needs: build-win-nightly
runs-on: ubuntu-latest
steps:
- name: Checkout MaaRelease
- name: Fetch MaaRelease
uses: actions/checkout@v5
with:
repository: ${{ format('{0}/{1}', github.repository_owner, 'MaaRelease') }}
@@ -284,7 +280,6 @@ 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:
@@ -292,24 +287,24 @@ jobs:
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
- name: Checkout code
uses: actions/checkout@v5
with:
path: MaaAssistantArknights
token: ${{ secrets.MAARELEASE_RELEASE }}
show-progress: false
- name: Download MAA from GitHub
uses: actions/download-artifact@v6
- name: Download MAA from Github
uses: actions/download-artifact@v5
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

View File

@@ -1,4 +1,4 @@
name: Release Pipeline (OTA)
name: release-ota
on:
release:
@@ -22,10 +22,9 @@ env:
jobs:
create-tag:
name: Create OTA Release Tag in MaaRelease
runs-on: ubuntu-latest
steps:
- name: Checkout MaaRelease
- name: Fetch MaaRelease
uses: actions/checkout@v5
with:
show-progress: false
@@ -34,7 +33,7 @@ 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
@@ -77,7 +76,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@v5
uses: actions/upload-artifact@v4
with:
name: MaaReleaseConfig
path: ./build-ota/config
@@ -87,7 +86,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,12 +94,12 @@ jobs:
- x64
steps:
- name: Download release config
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
name: MaaReleaseConfig
path: ./MaaReleaseConfig
- name: Checkout MaaRelease
- name: Fetch MaaRelease
uses: actions/checkout@v5
with:
show-progress: false
@@ -110,7 +108,7 @@ jobs:
fetch-depth: 0
token: ${{ secrets.MAARELEASE_RELEASE }}
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5
with:
show-progress: false
@@ -139,21 +137,20 @@ jobs:
prerelease: ${{ fromJSON(needs.create-tag.outputs.prerelease) }}
overwrite: true
- name: Create issue on failure
- name: Create issue if failed
if: failure()
uses: actions-cool/issues-helper@v3
with:
actions: "create-issue"
title: "Failed to make OTA release for Windows"
title: "Failed make release OTA 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 +166,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,7 +174,7 @@ 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
@@ -192,17 +189,16 @@ jobs:
prerelease: ${{ fromJSON(needs.create-tag.outputs.prerelease) }}
overwrite: true
- name: Create issue on failure
- name: Create issue if failed
if: failure()
uses: actions-cool/issues-helper@v3
with:
actions: "create-issue"
title: "Failed to make OTA release for macOS"
title: "Failed make release OTA 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,151 +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
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: macos-latest
continue-on-error: true
env:
RELEASE_TAG: ${{ needs.meta.outputs.RELEASE_TAG }}
steps:
- name: Upload MAA win x64
uses: MirrorChyan/uploading-action@v1
continue-on-error: true
with:
filetype: latest-release
filename: "*MAA-*-win-x64.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: win
arch: x64
- name: Upload MAA win arm64
uses: MirrorChyan/uploading-action@v1
continue-on-error: true
with:
filetype: latest-release
filename: "*MAA-*-win-arm64.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: win
arch: arm64
- name: Upload MAA macos arm64
uses: MirrorChyan/uploading-action@v1
continue-on-error: true
with:
filetype: latest-release
filename: "MAA-*-macos-universal.dmg"
extra_zip: true
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: macos
arch: arm64
- name: Upload MAA macos x64
uses: MirrorChyan/uploading-action@v1
continue-on-error: true
with:
filetype: latest-release
filename: "MAA-*-macos-universal.dmg"
extra_zip: true
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: macos
arch: x64
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 }}

View File

@@ -1,4 +1,4 @@
name: Game Resource Update
name: Resource Updater
on:
schedule:
@@ -12,7 +12,6 @@ on:
jobs:
clone-resources-official:
name: Download Official Resources
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
steps:
@@ -36,15 +35,14 @@ jobs:
/gamedata/excel/roguelike_topic_table.json
/gamedata/excel/activity_table.json
- name: Upload Official resources
uses: actions/upload-artifact@v5
- name: Upload Official
uses: actions/upload-artifact@v4
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:
@@ -79,15 +77,14 @@ jobs:
/kr/gamedata/excel/roguelike_topic_table.json
/kr/gamedata/excel/activity_table.json
- name: Upload Overseas resources
uses: actions/upload-artifact@v5
- name: Upload Official
uses: actions/upload-artifact@v4
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:
@@ -95,9 +92,10 @@ jobs:
uses: actions/checkout@v5
with:
show-progress: false
repository: arkntools/arknights-data-tw-for-maa
ref: main
repository: arkntools/arknights-toolbox-update
ref: data-tw
path: ./excel
token: ${{ secrets.ARKNTOOLS_MAA_RESOURCE_UPDATER}}
- name: Download stages.json from Penguin Stats
run: |
@@ -119,22 +117,21 @@ jobs:
done
wait
- name: Upload Taiwan resources
uses: actions/upload-artifact@v5
- name: Upload Official
uses: actions/upload-artifact@v4
with:
name: txwy
path: ./excel
compression-level: 0
update-game-resources:
name: Update Game Resources
# In case of rate limitations on the runners/instances, add dependency, by removing the comment
# needs: [clone-resources-official, clone-resources-overseas, clone-resources-txwy]
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: macos-latest
steps:
- name: Checkout repository
- name: Checkout MAA
uses: actions/checkout@v5
with:
# TL;DR https://github.com/MaaAssistantArknights/MaaAssistantArknights/issues/9872#issuecomment-2251378371
@@ -143,10 +140,6 @@ jobs:
show-progress: false
fetch-depth: 3
- name: Fetch submodules
run: |
git submodule update --init --depth 1 src/MaaUtils
- name: Restore ResourceUpdater from cache
id: resupd-cache
uses: actions/cache/restore@v4
@@ -162,7 +155,7 @@ jobs:
uses: actions/cache@v4
with:
path: |
./src/MaaUtils/MaaDeps
./MaaDeps
key: ${{ runner.os }}-arm64-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- name: Bootstrap MaaDeps
@@ -203,69 +196,69 @@ jobs:
- name: Download txwy
id: download-txwy
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
continue-on-error: true
with:
name: txwy
path: ./tools/ResourceUpdater/Overseas/tw/gamedata/excel
- name: Delay for txwy
- name: Delay txwy
if: steps.download-txwy.outcome == 'failure'
run: |
sleep 30
sleep 10
- name: Re-download txwy
if: steps.download-txwy.outcome == 'failure'
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
name: txwy
path: ./tools/ResourceUpdater/Overseas/tw/gamedata/excel
- name: Download Overseas
id: download-overseas
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
continue-on-error: true
with:
name: overseas
path: ./tools/ResourceUpdater/Overseas
- name: Delay for Overseas
- name: Delay Overseas
if: steps.download-overseas.outcome == 'failure'
run: |
sleep 30
sleep 10
- name: Re-download Overseas
if: steps.download-overseas.outcome == 'failure'
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
name: overseas
path: ./tools/ResourceUpdater/Overseas
- name: Download Official
id: download-official
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
continue-on-error: true
with:
name: official
path: ./tools/ResourceUpdater/Official
- name: Delay for Official
- name: Delay Official
if: steps.download-official.outcome == 'failure'
run: |
sleep 30
sleep 10
- name: Re-download Official
if: steps.download-official.outcome == 'failure'
uses: actions/download-artifact@v6
uses: actions/download-artifact@v5
with:
name: official
path: ./tools/ResourceUpdater/Official
- name: Update resources
- name: Run Resource Updater
run: |
./tools/ResourceUpdater/res_updater
- name: Sort tasks
- name: Task Sorting
id: task_sorting
run: |
python3 tools/TaskSorter/TaskSorter.py
@@ -279,7 +272,7 @@ jobs:
- name: Update version.json date if necessary
id: update_version
run: |
sh ./tools/ResourceUpdater/version.sh
./tools/ResourceUpdater/version.zsh
- name: Setup python
if: steps.update_version.outputs.contains_png == 'True'
@@ -287,7 +280,7 @@ jobs:
with:
python-version: "3.11"
- name: Cache python packages
- name: Cache Python packages
if: always() && steps.update_version.outputs.contains_png == 'True'
id: cache_python
uses: actions/cache@v4
@@ -306,12 +299,12 @@ jobs:
with:
crate: oxipng
- name: Optimize png images
- name: Run optimize_templates
if: steps.update_version.outputs.contains_png == 'True'
run: |
python3 tools/OptimizeTemplates/optimize_templates.py -p resource/template/items/ resource/template/infrast/
- name: Commit changes
- name: Add files to git
if: steps.update_version.outputs.changes == 'True'
id: add_files
run: |
@@ -356,27 +349,4 @@ jobs:
# - 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: Add a comment on failure
if: failure() && github.ref == 'refs/heads/dev'
run: |
# Minimize old failure comments
comments=$(gh api repos/${{ github.repository }}/issues/14493/comments --jq '.[] | select(.user.login == "github-actions[bot]") | select(.body | contains("${{ github.server_url }}/${{ github.repository }}/actions/runs/")) | .node_id')
for node_id in $comments; do
gh api graphql -f query='
mutation {
minimizeComment(input: {subjectId: "'"$node_id"'", classifier: OUTDATED}) {
minimizedComment {
isMinimized
}
}
}'
done
# Add new failure comment
gh issue comment 14493 -R ${{ github.repository }} --body "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
@MistEO @ABA2396 @Constrat"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# gh workflow run release-nightly-ota -f release_body="Auto Release of Resource Updates"

View File

@@ -1,44 +1,41 @@
name: Smoke Test
name: smoke-testing
on:
push:
paths:
- ".github/workflows/smoke-testing.yml"
- "3rdparty/include/**"
- "cmake/**"
- "CMakeLists.txt"
- "include/**"
- "resource/**"
- "src/Cpp/**"
- "src/MaaCore/**"
- "tools/maadeps-download.py"
- "tools/SmokeTesting/**"
- "resource/**"
- "!**/*.md"
- "cmake/**"
- "CMakeLists.txt"
- "tools/maadeps-download.py"
pull_request:
paths:
- ".github/workflows/smoke-testing.yml"
- "3rdparty/include/**"
- "cmake/**"
- "CMakeLists.txt"
- "include/**"
- "resource/**"
- "src/Cpp/**"
- "src/MaaCore/**"
- "tools/maadeps-download.py"
- "tools/SmokeTesting/**"
- "resource/**"
- "!**/*.md"
- "cmake/**"
- "CMakeLists.txt"
- "tools/maadeps-download.py"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.ref == 'refs/heads/dev' && github.sha }}
cancel-in-progress: true
jobs:
smoke-testing:
name: Run Smoke Test
# Prevent duplicate runs on organization branches with PRs
if: github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name !=
github.event.pull_request.base.repo.full_name
runs-on: macos-latest
steps:
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v5
with:
show-progress: false
@@ -48,7 +45,7 @@ jobs:
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
- name: Restore cache smoke-testing
id: smoke-cache
uses: actions/cache/restore@v4
with:
@@ -56,7 +53,6 @@ jobs:
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
@@ -64,14 +60,14 @@ jobs:
- name: Fetch submodules
if: steps.smoke-cache.outputs.cache-hit != 'true'
run: |
git submodule update --init --depth 1 src/MaaUtils
git submodule update --init --depth 1 3rdparty/EmulatorExtras
- name: Cache MaaDeps
if: steps.smoke-cache.outputs.cache-hit != 'true'
id: maadeps-cache
uses: actions/cache@v4
with:
path: ./src/MaaUtils/MaaDeps
path: ./MaaDeps
key: ${{ runner.os }}-arm64-maadeps-${{ hashFiles('tools/maadeps-download.py') }}
- name: Bootstrap MaaDeps
@@ -103,23 +99,22 @@ jobs:
mkdir -p install
cmake --install build --prefix install --config Debug
- name: Make link to Smoke Test cache
- name: Make link for cache smoke-testing
if: steps.smoke-cache.outputs.cache-hit == 'true'
run: |
ln -s "$(pwd)/resource" install/resource
- name: Run tests
run: |
sh ./tools/SmokeTesting/run_tests.sh
./tools/SmokeTesting/run_tests.zsh
- name: Save cache for Smoke Test (only in dev)
- name: Save cache smoke-testing (only in dev)
if: steps.smoke-cache.outputs.cache-hit != 'true' && github.ref == 'refs/heads/dev'
uses: actions/cache/save@v4
with:
key: ${{ steps.cache_key.outputs.key }}
path: |
./install/libfastdeploy_ppocr.dylib
./install/libMaaUtils.dylib
./install/libMaaCore.dylib
./install/libonnxruntime.1.19.2.dylib
./install/libopencv_world4.4.11.0.dylib
@@ -127,7 +122,7 @@ jobs:
- name: Upload logs
if: always()
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v4
with:
name: logs
path: ./install/debug

View File

@@ -1,4 +1,4 @@
name: Issue Staleness Management
name: "Inactive Issues Closer"
on:
schedule:
@@ -11,7 +11,6 @@ env: # config
jobs:
stale:
name: Handle Stale Issues
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
permissions:
@@ -21,7 +20,7 @@ jobs:
pull-requests: none
contents: none
steps:
- name: Handle stale issues
- name: Close inactive issues
uses: actions/stale@v10
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,27 +1,26 @@
name: Resource Sync
name: sync-resource
on:
push:
branches:
- dev
paths:
- ".github/workflows/sync-resource.yml"
- "resource/**"
- ".github/workflows/sync-resource.yml"
workflow_dispatch:
jobs:
sync-resource:
name: Sync Resource to MaaResource
if: github.repository_owner == 'MaaAssistantArknights'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- name: Checkout MaaAssistantArknights
uses: actions/checkout@v5
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'

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@v5
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:
@@ -24,51 +24,48 @@ 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
steps:
- name: Checkout repository
- name: Checkout
uses: actions/checkout@v5
with:
show-progress: false
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
package_json_file: "./docs/package.json"
version: 10
run_install: false
- name: Setup node.js environment
uses: actions/setup-node@v6
- name: Setup Node.js environment
uses: actions/setup-node@v5
with:
node-version: 24
cache: pnpm
cache-dependency-path: "./docs/pnpm-lock.yaml"
- name: Install node modules
- name: Install dependencies
run: pnpm install --frozen-lockfile
working-directory: "./docs"
- name: Build
- name: Build documentation
run: pnpm run build
working-directory: "./docs"
- name: Upload artifact to GitHub
uses: actions/upload-artifact@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
name: dist
path: "./docs/.vuepress/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')
if: ${{ inputs.deploy-to-prod == true || github.ref == 'refs/heads/master' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: "./docs/.vuepress/dist"

3
.gitignore vendored
View File

@@ -457,8 +457,7 @@ tools/RoguelikeRecruitmentTool/output
.lycheecache
# MaaDeps
MaaDeps
src/MaaUtils/*
/MaaDeps/*
# ResourceUpdater workflow
/original/*

3
.gitmodules vendored
View File

@@ -10,6 +10,3 @@
[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

View File

@@ -4,47 +4,36 @@ ci:
autofix_prs: true
repos:
- repo: https://github.com/shssoichiro/oxipng
rev: v9.1.5
rev: v9.1.4
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.1
rev: v20.1.0
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.6.2
rev: v3.5.3
hooks:
- id: prettier
name: Prettier (Config Files)
name: prettier (config files)
files: ^((\.github/ISSUE_TEMPLATE|resource|src|tools)/.*|\.pre-commit-config\.yaml|package-definition\.json)
types_or:
- yaml
- json
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.5.3
hooks:
- id: prettier
name: Prettier (Documentation)
name: prettier (docs)
files: ^docs/.*
- repo: https://github.com/psf/black
rev: 25.9.0
hooks:
- id: black
name: Black Formatter (Python)
- repo: https://github.com/pycqa/isort
rev: 6.0.1
hooks:
- id: isort
name: Isort (Python)
args: ["--profile", "black", "--filter-files"]
- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.18.1
rev: v0.17.2
hooks:
- id: markdownlint-cli2
name: MarkdownLint (Documentation)
files: ^docs/.*|^README\.md$
types:
- markdown
args: ["--fix", "--config", "docs/.markdownlint.yaml"]
args: ["--fix", "--config", "docs/.markdownlint.yaml", "#**/node_modules"]

View File

@@ -1,12 +1,18 @@
**/node_modules/
**/pnpm-lock.yaml
docs/**/*.md
MaaDeps/
3rdparty/
src/maa-cli
src/MaaMacGui
src/MaaUtils
# website/
# docs/
resource/Arknights-Tile-Pos/
tools/OptimizeTemplates/optimize_templates.json
CITATION.cff
CHANGELOG.md
## FUCK FUCK

View File

@@ -0,0 +1,688 @@
// IWYU pragma: private, include <meojson/json.hpp>
#pragma once
#include <initializer_list>
#include <optional>
#include <ostream>
#include <string>
#include <tuple>
#include <type_traits>
#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 jsonization_t,
std::enable_if_t<
_utils::has_to_json_in_templ_spec<jsonization_t, string_t>::value
&& !_utils::has_to_json_array_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
basic_array(const jsonization_t& value)
: basic_array(ext::jsonization<string_t, jsonization_t>().to_json(value))
{
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_to_json_array_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
basic_array(const jsonization_t& value)
: basic_array(ext::jsonization<string_t, jsonization_t>().to_json_array(value))
{
}
template <
typename jsonization_t,
std::enable_if_t<
std::is_rvalue_reference_v<jsonization_t&&>
&& _utils::has_move_to_json_in_templ_spec<jsonization_t, string_t>::value
&& !_utils::has_move_to_json_array_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
basic_array(jsonization_t&& value)
: basic_array(ext::jsonization<string_t, jsonization_t>().move_to_json(std::move(value)))
{
}
//template <
// typename jsonization_t,
// std::enable_if_t<
// std::is_rvalue_reference_v<jsonization_t&&>
// && _utils::has_move_to_json_array_in_templ_spec<jsonization_t, string_t>::value,
// bool> = true>
//basic_array(jsonization_t&& value)
// : basic_array(
// ext::jsonization<string_t, jsonization_t>().move_to_json_array(std::move(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;
template <
typename value_t,
size_t Size,
template <typename, size_t> typename fixed_array_t = std::array>
fixed_array_t<value_t, Size> as_fixed_array() const;
template <typename... elem_ts>
std::tuple<elem_ts...> as_tuple() const;
template <typename first_t, typename second_t>
std::pair<first_t, second_t> as_pair() const;
template <
typename value_t,
std::enable_if_t<
_utils::has_from_json_array_in_templ_spec<value_t, string_t>::value,
bool> = true>
value_t as() const&
{
value_t res;
ext::jsonization<string_t, value_t>().from_json_array(*this, res);
return res;
}
template <
typename value_t,
std::enable_if_t<
_utils::has_move_from_json_array_in_templ_spec<value_t, string_t>::value,
bool> = true>
value_t as() &&
{
value_t res;
ext::jsonization<string_t, value_t>().move_from_json_array(std::move(*this), res);
return res;
}
// 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 value_t,
size_t Size,
template <typename, size_t> typename fixed_array_t = std::array,
std::enable_if_t<_utils::is_fixed_array<fixed_array_t<value_t, Size>>, bool> = true>
explicit operator fixed_array_t<value_t, Size>() const
{
return as<fixed_array_t<value_t, Size>>();
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_from_json_in_templ_spec<jsonization_t, string_t>::value
&& !_utils::has_from_json_array_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
explicit operator jsonization_t() const&
{
jsonization_t dst {};
if (!ext::jsonization<string_t, jsonization_t>().from_json(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_from_json_array_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
explicit operator jsonization_t() const&
{
jsonization_t dst {};
if (!ext::jsonization<string_t, jsonization_t>().from_json_array(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_move_from_json_in_templ_spec<jsonization_t, string_t>::value
&& !_utils::has_move_from_json_array_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
explicit operator jsonization_t() &&
{
jsonization_t dst {};
if (!ext::jsonization<string_t, jsonization_t>().from_json(std::move(*this), dst)) {
throw exception("Wrong JSON");
}
return dst;
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_move_from_json_array_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
explicit operator jsonization_t() &&
{
jsonization_t dst {};
if (!ext::jsonization<string_t, jsonization_t>().move_from_json_array(
std::move(*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;
}
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
{
return as<collection_t<value_t>>();
}
template <typename string_t>
template <typename value_t, size_t Size, template <typename, size_t> typename fixed_array_t>
inline fixed_array_t<value_t, Size> basic_array<string_t>::as_fixed_array() const
{
return as<fixed_array_t<value_t, Size>>();
}
template <typename string_t>
template <typename... elem_ts>
inline std::tuple<elem_ts...> basic_array<string_t>::as_tuple() const
{
return as<std::tuple<elem_ts...>>();
}
template <typename string_t>
template <typename first_t, typename second_t>
inline std::pair<first_t, second_t> basic_array<string_t>::as_pair() const
{
return as<std::pair<first_t, second_t>>();
}
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,35 @@
// IWYU pragma: private, include <meojson/json.hpp>
#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,605 @@
// IWYU pragma: private, include <meojson/json.hpp>
#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 jsonization_t,
std::enable_if_t<
_utils::has_to_json_in_templ_spec<jsonization_t, string_t>::value
&& !_utils::has_to_json_object_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
basic_object(const jsonization_t& value)
: basic_object(ext::jsonization<string_t, jsonization_t>().to_json(value))
{
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_to_json_object_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
basic_object(const jsonization_t& value)
: basic_object(ext::jsonization<string_t, jsonization_t>().to_json_object(value))
{
}
template <
typename jsonization_t,
std::enable_if_t<
std::is_rvalue_reference_v<jsonization_t&&>
&& _utils::has_move_to_json_in_templ_spec<jsonization_t, string_t>::value
&& !_utils::has_move_to_json_object_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
basic_object(jsonization_t&& value)
: basic_object(ext::jsonization<string_t, jsonization_t>().move_to_json(std::move(value)))
{
}
//template <
// typename jsonization_t,
// std::enable_if_t<
// std::is_rvalue_reference_v<jsonization_t&&>
// && _utils::has_move_to_json_object_in_templ_spec<jsonization_t, string_t>::value,
// bool> = true>
//basic_object(jsonization_t&& value)
// : basic_object(
// ext::jsonization<string_t, jsonization_t>().move_to_json_object(std::move(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;
template <
typename value_t,
std::enable_if_t<
_utils::has_from_json_object_in_templ_spec<value_t, string_t>::value,
bool> = true>
value_t as() const&
{
value_t res;
ext::jsonization<string_t, value_t>().from_json_object(*this, res);
return res;
}
template <
typename value_t,
std::enable_if_t<
_utils::has_move_from_json_object_in_templ_spec<value_t, string_t>::value,
bool> = true>
value_t as() &&
{
value_t res;
ext::jsonization<string_t, value_t>().move_from_json_object(std::move(*this), res);
return res;
}
// 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_templ_spec<jsonization_t, string_t>::value
&& !_utils::has_from_json_object_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
explicit operator jsonization_t() const&
{
jsonization_t dst {};
if (!ext::jsonization<string_t, jsonization_t>().from_json(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_from_json_object_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
explicit operator jsonization_t() const&
{
jsonization_t dst {};
if (!ext::jsonization<string_t, jsonization_t>().from_json_object(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_move_from_json_in_templ_spec<jsonization_t, string_t>::value
&& !_utils::has_move_from_json_object_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
explicit operator jsonization_t() &&
{
jsonization_t dst {};
if (!ext::jsonization<string_t, jsonization_t>().from_json(std::move(*this), dst)) {
throw exception("Wrong JSON");
}
return dst;
}
template <
typename jsonization_t,
std::enable_if_t<
_utils::has_move_from_json_object_in_templ_spec<jsonization_t, string_t>::value,
bool> = true>
explicit operator jsonization_t() &&
{
jsonization_t dst {};
if (!ext::jsonization<string_t, jsonization_t>().move_from_json_object(
std::move(*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.insert_or_assign(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
{
return as<map_t<string_t, value_t>>();
}
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,207 @@
// IWYU pragma: private, include <meojson/json.hpp>
#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 (
_utils::is_collection<std::decay_t<in_t>> || _utils::is_fixed_array<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 if constexpr (std::is_constructible_v<basic_value<string_t>, in_t>) {
return basic_value<string_t>(std::forward<in_t>(in));
}
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 (_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_fixed_array<std::decay_t<out_t>>) {
if (!in.is_array()) {
return false;
}
auto&& in_as_arr = in.as_array();
constexpr size_t out_size = _utils::fixed_array_size<out_t>;
if (in_as_arr.size() != out_size) {
return false;
}
for (size_t i = 0; i < out_size; ++i) {
auto&& j_elem = in_as_arr.at(i);
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.at(i) = 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::mapped_type;
elem_t elem {};
if (!deserialize<elem_t, deserializer_t, string_t>(j_elem, elem, deserializer)) {
return false;
}
out.emplace(std::forward<decltype(key)>(key), std::move(elem));
}
return true;
}
else if constexpr (std::is_constructible_v<out_t, basic_value<string_t>>) {
out = out_t(in);
return true;
}
else {
_serialization_helper::unable_to_deserialize<out_t>();
}
}
} // namespace json

View File

@@ -0,0 +1,7 @@
// IWYU pragma: private, include <meojson/json.hpp>
#pragma once
#include "array.hpp"
#include "object.hpp"
#include "value.hpp"

View File

@@ -0,0 +1,473 @@
// IWYU pragma: private, include <meojson/json.hpp>
#pragma once
#include <iomanip>
#include <limits>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
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 string_t, typename T, typename = void>
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_fixed_array = false;
template <template <typename, size_t> typename arr_t, typename value_t, size_t size>
constexpr bool is_fixed_array<arr_t<value_t, size>> = true;
template <typename T, typename = std::enable_if_t<is_fixed_array<T>>>
constexpr size_t fixed_array_size = 0;
template <template <typename, size_t> typename arr_t, typename value_t, size_t size>
constexpr size_t fixed_array_size<arr_t<value_t, size>> = size;
template <typename T, typename = void>
constexpr bool is_collection = false;
template <typename T>
constexpr bool is_collection<T> = is_container<T> && !is_map<T> && !is_fixed_array<T>;
template <typename T>
constexpr bool is_variant = false;
template <typename... args_t>
constexpr bool is_variant<std::variant<args_t...>> = true;
template <typename T>
constexpr bool is_tuple = false;
template <typename... args_t>
constexpr bool is_tuple<std::tuple<args_t...>> = true;
template <typename T>
constexpr bool is_pair = false;
template <typename... args_t>
constexpr bool is_pair<std::pair<args_t...>> = true;
template <typename T, typename = void>
constexpr bool is_tuple_like = false;
template <template <typename...> typename tuple_t, typename... args_t>
constexpr bool is_tuple_like<
tuple_t<args_t...>,
std::void_t<decltype(std::tuple_size<tuple_t<args_t...>>::value)>> =
std::tuple_size<tuple_t<args_t...>>::value == sizeof...(args_t);
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 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, 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_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_to_json_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, 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_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, 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_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, 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 T, typename string_t>
class has_move_to_json_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().move_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_move_from_json_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().move_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 T, typename string_t>
class has_to_json_array_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().to_json_array(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_array_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().check_json_array(std::declval<json::basic_array<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_array_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().from_json_array(std::declval<json::basic_array<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 T, typename string_t>
class has_move_to_json_array_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().move_to_json_array(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_move_from_json_array_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().move_from_json_array(std::declval<json::basic_array<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 T, typename string_t>
class has_to_json_object_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().to_json_object(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_object_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().check_json_object(std::declval<json::basic_object<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_object_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().from_json_object(std::declval<json::basic_object<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 T, typename string_t>
class has_move_to_json_object_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().move_to_json_object(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_move_from_json_object_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(std::declval<ext::jsonization<string_t, U>>().move_from_json_object(std::declval<json::basic_object<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>
inline 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>
inline constexpr string_t true_string()
{
return { 't', 'r', 'u', 'e' };
}
template <typename string_t>
inline constexpr string_t false_string()
{
return { 'f', 'a', 'l', 's', 'e' };
}
template <typename string_t>
inline constexpr string_t null_string()
{
return { 'n', 'u', 'l', 'l' };
}
template <typename string_t, typename any_t>
inline string_t to_basic_string(any_t&& arg)
{
#ifdef MEOJSON_KEEP_FLOATING_PRECISION
using real_type = std::remove_reference_t<any_t>;
if constexpr (std::is_floating_point_v<real_type>) {
if constexpr (std::is_same_v<string_t, std::string>) {
std::ostringstream oss;
oss << std::setprecision(std::numeric_limits<real_type>::max_digits10) << arg;
return oss.str();
}
else if constexpr (std::is_same_v<string_t, std::wstring>) {
std::wostringstream oss;
oss << std::setprecision(std::numeric_limits<real_type>::max_digits10) << arg;
return oss.str();
}
}
#endif
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
#include "../reflection/extensions.hpp"

1142
3rdparty/include/meojson/common/value.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,9 @@
#pragma once
// IWYU pragma: begin_exports
#include "common/types.hpp"
#include "parser/parser.hpp"
#include "reflection/jsonization.hpp"
// IWYU pragma: end_exports

View File

@@ -0,0 +1,139 @@
// IWYU pragma: private, include <meojson/json.hpp>
#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,161 @@
// IWYU pragma: private, include <meojson/json.hpp>
#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;
};
}
#ifndef MEOJSON_DISABLE_PACKED_BYTES
#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
#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,79 @@
// IWYU pragma: private, include <meojson/json.hpp>
#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,140 @@
// IWYU pragma: private, include <meojson/json.hpp>
#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,862 @@
// IWYU pragma: private, include <meojson/json.hpp>
#pragma once
#include <cctype>
#include <fstream>
#include <optional>
#include <ostream>
#include <string>
#include <tuple>
#include "../common/types.hpp"
#include "packed_bytes.hpp"
namespace json
{
// ****************************
// * parser declare *
// ****************************
template <
bool accept_jsonc = false,
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_comment() noexcept;
bool skip_digit();
bool skip_unicode_escape(uint16_t& pair_high, string_t& result);
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 parsing_t>
auto parsec(const parsing_t& content);
template <typename char_t>
auto parsec(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 = false, bool with_commets = false);
template <typename ifstream_t = std::ifstream, typename path_t = void>
auto open(const path_t& path, bool check_bom = false, bool with_commets = 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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline std::optional<basic_value<string_t>>
parser<accept_jsonc, string_t, parsing_t, accel_traits>::parse(const parsing_t& content)
{
return parser<accept_jsonc, string_t, parsing_t, accel_traits>(content.cbegin(), content.cend())
.parse();
}
template <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline std::optional<basic_value<string_t>>
parser<accept_jsonc, 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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<accept_jsonc, 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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<accept_jsonc, 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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t>
parser<accept_jsonc, 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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<accept_jsonc, 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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<accept_jsonc, 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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<accept_jsonc, 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>();
}
if constexpr (accept_jsonc) {
if (*_cur == ']') {
break;
}
}
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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline basic_value<string_t> parser<accept_jsonc, 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>();
}
if constexpr (accept_jsonc) {
if (*_cur == '}') {
break;
}
}
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>();
}
auto emplaced = result.emplace(std::move(*key_opt), std::move(val)).second;
if (!emplaced) {
return invalid_value<string_t>();
}
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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline std::optional<string_t>
parser<accept_jsonc, string_t, parsing_t, accel_traits>::parse_stdstring()
{
if (*_cur == '"') {
++_cur;
}
else {
return std::nullopt;
}
string_t result;
auto no_escape_beg = _cur;
uint16_t pair_high = 0;
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;
}
if (pair_high && *_cur != 'u') {
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':
if (!skip_unicode_escape(pair_high, result)) {
return std::nullopt;
}
break;
default:
// Illegal backslash escape
return std::nullopt;
}
no_escape_beg = ++_cur;
break;
}
case '"': {
if (pair_high) {
return std::nullopt;
}
result += string_t(no_escape_beg, _cur++);
return result;
}
default:
if (pair_high) {
return std::nullopt;
}
++_cur;
break;
}
}
return std::nullopt;
}
template <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline bool parser<accept_jsonc, string_t, parsing_t, accel_traits>::skip_unicode_escape(
uint16_t& pair_high,
string_t& result)
{
uint16_t cp = 0;
for (int i = 0; i < 4; ++i) {
if (++_cur == _end) {
return false;
}
if (!std::isxdigit(static_cast<unsigned char>(*_cur))) {
return false;
}
cp <<= 4;
if ('0' <= *_cur && *_cur <= '9') {
cp |= *_cur - '0';
}
else if ('a' <= *_cur && *_cur <= 'f') {
cp |= *_cur - 'a' + 10;
}
else if ('A' <= *_cur && *_cur <= 'F') {
cp |= *_cur - 'A' + 10;
}
else {
return false;
}
}
uint32_t ext_cp = cp;
uint16_t hi_cp = 0, lo_cp = 0;
if (0xD800 <= cp && cp <= 0xDBFF) {
if (pair_high) {
return false;
}
pair_high = cp;
return true;
}
if (0xDC00 <= cp && cp <= 0xDFFF) {
if (!pair_high) {
return false;
}
ext_cp = (((pair_high - 0xD800) << 10) | (cp - 0xDC00)) + 0x10000;
hi_cp = pair_high;
lo_cp = cp;
pair_high = 0;
}
if constexpr (std::is_same_v<typename string_t::value_type, char>) {
// utf8
if (ext_cp <= 0x7F) {
result.push_back(static_cast<char>(ext_cp));
}
else if (ext_cp <= 0x7FF) {
result.push_back(static_cast<char>(((ext_cp >> 6) & 0b00011111) | 0b11000000u));
result.push_back(static_cast<char>((ext_cp & 0b00111111) | 0b10000000u));
}
else if (ext_cp <= 0xFFFF) {
result.push_back(static_cast<char>(((ext_cp >> 12) & 0b00001111) | 0b11100000u));
result.push_back(static_cast<char>(((ext_cp >> 6) & 0b00111111) | 0b10000000u));
result.push_back(static_cast<char>((ext_cp & 0b00111111) | 0b10000000u));
}
else {
result.push_back(static_cast<char>(((ext_cp >> 18) & 0b00000111) | 0b11110000u));
result.push_back(static_cast<char>(((ext_cp >> 12) & 0b00111111) | 0b10000000u));
result.push_back(static_cast<char>(((ext_cp >> 6) & 0b00111111) | 0b10000000u));
result.push_back(static_cast<char>((ext_cp & 0b00111111) | 0b10000000u));
}
}
else if constexpr (std::is_same_v<typename string_t::value_type, wchar_t>) {
if constexpr (sizeof(wchar_t) == 4) {
result.push_back(static_cast<wchar_t>(ext_cp));
}
else if constexpr (sizeof(wchar_t) == 2) {
if (ext_cp <= 0xFFFF) {
result.push_back(static_cast<wchar_t>(ext_cp));
}
else {
result.push_back(static_cast<wchar_t>(hi_cp));
result.push_back(static_cast<wchar_t>(lo_cp));
}
}
else {
static_assert(!sizeof(typename string_t::value_type), "Unsupported wchar");
}
}
else {
static_assert(!sizeof(typename string_t::value_type), "Unsupported type");
}
return true;
}
template <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline bool
parser<accept_jsonc, 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 <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline bool parser<accept_jsonc, string_t, parsing_t, accel_traits>::skip_whitespace() noexcept
{
while (_cur != _end) {
switch (*_cur) {
case ' ':
case '\t':
case '\r':
case '\n':
++_cur;
break;
case '/':
if constexpr (accept_jsonc) {
if (!skip_comment()) {
return false;
}
// else continue;
}
else {
return false;
}
break;
case '\0':
return false;
default:
return true;
}
}
return false;
}
template <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
bool json::parser<accept_jsonc, string_t, parsing_t, accel_traits>::skip_comment() noexcept
{
if (_cur == _end || *_cur != '/') {
return false;
}
if (++_cur == _end) {
return false;
}
enum class comment_type
{
invalid,
line,
block,
} t = comment_type::invalid;
switch (*_cur++) {
case '/':
t = comment_type::line;
break;
case '*':
t = comment_type::block;
break;
default:
return false;
}
while (_cur != _end) {
switch (*_cur++) {
case '\n':
if (t == comment_type::line) {
return true;
}
break;
case '*':
if (t == comment_type::block && _cur != _end && *_cur == '/') {
++_cur;
return true;
}
break;
default:
break;
}
}
// _cur == _end
return t == comment_type::line;
}
template <bool accept_jsonc, typename string_t, typename parsing_t, typename accel_traits>
inline bool parser<accept_jsonc, 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<false, 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, bool with_commets)
{
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 with_commets ? parsec(str) : parse(str);
}
template <typename ifstream_t, typename path_t>
auto open(const path_t& filepath, bool check_bom, bool with_commets)
{
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, with_commets);
ifs.close();
return opt;
}
template <typename parsing_t>
auto parsec(const parsing_t& content)
{
using string_t = std::basic_string<typename parsing_t::value_type>;
return parser<true, string_t, parsing_t>::parse(content);
}
template <typename char_t>
auto parsec(char_t* content)
{
return parsec(std::basic_string_view<std::decay_t<char_t>> { content });
}
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

View File

@@ -0,0 +1,656 @@
// IWYU pragma: private, include <meojson/json.hpp>
#pragma once
#include <cstddef>
#include <filesystem>
#include <queue>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include "../common/types.hpp"
namespace json::ext
{
template <typename string_t, typename impl_t, typename var_t, size_t len> // (size_t)-1 for no
// restriction
class __jsonization_array
{
public:
json::basic_value<string_t> to_json(const var_t& value) const
{
return static_cast<const impl_t*>(this)->to_json_array(value);
}
bool check_json(const json::basic_value<string_t>& json) const
{
if (!json.is_array()) {
return false;
}
const auto& arr = json.as_array();
if constexpr (len != static_cast<size_t>(-1)) {
if (len != arr.size()) {
return false;
}
}
return static_cast<const impl_t*>(this)->check_json_array(arr);
}
bool from_json(const json::basic_value<string_t>& json, var_t& value) const
{
if (!json.is_array()) {
return false;
}
const auto& arr = json.as_array();
if constexpr (len != static_cast<size_t>(-1)) {
if (len != arr.size()) {
return false;
}
}
return static_cast<const impl_t*>(this)->from_json_array(arr, value);
}
json::basic_value<string_t> move_to_json(var_t value) const
{
return static_cast<const impl_t*>(this)->move_to_json_array(std::move(value));
}
bool move_from_json(json::basic_value<string_t> json, var_t& value) const
{
if (!json.is_array()) {
return false;
}
auto& arr = json.as_array();
if constexpr (len != static_cast<size_t>(-1)) {
if (len != arr.size()) {
return false;
}
}
return static_cast<const impl_t*>(this)->move_from_json_array(std::move(arr), value);
}
};
template <typename string_t, typename impl_t, typename var_t>
class __jsonization_object
{
public:
json::basic_value<string_t> to_json(const var_t& value) const
{
return static_cast<const impl_t*>(this)->to_json_object(value);
}
bool check_json(const json::basic_value<string_t>& json) const
{
if (!json.is_object()) {
return false;
}
const auto& obj = json.as_object();
return static_cast<const impl_t*>(this)->check_json_object(obj);
}
bool from_json(const json::basic_value<string_t>& json, var_t& value) const
{
if (!json.is_object()) {
return false;
}
const auto& obj = json.as_object();
return static_cast<const impl_t*>(this)->from_json_object(obj, value);
}
json::basic_value<string_t> move_to_json(var_t value) const
{
return static_cast<const impl_t*>(this)->move_to_json_object(std::move(value));
}
bool move_from_json(json::basic_value<string_t> json, var_t& value) const
{
if (!json.is_object()) {
return false;
}
auto& obj = json.as_object();
return static_cast<const impl_t*>(this)->move_from_json_object(std::move(obj), value);
}
};
template <typename string_t>
class jsonization<string_t, std::nullptr_t>
{
public:
json::basic_value<string_t> to_json(const std::nullptr_t&) const
{
return json::basic_value<string_t> {};
}
bool check_json(const json::basic_value<string_t>& json) const { return json.is_null(); }
bool from_json(const json::basic_value<string_t>& json, std::nullptr_t&)
{
return check_json(json);
}
};
template <typename string_t>
class jsonization<
string_t,
std::filesystem::path,
std::enable_if_t<
std::is_same_v<string_t, std::filesystem::path::string_type>
|| std::is_same_v<string_t, std::string>>>
{
public:
json::basic_value<string_t> to_json(const std::filesystem::path& path) const
{
if constexpr (std::is_same_v<string_t, std::filesystem::path::string_type>) {
return path.native();
}
else if constexpr (std::is_same_v<string_t, std::string>) {
#if __cplusplus >= 202002L
std::u8string u8str = path.u8string();
return std::string { u8str.begin(), u8str.end() };
#else
return path.u8string();
#endif
}
#if __cplusplus >= 202002L
else if constexpr (std::is_same_v<string_t, std::u8string>) {
return path.u8string();
}
#endif
}
bool check_json(const json::basic_value<string_t>& json) const { return json.is_string(); }
bool from_json(const json::basic_value<string_t>& json, std::filesystem::path& path) const
{
path = json.as_string();
return true;
}
};
template <
typename string_t,
template <typename, size_t> typename arr_t,
typename value_t,
size_t size>
class jsonization<string_t, arr_t<value_t, size>>
: public __jsonization_array<
string_t,
jsonization<string_t, arr_t<value_t, size>>,
arr_t<value_t, size>,
size>
{
public:
json::basic_array<string_t> to_json_array(const arr_t<value_t, size>& value) const
{
json::basic_array<string_t> result;
for (size_t i = 0; i < size; i++) {
result.emplace_back(value.at(i));
}
return result;
}
bool check_json_array(const json::basic_array<string_t>& arr) const
{
return arr.template all<value_t>();
}
bool from_json_array(const json::basic_array<string_t>& arr, arr_t<value_t, size>& value) const
{
if (!check_json_array(arr)) {
return false;
}
for (size_t i = 0; i < size; i++) {
value.at(i) = arr[i].template as<value_t>();
}
return true;
}
json::basic_array<string_t> move_to_json_array(arr_t<value_t, size> value) const
{
json::basic_array<string_t> result;
for (size_t i = 0; i < size; i++) {
result.emplace_back(std::move(value.at(i)));
}
return result;
}
bool move_from_json_array(json::basic_array<string_t> arr, arr_t<value_t, size>& value) const
{
if (!check_json_array(arr)) {
return false;
}
for (size_t i = 0; i < size; i++) {
value.at(i) = std::move(arr[i]).template as<value_t>();
}
return true;
}
};
template <typename string_t, typename collection_t>
class jsonization<string_t, collection_t, std::enable_if_t<_utils::is_collection<collection_t>>>
: public __jsonization_array<
string_t,
jsonization<string_t, collection_t>,
collection_t,
(size_t)-1>
{
public:
json::basic_array<string_t> to_json_array(const collection_t& value) const
{
json::basic_array<string_t> result;
for (const auto& val : value) {
result.emplace_back(val);
}
return result;
}
bool check_json_array(const json::basic_array<string_t>& arr) const
{
return arr.template all<typename collection_t::value_type>();
}
bool from_json_array(const json::basic_array<string_t>& arr, collection_t& value) const
{
if (!check_json_array(arr)) {
return false;
}
value = {};
for (const auto& val : arr) {
if constexpr (_utils::has_emplace_back<collection_t>::value) {
value.emplace_back(val.template as<typename collection_t::value_type>());
}
else {
value.emplace(val.template as<typename collection_t::value_type>());
}
}
return true;
}
json::basic_array<string_t> move_to_json_array(collection_t value) const
{
json::basic_array<string_t> result;
for (auto& val : value) {
result.emplace_back(std::move(val));
}
return result;
}
bool move_from_json_array(json::basic_array<string_t> arr, collection_t& value) const
{
if (!check_json_array(arr)) {
return false;
}
for (auto& val : arr) {
if constexpr (_utils::has_emplace_back<collection_t>::value) {
value.emplace_back(std::move(val).template as<typename collection_t::value_type>());
}
else {
value.emplace(std::move(val).template as<typename collection_t::value_type>());
}
}
return true;
}
};
template <typename string_t, template <typename...> typename tuple_t, typename... args_t>
class jsonization<
string_t,
tuple_t<args_t...>,
std::enable_if_t<_utils::is_tuple_like<tuple_t<args_t...>>>>
: public __jsonization_array<
string_t,
jsonization<string_t, tuple_t<args_t...>>,
tuple_t<args_t...>,
std::tuple_size_v<tuple_t<args_t...>>>
{
public:
constexpr static size_t tuple_size = std::tuple_size_v<tuple_t<args_t...>>;
json::basic_array<string_t> to_json_array(const tuple_t<args_t...>& value) const
{
json::basic_array<string_t> result;
to_json_impl(result, value, std::make_index_sequence<tuple_size>());
return result;
}
template <std::size_t... Is>
void to_json_impl(
json::basic_array<string_t>& arr,
const tuple_t<args_t...>& t,
std::index_sequence<Is...>) const
{
using std::get;
(arr.emplace_back(get<Is>(t)), ...);
}
bool check_json_array(const json::basic_array<string_t>& arr) const
{
return check_json_impl(arr, std::make_index_sequence<tuple_size>());
}
template <std::size_t... Is>
bool check_json_impl(const json::basic_array<string_t>& arr, std::index_sequence<Is...>) const
{
return (arr[Is].template is<std::tuple_element_t<Is, tuple_t<args_t...>>>() && ...);
}
bool from_json_array(const json::basic_array<string_t>& arr, tuple_t<args_t...>& value) const
{
if (!check_json_array(arr)) {
return false;
}
from_json_impl(arr, value, std::make_index_sequence<tuple_size>());
return true;
}
template <std::size_t... Is>
void from_json_impl(
const json::basic_array<string_t>& arr,
tuple_t<args_t...>& t,
std::index_sequence<Is...>) const
{
using std::get;
((get<Is>(t) = arr[Is].template as<std::tuple_element_t<Is, tuple_t<args_t...>>>()), ...);
}
json::basic_array<string_t> move_to_json_array(tuple_t<args_t...> value) const
{
json::basic_array<string_t> result;
move_to_json_impl(result, std::move(value), std::make_index_sequence<tuple_size>());
return result;
}
template <std::size_t... Is>
void move_to_json_impl(
json::basic_array<string_t>& arr,
tuple_t<args_t...> t,
std::index_sequence<Is...>) const
{
using std::get;
(arr.emplace_back(std::move(get<Is>(t))), ...);
}
bool move_from_json_array(json::basic_array<string_t> arr, tuple_t<args_t...>& value) const
{
if (!check_json_array(arr)) {
return false;
}
move_from_json_impl(arr, value, std::make_index_sequence<tuple_size>());
return true;
}
template <std::size_t... Is>
void move_from_json_impl(
json::basic_array<string_t> arr,
tuple_t<args_t...>& t,
std::index_sequence<Is...>) const
{
using std::get;
((get<Is>(t) =
std::move(arr[Is]).template as<std::tuple_element_t<Is, tuple_t<args_t...>>>()),
...);
}
};
template <typename string_t, typename map_t>
class jsonization<
string_t,
map_t,
std::enable_if_t<_utils::is_map<map_t> && std::is_same_v<typename map_t::key_type, string_t>>>
: public __jsonization_object<string_t, jsonization<string_t, map_t>, map_t>
{
public:
json::basic_object<string_t> to_json_object(const map_t& value) const
{
json::basic_object<string_t> result;
for (const auto& [key, val] : value) {
result.emplace(key, val);
}
return result;
}
bool check_json_object(const json::basic_object<string_t>& arr) const
{
for (const auto& [key, val] : arr) {
if (!val.template is<typename map_t::mapped_type>()) {
return false;
}
}
return true;
}
bool from_json_object(const json::basic_object<string_t>& arr, map_t& value) const
{
// TODO: 是不是直接from不check了算了
if (!check_json_object(arr)) {
return false;
}
value = {};
for (const auto& [key, val] : arr) {
value.emplace(key, val.template as<typename map_t::mapped_type>());
}
return true;
}
json::basic_object<string_t> move_to_json_object(map_t value) const
{
json::basic_object<string_t> result;
for (auto& [key, val] : value) {
result.emplace(key, std::move(val));
}
return result;
}
bool move_from_json_object(json::basic_object<string_t> arr, map_t& value) const
{
// TODO: 是不是直接from不check了算了
if (!check_json_object(arr)) {
return false;
}
value = {};
for (auto& [key, val] : arr) {
value.emplace(key, std::move(val).template as<typename map_t::mapped_type>());
}
return true;
}
};
template <typename string_t, typename... args_t>
class jsonization<string_t, std::variant<args_t...>>
{
public:
using variant_t = std::variant<args_t...>;
constexpr static size_t variant_size = std::variant_size_v<variant_t>;
json::basic_value<string_t> to_json(const variant_t& value) const
{
json::basic_value<string_t> result;
to_json_impl(result, value, std::make_index_sequence<variant_size>());
return result;
}
template <std::size_t... Is>
void to_json_impl(
json::basic_value<string_t>& val,
const variant_t& t,
std::index_sequence<Is...>) const
{
using std::get;
std::ignore = ((t.index() == Is ? (val = get<Is>(t), true) : false) || ...);
}
bool check_json(const json::basic_value<string_t>& json) const
{
return check_json_impl(json, std::make_index_sequence<variant_size>());
}
template <std::size_t... Is>
bool check_json_impl(const json::basic_value<string_t>& val, std::index_sequence<Is...>) const
{
return (val.template is<std::variant_alternative_t<Is, variant_t>>() || ...);
}
bool from_json(const json::basic_value<string_t>& json, variant_t& value) const
{
if (!check_json_impl(json, std::make_index_sequence<variant_size>())) {
return false;
}
from_json_impl(json, value, std::make_index_sequence<variant_size>());
return true;
}
template <std::size_t... Is>
void from_json_impl(
const json::basic_value<string_t>& json,
variant_t& t,
std::index_sequence<Is...>) const
{
std::ignore =
((json.template is<std::variant_alternative_t<Is, variant_t>>()
? (t = json.template as<std::variant_alternative_t<Is, variant_t>>(), true)
: false)
|| ...);
}
json::basic_value<string_t> move_to_json(variant_t value) const
{
json::basic_value<string_t> result;
move_to_json_impl(result, std::move(value), std::make_index_sequence<variant_size>());
return result;
}
template <std::size_t... Is>
void
move_to_json_impl(json::basic_value<string_t>& val, variant_t t, std::index_sequence<Is...>)
const
{
using std::get;
std::ignore = ((t.index() == Is ? (val = std::move(get<Is>(t)), true) : false) || ...);
}
bool move_from_json(json::basic_value<string_t> json, variant_t& value) const
{
if (!check_json_impl(json, std::make_index_sequence<variant_size>())) {
return false;
}
move_from_json_impl(std::move(json), value, std::make_index_sequence<variant_size>());
return true;
}
template <std::size_t... Is>
void move_from_json_impl(
json::basic_value<string_t> json,
variant_t& t,
std::index_sequence<Is...>) const
{
std::ignore =
((json.template is<std::variant_alternative_t<Is, variant_t>>()
? (t = std::move(json).template as<std::variant_alternative_t<Is, variant_t>>(),
true)
: false)
|| ...);
}
};
// TODO: check if has move_xxx in member
template <typename string_t, typename var_t>
class jsonization<
string_t,
var_t,
std::enable_if_t<
_utils::has_to_json_in_member<var_t>::value
&& _utils::has_check_json_in_member<var_t, string_t>::value
&& _utils::has_from_json_in_member<var_t, string_t>::value>>
{
public:
json::basic_value<string_t> to_json(const var_t& value) const { return value.to_json(); }
bool check_json(const json::basic_value<string_t>& json) const
{
var_t value;
return value.check_json(json);
}
bool from_json(const json::basic_value<string_t>& json, var_t& value) const
{
return value.from_json(json);
}
json::basic_value<string_t> move_to_json(var_t value) const { return to_json(value); }
bool move_from_json(json::basic_value<string_t> json, var_t& value) const
{
return from_json(json, value);
}
};
// really need this fucking queue?
template <typename string_t, typename value_t>
class jsonization<string_t, std::queue<value_t>>
: public __jsonization_array<
string_t,
jsonization<string_t, std::queue<value_t>>,
std::queue<value_t>,
(size_t)-1>
{
public:
json::basic_array<string_t> to_json_array(const std::queue<value_t>& value) const
{
return move_to_json_array(value);
}
bool check_json_array(const json::basic_array<string_t>& arr) const
{
return arr.template all<value_t>();
}
bool from_json_array(const json::basic_array<string_t>& arr, std::queue<value_t>& value) const
{
if (!check_json_array(arr)) {
return false;
}
value = {};
for (const auto& val : arr) {
value.emplace(val.template as<value_t>());
}
return true;
}
json::basic_array<string_t> move_to_json_array(std::queue<value_t> value) const
{
json::basic_array<string_t> result;
while (!value.empty()) {
result.emplace_back(std::move(value.front()));
value.pop();
}
return result;
}
bool move_from_json_array(json::basic_array<string_t> arr, std::queue<value_t>& value) const
{
if (!check_json_array(arr)) {
return false;
}
for (auto& val : arr) {
value.emplace(std::move(val).template as<value_t>());
}
return true;
}
};
}

View File

@@ -0,0 +1,648 @@
// IWYU pragma: private, include <meojson/json.hpp>
#pragma once
#include <string>
#include <type_traits>
#include <utility>
#include "../common/types.hpp"
#include "extensions.hpp"
namespace json::_jsonization_helper
{
template <typename value_t>
struct is_optional_t : public std::false_type
{
};
template <typename value_t>
struct is_optional_t<std::optional<value_t>> : public std::true_type
{
};
template <typename value_t>
inline constexpr bool is_optional_v = is_optional_t<value_t>::value;
struct next_is_optional_t
{
};
struct next_override_key_t
{
const char* key;
};
struct next_state_t
{
bool is_optional = false;
const char* override_key = nullptr;
};
struct va_arg_end
{
};
template <typename tag_t>
struct is_tag_t : public std::false_type
{
};
template <>
struct is_tag_t<next_is_optional_t> : public std::true_type
{
};
template <>
struct is_tag_t<next_override_key_t> : public std::true_type
{
};
struct dumper
{
void _to_json(json::object&, va_arg_end) const {}
template <typename... rest_t>
void _to_json(json::object& result, const char* key, rest_t&&... rest) const
{
_to_json(result, next_state_t {}, key, std::forward<rest_t>(rest)...);
}
template <
typename var_t,
typename... rest_t,
typename _ = std::enable_if_t<!is_tag_t<var_t>::value, void>>
void _to_json(
json::object& result,
next_state_t state,
const char* key,
const var_t& var,
rest_t&&... rest) const
{
if (state.override_key) {
key = state.override_key;
}
if constexpr (is_optional_v<var_t>) {
if (!state.is_optional) {
throw exception("std::optional must be used with MEO_OPT");
}
if (var.has_value()) {
result.emplace(key, var.value());
}
}
else {
result.emplace(key, var);
}
_to_json(result, std::forward<rest_t>(rest)...);
}
template <typename... rest_t>
void _to_json(
json::object& result,
next_state_t state,
const char*,
next_is_optional_t,
rest_t&&... rest) const
{
state.is_optional = true;
_to_json(result, state, std::forward<rest_t>(rest)...);
}
template <typename... rest_t>
void _to_json(
json::object& result,
next_state_t state,
const char*,
next_override_key_t override_key,
rest_t&&... rest) const
{
state.override_key = override_key.key;
_to_json(result, state, std::forward<rest_t>(rest)...);
}
};
struct checker
{
bool _check_json(const json::value&, std::string&, va_arg_end) const { return true; }
template <typename... rest_t>
bool _check_json(
const json::value& in,
std::string& error_key,
const char* key,
rest_t&&... rest) const
{
return _check_json(in, error_key, next_state_t {}, key, std::forward<rest_t>(rest)...);
}
template <
typename var_t,
typename... rest_t,
typename _ = std::enable_if_t<!is_tag_t<var_t>::value, void>>
bool _check_json(
const json::value& in,
std::string& error_key,
next_state_t state,
const char* key,
const var_t&,
rest_t&&... rest) const
{
if (state.override_key) {
key = state.override_key;
}
auto opt = in.find(key);
if constexpr (is_optional_v<var_t>) {
if (!state.is_optional) {
throw exception("std::optional must be used with MEO_OPT");
}
if (opt && !opt->is<typename var_t::value_type>()) {
error_key = key;
return false;
}
}
else {
if (state.is_optional) {
if (opt && !opt->is<var_t>()) {
error_key = key;
return false;
} // is_optional, ignore key not found
}
else if (!opt || !opt->is<var_t>()) {
error_key = key;
return false;
}
}
return _check_json(in, error_key, std::forward<rest_t>(rest)...);
}
template <typename... rest_t>
bool _check_json(
const json::value& in,
std::string& error_key,
next_state_t state,
const char*,
next_is_optional_t,
rest_t&&... rest) const
{
state.is_optional = true;
return _check_json(in, error_key, state, std::forward<rest_t>(rest)...);
}
template <typename... rest_t>
bool _check_json(
const json::value& in,
std::string& error_key,
next_state_t state,
const char*,
next_override_key_t override_key,
rest_t&&... rest) const
{
state.override_key = override_key.key;
return _check_json(in, error_key, state, std::forward<rest_t>(rest)...);
}
};
struct loader
{
bool _from_json(const json::value&, std::string&, va_arg_end) const { return true; }
template <typename... rest_t>
bool
_from_json(const json::value& in, std::string& error_key, const char* key, rest_t&&... rest)
{
return _from_json(in, error_key, next_state_t {}, key, std::forward<rest_t>(rest)...);
}
template <
typename var_t,
typename... rest_t,
typename _ = std::enable_if_t<!is_tag_t<var_t>::value, void>>
bool _from_json(
const json::value& in,
std::string& error_key,
next_state_t state,
const char* key,
var_t& var,
rest_t&&... rest)
{
if (state.override_key) {
key = state.override_key;
}
auto opt = in.find(key);
if constexpr (is_optional_v<var_t>) {
if (!state.is_optional) {
throw exception("std::optional must be used with MEO_OPT");
}
if (opt && !opt->is<typename var_t::value_type>()) {
error_key = key;
return false;
}
if (opt) {
var = std::move(opt)->as<typename var_t::value_type>();
}
else {
var = std::nullopt;
}
}
else {
if (state.is_optional) {
if (opt && !opt->is<var_t>()) {
error_key = key;
return false;
} // is_optional, ignore key not found
}
else if (!opt || !opt->is<var_t>()) {
error_key = key;
return false;
}
if (opt) {
var = std::move(opt)->as<var_t>();
}
}
return _from_json(in, error_key, std::forward<rest_t>(rest)...);
}
template <typename... rest_t>
bool _from_json(
const json::value& in,
std::string& error_key,
next_state_t state,
const char*,
next_is_optional_t,
rest_t&&... rest)
{
state.is_optional = true;
return _from_json(in, error_key, state, std::forward<rest_t>(rest)...);
}
template <typename... rest_t>
bool _from_json(
const json::value& in,
std::string& error_key,
next_state_t state,
const char*,
next_override_key_t override_key,
rest_t&&... rest)
{
state.override_key = override_key.key;
return _from_json(in, error_key, state, std::forward<rest_t>(rest)...);
}
};
} // 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_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 \
{ \
json::object result; \
json::_jsonization_helper::dumper()._to_json( \
result, \
_MEOJSON_EXPAND(_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__)) \
json::_jsonization_helper::va_arg_end {}); \
return result; \
}
#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 {},
#define MEO_KEY(key) json::_jsonization_helper::next_override_key_t { key },
#if defined(__clang__)
#pragma clang diagnostic pop // -Wgnu-zero-variadic-macro-arguments
#endif

View File

@@ -1,267 +1,49 @@
## v5.27.4
### 难道说? | Highlights
由于近期《明日方舟》的更新引入了部分不兼容的改动(比如公开招募界面的部分按钮改动,界园肉鸽的部分按钮改动),所以**旧版本可能无法使用自动公招、自动肉鸽等功能,建议更新到最新版本**。
当然本次版本更新恰逢即将到来的半周年更新所以如果半周年更新也引入了新的不兼容改动MAA 可能无法第一时间适配,敬请关注后续更新。
~~号外:《明日方舟:终末地》开启三测啦!(内容全面,资格就不全面.jpg~~
#### 自动肉鸽方面
本次更新,我们支持了界园肉鸽新 DLC 的新关卡、新通宝、新不期而遇等,添加了“刷常乐节点”策略。
我们也添加了萨米肉鸽的“刷稀有坍缩范式”策略,欢迎你尝试~
#### 界面主题方面
我们在这个版本正式适配了新的界面主题「齐聚」。并且,在部分自动任务中,如果当前的界面主题无法识别,牛牛会尝试自动切换到日间主题。~~这就是机械革命~~
#### 其他方面
我们继续修复了大量 bug例如外部通知功能现在会正确地将消息发送到所有渠道了。如果还有漏网之鱼欢迎你来反馈。
----
Due to recent updates to *Arknights* introducing some incompatible changes (such as changes to some buttons in the *Recruitment* interface and some in the *Sui's Garden Of Grotesqueries*), **older versions may not be able to use features like *Auto Recruit* and *Auto I.S.*. We recommend updating to the latest version.**
Of course, this update coincides with the upcoming thank-you celebration update, so if this update also introduces new incompatible changes, MAA may not be able to adapt immediately. Please stay tuned for future updates.
~~Breaking News: *Arknights: Endfield* has started its third closed beta test! (ALL content, but not for ALL you)~~
#### *Auto I.S.*
[CN ONLY] In this update, we support new stages, new *Tongbaos*, and new *Encounters* in the *Sui's Garden Of Grotesqueries* Expansion I, and added the "Farm playtime(常乐) nodes" strategy.
We have also added the "Farm collapse paradigms" strategy for *Sarkaz's Furnaceside Fables*. Feel free to try it out!
#### Interface Theme
[CN ONLY] We have officially adapted the new interface theme "Gathering(齐聚)" in this version.
Furthermore, in some automated tasks, if the current interface theme cannot be recognized, MAA will attempt to automatically switch to the *Light* theme. ~~This is Mechanic Revolution~~
#### Others
We have continued to fix numerous bugs; for example, the external notification function now correctly sends messages to all channels. If there are any bugs that have been missed, please feel free to provide feedback.
----
以下是详细内容:
# 5.27.0
## v5.26.0-beta.1
### 新增 | New
* 界面主题「齐聚」 @SherkeyXD
* 界园DLC新关卡战斗逻辑 @Saratoga-Official
* 界园 dlc2 通宝数据更新 @SherkeyXD
* 界园肉鸽不期而遇实现选项识别 (#14540) @Alan-Charred
* 添加主线 16 章导航 @ABA2396
* 支持剿灭关卡名识别 @ABA2396
* 添加刷常乐节点策略 (#13868) @travellerse
* 添加 生活队凹开局密文板 与 坍缩范式列表 TooltipBlock @ABA2396
* 支持生息演算与肉鸽遇到不认识的主题时自动切换,不认识的主题直接切换 @ABA2396
* 本次更新的公告右上角添加红点 @ABA2396
* 跳过使用未准备好的技能 && 全局计时器 (#13913) @Alan-Charred
* 增加纯数色匹配 (#14536) @ABA2396
* 资源更新完成后等待空闲时再加载 @ABA2396
* core 异常退出 ui 添加日志记录 @ABA2396
* 记录crash log时同时记录stacktrace, 并允许 ASST_DEBUG 在 debug 目录下生成 crash.log (#14526) @status102
* LogInfo 等宏支持is_enabled (#14551) @status102
* Update Submodules MaaMacGui, maa-cli @github-actions[bot]
### 改进 | Improved
* 繁中服主線、肉鴿導航 UI 更新 (#14433) @momomochi987
* 主线导航目标关卡默认在屏幕内时不再划到最右边后往前寻找 @ABA2396
* 更新 Windows 模拟器文档,调整支持列表并添加详细说明 (#14534) @AnnAngela
* 进入新任务后重置任务超时计时器, 以避免非单任务卡阻的误报 @status102
* GetImageFromRoi 工具优化 @SherkeyXD
* ProcSubTask static @status102
* 次生预案十里坡剑神 @ABA2396
* 设置指引添加更新设置 @ABA2396
* 设置指引添加性能设置 @ABA2396
### 修复 | Fix
* 齐聚主题无法导航肉鸽和生息演算 @Saratoga-Official
* 繁中服無法進入資源收藏關卡 (#14469) @momomochi987
* 修复 "设置-外部通知" 存在多个通知渠道时只会发送到第一个成功的渠道问题 (#14380) @Jim-Happy
* 开始唤醒会回到主界面 @ABA2396
* 无法加载 cache 资源 @ABA2396
* 无人机协助点击无效时增加自动重试机制 @ABA2396
* 每日任务领取因为延迟卡住、延迟载荷移位 @SherkeyXD
* 公招按钮 roi 错误 @Saratoga-Official
* 掉落识别修复、增加越界检查 (#14508) (#14312) @Alan-Charred @MistEO
* 尝试为活动商店小游戏适配新的时装购买动画、改用 CustomeSkip 来跳过新时装动画 (#14490) (#14566) @Alan-Charred
* 给生息演算点击 "开始行动" 的任务加点延迟,防止错过确认弹窗 @Alan-Charred
* 肉鸽跳一战打一战刷钱 (#14450) @Alan-Charred
* 界园肉鸽选项识别:不期而遇、狭路相逢、杂疑 @Saratoga-Official
* 傀影不期而遇选项 @Saratoga-Official
* 为界园放弃探索任务添加备用模版、dlc 背景无法放弃探索 (#14466) (#14425) @Alan-Charred
* 降低萨米肉鸽进入 "诡秘的预感" 所需模版匹配分数阈值 (#14521) @Alan-Charred
* 萨卡兹去伪存真弹窗有延迟无法退出 @Saratoga-Official
* 给 ClickToStartPoint 加点延迟 @Saratoga-Official
* 调换使用错误的排序函数 (#14555) @Alan-Charred
* 猪玛写 switch 不写 break @ABA2396
* 变更弃用的参数 @ABA2396
* Award 模板阈值还是小了 @Saratoga-Official
* ja-jp 泰拉大陆调查团 识别错误 @ABA2396
* YostarKR RoguelikeCustom-HijackSquad squad recognition @HX3N
* ocr for Executor the Ex Foedere for EN @Constrat
* JP Task.png (#14516) @Daydreamer114
* cleanup ifdef + fix AsstDestroy missed calls + lldb @Constrat
* precommit maacore @Constrat
* change default issue + remove always as not necessary、add always() @Constrat
* 文档首页语言选择按钮的宽度定义方式 (#14199) @lucienshawls
* 傀影肉鸽无法识别四结局 (#14193) @Saratoga-Official
* GamePassSkip2 识别到错误的跳过 @Saratoga-Official
* 萨米肉鸽不期而遇避战 @Saratoga-Official
* Google Play Games Developer shutdown @Constrat
* manual set resource version time @MistEO
* prettier @Constrat
### 文档 | Docs
* 补充模拟器相关文档和协议文档 (#14478) @Alan-Charred
* 添加关于实体设备上账号切换问题的说明 (#14468) @Initial-heart-1 @lucienshawls @HX3N @Constrat @Manicsteiner @momomochi987
* 在不支持列表中添加腾讯应用宝 (#14477) @JasonHuang79
* 修正插件相关描述 @SherkeyXD
* 全语言开发文档章节整理 (#14562) @MistEO
* 对 codespace 相关链接进行小修小补、把 codespace 移到犄角旮旯 (#14564) (#14560) @lucienshawls @MistEO
* 接入 DeepWiki (#14563) @lucienshawls
* vscode 插件文档视觉优化 @SherkeyXD
* 优化文档代码的高亮主题 @SherkeyXD
* 利用 tabs 优化视觉表现 @SherkeyXD
* 利用步骤容器优化视觉效果 @SherkeyXD
* 增加换行以提升文档在 GitHub 的阅读体验 (#14338) @status102
* 更正文档编写指南的一处 typo @lucienshawls
* 更新 algolia 配置 @SherkeyXD
* Sync documents in all languages, Powered by AI (#14572) @MistEO @Manicsteiner
* 补充vsc插件繁中文档 @Rbqwow
* 修复文档站 readme 盾换行 @Rbqwow
* 调整文档站的标题和尾注文本显示 (#14213) @lucienshawls
* 更新网页开发相关文档 (#14167) @Rbqwow @Manicsteiner
* 完善任务流程协议文档 (#13232) @zzyyyl
* 回调消息协议文档视觉更新 @SherkeyXD
* 集成文档视觉更新 @SherkeyXD
* 文档启用 b 站视频播放功能 @SherkeyXD
* 文档添加字段容器功能 @SherkeyXD
* 文档添加功能 @SherkeyXD
* 更新文档编写指南 @SherkeyXD
* 标题 MAA 统一采用缩写 @MistEO
* 再次调整文档站标题( @MistEO
* 调整文档站标题 @MistEO
* Update JP(#14227) @wallsman
* markdown pre-commit @zzyyyl
* add extension's evaluating feature @neko-para
* add telegram icon @SherkeyXD
### 其他 | Other
* 集成 MaaUtils (#14578) @MistEO
* WpfGui 增加对界园肉鸽 DLC 1 三个新分队的支持 @Alan-Charred
* 拆出界园岁兽残识地图导航 (#14432) @Alan-Charred
* 基建降低会清空其他干员效率的技能优先级 @ABA2396
* 重复访问好友添加 Ocr 兜底 @Saratoga-Official
* ui 日志增加 adb devices 输出 @ABA2396
* ALL the Announcements 中新公告标题中加 * @ABA2396
* 调整雷电截图增强提示,强制要求模拟器分辨率为横屏模式 @ABA2396
* 调整连接失败尝试启动模拟器的描述 @ABA2396
* 调整截图 @ABA2396
* 繁中服「輓歌燃燒殆盡」活動導航 (#14434) @momomochi987
* 同心暂时去掉5选项、Revert "chore: 同心暂时去掉5选项" @Saratoga-Official
* 将 Copilot ActionType::ResetTimer 更改为更适合的 ResetStopwatch (#14507) @Alan-Charred
* 增加 wpf 项目 cmake build 脚本 @status102
* 移除 devcontainer 轻量环境的部分非必要依赖 (#14499) @lucienshawls
* 添加 png 后缀 @SherkeyXD
* 文件路径错误 @ABA2396
* 我是铸币 @SherkeyXD
* SSS#8 global changes (add acahuallan, remove dossoles and barrenbeasts) @Constrat
* YostarKR SSS#8 SSSBuffChoose、update SSS#8 buff choose ocr for EN @HX3N @Constrat
* add SwitchTheme@ConfirmThemeChange.png @Constrat @Manicsteiner @HX3N @momomochi987
* translate ElapsedTime ref: 1edd00698200b7c2a14406d7a29f51689f6871d1 @Constrat
* polish PixelAnalyzer a bit (#14538) @Alan-Charred
* remove global templates for 3294b29f54dadc4198a40538e85b71902c79c875 @Constrat
* remove cancelled() and add mentions @Constrat
## v5.27.1
### 新增 | New
* SideStory「雪山降临1101」导航 @SherkeyXD
### 修复 | Fix
* macos GUI缺少MaaUtils库导致无法启动 @hguandl @MistEO
* 关卡复核错误 @ABA2396
### 文档 | Docs
* fix boolean capitalization in C# documentation comments (#14599) @Copilot @lucienshawls
### 其他 | Other
* 繁中服第十五章 & 重複訪問好友 text (#14594) @momomochi987
* Apply suggestion from @Copilot @MistEO @Copilot
* KR varius translations (#14591) @HX3N
* 调整 devcontainer 环境构建流程,使其适应 MaaUtils (#14580) @lucienshawls
* ClickStageName 同步 ClickedCorrectStage ocrReplace @ABA2396
## v5.27.2
### 新增 | New
* OS-喀兰贸易研发部 小游戏 (#14615) @ABA2396
### 修复 | Fix
* 技能点击范围会点到装备应变 (#14611) @Saratoga-Official @ABA2396
* 无法迁移旧资源路径文件 @ABA2396
* 肉鸽商店投资界面加一点延迟 @Saratoga-Official
* erosion resistant device regex fix for EN @Constrat
* Ines regex EN @Constrat
### 其他 | Other
* SideStoryStage ocrReplace @ABA2396
## v5.27.3
### 新增 | New
* 肉鸽添加凛御银灰圣聆初雪招募逻辑 @Saratoga-Official
* MaaCore 记录 Exception 时额外记录模块基址、Wpf i18n 字符串支持直接 string.Frmat @status102
* 增加华硕 GTII、GTIII 显卡超频工具注入导致崩溃提示 @ABA2396
* 战斗列表启用时, 浏览单个作业时自动添加到战斗列表 (#14625) @status102
### 改进 | Improved
* asst 增加 crash 时额外输出 core 版本, build 时间, 路径、空 stack trace 输出 @status102
* 优化切换主题逻辑/流程 @ABA2396
* 更好的随机点分布 (#14652) @MistEO
### 修复 | Fix
* 修复界园保留招募券在1080p下识别异常 (#14637) @travellerse
* 提高干员部署滑动的最小时长,减少因模拟器丢帧导致的部署失败 @ABA2396
* 肉鸽刷开局期望奖励无法动态隐藏/显示 @ABA2396
* 给 mujica 擦屁股 @ABA2396
* 部分情况下 钼铅 识别错误 @ABA2396
* OS 小游戏回合结束改用 ocr @ABA2396
* 结束回合按钮怎么还会闪烁啊 @ABA2396
* 动画或卡顿导致概率无法使用无人机 @ABA2396
* Wpf 增加悖论模拟模式非悖论作业检测 @status102
* 界园入暂亭 ocrfix @Saratoga-Official
* update to new data repo @Constrat
* move to public arkntools repo for txwy @Constrat
* update Sami floor regex fix 14630 (hopeful) @Constrat
* move from Template to OcrDetect for BattleQuickFormationClear @Constrat
* Exusiai Alter ocr regex for EN @Constrat
* Dorothy S2 ocr regex @Constrat
* JP 1ns ocr @Daydreamer114
### 其他 | Other
* wpf 拆分添加作业和作业集按钮 (#14624) @status102 @ABA2396
* CropRoi -> ImageCropper (#14379) (#14647) @MistEO @lucienshawls
* use MaaUtils instead of Utils (#14579) @MistEO
* add logs @MistEO
* remove expired token @Constrat
## v5.27.4
### 新增 | New
* 自动战斗新增组名重复检查 (#14710) @status102
* 增加界园指挥分队存钱难度选择 tip @ABA2396
* mac 增加小游戏入口 @hguandl @ABA2396
### 改进 | Improved
* 更新 243 极限效率一天三换排班表20251111 修订) (#14708) @Zsm1n
### 修复 | Fix
* 月度小队无法自动切换 @ABA2396
* 优化 「齐聚」 主题识别匹配 @Saratoga-Official @ABA2396
* 肉鸽中取消选中状态可能会点到二倍速、肉鸽 ClickToDrops 可能会点到藏品栏 @Saratoga-Official
### 其他 | Other
* update dependabot with ci members @Constrat
* preload AD for Global (#14700) @Constrat
* 统一显示效果 @ABA2396
* 水月萨米肉鸽不期而遇避战 @Saratoga-Official
* 重写完成后动作仅一次的 ui 字符串 (#14196) @Rbqwow
* 贸易站没其他好用的人再用锏 @ABA2396
* YostarJP Sarkaz roguelike StageEncounter (#14223) @Manicsteiner
* YostarJP sami roguelike 720p (#14210) @Manicsteiner
* YostarJP Mizuki StageEncounter (#14206) @Manicsteiner
* devcontainer.json (#14169) @Rbqwow @lucienshawls

View File

@@ -1,8 +1,11 @@
cmake_minimum_required(VERSION 3.28)
project(MAA)
include(src/MaaUtils/MaaUtils.cmake)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules")
option(BUILD_WPF_GUI "build MaaWpfGui" ${WIN32})
option(BUILD_DEBUG_DEMO "build debug demo" OFF)
@@ -14,9 +17,11 @@ option(INSTALL_FLATTEN "do not use bin lib include directory" ON)
option(WITH_EMULATOR_EXTRAS "build with emulator extras" ${WIN32})
option(WITH_HASH_VERSION "generate version from git hash" OFF)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules")
include(${PROJECT_SOURCE_DIR}/MaaDeps/maadeps.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/config.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/utils.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/version.cmake)
if(APPLE)
include(${PROJECT_SOURCE_DIR}/cmake/macos.cmake)
@@ -29,6 +34,12 @@ endif()
add_library(HeaderOnlyLibraries INTERFACE)
target_include_directories(HeaderOnlyLibraries INTERFACE 3rdparty/include)
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs)
find_package(Boost REQUIRED CONFIG COMPONENTS system)
find_package(ZLIB REQUIRED)
find_package(fastdeploy_ppocr REQUIRED)
find_package(ONNXRuntime REQUIRED)
add_subdirectory(src/MaaCore)
if(BUILD_WPF_GUI)
@@ -48,4 +59,3 @@ endif()
if(BUILD_DEBUG_DEMO OR BUILD_SMOKE_TEST)
add_subdirectory(src/Cpp)
endif()

View File

@@ -1,26 +0,0 @@
# MAA Code of Conduct
Like the technical community as a whole, the MAA team and community is made up of a mixture of professionals and volunteers from all over the world, working on every aspect of the mission - including mentorship, teaching, and connecting people.
Diversity is one of our huge strengths, but it can also lead to communication issues and unhappiness. To that end, we have a few ground rules that we ask people to adhere to. This code applies equally to founders, mentors and those seeking help and guidance.
This isnt an exhaustive list of things that you cant do. Rather, take it in the spirit in which its intended - a guide to make it easier to enrich all of us and the technical communities in which we participate.
This code of conduct applies to all spaces managed by the MAA project or MaaAssistantArknights. This includes IRC, the mailing lists, the issue tracker, DSF events, and any other forums created by the project team which the community uses for communication. In addition, violations of this code outside these spaces may affect a person's ability to participate within them.
- **Be friendly and patient.**
- **Be welcoming.** We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
- **Be considerate.** Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
- **Be respectful.** Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. Its important to remember that a community where people feel uncomfortable or threatened is not a productive one. Members of the MAA community should be respectful when dealing with other members as well as with people outside the MAA community.
- **Be careful in the words that you choose.** We are a community of professionals, and we conduct ourselves professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behavior aren't acceptable. This includes, but is not limited to:
- Violent threats or language directed against another person.
- Discriminatory jokes and language.
- Posting sexually explicit or violent material.
- Posting (or threatening to post) other people's personally identifying information ("doxing").
- Personal insults, especially those using racist or sexist terms.
- Unwelcome sexual attention.
- Advocating for, or encouraging, any of the above behavior.
- Repeated harassment of others. In general, if someone asks you to stop, then stop.
- **When we disagree, try to understand why.** Disagreements, both social and technical, happen all the time and MAA is no exception. It is important that we resolve disagreements and differing views constructively. Remember that were different. The strength of MAA comes from its varied community, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesnt mean that theyre wrong. Dont forget that it is human to err and blaming each other doesnt get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
Original text courtesy of the [Speak Up! project](http://web.archive.org/web/20141109123859/http://speakup.io/coc.html).

View File

@@ -21,9 +21,6 @@
<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 -->
@@ -101,9 +98,11 @@ MAA 支持命令行界面CLI操作支持 LinuxmacOS 和 Windows
### 主要关联项目
**目前项目组非常缺前端大佬,若您有相关经验,欢迎加入我们!**
- 全新框架:[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)
- [作业站](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)
@@ -113,9 +112,15 @@ MAA 以中文(简体)为第一语言,翻译词条均以中文(简体)
### 参与开发
请参阅 [开发指南](https://docs.maa.plus/zh-cn/develop/development.html)。
#### Windows
### API
请参阅 [开始开发](https://docs.maa.plus/zh-cn/develop/development.html)。
#### Linux | macOS
请参阅 [Linux 编译教程](https://docs.maa.plus/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)
@@ -131,11 +136,15 @@ MAA 以中文(简体)为第一语言,翻译词条均以中文(简体)
- [任务流程协议](https://docs.maa.plus/zh-cn/protocol/task-schema.html)
- [自动抄作业协议](https://docs.maa.plus/zh-cn/protocol/copilot-schema.html)
### 外服适配
#### 外服适配
请参阅 [外服适配教程](https://docs.maa.plus/zh-cn/develop/overseas-client-adaptation.html),对于国服已支持的功能,绝大部分的外服适配工作仅需要截图 + 简单的 JSON 修改即可。
### Issue bot
#### 想参与开发,但不太会用 GitHub?
[GitHub Pull Request 流程简述](https://docs.maa.plus/zh-cn/develop/development.html#github-pull-request-流程简述)
#### Issue bot
请参阅 [Issue Bot 使用方法](https://docs.maa.plus/zh-cn/develop/issue-bot-usage.html)

View File

@@ -1,2 +1,83 @@
set(debug_comp_defs "_DEBUG;ASST_DEBUG")
add_compile_definitions("$<$<CONFIG:Debug>:${debug_comp_defs}>")
if(APPLE)
set(CMAKE_INSTALL_RPATH "@loader_path;@executable_path")
set(CMAKE_BUILD_RPATH "@loader_path;@executable_path")
elseif(UNIX)
set(CMAKE_INSTALL_RPATH "$ORIGIN")
set(CMAKE_BUILD_RPATH "$ORIGIN")
endif()
if(MSVC)
add_compile_options("/utf-8")
add_compile_options("/MP")
add_compile_options("/W4;/WX;/Gy;/permissive-;/sdl")
add_compile_options("/wd4127") # conditional expression is constant
add_compile_options("/wd4251") # export dll with templates
add_compile_options("/DWINVER=0x0A00")
add_compile_options("/D_WIN32_WINNT=0x0A00")
# https://github.com/actions/runner-images/issues/10004 https://github.com/microsoft/STL/releases/tag/vs-2022-17.10
add_compile_definitions("_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR")
set(release_link_options "/OPT:REF;/OPT:ICF")
add_link_options("$<$<CONFIG:Release>:${release_link_options}>")
SET(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO "RelWithDebInfo;Release;")
SET(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL "MinSizeRel;Release;")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
add_compile_options("-Wall;-Werror;-Wextra;-Wpedantic;-Wno-missing-field-initializers")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13)
add_compile_options("-Wno-restrict")
endif()
endif()
if(LINUX)
function(copy_and_add_rpath_library LIBNAME)
execute_process(
COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=${LIBNAME}.so.1 -target ${CMAKE_CXX_COMPILER_TARGET} --sysroot=${CMAKE_SYSROOT}
OUTPUT_VARIABLE LIB_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if("${LIB_PATH}" STREQUAL "${LIBNAME}.so.1")
message(FATAL_ERROR "Could not locate ${LIBNAME}.so.1 using compiler")
endif()
file(READ_SYMLINK "${LIB_PATH}" LINK_TARGET)
if(NOT LINK_TARGET)
set(LIB_PATH_REAL "${LIB_PATH}")
elseif(NOT IS_ABSOLUTE "${LINK_TARGET}")
get_filename_component(LIB_PATH_DIR "${LIB_PATH}" DIRECTORY)
file(REAL_PATH "${LIB_PATH_DIR}/${LINK_TARGET}" LIB_PATH_REAL)
else()
set(LIB_PATH_REAL "${LINK_TARGET}")
endif()
if(NOT EXISTS "${LIB_PATH_REAL}")
message(FATAL_ERROR "File not found: ${LIB_PATH_REAL}")
endif()
message(STATUS "${LIBNAME}.so.1 path: ${LIB_PATH_REAL}")
install(FILES "${LIB_PATH_REAL}" DESTINATION . RENAME "${LIBNAME}.so.1" COMPONENT libcxx)
get_filename_component(LIB_PATH_DIR "${LIB_PATH_REAL}" DIRECTORY)
list(APPEND CMAKE_BUILD_RPATH "${LIB_PATH_DIR}")
set(CMAKE_BUILD_RPATH "${CMAKE_BUILD_RPATH}" PARENT_SCOPE)
endfunction()
copy_and_add_rpath_library(libc++)
copy_and_add_rpath_library(libc++abi)
copy_and_add_rpath_library(libunwind)
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

View File

@@ -4,30 +4,24 @@ if (BUILD_XCFRAMEWORK)
COMMAND xcodebuild -create-xcframework -library libMaaCore.dylib -headers ${PROJECT_SOURCE_DIR}/include -output MaaCore.xcframework
DEPENDS MaaCore
)
add_custom_command(OUTPUT MaaUtils.xcframework
COMMAND rm -rf MaaUtils.xcframework
COMMAND xcodebuild -create-xcframework -library libMaaUtils.dylib -output MaaUtils.xcframework
DEPENDS MaaUtils
)
add_custom_command(OUTPUT OpenCV.xcframework
COMMAND rm -rf OpenCV.xcframework
COMMAND xcodebuild -create-xcframework -library "${MAADEPS_DIR}/runtime/${MAADEPS_TRIPLET}/libopencv_world4.408.dylib" -output OpenCV.xcframework
COMMAND xcodebuild -create-xcframework -library "${PROJECT_SOURCE_DIR}/MaaDeps/runtime/${MAADEPS_TRIPLET}/libopencv_world4.408.dylib" -output OpenCV.xcframework
)
add_custom_command(OUTPUT ONNXRuntime.xcframework
COMMAND rm -rf ONNXRuntime.xcframework
COMMAND xcodebuild -create-xcframework -library "${MAADEPS_DIR}/runtime/${MAADEPS_TRIPLET}/libonnxruntime.1.18.0.dylib" -output ONNXRuntime.xcframework
COMMAND xcodebuild -create-xcframework -library "${PROJECT_SOURCE_DIR}/MaaDeps/runtime/${MAADEPS_TRIPLET}/libonnxruntime.1.18.0.dylib" -output ONNXRuntime.xcframework
)
add_custom_command(OUTPUT fastdeploy_ppocr.xcframework
COMMAND rm -rf fastdeploy_ppocr.xcframework
COMMAND xcodebuild -create-xcframework -library "${MAADEPS_DIR}/runtime/${MAADEPS_TRIPLET}/libfastdeploy_ppocr.dylib" -output 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 MaaCore.xcframework MaaUtils MaaUtils.xcframework OpenCV.xcframework ONNXRuntime.xcframework fastdeploy_ppocr.xcframework
DEPENDS MaaCore MaaCore.xcframework OpenCV.xcframework ONNXRuntime.xcframework fastdeploy_ppocr.xcframework
)
endif (BUILD_XCFRAMEWORK)

View File

@@ -0,0 +1,41 @@
find_path(ONNXRuntime_INCLUDE_DIR NAMES onnxruntime/onnxruntime_c_api.h)
find_library(ONNXRuntime_LIBRARY_IMP NAMES onnxruntime)
if (WIN32)
get_filename_component(ONNXRuntime_PATH_LIB ${ONNXRuntime_LIBRARY_IMP} DIRECTORY)
find_file(ONNXRuntime_LIBRARY NAMES onnxruntime_maa.dll PATHS "${ONNXRuntime_PATH_LIB}/../bin")
find_file(ONNXRuntime_LIBRARY_IMP_DEBUG NAMES onnxruntime.lib PATHS "${ONNXRuntime_PATH_LIB}/../debug/lib")
find_file(ONNXRuntime_LIBRARY_DEBUG NAMES onnxruntime_maa.dll PATHS "${ONNXRuntime_PATH_LIB}/../debug/bin")
else ()
set(ONNXRuntime_LIBRARY ${ONNXRuntime_LIBRARY_IMP})
endif (WIN32)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
ONNXRuntime
REQUIRED_VARS ONNXRuntime_LIBRARY_IMP ONNXRuntime_INCLUDE_DIR
)
if(ONNXRuntime_FOUND)
set(ONNXRuntime_INCLUDE_DIRS ${ONNXRuntime_INCLUDE_DIR})
if(NOT TARGET ONNXRuntime::ONNXRuntime)
add_library(ONNXRuntime::ONNXRuntime SHARED IMPORTED)
set_property(TARGET ONNXRuntime::ONNXRuntime APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
if (WIN32)
set_property(TARGET ONNXRuntime::ONNXRuntime APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(ONNXRuntime::ONNXRuntime PROPERTIES
IMPORTED_IMPLIB_RELEASE "${ONNXRuntime_LIBRARY_IMP}"
)
set_target_properties(ONNXRuntime::ONNXRuntime PROPERTIES
IMPORTED_IMPLIB_DEBUG "${ONNXRuntime_LIBRARY_IMP_DEBUG}"
IMPORTED_LOCATION_DEBUG "${ONNXRuntime_LIBRARY_DEBUG}"
)
endif (WIN32)
set_target_properties(ONNXRuntime::ONNXRuntime PROPERTIES
IMPORTED_LOCATION_RELEASE "${ONNXRuntime_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${ONNXRuntime_INCLUDE_DIR}"
)
endif()
endif()

View File

@@ -1,3 +1,50 @@
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()
if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.3) # for to_chars
get_osx_architecture()
endif(APPLE)
if(NOT DEFINED MAADEPS_TRIPLET)
detect_maadeps_triplet(MAADEPS_TRIPLET)
endif()
# 创建资源目录链接的函数
function(create_resource_link TARGET_NAME OUTPUT_DIR)
if(WIN32)
@@ -23,4 +70,4 @@ function(create_resource_link TARGET_NAME OUTPUT_DIR)
COMMENT "Creating symlink for resource directory for ${TARGET_NAME}"
)
endif()
endfunction()
endfunction()

27
cmake/version.cmake Normal file
View File

@@ -0,0 +1,27 @@
# define MAA_HASH_VERSION from git
set(MAA_HASH_VERSION
"DEBUG_VERSION"
CACHE STRING "maa version")
if(WITH_HASH_VERSION AND MAA_HASH_VERSION STREQUAL "DEBUG_VERSION")
find_package(Git)
if(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_HASH_VERSION "${output}")
else()
message(WARNING "git rev-parse returning ${result}, output:\n${err}")
endif()
endif()
endif()
message(STATUS "MAA_HASH_VERSION=${MAA_HASH_VERSION}")
add_compile_definitions(MAA_VERSION="${MAA_HASH_VERSION}")

View File

@@ -1 +1,2 @@
**/pnpm-lock.yaml
**/*.md

View File

@@ -11,6 +11,12 @@ module.exports = {
arrowParens: 'always',
overrides: [
{
files: ['**/*.ts'],
options: {
semi: true,
},
},
{
files: ['**/*.*css'],
options: {
@@ -30,11 +36,5 @@ module.exports = {
tabWidth: 4,
},
},
{
files: ['**/*.md'],
options: {
embeddedLanguageFormatting: 'off',
},
},
],
}

View File

@@ -1,21 +1,11 @@
import { defineClientConfig } from 'vuepress/client'
import { defineClientConfig } from 'vuepress/client';
import { getAsciiArt } from './plugins/asciiArt'
import ImageGrid from './components/ImageGrid.vue';
import AsciiArt from './components/AsciiArt.vue'
import ImageGrid from './components/ImageGrid.vue'
import Redirect from './components/Redirect.vue'
import './styles/index.scss'
import './styles/index.scss';
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>

View File

@@ -1,25 +0,0 @@
<template />
<script setup lang="ts">
import { onMounted } from 'vue'
import { useRouter, useRoute } from 'vuepress/client'
interface Props {
to?: string
}
const props = defineProps<Props>()
const route = useRoute()
const router = useRouter()
function resolvePath(to: string) {
const target = new URL(to, 'http://example.com' + route.path) // 使用一个虚拟的基础 URL
return target.pathname
}
onMounted(() => {
if (!props.to) return
const targetPath = resolvePath(props.to)
router.replace(targetPath)
})
</script>

View File

@@ -1,13 +1,11 @@
import { viteBundler } from '@vuepress/bundler-vite'
import { defineUserConfig } from 'vuepress'
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics'
import { plumeTheme } from 'vuepress-theme-plume'
import { viteBundler } from '@vuepress/bundler-vite';
import { defineUserConfig } from 'vuepress';
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics';
import { plumeTheme } from 'vuepress-theme-plume';
import { genSiteLocales } from './navigation/genLocales'
import DocSearchConfig from './plugins/search';
import DocSearchConfig from './plugins/search'
const isProd = process.env.NODE_ENV === 'production'
const isProd = process.env.NODE_ENV === 'production';
export default defineUserConfig({
base: '/',
@@ -17,7 +15,33 @@ export default defineUserConfig({
host: '0.0.0.0',
port: 3001,
locales: genSiteLocales(),
locales: {
'/zh-cn/': {
lang: 'zh-CN',
title: 'MAA 文档站',
description: '文档',
},
'/zh-tw/': {
lang: 'zh-TW',
title: 'MAA 文件站',
description: '文件',
},
'/en-us/': {
lang: 'en-US',
title: 'MAA Documentation Site',
description: 'Documentation',
},
'/ja-jp/': {
lang: 'ja-JP',
title: 'MAA ドキュメントサイト',
description: 'ドキュメント',
},
'/ko-kr/': {
lang: 'ko-KR',
title: 'MAA 문서 사이트',
description: '문서',
},
},
head: [
['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
@@ -64,16 +88,17 @@ export default defineUserConfig({
contributors: false,
changelog: false,
blog: false,
cache: 'filesystem',
search: DocSearchConfig,
codeHighlighter: {
themes: { light: 'snazzy-light', dark: 'night-owl' },
themes: { light: 'one-light', dark: 'one-dark-pro' },
},
markdown: {
annotation: true,
image: {
lazyload: true,
mark: true,
@@ -107,4 +132,4 @@ export default defineUserConfig({
id: 'G-FJQDKG394Z',
}),
],
})
});

5
docs/.vuepress/env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
declare module '*.vue' {
import { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}

View File

@@ -0,0 +1,19 @@
import { defineNavbarConfig } from 'vuepress-theme-plume';
export const enusNavbar = defineNavbarConfig([
{
text: 'User Manual',
icon: 'mdi:user',
link: '/en-us/manual/newbie.html',
},
{
text: 'Development Docs',
icon: 'ph:code-bold',
link: '/en-us/develop/development.html',
},
{
text: 'Protocol Docs',
icon: 'basil:document-solid',
link: '/en-us/protocol/integration.html',
},
]);

View File

@@ -0,0 +1,5 @@
export * from './zh-cn';
export * from './zh-tw';
export * from './en-us';
export * from './ja-jp';
export * from './ko-kr';

View File

@@ -0,0 +1,19 @@
import { defineNavbarConfig } from 'vuepress-theme-plume';
export const jajpNavbar = defineNavbarConfig([
{
text: '使用説明',
icon: 'mdi:user',
link: '/ja-jp/manual/newbie.html',
},
{
text: '開発関連',
icon: 'ph:code-bold',
link: '/ja-jp/develop/development.html',
},
{
text: 'プロトコルドキュメント',
icon: 'basil:document-solid',
link: '/ja-jp/protocol/integration.html',
},
]);

View File

@@ -0,0 +1,19 @@
import { defineNavbarConfig } from 'vuepress-theme-plume';
export const kokrNavbar = defineNavbarConfig([
{
text: '사용자 설명서',
icon: 'mdi:user',
link: '/ko-kr/manual/newbie.html',
},
{
text: '개발 문서',
icon: 'ph:code-bold',
link: '/ko-kr/develop/development.html',
},
{
text: '프로토콜 문서',
icon: 'basil:document-solid',
link: '/ko-kr/protocol/integration.html',
},
]);

View File

@@ -0,0 +1,19 @@
import { defineNavbarConfig } from 'vuepress-theme-plume';
export const zhcnNavbar = defineNavbarConfig([
{
text: '用户手册',
icon: 'mdi:user',
link: '/zh-cn/manual/newbie.html',
},
{
text: '开发文档',
icon: 'ph:code-bold',
link: '/zh-cn/develop/development.html',
},
{
text: '协议文档',
icon: 'basil:document-solid',
link: '/zh-cn/protocol/integration.html',
},
]);

View File

@@ -0,0 +1,19 @@
import { defineNavbarConfig } from 'vuepress-theme-plume';
export const zhtwNavbar = defineNavbarConfig([
{
text: '用戶說明書',
icon: 'mdi:user',
link: '/zh-tw/manual/newbie.html',
},
{
text: '開發文件',
icon: 'ph:code-bold',
link: '/zh-tw/develop/development.html',
},
{
text: '協議文件',
icon: 'basil:document-solid',
link: '/zh-tw/protocol/integration.html',
},
]);

View File

@@ -1,29 +0,0 @@
import { SiteLocaleConfig, LocaleConfig } from 'vuepress'
import { ThemeLocaleData } from 'vuepress-theme-plume'
import { locales } from './i18n'
import { genNavigationComponents } from './genNavigationComponents'
export function genSiteLocales(): SiteLocaleConfig {
const siteLocales: SiteLocaleConfig = {}
for (const locale of locales) {
siteLocales[`/${locale.name}/`] = {
lang: locale.htmlLang,
title: locale.siteTitle,
description: locale.siteDescription,
}
}
return siteLocales
}
export function genThemeLocales(): LocaleConfig<ThemeLocaleData> {
const themeLocales: LocaleConfig<ThemeLocaleData> = {}
for (const locale of locales) {
const navigationComponents = genNavigationComponents(locale)
themeLocales[`/${locale.name}/`] = {
navbar: navigationComponents.navbar,
collections: navigationComponents.collections,
}
}
return themeLocales
}

View File

@@ -1,161 +0,0 @@
import * as fs from 'fs'
import * as path from 'path'
import { default as matter } from 'gray-matter'
import { ThemeCollectionItem, ThemeNavItem, ThemeSidebarItem } from 'vuepress-theme-plume'
import { Locale } from './i18n'
interface MetaData {
baseName: string
order: number
title: string
icon: string
index: boolean
}
interface NavigationComponents {
navbar: ThemeNavItem[]
collections: ThemeCollectionItem[]
}
type SidebarItem = ThemeSidebarItem | string
function getMetaData(dir: string, entry: fs.Dirent): MetaData | null {
const currentPath = path.join(dir, entry.name)
if (!fs.existsSync(currentPath)) {
return null
}
let mdFilePath = ''
if (entry.isDirectory()) {
mdFilePath = path.join(currentPath, 'README.md')
} else if (entry.isFile() && entry.name.endsWith('.md') && entry.name.toLowerCase() !== 'readme.md') {
mdFilePath = currentPath
} else {
return null
}
if (!fs.existsSync(mdFilePath)) {
return null
}
const fileContent = fs.readFileSync(mdFilePath, 'utf-8')
const meta = matter(fileContent).data ?? {}
// 文件名,不含扩展名
const baseName = path.parse(entry.name).name
// 获取顺序目录的order在meta.dir.order里文件的order在meta.order里默认值为一个大数
const order = Number((entry.isDirectory() ? meta?.dir?.order : meta?.order) ?? Number.MAX_SAFE_INTEGER)
// 获取标题先从matter里找title再用正则获取一级标题最后fallback到文件名不含扩展名
const title = String(meta?.title ?? RegExp('# (.+)').exec(fileContent)?.[1] ?? baseName)
// 获取图标
const icon = String(meta?.icon ?? '')
// 是否添加到索引文件永远为true目录则看meta.index默认true
const index = entry.isDirectory() ? (Boolean(meta?.index) ?? true) : true
return {
baseName: baseName,
order: order,
title: title,
icon: icon,
index: index,
}
}
function getSidebarItems(dir: string): SidebarItem[] {
interface WrappedSidebarItem {
sidebarItem: SidebarItem
order: number
}
const entries = fs.readdirSync(dir, { withFileTypes: true }).filter((e) => !e.name.startsWith('.'))
const sidebarItemsWithOrder: WrappedSidebarItem[] = []
for (const entry of entries) {
let sidebarItem: SidebarItem
const metaData = getMetaData(dir, entry)
if (!metaData) {
continue
}
if (entry.isDirectory()) {
const children = getSidebarItems(path.join(dir, entry.name))
// 可折叠的子目录
sidebarItem = {
text: metaData.title,
// 只有当目录设置了index: true时才生成链接否则点击时不跳转、只切换折叠状态
link: metaData.index ? `${metaData.baseName}/` : undefined,
icon: metaData.icon,
// 目前没有文档使用了badge这个特性故不处理
// badge: undefined,
collapsed: true,
// 前面不能加斜杠,必须用相对路径
prefix: `${metaData.baseName}/`,
items: children,
}
} else if (entry.isFile() && entry.name.endsWith('.md') && entry.name.toLowerCase() !== 'readme.md') {
// 普通文件,取完整文件名作为链接
sidebarItem = entry.name
}
sidebarItemsWithOrder.push({ sidebarItem: sidebarItem, order: metaData.order })
}
sidebarItemsWithOrder.sort((a, b) => a.order - b.order)
return sidebarItemsWithOrder.map((i) => i.sidebarItem)
}
export function genNavigationComponents(
locale: Locale,
baseDir = path.resolve(__dirname, '../../'),
): NavigationComponents {
interface WrappedNavigationComponent {
navItem: ThemeNavItem
collectionItem: ThemeCollectionItem
order: number
}
const navigationComponentsWithOrder: WrappedNavigationComponent[] = []
// 进入指定语言目录即docs/<i18n>/
const langDir = path.join(baseDir, locale.name)
// 获取所有非隐藏文件和目录
const entries = fs.readdirSync(langDir, { withFileTypes: true }).filter((e) => !e.name.startsWith('.'))
for (const entry of entries) {
if (!entry.isDirectory()) continue
const metaData = getMetaData(langDir, entry)
if (!metaData) {
continue
}
const navbarItem: ThemeNavItem = {
text: metaData.title,
icon: metaData.icon,
link: `/${locale.name}/${metaData.baseName}/`,
}
const collectionItem: ThemeCollectionItem = {
type: 'doc',
title: metaData.title,
dir: metaData.baseName,
linkPrefix: `/${metaData.baseName}/`,
sidebar: getSidebarItems(path.join(langDir, entry.name)),
}
navigationComponentsWithOrder.push({
navItem: navbarItem,
collectionItem: collectionItem,
order: metaData.order,
})
}
navigationComponentsWithOrder.sort((a, b) => a.order - b.order)
return {
navbar: navigationComponentsWithOrder.map((i) => i.navItem),
collections: navigationComponentsWithOrder.map((i) => i.collectionItem),
}
}

View File

@@ -1,45 +0,0 @@
export interface Locale {
name: string
// displayName: string
htmlLang: string
siteTitle: string
siteDescription: string
}
export const locales: Locale[] = [
{
name: 'zh-cn',
// displayName: '简体中文',
htmlLang: 'zh-CN',
siteTitle: 'MAA 文档站',
siteDescription: '文档',
},
{
name: 'zh-tw',
// displayName: '繁體中文',
htmlLang: 'zh-TW',
siteTitle: 'MAA 文件站',
siteDescription: '文件',
},
{
name: 'en-us',
// displayName: 'English',
htmlLang: 'en-US',
siteTitle: 'MAA Documentation Site',
siteDescription: 'Documentation',
},
{
name: 'ja-jp',
// displayName: '日本語',
htmlLang: 'ja-JP',
siteTitle: 'MAA ドキュメントサイト',
siteDescription: 'ドキュメント',
},
{
name: 'ko-kr',
// displayName: '한국어',
htmlLang: 'ko-KR',
siteTitle: 'MAA 문서 사이트',
siteDescription: '문서',
},
]

View File

@@ -0,0 +1,8 @@
import { defineNotesConfig } from 'vuepress-theme-plume';
import { genNotes } from './genSidebar';
export const enusNotes = defineNotesConfig({
dir: 'en-us',
link: '/en-us/',
notes: genNotes('en-us'),
});

View File

@@ -0,0 +1,119 @@
import * as fs from 'fs';
import * as path from 'path';
import * as matterModule from 'gray-matter';
import { defineNoteConfig, ThemeNote, ThemeSidebarItem } from 'vuepress-theme-plume';
const matter = (matterModule as any).default;
interface MetaData {
baseName: string;
order: number;
title: string;
icon: string;
index: boolean;
}
function getMetaData(dir: string, entry: fs.Dirent): MetaData | null {
const currentPath = path.join(dir, entry.name);
if (!fs.existsSync(currentPath)) {
return null;
}
let mdFilePath = '';
if (entry.isDirectory()) {
mdFilePath = path.join(currentPath, 'README.md');
} else if (entry.isFile() && entry.name.endsWith('.md') && entry.name.toLowerCase() !== 'readme.md') {
mdFilePath = currentPath;
} else {
return null;
}
if (!fs.existsSync(mdFilePath)) {
return null;
}
const fileContent = fs.readFileSync(mdFilePath, 'utf-8');
const meta = matter(fileContent).data ?? {};
const baseName = path.parse(entry.name).name;
// 获取顺序目录的order在meta.dir.order里文件的order在meta.order里默认值为一个大数
const order = Number((entry.isDirectory() ? meta?.dir?.order : meta?.order) ?? Number.MAX_SAFE_INTEGER);
// 获取标题先从matter里找title再用正则获取一级标题最后fallback到文件名不含扩展名
const title = String(meta?.title ?? RegExp('# (.+)').exec(fileContent)?.[1] ?? baseName);
// 获取图标
const icon = String(meta?.icon ?? '');
// 是否作为索引页文件永远为true目录则看meta.index默认true
const index = entry.isDirectory() ? (Boolean(meta?.index) ?? true) : true;
return {
baseName: baseName,
order: order,
title: title,
icon: icon,
index: index,
};
}
export function genNotes(lang: string, baseDir = path.resolve(__dirname, '../../')): ThemeNote[] {
// 进入指定语言目录即docs/<i18n>/
const langDir = path.join(baseDir, lang);
// 递归获取目录和文件
function getItems(dir: string, isRoot: boolean): any[] {
// 将内容与对应顺序进行包装
interface Wrapped {
content: ThemeNote | ThemeSidebarItem | string;
order: number;
}
let itemsWithOrder: Wrapped[] = [];
// 获取所有非隐藏文件和目录
const entries = fs.readdirSync(dir, { withFileTypes: true }).filter((e) => !e.name.startsWith('.'));
for (const entry of entries) {
const metaData = getMetaData(dir, entry);
if (!metaData) {
continue;
}
if (entry.isDirectory()) {
// 递归获取子目录内容
const children = getItems(path.join(dir, entry.name), false);
if (isRoot) {
// 一级目录,作为“专题”
const item = defineNoteConfig({
dir: metaData.baseName,
link: `/${metaData.baseName}/`,
text: metaData.title,
sidebar: children,
});
itemsWithOrder.push({ content: item, order: metaData.order });
} else {
// 非一级目录,作为可折叠的子目录
const item: ThemeSidebarItem = {
text: metaData.title,
// 只有当目录设置了index: true时才生成链接否则点击时不跳转、只切换折叠状态
link: metaData.index ? `${metaData.baseName}/` : undefined,
icon: metaData.icon,
// 目前没有文档使用了这个特性,故不处理
// badge: undefined,
collapsed: true,
// 前面不能加斜杠,必须用相对路径
prefix: `${metaData.baseName}/`,
items: children,
};
itemsWithOrder.push({ content: item, order: metaData.order });
}
} else if (entry.isFile() && entry.name.endsWith('.md') && entry.name.toLowerCase() !== 'readme.md') {
// 普通文件,取完整文件名作为链接
const item = entry.name;
itemsWithOrder.push({ content: item, order: metaData.order });
}
}
// 当前dir的内容读取完毕进行排序并返回返回时丢弃order
itemsWithOrder.sort((a, b) => a.order - b.order);
return itemsWithOrder.map((i) => i.content);
}
// 递归起点只有这里是一级目录isRoot传true
return getItems(langDir, true);
}

View File

@@ -0,0 +1,5 @@
export * from './zh-cn';
export * from './zh-tw';
export * from './en-us';
export * from './ja-jp';
export * from './ko-kr';

View File

@@ -0,0 +1,8 @@
import { defineNotesConfig } from 'vuepress-theme-plume';
import { genNotes } from './genSidebar';
export const jajpNotes = defineNotesConfig({
dir: 'ja-jp',
link: '/ja-jp/',
notes: genNotes('ja-jp'),
});

View File

@@ -0,0 +1,8 @@
import { defineNotesConfig } from 'vuepress-theme-plume';
import { genNotes } from './genSidebar';
export const kokrNotes = defineNotesConfig({
dir: 'ko-kr',
link: '/ko-kr/',
notes: genNotes('ko-kr'),
});

View File

@@ -0,0 +1,8 @@
import { defineNotesConfig } from 'vuepress-theme-plume';
import { genNotes } from './genSidebar';
export const zhcnNotes = defineNotesConfig({
dir: 'zh-cn',
link: '/zh-cn/',
notes: genNotes('zh-cn'),
});

View File

@@ -0,0 +1,8 @@
import { defineNotesConfig } from 'vuepress-theme-plume';
import { genNotes } from './genSidebar';
export const zhtwNotes = defineNotesConfig({
dir: 'zh-tw',
link: '/zh-tw/',
notes: genNotes('zh-tw'),
});

View File

@@ -1,90 +0,0 @@
const modules = import.meta.glob<string>('../../asciiArts/*.txt', { query: '?raw', import: 'default', eager: true })
const asciiArts: Record<string, string> = {}
for (const path in modules) {
const name = path
.split('/')
.pop()
?.replace(/\.txt$/, '')!
asciiArts[name] = modules[path]
}
export type ThemeType = 'auto' | 'disable' | string
export type AsciiArtScope = 'web' | 'console'
interface AsciiArtData {
name: string | null
text: string
}
function pickAsciiArt(
arts: Record<string, string>,
artFilter?: (key: string) => boolean,
keyToName?: (key: string) => string,
): AsciiArtData {
const keys = artFilter ? Object.keys(arts).filter(artFilter) : Object.keys(arts)
let asciiArtKey: string | null
let asciiArtText: string
if (keys.length === 0) {
asciiArtKey = null
asciiArtText = ''
} else if (keys.length === 1) {
asciiArtKey = keys[0]
asciiArtText = arts[keys[0]]
} else {
const randomKey = keys[Math.floor(Math.random() * keys.length)]
asciiArtKey = randomKey
asciiArtText = arts[randomKey]
}
if (!asciiArtKey || !keyToName) {
return { name: asciiArtKey, text: asciiArtText }
} else {
return { name: keyToName(asciiArtKey), text: asciiArtText }
}
}
export function getAsciiArt(name?: string, theme: ThemeType = 'auto', scope: AsciiArtScope = 'web'): AsciiArtData {
let resolvedTheme: ThemeType = theme
// auto 模式
if (theme === 'auto') {
let currentTheme: string | null = null
if (scope === 'web' && typeof document !== 'undefined') {
// 浏览器中读取 HTML data-theme
currentTheme = document?.documentElement?.getAttribute('data-theme')
} else if (scope === 'console' && typeof window !== 'undefined' && window.matchMedia) {
// fallback: 检查系统首选主题
currentTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
resolvedTheme = currentTheme ? currentTheme : 'disable'
}
if (resolvedTheme !== 'disable') {
// light/dark/... 模式
return name
? pickAsciiArt(
asciiArts,
(k) => k === name + '.' + resolvedTheme,
() => name,
)
: pickAsciiArt(
asciiArts,
(k) => k.endsWith('.' + resolvedTheme),
(k) => k.replace(new RegExp('\\.' + resolvedTheme + '$'), ''),
)
} else {
// disable 模式
return name
? pickAsciiArt(
asciiArts,
(k) => k === name || k.startsWith(name + '.'),
() => name,
)
: pickAsciiArt(
asciiArts,
() => true,
(k) => k,
)
}
}

View File

@@ -1,7 +1,7 @@
export default {
provider: 'algolia' as const,
appId: '9ARM1N4899',
apiKey: 'f42bf1d18c786d5a46da30eccac5cb34',
appId: '99JM20SIFG',
apiKey: '7596a5a8c95cd64d4cf3050c9a4f878e',
indexName: 'maa',
locales: {
'/zh-cn/': {
@@ -81,4 +81,4 @@ export default {
},
},
},
}
};

View File

@@ -1,5 +1,6 @@
import { defineThemeConfig } from 'vuepress-theme-plume'
import { genThemeLocales } from './navigation/genLocales'
import { defineThemeConfig } from 'vuepress-theme-plume';
import { zhcnNavbar, zhtwNavbar, enusNavbar, jajpNavbar, kokrNavbar } from './navbar';
import { zhcnNotes, zhtwNotes, enusNotes, jajpNotes, kokrNotes } from './notes';
export default defineThemeConfig({
logo: '/images/maa-logo_512x512.png',
@@ -24,7 +25,28 @@ export default defineThemeConfig({
footer: false,
locales: genThemeLocales(),
locales: {
'/zh-cn/': {
navbar: zhcnNavbar,
notes: zhcnNotes,
},
'/zh-tw/': {
navbar: zhtwNavbar,
notes: zhtwNotes,
},
'/en-us/': {
navbar: enusNavbar,
notes: enusNotes,
},
'/ja-jp/': {
navbar: jajpNavbar,
notes: jajpNotes,
},
'/ko-kr/': {
navbar: kokrNavbar,
notes: kokrNotes,
},
},
autoFrontmatter: false,
@@ -104,4 +126,4 @@ export default defineThemeConfig({
// },
// ],
//},
})
});

View File

@@ -25,7 +25,7 @@
align-items: center;
justify-content: center;
.doc-hero-container {
.container {
margin: 0 auto;
display: flex;
align-items: center;
@@ -37,24 +37,4 @@
font-size: var(--vp-home-hero-name-font-size) !important;
line-height: var(--vp-home-hero-name-line-height) !important;
}
/* 首页标题 */
.name.clip {
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}
/* 按钮组容器 */
.actions .action {
display: flex;
flex-wrap: wrap;
justify-content: center;
max-width: 100%;
padding: 0;
}
/* 按钮 */
.actions .action .vp-button {
margin: 0.5rem;
min-width: 7em;
}
}

View File

@@ -1,2 +1,22 @@
@use "home.scss";
@use "fonts.scss";
/* 按钮组容器 */
.actions .action {
display: flex;
flex-wrap: wrap;
justify-content: center;
max-width: 100%;
padding: 0;
}
/* 按钮 */
.actions .action .vp-button {
margin: 0.5rem;
min-width: 7em;
}
/* 首页标题 */
.is-home .name.clip {
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}

View File

@@ -1,14 +1,74 @@
---
home: true
pageInfo: false
breadcrumb: false
navbar: false
config:
- type: custom
sidebar: false
index: false
lastUpdated: false
editLink: false
contributors: false
prev: false
next: false
---
# Ciallo (∠・ω< )⌒★
!: .7:~!.
:. :?55 ?5~::~J?:
:P5YY?7~: .!JYYJG. 7P! .7Y!.
^5JJJY55PPG5!. .?JYYJYG. ~P?. ~YJ: :PY~.
^5YYYYYYY5PPPPYJ??7!!~^!?YYYYY5 :5Y. :J5!. :G&#5^
~PY5555555PPPGGPPPPPPPPJ5YJYPP???5?57::.. !5J: Y&##P:
^YPP5JJY5555PPGG55PP555PGPPPGGPPP555555555YYYJJ?77!~^::...~5P^:. P####7
!GPPJ?JYYYYY5P5PPG57:...:!5GG?~!?JJY55PPPPPPPPPPP555555555YJYPYY57!~^::... .#####!
.?PY?YYYYYYY5PP5G?..:!??JYYPYY7. .....:^~!?JJYY5PPPPPPPP555555555555YYYJJ?77!!B####P
.5Y5YYYYYY555PGY.!?JYJJYJ5P:.?5^...:.::^~^^:::... ....::::~!!7?J5P55PPPPPPPP5P55PPGG###P
.:~?P5555PPPPGY:J77YYJY55Y: ..Y5?Y5PPGGBBBBGGGGP555J!^. ^~...^!77YY5G5::~:.:!5#G
^5555PPPG5?.~5JYYYYY7: :~?5PPG5BBBBB########BBB####BGY7~~^:..:: .:^!~^~?Y7~. ~J7Y
P55PPPGY~...?5?~^:...:75PPGP5PG#####BGBBBBBBBBBB######BGGP5YJ???7!7JY?!!?YYYJYJ^^GJ..
.!?P5YPB^ ....... :!J5PGB#######BBGY7~!!7?PYJJ7!5GB#######BBGPYYYYYJJYYYYY55GB##5^...
75YYPG... .::::JPGB####BGPP55J5?^~~~~~~~~~~!!!?PGGBBBB#####GGBBBBBB##########&G^^~
Y5YYGY... ..:^7PBB#####GPJ!~~~^~?!~~~~~~~~~~~~!!J7!7?JPG###&###############&&&&#5~
.PYY5B!... ..^YBBBB####GPJ~~~~~~~?J!~~~^^~~^^~~^^~!!::^75GB####&&&&&&&&&&&&&&##BG5
^PYY5G:... .^^PB#B###GP5JJ7~~~~~~~!7?~~~^..^:^~~~!~~~!~^~~5JJ555G##&&&&&&&#BBBGGPB!
?5YYPP......:Y######G?!~!!~~~~~~~~~~!7^^^^:^~~~~~~!7~~!~^~~~~~!7?YGB###P77!:JGGGGB:
5YYYGJ..... 7#B####GP!^~!~~~~~~^::!~~!~^^~~~~!~~~~~!J!~7!^~~~~~~!?Y5GPPPP5!^PGGGGP
.7 .PYY5B^..:::^GBB####PP!^7!^^~~~^^:^7~~~!^~~~~~~7~!~~~?5J?J7:~~~~~~~!?YY5YY55PPPGGGP~^:
^5 ~PYYPG...:.!PP#####G5J!:?!^^~~~~~~~!~~~~^~~~~~!J7!??!!J!!JY~^~~~~~~~7?Y5YY55PPGGGGGB#P
^G J5YYGY ..:!5YB####G5!^~~?!~~~!~~~~~~~~~~~~~~~!!77..~7^7~ .:7^~~~~!!~JGPPGGGBGB#####GY^
^&! 5YY5B! ^J5YGB###B5!^~~~?7~~~!7~~^??!~~~!~~~~~~!! :77!.::~!^~~~7!~!Y#########BBP.
^&#~ :PYY5B:.!YYJG#####GG!~~~~7?~~~?J~7?P57~~~!7~~~!~7:..:~5PGBJ~^!^~~!J~~!7P##B###BGGG7
^&##Y^. ?P5PGGJYYYYPB#####BG!~~~~~J!~!55?7?!~J?!!77!777?::JPBBGP5J~ .!~~7J!~~!7YGG5BBGGGGB:
^&###BG555YYYY5GBBB#B#####GGJ~~~~^7775?J~!!. .~??J?7??J7..!:7?7YY~7.^!~?J!~~!!7JP55PBGGGGG
^&######BBBBB##########&#B5?!~~~~^~55~.7!~ .:~!7Y?J?!!~. :!~^~~.:^.??!!!7??YP5PPBGGGGY
^&###################&##PY7~~!~~~~:?~ ^7:.^?5GBG7?!:. ... .:.:J????JY?5P57^:^75B!
.G&&&############&&&###GYJ?~~7~~~~~:!:.!YBBBGP5J^ :?JJJJJYGPG!. .. .5:
!G#&&&&&&&&&&&&#B5?GGGY5?~~~~~~~~~^755^~Y??57~!. :!J55YYY55GGPY .. !.
.~YBBBB#GYJ?77~7BBP55P7~7!~!77~~~~~~~^^^!~^~! .. ^?PGGGGGGG#BP7 !.
^5YY5G^ ..!GP55555?YP5!~~!77!!7?J~. .^:. .. .^5#57GPBBBBBBP. ....:Y~
J5YYPP:...:7GP55555GP5G#P!~~~~!!!7?!:^:...:^: .^J?5GP:.YPY7JJGB#Y .:~YGB.
5YYYGJ:::^YGGY:?P5G#BB##B57!77777777???JJJYY: ..:~!!YP7YYP^ ??7~7?5BBBJ~!5GG5
:PYY5B!:^JGGPGBJ5G#####PJGPPJ5Y????JJJJJYY5PY?YYY??JJJ?7~!~JBG: !??~!?YBBB#BGGGB7
!5YYPG5GGGGB###?B###BB?!JG555PPYJ?77JYPP5GP57!J5G5YGGPY?!:~BP~^. .7J?~~?JBBB#5Y5GB:
Y5YY5Y5P5555^!PJPJ^?GGJYGPPGG!. .!Y55Y5Y: ..:~7J555JPPY!P~.~YJ?~~7JG#BBYYYPG
^7?JJYYYY5557:.^^:7PPY55YY5P7 :. !PPPBP^... .J!5B5BB57JYP7?~~!J5B#GYYYGY
...^J!~^.^JG55PPPPPGJ... . ~BP55PYJJJ?7~~^?5PGBBBGGPGGJ!^^~JYBBPYY5B!
^^^:.:::?. ...:^~~~~^^::~!.:PGGPPPP5555555555555555PPPGP??7?YYBB5YY5B.
...::^~!7?JYY5PPPPPPPP55555YYY555555555YYPP
...:^~!7??JY555PPPPP55555BJ
...:^^~!7?Y^
. . . . .
7^.::?7:^: :7?:...P:. :~~~7P~~~^. :Y ^~~G!~~. :J?:^.?^^5 !! Y^ :Y.. ~~~^~Y!^~~: :P. :^^^JY .7~!.!~::5.
.:. ~^ 7? :YJ.77.P:~? Y~~7P~~J: ~7J^~!!P7!!.::JY?J:Y ?^ ^G..5~:P^!J .YJ Y7^Y Y^ :? 5 Y. P
Y: :JY? .GY^?!.P.~? :!!~?P~~?7 .^? !?!!!7? .7YJ~~ Y ^7.~Y.!7!!!~7: :7!~Y.!!^ 5 ~7^^!G^^::? 5 7~^~?J
Y^ ^~^.~! 7?!.::.P::: ~!!~?P!!J5. .J JJ!!~7P .57:^P Y: Y^ J.5 !7 PY 7~ :5 ^^ .YP: P :5~5:^^^^.P
!!~7!^:::!~ ~! Y .~?7 .. .? ?^ .~!J ?7^~5 J::. J.7::^7^~! :5 .!: : :~J ^^!?
.
<!-- Use a Random Ascii Art -->
<AsciiArt theme="auto" />
<!-- Use a specific Ascii Art -->
<!-- <AsciiArt name="maa-logo-with-endorsement" theme="disable" /> -->
<!-- markdownlint-disable-file -->

View File

@@ -1,5 +1,5 @@
---
home: true
pageLayout: home
title: 首页
config:
- type: doc-hero

View File

@@ -1,59 +0,0 @@
!: .7:~!.
:. :?55 ?5~::~J?:
:P5YY?7~: .!JYYJG. 7P! .7Y!.
^5JJJY55PPG5!. .?JYYJYG. ~P?. ~YJ: :PY~.
^5YYYYYYY5PPPPYJ??7!!~^!?YYYYY5 :5Y. :J5!. :G&#5^
~PY5555555PPPGGPPPPPPPPJ5YJYPP???5?57::.. !5J: Y&##P:
^YPP5JJY5555PPGG55PP555PGPPPGGPPP555555555YYYJJ?77!~^::...~5P^:. P####7
!GPPJ?JYYYYY5P5PPG57:...:!5GG?~!?JJY55PPPPPPPPPPP555555555YJYPYY57!~^::... .#####!
.?PY?YYYYYYY5PP5G?..:!??JYYPYY7. .....:^~!?JJYY5PPPPPPPP555555555555YYYJJ?77!!B####P
.5Y5YYYYYY555PGY.!?JYJJYJ5P:.?5^...:.::^~^^:::... ....::::~!!7?J5P55PPPPPPPP5P55PPGG###P
.:~?P5555PPPPGY:J77YYJY55Y: ..Y5?Y5PPGGBBBBGGGGP555J!^. ^~...^!77YY5G5::~:.:!5#G
^5555PPPG5?.~5JYYYYY7: :~?5PPG5BBBBB########BBB####BGY7~~^:..:: .:^!~^~?Y7~. ~J7Y
P55PPPGY~...?5?~^:...:75PPGP5PG#####BGBBBBBBBBBB######BGGP5YJ???7!7JY?!!?YYYJYJ^^GJ..
.!?P5YPB^ ....... :!J5PGB#######BBGY7~!!7?PYJJ7!5GB#######BBGPYYYYYJJYYYYY55GB##5^...
75YYPG... .::::JPGB####BGPP55J5?^~~~~~~~~~~!!!?PGGBBBB#####GGBBBBBB##########&G^^~
Y5YYGY... ..:^7PBB#####GPJ!~~~^~?!~~~~~~~~~~~~!!J7!7?JPG###&###############&&&&#5~
.PYY5B!... ..^YBBBB####GPJ~~~~~~~?J!~~~^^~~^^~~^^~!!::^75GB####&&&&&&&&&&&&&&##BG5
^PYY5G:... .^^PB#B###GP5JJ7~~~~~~~!7?~~~^..^:^~~~!~~~!~^~~5JJ555G##&&&&&&&#BBBGGPB!
?5YYPP......:Y######G?!~!!~~~~~~~~~~!7^^^^:^~~~~~~!7~~!~^~~~~~!7?YGB###P77!:JGGGGB:
5YYYGJ..... 7#B####GP!^~!~~~~~~^::!~~!~^^~~~~!~~~~~!J!~7!^~~~~~~!?Y5GPPPP5!^PGGGGP
.7 .PYY5B^..:::^GBB####PP!^7!^^~~~^^:^7~~~!^~~~~~~7~!~~~?5J?J7:~~~~~~~!?YY5YY55PPPGGGP~^:
^5 ~PYYPG...:.!PP#####G5J!:?!^^~~~~~~~!~~~~^~~~~~!J7!??!!J!!JY~^~~~~~~~7?Y5YY55PPGGGGGB#P
^G J5YYGY ..:!5YB####G5!^~~?!~~~!~~~~~~~~~~~~~~~!!77..~7^7~ .:7^~~~~!!~JGPPGGGBGB#####GY^
^&! 5YY5B! ^J5YGB###B5!^~~~?7~~~!7~~^??!~~~!~~~~~~!! :77!.::~!^~~~7!~!Y#########BBP.
^&#~ :PYY5B:.!YYJG#####GG!~~~~7?~~~?J~7?P57~~~!7~~~!~7:..:~5PGBJ~^!^~~!J~~!7P##B###BGGG7
^&##Y^. ?P5PGGJYYYYPB#####BG!~~~~~J!~!55?7?!~J?!!77!777?::JPBBGP5J~ .!~~7J!~~!7YGG5BBGGGGB:
^&###BG555YYYY5GBBB#B#####GGJ~~~~^7775?J~!!. .~??J?7??J7..!:7?7YY~7.^!~?J!~~!!7JP55PBGGGGG
^&######BBBBB##########&#B5?!~~~~^~55~.7!~ .:~!7Y?J?!!~. :!~^~~.:^.??!!!7??YP5PPBGGGGY
^&###################&##PY7~~!~~~~:?~ ^7:.^?5GBG7?!:. ... .:.:J????JY?5P57^:^75B!
.G&&&############&&&###GYJ?~~7~~~~~:!:.!YBBBGP5J^ :?JJJJJYGPG!. .. .5:
!G#&&&&&&&&&&&&#B5?GGGY5?~~~~~~~~~^755^~Y??57~!. :!J55YYY55GGPY .. !.
.~YBBBB#GYJ?77~7BBP55P7~7!~!77~~~~~~~^^^!~^~! .. ^?PGGGGGGG#BP7 !.
^5YY5G^ ..!GP55555?YP5!~~!77!!7?J~. .^:. .. .^5#57GPBBBBBBP. ....:Y~
J5YYPP:...:7GP55555GP5G#P!~~~~!!!7?!:^:...:^: .^J?5GP:.YPY7JJGB#Y .:~YGB.
5YYYGJ:::^YGGY:?P5G#BB##B57!77777777???JJJYY: ..:~!!YP7YYP^ ??7~7?5BBBJ~!5GG5
:PYY5B!:^JGGPGBJ5G#####PJGPPJ5Y????JJJJJYY5PY?YYY??JJJ?7~!~JBG: !??~!?YBBB#BGGGB7
!5YYPG5GGGGB###?B###BB?!JG555PPYJ?77JYPP5GP57!J5G5YGGPY?!:~BP~^. .7J?~~?JBBB#5Y5GB:
Y5YY5Y5P5555^!PJPJ^?GGJYGPPGG!. .!Y55Y5Y: ..:~7J555JPPY!P~.~YJ?~~7JG#BBYYYPG
^7?JJYYYY5557:.^^:7PPY55YY5P7 :. !PPPBP^... .J!5B5BB57JYP7?~~!J5B#GYYYGY
...^J!~^.^JG55PPPPPGJ... . ~BP55PYJJJ?7~~^?5PGBBBGGPGGJ!^^~JYBBPYY5B!
^^^:.:::?. ...:^~~~~^^::~!.:PGGPPPP5555555555555555PPPGP??7?YYBB5YY5B.
...::^~!7?JYY5PPPPPPPP55555YYY555555555YYPP
...:^~!7??JY555PPPPP55555BJ
...:^^~!7?Y^
. . . . .
7^.::?7:^: :7?:...P:. :~~~7P~~~^. :Y ^~~G!~~. :J?:^.?^^5 !! Y^ :Y.. ~~~^~Y!^~~: :P. :^^^JY .7~!.!~::5.
.:. ~^ 7? :YJ.77.P:~? Y~~7P~~J: ~7J^~!!P7!!.::JY?J:Y ?^ ^G..5~:P^!J .YJ Y7^Y Y^ :? 5 Y. P
Y: :JY? .GY^?!.P.~? :!!~?P~~?7 .^? !?!!!7? .7YJ~~ Y ^7.~Y.!7!!!~7: :7!~Y.!!^ 5 ~7^^!G^^::? 5 7~^~?J
Y^ ^~^.~! 7?!.::.P::: ~!!~?P!!J5. .J JJ!!~7P .57:^P Y: Y^ J.5 !7 PY 7~ :5 ^^ .YP: P :5~5:^^^^.P
!!~7!^:::!~ ~! Y .~?7 .. .? ?^ .~!J ?7^~5 J::. J.7::^7^~! :5 .!: : :~J ^^!?
.

View File

@@ -1,72 +0,0 @@
. ,:.
;, .,NNd'.
. 'cl:. .' ..
;;'... ,clllc. .' ..
.,cll;::;,'. .loclllc. .'. '.
.;lolllccc::;;,. .olccclcc. ., '. ...
:ccccccccc:::;,.. locccllcc. '' .,. .....
ccccccccccc:::;;::::;;,,'...xlccclcc: ''. '. .....
.cccccc:::::::::,;;;;:::ccc:llc:clcc''....lkx. ,. ......
';::;:cc:;::;;;;;,;::::;;;::,c::cc:,:ccccc::;;;,''.... ,. .......
,:::;odlcccc::::;;';:::::;;::;,;:;;,;::cccccccccccccccc::;;;,''.... .'';. .......
,:::;dlcccccccc:::;,;::;:d00Oxoc;,';;,,,,,;;;;;:::::cccccccccccccccc::;;;,'.',dx:. .......
.:::;dolcccccccc:::::;,lONNNNNKOkl;;:kXXKOkkxdolc::;,,,,;;;:::::::ccccccccccccccc:::;;,''.... .......
;;lolccccccccc:::::;kNNNOdlc:::cc;l;lXNNNNNNNNNXWWNXKOkxdoc:;;,,,,;;;;:::::ccccccccccccccccc:::;;,'......'.....
:occccccccccc::::;:NKdl:llllcccc,KO;:O0KXXNNNNNWNNNWWWWWWNXNNXKOdollccc:;,,,,;;;;::::::ccccccccccccccc:.;.....
,lcccccccccc:::::;;O0:odllllllll;lNNO;;0KKK000OkxooooddxxO0XNNNK0KXNNNNWWNX0Okxdoc;;;,,,,,;;;::::::;c:;;':.....
.::ccccc:::::;;,xO:xxoccllllc;cXNNNk;okdlc:,,;;;;,;;;;,,',;:ldx0NNNNNMWNNNNNNNN0x0XXKkdlcl:;;;,,c0OOok0xl,...
';::::;;;;;;;kKoloocllllc;:kNNNKkd,;,;:;;;;;,'''.'',,,;;;;;,'',:oxKNNNNKKXNNXkkKNNNKKkkxddxdo,XOOOXWNNNXd;.
:ccc::::::::dNN;lllll:;:oONN0Oxc;:,:'c,,,,'''..........''...'...''':lddkOKXXX00XNNXNKKkxdxxxdccoxXNNNNKclO;
'cc:::::;;;oK0X0;c::cod0XKXNKo;:c:;oolo'.......''....''........''..',;:cc:cclodxk0KNKOdodOKKOcc;:::oKN0;.;O0
;c;;;;;,'oXXNXN0;lxKNNXXXKXd::c:;,,',,......',,c;,,;;;;;;,,;;''.....'''',;:clolc:::c:c:;clc::cloool:,o0cckXN
.:ccc;'ONNNKXNNXXXXNX0Od::c;,''''.....''';;:dxxxdol:,::coxx;;,.......'''''',:ccloooooooooooollc:,...;K0XN0
,cccc;'XXNNNNNN00XO0NOc;c;'.....',',,,,;;;okkkkkkkkxoxxdddkkc:;'........'...'::;;;;:::;;;,,,'........,0K0O
;ccc:;;XXXNNNNNX00O0x;:;'......;';;::ccolckkkkkkkkkkkkkkkxodxo;;;;;,,,'......,,.......................;OO,
:ccc:,lXKXXNNNXK0Oko,:'.'.....'c,odxxkkkkoxkkkkkkkkkkkkkkkkdodcoolc::;c'...............................o.
.cccc;,xXKXXNNXKKOOl;,'''.....';;lxkkkkkkklldkkkkkkkkkkkkkkkk0xoxkOOkxo,:'............................''.
'cccc;'0XXXXNNXKKOo,,.'''...'',:lkkkkkkkkkdoldkkkkkOkkkOkkkkkOOkddkKKOxo;:,..........................';,
,cccc;,XXXXXNNKK0l''''''...';;::lxkkkkkkkkxoolkkkkOXKOkKKkkkkxkkkxdxO0kkx,;;;,,,,..................,;;;'
;ccc:,cNXXXXNXxkd.,''...';::coxdxkkkkkkkkkkddodOkkO0XXOkOkkkkkdxkkkdxOkkklxxdlccc:''..........,:l';;;;;'
:ccc:,xXXXXXNX0O;',..'...:ldkkddkkkkkkkkkkkkkxoOk0Ok0KOkkkkkkkkddkkkdk0kkkkkkkxoooc,'....,odxok0x';;;;;.
.cccc;'0XXKKXNXXx''..'..',cdkkkxkkkkkkkkkOOxkkkxxOOOkkkkkkkkkkkkkooxkxox0kkkkkkkkxool;;;,'';loxX0l,;;;;,.
'cccc;'XXXKKKNN0''''.'..'::xkkokkkkkkkkkkKKdxkkko0kOkkkkkkdkkkkkkkloxkdokOkkkkkkkkxocl:;::;;;,cxk:,;;;;,
,ccc:;:XXKKK0XN:;'''...',:ckkklkkkkkkkk0OK0dxkkkdkOkkkkkkkoxkkkkkkx:lodolkOkkkkkkkkkdlcclcccccc:;,,,,,,'.
. ;ccc:,dXXKKKkkx,,'''...':,;o0klkO0kkkkkkkkkdxkkkkx0kkkkkkkkoOddxdddcc:lllcKkkkkkkkkkkkdlc::ccc::::::::;;,,,,
. .cccc:,ONXKKK0d'c'''...',c:dx0xlkkkkkkkkkkkkdkkkkkk0kkkkkkxdcddo:ddxd:kxccld0kkkkkkkkxxolc::ccc::;:,;;;;;,,,,
. .cccc;'NNXKKK0;c:''...';;:kkkkxoxkkkkxkkkkkkxkkkkkkOkkkkkdoolkKXOlkkkcOXXko:0kkkkkkxkkd:cccc::::;;;'.........
.. ,ccc:;;WNXK0k::l'''...,,ckkkkkdoxkkkkxkkkkkkxkkkkkkkkkkkkkkkdoXNN0ckkoxXNNN0xOkkkkkdxkxc',,;''''''........
.. ;ccc:;lWNXKk;cl,''...',ldkkkkkdodkkkkxdkkkkxoxkkkkkxkkkkkkkkxoNNNNKlkoxNNNNNo0kkkkkoxkxd:''.........'''.
... :ccc;,xNNXl;ll:'''...,,cdkkkkkxldkkkkdokkkkl:cxkkkkxxxxkxxkkd0NNNNN0lcllc::lc0kkkkxlkkxxd,.........';;;.
.... .cccc;,ONk:colc,'.....';,xkkkkkkcdkkkklckkoc;:oxkkkxxdxxxxxxxoNK0K0xl,;'':kKNx0kkkkookxxxdc'......',;;;;
...'. 'ccc:;'l:clll:;........;'xkkkkk0cdxkkx::dcoxckcldkxxxdxxxxddlOk0d:''',;:ccXWNxOkkkocxkxxxdl,':l';';;;;;,
....',....';,,,,:lll:::;';.......:'kkkkkkOooxkxoc:lkkcOKOlcodxdddddddlxXX;'',;:::cdxOWWxkkkolokkxxxoo,:;:',';;;;;,
.....',:ccccllllc:;,'''..'......'c':kkkkkkOodxo;ockkddXNNXOocooooooolc0WNdOXlodl:;xxxWXdkxlloxxxxxdoo;:::;,';;;;;'
........',;;;;,''''.............:;ldkkkkkk0olc;x0ckxdNNNXK00xdccoollooco0WWWNkxxxOOkoNxdOKloxxxxddooo,::::;,;;;;;.
............'.................',,dxxkkkkkk0d:;kXKoxoXNNNKKOxdkOlocoxxkOKNWWWWNkdkkdxKNXK0Nldddddooooo,c:::,,;;;;;
..............................;clxxkxkkkkkkKckKXkoxNNXOo:''';l,lxKNWWWWWWWWWWWWX00KNNNdXK0cooooolllol;c;:xxkxl,;,
.............................'lolxxxdxkkkkkOOdNNxONOl,''',,;OWOXWWWWWNNNNNNWWWWNNNNNNXXXXoollllllc,o::ckNNNNNN0:'
...........................',locxxxxdkkkkkk0xdXNx:'',;;::colKWWWWWNNNNNNNNNWWNNNNNNNNNXdllllllll;;,,cXWWNX00NNK.
......................'o:,;,ll:xkkkkkkkkkkkOko:,lk::cl::lxxkWWWWWNNNNNNNNWWWNNNNNNNKdccccccllc;;::,KWWNNNNXNNX.
................':okc';,,cc:xkkxkxdkkkkkkklloNW0lxxdoOkkdWWWWWNNNNNNWWWWWWWWWN0o::::cccc:;;,',c:WWWNNNNNNNN:
.'.....';clodkkkc'',:cc:lxkxxkkxldkkkkkkkxod0KkxxO0OdkWWWWWWNWWWWWNNWWWWWWWKdl:;;;;,,,:;''':lWWNNNNNNNNN,
;cccc;:0NNNKOkkx';::::c;dxd:ckkkkoodxkkkkkxdckNOxdddONNWWWWWWWWWNOOXWWWWWWWWNx,',,':,'''',,;;NWNNNNNNNKd
:ccc:,oKNNNNXXx,;::::c:;lc;:;oxkkkxxddddoc;xXNNNXXXNNNNNNNNWWWWWNNWWWWWWWWNk:'':kO,:,,,,'''''oNXNNNNX0:.
.cccc;'kKNXXK0O,;;::::cc;;:c;''oxkxxkkkxxxxlckXK0XKXXXNX0NNNNWWWWWWWWWWWN0o:l;;'xK0;;:ldolc''''0XOOkd:;,
'cccc;,kO0O0Ox,;;:o:::cc,':,'..,dxxxxxxxxdxxxolodxkkxdoloNNNWWWWWWWWWNKko,,x:l;:KXXcoodkool,'''kNNK0:,;,
,cccc;;OO000x,;;,kNk;::'......',;ddddddddddddddddllccllcKWWWWWWWWN0kxddo,ckl;o,ONNNcoodkooo,''''oxkc,;;'
:ccc:;c000Oc,;;;;;o::;.......;,;::docdddooooooolllllcc;dOOOOOOkoc:;::cddkkx,,:xNNNXooodkdoo;'''''';;;;;.
.cccc:,oOko;,;;;,'.d,,......,ld;;::;c,:cloooolllccccc:;:;;'''';olcodkkkOxOd;;'oNNNNXxoodkxoo;''''.::;;;;.
'cccc;,:,,;;;,,'..'d''...'':ddd':::::;c:,;cllc:cc::;;::lxxl:;';oc;',:ldk0Nc'':0kNNNKolodkkoo:''''.ccc;;;
,cccc;::,,,,,,',,''d..';,:,odd:;:::,c:dOXNWWWN0x;,:l':c:OXXXK0xoc;'''';'.d:;lXX:oNN0llodkkool'''''cccc;,
;cccccccccccccoNKd:o:o0Nd',;::,;;;,'cKWWWWWWWWWWXdc,lc:;;kXNNNNNXK0kxllld:;:':;,kKkocllxkkdol,.'',ccc:;'
,;:::ccccccccckNN0dkWXd;;ccccccc::,KNNNNNNNNNNOKWNc:::;,:OXNXNNNNNNNdx0k,.;,';dxcd;:dckkkxol;'.';ccc:;'
',:ONNKk:,;;:cccccccccNNNNNNNNNNNkKNWo,,;;,'oxOKKXXNNNNK:;;;,c,''';o;:;klkkkkolc,'.:ccc:;.
,kOxkNNO:. .,,,;;;:;;0NNXXXNNNNNKNNWl;;ccccc::::cclodxdoo:,;',';;,;,;,olkkkkolc,,'cccc:,
'XKl dXX: .:kKXXXXNK0NNK;;;ccccccccccccccccc::;;;;,;;;,,,,;xkkkkllc,,,cccc;,
ox;,,,,;;;::::::ccccccccccccccccccc::;::ccc::;'',cccc;'
.,,,,;;;;::::cccccccccccccccccccc::c:ccc;.
.,,;;;::::::cccccccccc:cc:,
.,,;;;:::::cc;,
..,'

View File

@@ -1,72 +0,0 @@
xX XkdKx
kxk: cKk :kKd
lXK .NOoldx .XO' xK0
cxxO0KXN0 Okolllok l0x xK0
kXkollxddxkOKk Klcolllok XOc OKO d'
xxlcllloooddxxkKx Oclooolook ;Kk. OKO x0KNl
.dooooooooodddxkKXNNNNXl. lcooollooc KOd ,kKx :000KK
,ooooooooooodddxxddddxxkkO00K;looolood' .NOO: OKN O000Kx
loooooodddddddddkxxxxdddooodllodolooOO0KKKl,;0d. 'k0O .00000X.
cOxddxdoodxddxxxxxkxddddxxxddkoddoodkdoooooddxxxkOO0KXXNNWK; 'kKO l00000K'
dkdddxc:looooddddxxOxdddddxxddxkxdxxkxddooooooooooooooooddxxxkOO0KXXNNWO' cOOxK; 0000000
okdddx:loooooooodddxkxddxd:..';coxkOxxkkkkkxxxxxdddddooooooooooooooooddxxxkO0Ok:;d0Nd. c000000x
;dddx:cloooooooodddddxkl' .',lxxd,...',,;:cloddxkkkkxxxdddddddooooooooooooooodddxxkOO00KXNNNWKc .0000000
xxlclooooooooodddddx, ':lodddooxlxl. . ..',;:codxxkkkkxxxxdddddooooooooooooooooodddxxkO00KXK0O00000
cdcoooooooooooddddxd .:ldllllooook.'xd'.... . ..':cllooodxkkkkxxxxddddddoooooooooooooood0x00000
;kloooooooooodddddxx'.dc:llllllllxl 'xx.......',;cccc::;;'.. .... ..',;:coxxxkkkkkxxxddddddxodxxOd00000
:ddooooodddddxxk;'d;;coolllloxo. ,xc,:lodkkxxxxkxxxxkkOkxdl:;. .;....,:loldxxxkko.''c,.;lk000
Oxddddxxxxxxx,.clccolllloxd, .,:kxkxdxxxxxkOOO0OOkkkxxxxxkOOkdc;. ... .,,. ..,,;::;:ck.'''. .:x0
ddooodddddddd: xllllldxdc' .';oxdkdOokkkkOOO0000000000OO000O000OOOdl::,'....... . ..,;:;;;:ooc;. .ol'x
.Ooodddddxxxc....xoddoc:.... .cxdodxcclcO0000000OO0000OO00000000OO00Okxdoodoolc:;,.. .':c:'..'ooxdddc. .x0x'.
,xoxxxxxkOc.. . .xl;. .....:ddodxkkOkk000000OkkoxkkxxxxxxkkxxOO00000OOOOkxdolclodddododxoloddolcccldkc.oo,.
0doooxO' .. .... ..':ddoxkOOOO00000OOOxxd:;;;:cldkddoc;;xxk0000000OOOOOOkdoolccccccccccccllodk000x... .
.kooooxO.. ...'. 'oxoxO00000OkOkkkkxxxc,,,,,,,,;c;;:::,,odxO00000000O000OddxxxxdddxxxkkkO00000000k...'
;xooodxx... ...'.;xdxO000000xOxxddooclo,,,,,,,,,,,,,,,;c:;cxxxxxkkkO000000kk00000000000000000000000x'':
ldooodkl.... ...',ckdO0O00000Ookc:;;,,,,c;,,,,,,,,,,,,,,,,:c:occloddxoO0000000000000000000000000KKKK00c'
xooooxk;.... ...''lxkOOO00000Oxxl;,,,,,,,ll:,,,,,,,,,,,,,,,,.;c;,'',;ckdO000KKKKKK0000000000000KKKKK0OOx
kooooxO..... ...'ckk0OOO000OOkdl,,,,,,,,,:cl:,,,,,',,,',,,,,'',::,..';cxdk00000KKKKKKKKKKKKKKKKKKKK0Oxkc
.kooooxk..... ...lOOOOOO000Oxxddl;,,,,,,,,;ccl,,,,'..',..,,,,;,,,;:;'.,,;kxxxkkkk0KKKKKKKKKKKKKKKK0kxxxO'
;xooodko .... .;,:0kOO000Oxddoc;:;,,,,,,,,,,::c:',,'...',',,,,,:;,,,:;',,,l;;:looodOO0KKKKKKKK0kdlOxxxxxO
odooodk;..... ..'xOk00O000dl:,,::,,,,,,,,,,,,,;c',.',..',,,,,,,,::,,,:,.,,,,,,,;cccokO0000kc:;c,.;Oxxxxxk
kooooxO...... ..;OO00O00Oko:,,,;,,,,,,,,,'';,,,;;''',,,,,,,,,,,,,cc;,;c;.,,,,,,,,;cclxxxkOOxlc;..lkxxxxkl
OooooxO...... .OOOO0O00Odd;,,c,,,,,,,,,,..:;,,,c.,',,,,,,:,,,,,,,lc;,:c,',,,,,,,,;coldxddxxxko;,dkxxxxk;
Nc 'kooodxd....... dxOOO000Okdo,,,l,,,,,,,,.'..:;,,,:,',,,,,,,c;,,,,,,;dlc:cl,',,,,,,,,,:looloooooodxkkkkkkOXNd.
0' cxooodk:.....,,;kkOOO000Odkxc.,l,'.,,,,,,,,,:;,,,,;.,,,,,,,,c'::;:::oodlllo.,,,,,,,,,,,:loddoooddddddddxxkkkk
0, xoooodk' .....:OoOOO000Okod:;.;l,,,,,,,,,,,,:,,,,,,.,,,,,,;:o::cd::;:d,;ool:.,,,,,,,,;;cloddoooddxdkxxxxxkkkk
0c 0ooooxO .....xodOO000Oxxd,,,,;c;,,,,;,,,,,,;,,,,,,',,,,,:ccl,..'l,,,o'..,cd.,,,,,,;,,:dooooddddxxxO00000000l
0k .kooodxx ...,ddlOOO000kko,,,,,:c;,,,,;,,,,,,;,,,,,,,,,,,,,,,:c. .o,,c;. .;',,,,,:;,;oOkkxOOOOOO0000000l
00; ;xooodxl ..,xolkOO000Okl:,,,,,:c:,,,,;:,,,,;c;,,,,,;,,,,,,,,;c .l,c; c.,,,,,c;,;:dOO000000000OOOO
00K. ddoooxk; .lxlldOOO000kko:,,,,,;l:,,,,:c,,,,ldo;,,,,;;;;,;;,,:. .lolloddlo.,,,,;l,,;;:k000000000Oxxxx
000K, Oooooxk' ,doclokO00000Oxk;,,,,,,o:,,,,lo,,coxdc;,,,;;:;;;;;;;c ....;lkxOOd,. ;.,,,,cc,;;;:oO000000Okxxxxl
000O0O OooodxOldollldx00000000xO;,,,,,.o:;,,;dd:oc;o,ol:,;;;:;;;;::l',.:dOOOkxdoo. ;',,,co;,;;;:lkOdlOxOxxxxxk;
0000Ok0XXXOxkkkkdllldddxOx0000000dO,,,,,,'cc;,;codl,,o'.'loc:;:::::::l;..xOOkxdddo:;' ;,,,clc,,;;;cckdxdOkOxxxxxk.
00000OkdoooollllodxkOOO00O000000OoOd,,,,,,'c:;cxco,,::. .'coccccccclo. :'.lc:ldx;;; .:,;llc;;;;;:ccxdddxkOxxxxxO
00000000OkxxxxkOOOO000000000KK00dxl:,,,,,,.clox;.o,;: ....;:ooccllccoc. ,;;;'',c ;:'.lc;;;;::ccckddddxkxxxxxx
000000000000O00000000000000KK0Okk:;;,,,,,,.:dx,..c;c. ..';:,'lcoc;;,'. ,:,,:;. ... l:::::ccccckodddkkxxxxxl
K0000000000000000000000000KK00xol;;,;,,,,,,.o,..,c; .'cdOOOxlkl;. .... :...occccclllclxoxd;;,;lkxk,
0KK000000000000000000000KKK00Olcl;;;:;,,,,,'': ;' 'lkOOOkkx' '. ....ccllllllokcddo, .dO.
kKKKK00000000KK00000KKKKK00Oklco;;;;:,,,,,,.;:. ;dOOkxxddocl. .:llllllllxxkko. ... .x
:0KKKKKKKKKKKKKKKKKKKKOcdkxklld;,,,,,,,,,,,',cdkl,ddolddl;;, .:oooooolloxxddk. . .o
.00KKKKKKKKKKKKKKOdc,oOxkkood;,,;,;:,,,,,,,llc .l;;:c',,: .cddddoooodxxkOkod d
.0O00000Oxolc:,,,oOOkdoodl;,;;,,;l:,,,,,,,;c:..,;;'.':, .:ldxxxxkkkdxOOOdl l
;xooooxd. .',,;Oxddddox:;:do,,,,cc:;,,,,,;:o, ';:::' ''. ;kOkkOdkOOOOkkxx .:
odooodkc. ..;kxddddodxloxdxc;,,,;;::::cox;. ... ,dOOd,'kdkkkkOOOOOc . ..dx
OooooxO,. ....'kxxddddooxxdoxOOc;,;;,,,;;;;lo,........ .. .cdlxxO;..xxdl:cloOOOO..'',:dxkc
Oooooxk,'.'.';kxxdcdddookOdkO00k:;;;;;;;;:;;;clc:;,,;:clc .,ckk;dlxd...occ:,cclkOOO, ..dkxk'
,kooooxx''...;kxxk, ,xddO000000Okx::::::::::::::::lloollo. .,;::cko,lxck' occ:,ccckOOOOc;,okxxO
cdooodxo...'okxxxxxcddx0000000xkxdd:co:::cccccccllllloox:'''''',codxddo::,,;kkd; .ccc:,:ccxOOOOOOxxxxxx
koooodkc',cxkxxxkO0:kk000000kl:xxddxokdolcccclllooooodxdxxOOOOxcloc:,,,';':xxOc .;cc:,;ccxOOOO0ddxxxxo
OooooxkdkkxxxkkO00O:OO000OOd:::Odddddxodkxollodooddxxddl;;ldxOxcoxOkdl:,. oOOd., .clc:,,ccdOOOO0oooxxx:
.kooooxddkkkkkkOkkOO:00Oxkdkc::dxdddkod:'. .;xkdlOdod'.....;coxOOOOxO0:dxl..dc .llc:,,cclOOOOOooooxk'
;xoooooooooooooc .:dcdc. :OkxddkxxxkOo. .:oklodxx,. ...,;lll:dxdOdxk,.,coll;,,:clk0OOkooodxO.
.dxdddooooooooo, .:, .:xxoooooooddk. '. odddxkd'. . :;.,k0xkOx:;o:xd:o,,,;clxO0Oxooodxk
.ckd' .,dkxxdooooooooo ,. ckkxxkOc;'.... .dxxxkokOOOxcxdx,l,,,,clokO0dooodxo
'k,';, 'dc'dkkkxxxdxx. ... . lxxoooooddddoolc:;:ccdkxOkOxxkxkxkcl,,,,clokkOoooodk:
.k..; ,..dc ,c,..... .. .xxxoooooooooooooooooddxxxxkxxxkkkkx;,,,,llokkkooooxk.
,,lkkkkxxxddddddoooooooooooooooooooddxddoooddxOOkooooxO
;xkkkxxxxddddooooooooooooooooooooddodoooxx
.lkkxxxddddddoooooooooodoodkc
.dkkxxxdddddooxk'
;xkO.

View File

@@ -1,54 +0,0 @@
WWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWMMWWMMMMMMMMWKOdoxdxolc:::;;:::;;;;;:ccclc:c:lxNWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWMWWWWWWWWMMMMMMMMMMMMMMMMMMMMMMMW
WWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWMMWMWMMMMMMWWKkxodxddl;;;;;;;;;;;;;;;,,;;:::;;;:l0NWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWMMWWWMWWWMWWMMMMMMMMMMMMMMMMMMMMMMMMM
WMMMMMWMMMMMMMMMMMMMMMMMMMMMMMMWWWMMMMMMMMWWWXOxdoddOk::;;:cllccc::::;;,,,,,;;;;;,;:xKWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWMMWWWMWWWWWMMMMMMMMMMMMMMMMMMMMMM
WWWWWMMMMMMMMMMMMMMMMMMMMMMMMMWWWWWWMMMMMWWWKOOoloOkdc::::::::::::;;:;::;,,,,,,;;,,,;cxKNWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWMMMMMMMMMMMMMMMMMMMMWMMM
WWWWWWMMMMMMMMMMMMMWMMMMMMMMMMMWWWWWMMMMWWWXkOoclOXlccccc::::::cc:::;:;,::;,,';,;;,,,,:ok0NWWWWWWWWWWNWWWWWWWWWWWWWWWWWWWWWWWWWWWWWMMMMWMMMMMMMMMMMMMMMMMMMMWMMM
WWWWWWWWWMWMWWWWMMMMMMMMMMMMWWWWWWWWWMMMWWWOxo:ckl:lcc:ccclooooldolc:::;;;:;;,';;;,,,,;;lkOKNWWWWWWWWWWWWWNNWWWWWWWWWWWWWWWWWWWWWWWWMMMMMMMMMMMMWWMMMMMMMWWMMMMM
WWMWWWWWWWMWWWWMMMMMMMMWMWMMMWWWWWWWWWWWWMKxdc:ll::c;::lldxxdoolldddlllc:;;:;:;,;;,,,;;;;cxO0KWWWNWWWWWWNNWWWWWWWWWWWWWWWWWWWWWWWWWWMMMMMMMMMMMMWWWWMMMMMWWWWWWW
WMMMMWWWWWWWWWWWWWWMWMWWMMMWMWWWWWWWWWWMNWkxlc::locc;ccollllclccc:looll:c:;:c::,;;,'',;,;;cxkO0XWNNNNNWWWWWNNWWWWWWWWWWWWWWWWWWWWWWWWWMWMMMMMMMMWMMMMMMMMMWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWNWNW0Oco:;:llll:c:clccccllc:::c::::;:;,ccc;',,'',;;;,,:x00O0NWNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWWWWWWMMMMMMMMMWWMMMMMMMWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWNNXMxx:l;;:olo::;::clllolllc::::;;;;;;;oc;,,,',''';;;,,;l00OkNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWWWWWWMWMMMMMMMWMMMMMMMWMWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXNNNNoo;c,::c:::c:;:clllllllcc:::;;;;,;;lko,,''''''':l:;;;;o00xXNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWWMMMMMMMMMWMWWWMMWWMWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWMWNNNWKX:c;c::;:;;:l;;;clllcccccc::;;;;;;;;c0kl'''.'..'.,::;;:;;oOkXNNNNNNNNNNNWWWWWWWWWWWWWWWWWWWWWWWWMMMMMMWMWWWMMMMWWWWMWMMW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWNWNWOk;;;l;',;,:xlcc::ccccc:ccc:lc;;;;;,;c0xO;'''.'..',;,:,,::,;co0KNWWNWNNNNNNNWNNWWWWNWNWNWWWWWWWMWWMMWWMWWWWWWMMMWWWWMWWWW
WWWWWWWWWNWWWWWWWWWWWWWWWWWWMWWWWWWNMXN0o;';c'',;,lOxlcooc:::cl::cldx::lco:,dOxOl,.'......',,:;,,:;;;:O00KXNWNNWWWNNNWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWMWWWWWWWW
WWWWWWWWWWNWWWWWWWWWWWWWWWWMWWWWWNXMNKNOl:';;'.',;lO0OdloxxddoollxxxxdxxxxxxOOOOx,.........',,:;;,,;;:oONNNXXNWWWWNNNNWWNWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWMWWWWWWWNNXMKXXOc:'','..';okkxollodxkkkkkOOkxxolcccoxkOOx,..'.......,:;;:;;,;:clxKWWNNNWMMWWWWWNNWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWNWNWWNWWWWWWWWWWWWWWWWWWNXWW0NKO::'''.'..',';'.''.;x00000000x;',;,','',lo'..''.......,:;;:;;;,:cldOXWWNNNNNNWWWWWWNWWWWWWWWWWWWWWWMWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWN0NNKNKkcc,,'.....:kxldkkdkO00OOOOOOOkoOOxoxOo:c'...',... .,'',;:;,,;;;,;cox0NNWNNNNXNNNWMWWNNWWWNNNWWWWWWMMWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWNKO00KX00ll,,'.....ckOxxxxkOOOOOOOOOOOOkxdxdkkxkc....',,... .,,,;,;:,;;;;,,;clkKNNNWWWWWWNNNNWWWWWWWWNNNNNNNNWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXW0OOoXK0kol,''.....'xkkkkOOOOOOkkkOkkkkkkkkkkkko...:;',,... .';,;:;;;;:c:;;;;:cldOKKXXWWWWWWWWNNXXNWWWWWWWWWWNXXNNNNWWWWWNWWWWWWN
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXNX0xdKKKodl,','.....,xkkkkkkkkkxxkkkkkkkkkkkkxo:;':l;','.... .';;;:::;,;ccllc:cclodxO00KKXNXXXXXXKKKK0OO00KKKXXXNWWNNNNNNNNNNNNNN
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWK0NNkxKKOo:o,',,.....;.okkkkkkkkkkkkkkkkkkkkkxdoo,clc,'.,.... .''::;:cc:,,:clloooddoloddxkOO0KXK0OOOOKKXNNNXXKKKKKKXXXXK0KXNNNNNNN
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWNKkKWNxKNxl::,,.''.':.;ocokkkkkkkkkkkkkkkkkkkxxxx;cl:,,''''......',:;;:ccl;',;:lloooddoldxxxxxkkkOOOKKKKKXKK0KNWWNNNNXNNXXNXKXNNNNN
WWWWWWWWWWWWWWWWWWWNNWWWWWWWWNXdKNW0KOdl:;,,.....,cdoxoxkkkkxkkkkkkxxkkkkxxxx::c;,,,,'.... ...'';:;:ccccc;,;,;cldoodddxk0NNNNNNXNNNNWNNNNNXXXNNNNXXXNXXNNXNNXXNN
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWNXdKWOkOxoc:,,''.....':dxxxxxkkkxkkkkxxxxxxxxxxlccc::;;;'.'.......',;;;clcccl::cccclolodxkxOKNNWWWWNNNNNNNNNNNNXXNNNNNNNNNNNXXXXXXN
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWX00Kdxxdlc:'.,'.'....;coxxxxxxxxxxxxxxxxxxxxdodoooc:;:c:',,......';;,,clllccc;lloodddoddxkOOO0XNNNNNNNNNNNNNNNNNXXNNNXXXNNXNXXXXXX
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWN0xdoddoll',..'.......:llldxxxxxxxxxxxxxxxoooddddddoc::cl,;,......,;;;cooooolcolooodddxxkk0OOOO0KXXNNNNNNXNNXNNXNXXNWNXXNNXXXXXXXX
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWNK0dcooolx,,,;.'........clllccdxxxxxxxxxo:oddddddxxdddddolo;;.......:::ccoooddod0xdodddxkOOKXK0OOXNXXXXKXNNNNXXXXNXXXXWXXXXNXXXXXXX
NNNNWWWNWWWWNWWWNWWWWWWWWWWWWKKkccccldd',,:;,'.......'cloc,,:ldxxxoc;;;ddddddddxxxxxdxkxooc'...'.;cc:llloodddkWXOdxxxxkkO0XWX00XNNNNXKKXNNXXNNNXNXXXWXXNXXXXXXXK
NNNNNWNNNNNNNNNNNWWNWWNNWWWWN0Kkc;loxO:;.,:;:.....,:''':oc;;,;;:c:;;,,,ddoooddddxxxxxxkkOokd;''',':lclodoooxxxxXWNKOkOk0KKXXNXKXKNWWNNNXXNNXNXXNXXXXNWNNNXXXKKKK
NNNNNNNNNNNNNNNWWWWWWWWNNNNWKOXO:lxxOd',',:,:. :..,::'.'''',;;;;;;,,''.dddooooddddxxxxxkOkdOx,.,,,,;loddxxdokxxk0NWWK00OXNNNNNXNWXNNWNNNNXXXXNXXXXXXNMXXXXXXXKKK
NNNNNNNNNWWNWNWWWWNNWWWWWNWKkKXkoKXOo',;;';;;..l. :o:,..'...':;;;;,,,,,,:ddddoodddddxxxxkkxxOx,'c:,,;oddOKxdkkx0K0KNWNX0KXNNNNNNNNXXXNNNNNNNNNXXXXXXNMXXNXXXXKKK
NNNNNNNNNNNNWNWWNNXXWWWWWWNkxOKONW0::':l:,;:'..l. dloc:'..'.:c::::;:::cc:xxxxxddxddddddxxxkdxOx,;dlc;;coOWOxkkONX0XKXNW0NNKKXNWWNWKXXXWNXXXXXNNNNXXXWNXXXXXXXKXK
XNNNNNNNNNNNNNNNNNNXXWWWWW0olx0WKd,:::ol:::;.,.l. lxdoc:....'::::cclllllcx0OOOOkkX0xdxOkxkkkdxOx,;dol;;coOkx0OKNNX0XKXXKNNXKXXXNWNXXXXXXXXXKNXXKXNXNNXXXXNXXXXKX
NNNNNNNNNXXXNNNNNNNNXXXXNKxclxxo:;lxdcdoOkd,.'.. .xxdlcc.. ,'.;cllllllcc:,:oxkkO00NNKkk0XNNX0kxxkc;dddl:cllldx0XNNX0X0KXNNXXXXXXNXXXXXXXXXXKXXKKKKXKKXXXXXXXXXXK
NNXNNNNNNNNNXNNNNNNNNWNX0OOdllllokkkxcxOW0d;;'' ;dxolc,. .;:;';cc:ccc::;::;:ccxOkk00XNXKKNWWNKkxkxcloddlcdxodxxOKXX0XKXXXKKXWNWWXXXXKXXXXXXXXXXXXKXKXXXXXXXXXXX
NNNNXXXNNNNNNNNNNNNNNWWWXXKOd0kO0000OlxNXxcOco...c,;,'...;odxol:';:::;:clcll:ll:oxkxkxk0KXXXXXXN0kkkkxolooodXNK0OkxOOKXXNXXXXXNNXXXXXXXXXXXKKXXXXKXKKKXXXXNXXXXX
NNNNNXNNNNNNNNNNNNNWWWWNNNXKk0XNKKKKXkx0okK0kl....;..l,,,dodxxxdo'.cccooooololoocoxxxxddxkkkkkkO0KKkO0KKxk0kxKNNNXKOOkO0XXXXXXXXXXXXXXXXXXXXKKKKXKKKXKXXXXXXXXXX
XNNNXNNNNNNNNNNNNNNNWWWWWXXXXNNNXK0OOkolxxkkxxc:.....;l:xdlldxko;,;':clooooollooolodddddoodddxkO00KkKKXNXOOKkxO0KKXXK0OkxO0XXXXXXXXXXXXXXXXXXKKXXXXXXXKXXXXXXXXX
NNNNNNNNNNNNNNNNNNNNWWWWWXXKKXXXXXXXXX0xdO0OO0ool:,..,:codoclc.'',,;'.'loooooooooocoddddddodxOOO0O0xKKXNNX0XNKk0KXXKKXXXK0kk0XXXXXXXXXXXXXXXXKXXXXXXXXXXXXXXKXXX
NNNNNNNNNNNNNNNNNNNNWWWWWNKW0KXXXXXXXXK000OO00:dxxxc:,...',;,'.'.',,,'':ooooooolool:oodddxxxxxkkkk0x00NNNNNKXXXkK0XXXXXXXXX0kkKXXXXXXXXXXXXXXKXXXXXXXXXXXXXXXXXX
NNNNNNNNNNNNNNNNNNNNNWWWWNWN0KKXXXXXK0KK00000dllodloxxkdc;,''..''..',cl,,loooolllolccddddxxxxxdddkOxOOKNNNNNXXNKkKKXXXXXXXXXK0O0XXXXXXXXXXKXKXXXXXXXXXKXXXXXXXXX
XNNXNNNNNNNNNNNNNNNNNWWWWXW0k00KKXX0KXXK0000xkkxdloxOkkkkxdll:l:lllloooo:,cooollllll:oddddddxxxxddkxO00KXNNNNKXN0kX0XXXXXXXXXKXKOKXXXXXKKXXXKKXXXXXKXKKXXXXXXXXX
XXXXNNNNNNNNNNNNNNNNNNNWXNXkdOO00K0XXXXKK00KOxxOxkOO00Okkkkdo::lldoooooooc::oooooooolcddddxxxxxxldxdx000XNNNNN0XNx00XXXXXXXXXXXXXOXXXXXXXXKXXKKXXXXXXKKXXXXXXXXX
XXXXNNNNNNNNNNNNNNNNNNNNXN0OxkOOO0KKKKKK00KKX0xkOO0KK000OkOxkl:;cdxdoooooocclooooooooooddxxxxxxllodxxO0KNNNXXXX0KKx0XXXXXXXXKKKK0KXXXXXXXXXXXXXXXXXKXXKXXXXXXXXX
XXXXXXNNNNNNNNNNNNNNNNNXWKOkxxkkk0KKKK0000KXKOxOO0KKK0000OOkkkl:clkxddoooodcooooooooooldxxxxxxl:clddkxO0XNNNNNXXKKkKXXXXXXXXXKXXXXXXXXXXXXXXXXXXXXXXXXKKKKXXXXXX
KKXXXXXXXXXXXXXXNNNNNXXXWkkxxxkkxxO0K00000XX0xOOOKKKK000000kOOkxdxxxddoddddoddddooooooooxxxxko:cloookxk0KNXXXXXXXKkKXXXXXXKKXKKXXXXXXXXXXXXXXXXXXNNNXXXKXXXXXXXX
KKXXXXXXXXXXXXXXXNNXXXKWKkxxxxxxxxxdk0000KXKOkOO0KKKK000000kOOkkxdkxddoxdddxddddddddooolxkkko:cdddolkxkO0XNXXXXXXXK0KKKKKXXKXKKKKKKXXXXKKKKXXXXXNXNNNXXXXXXXXXXX
KKXXXXXXXXKXXXXXXXXXXKNWkxxxxxdxxxdolxxOKKXKOkO0KKKKK000000kkOOkxdkxddoxxxxdxxxxdddddoolokkd:lxxdxcokxkO0KNXXXXXXXXKK0KKKKKKKKKKKKKXKKKKKKKKKXXXXXXNXXXXXXXXNXXX
0KKKKKKXXXKXXXXXXXNXKKKKxxxddxdxddlldxxdx0K0kOO0KKKKK000000kkkkkxdxxdodddxddxxxxxdddddoolxkooxxxxxlxkkkO00NNXXKXKKKKKK00KKKKKKKKKKKKKKKKKKKKKXXXXXXXXXXXXXXXNNXX
000KKKKXKKKKKKXXXXNX0OOxkxddxxxd::;lxxxxdd0OkO00KK00000000OkxkkkxdxxdoddddddddxxxdddddooldOOkxxxdddxxdkOOOKNNXKXKKKKKKKKKKKKKKKKKKKKKKK0KKKKKXXXXXXXXXXXXXXXXXXX
00KKKKKKKKKXXXXXXXXKO0OO0xddxxkd. .:kkxkO00OxO00000000000OOkxkkxxdxddodddddddddddddddooollk0000000OOklOOO00NNWNKKKK00K00K000000KKK00KKK0K00KKKKKKXKKKKKKKXXXXXXX
OO00000KKKKKXXXXXX0kkOO0OOdoxxxl. .oxkkkO00kxOO000000000OOOkkkxxxdddooddddddddddddddoooolcdO0000000K0dO000OXNNWWK0KK00000000K000000000000000K000KKKKKKKKKKKKKKKK
kkOOO00000KKKXXXKkO0000OOOOkxddOOcldxxxxkOOxxkkOOOOOOOOOOkkkxxxxddoooodooodoooddooooooollcok00000000kxOO00O0NNNWW00000OO00000OOOOOOOOOOOO000K00000KKKK000KK0K0KK

View File

@@ -1,54 +0,0 @@
.':c;:;clodddxxdddxxxxxdooolododl;
.,;c:;::lxxxxxxxxxxxxxxxkkxxdddxxxdl.
.';:c::',ddxxdolloooddddxxkkkkkxxxxxkxd;.
.''clc',:oddddddddddddxxdxddxkkkkkkxxkkkxo;.
.,'col'.loooooddddddoodddxdxkddxkkOxkxxkkkkdc,.
';cdo,ldloodooolccccl:clodddxxxdxxkOxxxkkkkxxl,'.
.;:odllddoxddll:;;:ccll:::lllodxxdxdxkxxkkkxxxxo;'..
,;loddlcooxoocllllolooodlcclldodxdoddkxxkOOkxkxxo;,'..
.'ocdxdlllldodoloooollodddoddddxdxkoooxOkkOOkxxxkkd;..'.
. ;;dlxxdclcddxddolllcllloddddxxxxxxxcoxkkkOkOOOxxxkkxl..',
. ccxokddodddodxdollllllloodddxxxxkxxl,ckkOOOOOOOdldxxxxc..;.
..doxoddxdxxdlxxxolllooooooddxxxxxxxxo.,lOOO0O00O0kddxxdxxc',.
',xxxlxOkxkd;looddooooodooodloxxxxxkxo.;'xOOO0O00Okxkdkkddkxoc..
. .cxOxoOOkxkl';loccodddolddol:;ddlocdk:';'lk0O000000Okkdxkkdxxxd'....
. . 'ldOxxO0Okxl'.':lc;;::ccll;;;;:;;;;;;'''';k000K0KK00Okkdxxkkxxdc' ..
. ...'odOOkO00Oxc,,;cllc:;,,,,,'',;;cloooc;,'';k0KO0KKX000kdxxdxxkxdol;.
. . .'ddOOOKO00OkOxO0OO0x;........;xOkxkOkOOklcOXKOOKKXX000kdxxdxxxkdol:'.
. . .,ookkO000K0d,;l:,,:,'..''''''',c'';c;'cdoOKX0Ok0KKNXkOOkxdxkkxxxkxoc;. .
.'......llkkOKK0KKo,';;;;,'''''''''''',;:;:,,;,oKXK0OkkKKXN0kkkxkxdkxxxxkkxol,.
. .''c...,clkOO0KKKKO;,,,,'''''',,,',,,,,,,,,,,,c0KXdxOkk0KXNXOxkxdxxxxdodxxxxdol:'.... .. ..
. ..;:...c:lkOkOKXXKKk;,,,,,,,,,;;,,,,,,,,,,,,;cdxOdlxOkO0KKXN0Oxxxdddxkxoollodoolc:;'..... ...........''........
.. ,;..'cdckOkk0X0XKx0c,,,,,,,,,,,,,,,,,,,,,;:cckolokO0k0KKKNXOOddxdoodkkdollccc::clc::;,''.....''''... ................
.,. ;. ;lddkk0OOKOd0xcoc,,,,,,,,,,,,,,,,,,,;;;;xoldkkOOOOKXXXX0OkdxxdoolxOkxdllccc::cl:;;;;;,,,'''.......... . .. ...
.:. ..':ldxkk00K0Kko:c;c;,,,,;,,,,,,;;,,,,;;;;ddoxkkkkO000XNXXKOOxdxdoooooxkxkxol:cc:::;,. . ... ... .. . ..
.:. ',';codkkOO00XXXOd:;;;;;,,,;,,,,;;;;;;;;;;loooddxxxO0O0KXXXK0Okxxxolooolddoooolclc:;,;'. .. ......
....:;;:lodOKkO0OKXXKxoc;;;;;;;;;;;;;;;;;;;;:c:cccodxdodOkkKKKXKKOxxkkollloooxllcc:::c::;,'''.. .. ... . ......
.;:c::cllOk00O00KKXK0dlll:;;;;;;;;;;;;;;;ccc::::::coddolkxkKKKKKKkxxxocccccloclccc:::;;,,.''''.... . . . .. .. ........
..:occcl;kkkx0O00KXKKK0ollloo:;;;;;;;;;cdc::::::;;:::::clcxx0KX00K0dddooccc::c:.;:c:::;,''....''. ...... .... .... .... .......
..,ooool::OkkdxkOKXX00K0Oolcokkdl:;;;coxxx::::::::;;;;;:;,;ccoOKK0O0xoodlllcc:::, .':;;;;,,'.. .... .... .. . ... .. ........
..,oxlc;'dx0kdxd0XKXKkdOOOdcoxxkxxdodxxkkk::ccc::::;;;;;;,,'c,:xOOOkOdlolc:ccc;;;;. .',',..... .... .. . .. .... .......
.'.'dl;;':OkOkdkd0NdXXkddO0OOOOkxxxxxxkkOO0:::cccc::::;;;;;,',:';k0kkkkxlc::;;:c,;;,. ...'. . . .... ...... ..........
.,..,c..'cOkxxOxxxXKlXNdcdk00O00KOdxxxxkkkkkkd::::cc:::::;;;;,,;;';kOodkkxc::'.;:,,;.... .... ... ...... .. .......
.. ,;'.' .ddOdldkxdOKXl0N:lcodOK0OKdoddddxdddood;;;;;::;::::::;;;,:;';kx:loxxoc' ';,,' ..... . ... .... ..... ... ..........
. .. .cl;. .:kdddcldddxXkXlKNl;:codKKK0Oddddoolllllo;.'''',,..;:;',;,,,:;';kx:clxxoc',;.'. ....... ..... .......... .... . .... ......
... .... .;ol;;cdxl;:o:c',:kKOK0NX;;:loo0XNkO0xolllllloodkdc;,,'.. .,,.. ..,;;,ox:::ldolll:;.. ...... ...... ...............................
. . ..'':llllc,,,;o;' .:xxOONNx:;clokKNKxdxOxoodoooddxddxdoo;',,... ... .,;,;olc::lo:;c:;;'............ ...............................
... ...':.,'....'l; .;o'oc0KKokxkOXXKxc:;cldOxdddxdololldlldc;,;,;,........ .,,,,;clccc:. ..',;''... ..... .......................... .....
. ..,.. .....,;.c,..,l0KXKxKXlkkk:c:;;;:cO0oooccccclclccoc;;;;::;,,,,,,'...,'...;,.,;. ..'','.........................................
. . .... ...'',cl;;,,;;od0XXK0xld;:ll:;,cxkxOdolcccccllccclc:::::cc:::;,'...,... .''.,;'.......',;'......................................
..............;:'.''.ccldkKKkdoc:colo0OOkkxO0Olccccccccccoc::::::c:;'''.'.;... ... .,...........,,....................................
. ..............''..d:;;;odkKX0OkxkO0O0OkkkOOdccccccclccldcc:::;;;;;,,,,.;.. ....,............,,..................................
.................:llc:lc;;,:oxkOO00OOK0Okolkklcccclllcloo::::;;;;;:::,';''. .. .,.............'.................................
. . . .,...............;,,;:lc;',,,,;:lldldllllccccdkoccclllllldc::::::;;;;::,;'.... .. .,..............'...............................
.... . .,:''.............';;';,''..',,,,:cddll:cccccccoddcccccccclo::::;;;;;;l:;:;.... .. ;...............'..............................
.... . .';,'''.............;,''......',';,ldxo:;:ccccccoolcccccccccc::;;;;;;llc:;;'.. .......;.............................................
...... . .',;;,,,............';''........'',,,ldol,;::cccc:occccccccccl:;;;;;;ldol::,;'.. ....,.............................................
................ ... ,,;;;,,;;'..........;'''..........,'',;:;;;::c::::c::::cccccccc;;;;,cdolccc,;,.. ........,.............................. ............
................. .... .,;;;;;;;;;:,.......',''...........,'',,;:,;::c;:::;::::::::cccl;,,,cdo:::cl,;,'.. ..................................... . ...........
...................... ,;;;;;:;;;:cl;;'....','............,,'',;:,;::c;;;;:;;;;:::::cclc,,:dl;;:;oc,;,'.. ........................................ ........ ...
.................. .....;;;::;:;::ll:;;:;...,''............,,,,,;:;;:c:::;::;;;;;:::::ccl;,cc;;;;;l;,,,'.. ................................................ ..
.................. ..'';,;::;;;:ddxl;;;;::.','............',;,,,;:;;:c::::::::;;;:::::ccl:'',;;;:::;;:,'''. ...................................................
....................'.''.;::;;,:KWKd,,;,'..';'...........'',;,,;;:;::c:::::::::::::::cccll,.......'',l'''.. .................................................
''.................,,''.'':c;;;lKNKc;,,,'..,;''.........''',,,;;;:::cc::::::::::::::cccclo:'.........:'...'. ................................................
,,'''............,'....'''',;::''ol:;;;;,'';;,,'''''''''',,,;;;;::cccc:ccc:ccc::ccccccclloc,........,;''..'. .....''.....''''''''''''.......................

View File

@@ -6,4 +6,4 @@ dir:
order: 2
---
<Redirect to="development" />
<Catalog base='/en-us/develop/' />

View File

@@ -15,16 +15,15 @@ You can quickly navigate to the section you want to see by searching for CI file
Workflow files are all stored under `.github/workflows`, and each file can be categorized into the following functional parts:
- [Code Testing](#code-testing)
- [Code Building](#code-building)
- [Code Security Scanning](#code-security-scanning)
- [Version Release](#version-release)
- [Resource Updates](#resource-updates)
- [Website Building](#website-building)
- [Issues Management](#issues-management)
- [Pull Requests Management](#pull-requests-management)
- [MirrorChyan Related](#mirrorchyan-related)
- [Others](#others)
+ [Code Testing](#code-testing)
+ [Code Building](#code-building)
+ [Version Release](#version-release)
+ [Resource Updates](#resource-updates)
+ [Website Building](#website-building)
+ [Issues Management](#issues-management)
+ [Pull Requests Management](#pull-requests-management)
+ [MirrorChyan Related](#mirrorchyan-related)
+ [Others](#others)
Additionally, we use [pre-commit.ci](https://pre-commit.ci/) to implement automatic code formatting and image resource optimization, which runs automatically after creating PRs and generally requires no special attention.
@@ -48,30 +47,14 @@ In addition to the necessary MaaCore, Windows build artifacts include MaaWpfGui,
This workflow runs automatically on any new commit and PR. When triggered by a release PR, the build artifacts from this run will be used directly for release and will create a Release.
### Code Security Scanning
Code security scanning uses CodeQL to analyze code and workflows for security vulnerabilities, with the following workflows:
`codeql-core.yml`
This workflow performs security analysis on the C++ and C# code of MaaCore and MaaWpfGui, detecting potential security vulnerabilities.
It runs automatically on PRs that modify relevant source code, and also executes daily scheduled checks at 11:45 UTC.
`codeql-wf.yml`
This workflow performs security analysis on GitHub Actions workflow files themselves, ensuring the security of the CI/CD processes.
It runs automatically on PRs that modify workflow files, and also executes daily scheduled checks at 12:00 UTC.
### Version Release
Version release is the necessary operation to publish updates to users, consisting of the following workflows:
- `release-nightly-ota.yml` Release nightly builds
- `release-ota.yml` Release stable/beta versions
- `release-preparation.yml` Generate changelog and prepare release for stable/beta versions
- `pr-auto-tag.yml` Generate tags for stable/beta versions
+ `release-nightly-ota.yml` Release nightly builds
+ `release-ota.yml` Release stable/beta versions
+ `gen-changelog.yml` Generate changelog for stable/beta versions
+ `pr-auto-tag.yml` Generate tags for stable/beta versions
::: tip
The "ota" in the above file names stands for Over-the-Air, which is what we commonly call "incremental update packages". Therefore, MAA's release process actually includes building OTA packages for past versions.
@@ -90,7 +73,7 @@ Note that nightly builds are only released for Windows users; macOS and Linux us
The release process for these two channels is relatively more complex. We'll explain the role of each workflow by simulating a release process:
1. Create a PR from `dev` to `master` branch, and the PR name must be `Release v******`
2. `release-preparation.yml` generates a changelog from the most recent stable/beta version to the current version (as a new PR)
2. `gen-changelog.yml` generates a changelog from the most recent stable/beta version to the current version (as a new PR)
3. Manually adjust the changelog and add brief descriptions
4. Merge the PR, triggering `pr-auto-tag.yml` to create tags and sync branches
5. The Release event triggers `release-ota.yml`, which builds OTA packages and uploads attachments after tagging master
@@ -99,17 +82,17 @@ The release process for these two channels is relatively more complex. We'll exp
This section of workflows is mainly responsible for MAA's resource updates and optimization, with the following specific workflows:
- `res-update-game.yml` Executes periodically to pull game resources from specified repositories
- `sync-resource.yml` Syncs resources to the MaaResource repository for resource updates
- `optimize-templates.yml` Optimizes template image sizes
+ `res-update-game.yml` Executes periodically to pull game resources from specified repositories
+ `sync-resource.yml` Syncs resources to the MaaResource repository for resource updates
+ `optimize-templates.yml` Optimizes template image sizes
### Website Building
`website-workflow.yml`
This workflow is mainly responsible for building and publishing MAA's documentation site.
This workflow is mainly responsible for building and publishing MAA's official website, including both the main page and documentation components.
Please note that website publishing is tightly bound to releases. Regular modifications to web components only trigger builds to ensure no errors occur, and actual deployment to GitHub Pages only happens during releases.
Please note that website publishing is tightly bound to releases. Regular modifications to web components only trigger builds to ensure no errors occur, and actual deployment to Azure only happens during releases.
### Issues Management
@@ -136,8 +119,8 @@ This workflow checks whether commit messages in PRs conform to [Conventional Com
MirrorChyan is a paid update mirror service, with the following related workflows:
- `release-package-distribution.yml` Sync update packages to MirrorChyan
- `mirrorchyan_release_note.yml` Generate MirrorChyan Release Notes
+ `mirrorchyan.yml` Sync update packages to MirrorChyan
+ `mirrorchyan_release_note.yml` Generate MirrorChyan Release Notes
### Others
@@ -152,7 +135,3 @@ Automatically ignores commits with commit messages containing `blame ignore` to
`cache-delete.yml`
Cleans up related caches after PR merges to save cache usage.
`update-submodules.yml`
Periodically updates submodules such as MaaMacGui and maa-cli to their latest versions. This workflow runs automatically at 21:50 UTC daily (before the nightly release), ensuring submodules remain up to date.

View File

@@ -3,78 +3,60 @@ order: 1
icon: iconoir:developer
---
# Development Guide
# Getting Started with Development
::: tip
This page mainly describes the PR workflow and MAA's file formatting requirements. If you want to learn specifically how to make changes to MAA's operational logic, please refer to the [Protocol Documentation](../protocol/)
:::
::: tip
You can [Ask DeepWiki](https://deepwiki.com/MaaAssistantArknights/MaaAssistantArknights) to learn about the overall architecture of the MAA project.
:::
## Introduction to GitHub Pull Request Flow
## I don't know programming but just want to modify some JSON files/documents, how can I do it?
### I don't know programming but just want to modify some JSON files/documents, how can I do it?
Welcome to the [Web-based PR Tutorial](./pr-tutorial.md) that anyone can understand (purely web-based on Github.com)
## I want to make simple modifications to a few lines of code, but configuring the environment is too tedious and pure web editing is difficult to use. What should I do?
Use the [GitHub Codespaces](https://github.com/codespaces) online development environment and try it out!
We've preset several different development environments for you to choose from:
- Blank environment with a bare Linux container (default)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MaaAssistantArknights/MaaAssistantArknights?devcontainer_path=.devcontainer%2Fdevcontainer.json)
- Lightweight environment, suitable for documentation site frontend development
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MaaAssistantArknights/MaaAssistantArknights?devcontainer_path=.devcontainer%2F0%2Fdevcontainer.json)
- Full environment, suitable for MAA Core related development (not recommended, suggest local development with full environment setup. See next section)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MaaAssistantArknights/MaaAssistantArknights?devcontainer_path=.devcontainer%2F1%2Fdevcontainer.json)
## Complete Environment Setup (Windows)
### I can program, but I've never used GitHub/C++/..., how do I get started?
1. If you forked the repository long ago, first delete it via your repository's `Settings` at the bottom.
2. Visit the [MAA main repository](https://github.com/MaaAssistantArknights/MaaAssistantArknights), click `Fork`, then `Create fork`.
3. Clone the dev branch of your repository with submodules:
```bash
git clone --recurse-submodules <your repository link> -b dev
```
```bash
git clone --recurse-submodules <your repository link> -b dev
```
::: warning
If using Git GUI clients like Visual Studio without `--recurse-submodules` support, run `git submodule update --init` after cloning to initialize submodules.
:::
::: warning
If using Git GUI clients like Visual Studio without `--recurse-submodules` support, run `git submodule update --init` after cloning to initialize submodules.
:::
4. Download prebuilt third-party libraries
**Python environment required - search for Python installation tutorials if needed**
_(tools/maadeps-download.py is located in the project root)_
```cmd
python tools/maadeps-download.py
```
```cmd
python tools/maadeps-download.py
```
5. Configure development environment
- Download and install `Visual Studio 2022 Community`, selecting `Desktop development with C++` and `.NET Desktop Development` during installation.
- Download and install `Visual Studio 2022 Community`, selecting `Desktop development with C++` and `.NET Desktop Development` during installation.
6. Double-click `MAA.sln` to open the project in Visual Studio.
7. Configure Visual Studio settings
- Select `RelWithDebInfo` and `x64` in the top configuration bar (Skip for Release builds or ARM platforms)
- Right-click `MaaWpfGui` → Properties → Debug → Enable native debugging (This enables breakpoints in C++ Core)
- Select `RelWithDebInfo` and `x64` in the top configuration bar (Skip for Release builds or ARM platforms)
- Right-click `MaaWpfGui` → Properties → Debug → Enable native debugging (This enables breakpoints in C++ Core)
8. Now you're ready to happily ~~mess around~~ start developing!
9. Commit regularly with meaningful messages during development
If you're not familiar with git usage, you might want to create a new branch for changes instead of committing directly to `dev`:
```bash
git branch your_own_branch
git checkout your_own_branch
```
```bash
git branch your_own_branch
git checkout your_own_branch
```
This keeps your changes isolated from upstream `dev` updates.
This keeps your changes isolated from upstream `dev` updates.
10. After development, push your local branch (e.g. `dev`) to your remote repository:
@@ -84,29 +66,30 @@ We've preset several different development environments for you to choose from:
11. Submit a Pull Request at the [MAA main repository](https://github.com/MaaAssistantArknights/MaaAssistantArknights). Ensure your changes are based on the `dev` branch, not `master`.
12. To sync upstream changes:
1. Add upstream repository:
```bash
git remote add upstream https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
```
```bash
git remote add upstream https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
```
2. Fetch updates:
```bash
git fetch upstream
```
```bash
git fetch upstream
```
3. Rebase (recommended) or merge:
```bash
git rebase upstream/dev # rebase
```
```bash
git rebase upstream/dev # rebase
```
or
or
```bash
git merge # merge
```
```bash
git merge # merge
```
4. Repeat steps 7, 8, 9, 10.
@@ -122,11 +105,11 @@ Please ensure that it has been formatted before submission, or [enable Pre-commi
The currently enabled formatting tools are as follows:
| File Type | Format Tool |
| --------- | --------------------------------------------------------------- |
| C++ | [clang-format](https://clang.llvm.org/docs/ClangFormat.html) |
| Json/Yaml | [Prettier](https://prettier.io/) |
| Markdown | [markdownlint](https://github.com/DavidAnson/markdownlint-cli2) |
| File Type | Format Tool |
| --- | --- |
| C++ | [clang-format](https://clang.llvm.org/docs/ClangFormat.html) |
| Json/Yaml | [Prettier](https://prettier.io/) |
| Markdown | [markdownlint](https://github.com/DavidAnson/markdownlint-cli2) |
### Use Pre-commit Hooks to Automatically Format Code
@@ -134,10 +117,10 @@ The currently enabled formatting tools are as follows:
2. Execute the following command in the root directory of the project:
```bash
pip install pre-commit
pre-commit install
```
```bash
pip install pre-commit
pre-commit install
```
If pre-commit still cannot be used after pip install, please check if the pip installation path has been added to the PATH.
@@ -147,9 +130,9 @@ The formatting tool will automatically run every time you submit to ensure that
1. Install clang-format version 20.1.0 or higher.
```bash
python -m pip install clang-format
```
```bash
python -m pip install clang-format
```
2. Use tools like 'Everything' to locate the installation location of clang-format.exe. As a reference, if you are using Anaconda, clang-format.exe will be installed in YourAnacondaPath/Scripts/clang-format.exe.
3. In Visual Studio, search for 'clang-format' in Tools-Options.
@@ -162,3 +145,11 @@ You are all set with the clang-format integrated in Visual Studio supporting c++
You can also format with `tools\ClangFormatter\clang-formatter.py` directly, by executing in the root folder of the project:
- `python tools\ClangFormatter\clang-formatter.py --clang-format=PATH\TO\YOUR\clang-format.exe --input=src\MaaCore`
## Develop in cloud using GitHub codespace
Create GitHub codespace with pre-configured C++ dev environments:
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg?color=green)](https://codespaces.new/MaaAssistantArknights/MaaAssistantArknights)
Then follow the instructions in vscode or [Linux tutorial](./linux-tutorial.md) to configure GCC 12 and the CMake project.

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