commit 388444d317b554c251a9c4b70bfd7f4be73f3b57
Author: Qiuzhizhe <42761326+quizhizhe@users.noreply.github.com>
Date: Wed Sep 21 19:47:03 2022 +0800
fist commit
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..87c6c85
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,76 @@
+# Documents: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: true
+AlignOperands: Align
+AlwaysBreakTemplateDeclarations : true
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine : true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine : true
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+AllowShortLambdasOnASingleLine: true
+AllowShortEnumsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+BinPackArguments: true
+BinPackParameters: true
+ConstructorInitializerIndentWidth: 0
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ColumnLimit: 0
+CommentPragmas: '^ IWYU pragma:'
+PointerAlignment: Left
+IndentWidth: 4
+SortIncludes: false
+MaxEmptyLinesToKeep: 2
+SpacesInSquareBrackets: false
+SpacesInParentheses : false
+SpaceBeforeAssignmentOperators: true
+SpacesInContainerLiterals: true
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+BreakConstructorInitializersBeforeComma: true
+SpaceAfterCStyleCast: false
+IndentCaseLabels: true
+TabWidth: 4
+UseTab: Never
+
+BreakBeforeBraces: Custom
+BraceWrapping:
+ # case 语句后面
+ AfterCaseLabel: false
+ # class定义后面
+ AfterClass: false
+ # 控制语句后面
+ AfterControlStatement: false
+ # enum定义后面
+ AfterEnum: false
+ # 函数定义后面
+ AfterFunction: false
+ # 命名空间定义后面
+ AfterNamespace: false
+ # struct定义后面
+ AfterStruct: false
+ # union定义后面
+ AfterUnion: false
+ # extern导出块后面
+ AfterExternBlock: false
+ # catch之前
+ BeforeCatch: false
+ # else之前
+ BeforeElse: false
+ # 缩进大括号(整个大括号框起来的部分都缩进)
+ IndentBraces: false
+ # 空函数的大括号是否可以在一行
+ SplitEmptyFunction: true
+ # 空记录体(struct/class/union)的大括号是否可以在一行
+ SplitEmptyRecord: true
+ # 空namespace的大括号是否可以在一行
+ SplitEmptyNamespace: true
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..3f3afb5
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: ['https://www.patreon.com/liteloaderbds','https://afdian.net/@liteldev']
diff --git a/.github/ISSUE_TEMPLATE/bug_report_en.yml b/.github/ISSUE_TEMPLATE/bug_report_en.yml
new file mode 100644
index 0000000..d43f485
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report_en.yml
@@ -0,0 +1,97 @@
+name: "[ENG] Bug Report"
+description: "Report a bug to us to make LiteLoader better."
+labels: ["bug"]
+body:
+- type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+ > **NOTICE**
+ > 1. Feedback issues should belong to LiteLoader.
+ > 2. Please do not report destructive Minecraft Bugs, please send a private message to the project developer in IM, and give feedback to Mojang in time.
+
+- type: dropdown
+ id: module
+ attributes:
+ label: Exceptions module
+ description: The module with problems
+ options:
+ - Core
+ - ScriptEngine
+ - AddonsHelper
+ - PeEditor
+ validations:
+ required: true
+- type: dropdown
+ id: os
+ attributes:
+ label: Operating System
+ multiple: false
+ options:
+ - Windows Server 2022
+ - Windows Server 2019
+ - Windows Server 2016
+ - Windows Server 2012
+ - Other Windows Server
+ - Windows 11
+ - Windows 10
+ - Other Windows
+ - GNU/Linux with docker
+ - GNU/Linux with wine
+ - Other
+ validations:
+ required: true
+- type: input
+ id: llversion
+ attributes:
+ label: "LiteLoader version"
+ placeholder: Type your LiteLoader version.
+ description: |
+ If you are using a beta LiteLoader downloaded from Actions, please type the corresponding Actions run ID (starting with #) or commit hash.
+ e.g. 2.1.0 Actions#1479 4f404a7
+ validations:
+ required: true
+- type: input
+ id: bdsversion
+ attributes:
+ label: "BDS version"
+ placeholder: Type your BDS version.
+ validations:
+ required: true
+- type: textarea
+ id: what-happened
+ attributes:
+ label: What happened?
+ description: |
+ What happened? Please describe your problem.
+ Also tell us, what did you expect to happen?
+ placeholder: Describe your problem clearly.
+ validations:
+ required: true
+- type: textarea
+ id: reproduce
+ attributes:
+ label: Steps to reproduce?
+ description: Describe in as clear and detailed a manner as possible how to reproduce.
+ placeholder: |
+ 1. Do something
+ 2. ...
+ 3. ...
+- type: textarea
+ id: logs
+ attributes:
+ label: Relevant log output
+ description: Please copy and paste any relevant log output.
+ render: shell
+- type: textarea
+ id: plugins
+ attributes:
+ label: Plugin list
+ description: Please copy the command output of `ll list`.
+ render: shell
+- type: markdown
+ attributes:
+ value: |
+ **If you have pictures or other content, please comment below. Screenshots,logs and dmp files are very important and helpful for solving your problem**
+ ******
+ Thank you again for your contribution to LiteLoader!
diff --git a/.github/ISSUE_TEMPLATE/bug_report_zh.yml b/.github/ISSUE_TEMPLATE/bug_report_zh.yml
new file mode 100644
index 0000000..4253bce
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report_zh.yml
@@ -0,0 +1,96 @@
+name: "[简体中文] 报告漏洞/问题"
+description: "向我们报告漏洞/问题以让LiteLoader变得更好"
+labels: ["bug"]
+body:
+- type: markdown
+ attributes:
+ value: |
+ 感谢您花时间填写这份Bug反馈!
+ > **注意事项**
+ > 1. 请勿反馈非加载器问题
+ > 2. 请勿在这里反馈具有严重影响的游戏漏洞, 相关问题请在聊天软件内私聊项目开发者并向及时向Mojang反馈
+
+- type: dropdown
+ id: module
+ attributes:
+ label: 异常模块
+ description: 出现问题的模块
+ options:
+ - Core(核心)
+ - ScriptEngine(脚本引擎)
+ - AddonsHelper
+ - PeEditor
+ validations:
+ required: true
+- type: dropdown
+ id: os
+ attributes:
+ label: 操作系统
+ multiple: false
+ options:
+ - Windows Server 2022
+ - Windows Server 2019
+ - Windows Server 2016
+ - Windows Server 2012
+ - 其他版本的 Windows Server
+ - Windows 11
+ - Windows 10
+ - 其他版本的 Windows
+ - GNU/Linux with docker
+ - GNU/Linux with wine
+ - 其他操作系统
+ validations:
+ required: true
+- type: input
+ id: llversion
+ attributes:
+ label: "LiteLoader版本"
+ placeholder: 输入您的LiteLoader版本
+ description: |
+ 如果您正在使用一个从Actions下载的beta版LiteLoader,请输入对应的Actions运行ID(#开头)或commit hash
+ e.g. 2.1.0 Actions#1479 4f404a7
+ validations:
+ required: true
+- type: input
+ id: bdsversion
+ attributes:
+ label: BDS版本
+ placeholder: 输入您的BDS版本
+ validations:
+ required: true
+- type: textarea
+ id: what-happened
+ attributes:
+ label: 发生了什么?
+ description: |
+ 发生了什么?请描述你的问题. 并告诉我们, 您希望我们做什么
+ placeholder: 清晰地描述你的问题
+ validations:
+ required: true
+- type: textarea
+ id: reproduce
+ attributes:
+ label: 复现此问题的步骤
+ description: 尽可能清晰且详细的描述如何复现
+ placeholder: |
+ 1. Do something
+ 2. ...
+ 3. ...
+- type: textarea
+ id: logs
+ attributes:
+ label: 有关的日志/输出
+ description: 请粘贴有关此问题的日志到此处
+ render: shell
+- type: textarea
+ id: plugins
+ attributes:
+ label: 插件列表
+ description: 请复制命令`ll list`的输出
+ render: shell
+- type: markdown
+ attributes:
+ value: |
+ **如果你有图片或其他内容,请在下面评论(comment)。截图,日志和dmp文件对解决你的问题很重要、很有帮助**
+ ******
+ 再次感谢您对LiteLoader的贡献!
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..87aadf6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: true
+contact_links:
+ - name: "Minecraft: Bedrock Edition Feedback"
+ url: https://bugs.mojang.com/projects/MCPE/issues
+ about: Please report MC game bugs here 反馈MC游戏问题
diff --git a/.github/ISSUE_TEMPLATE/feature_request_en.yml b/.github/ISSUE_TEMPLATE/feature_request_en.yml
new file mode 100644
index 0000000..639271d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request_en.yml
@@ -0,0 +1,26 @@
+name: "[ENG] Feature Request"
+description: Suggest an idea/Request for a new feature
+labels: ["enhancement"]
+body:
+- type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this feature request!
+- type: textarea
+ id: problem
+ attributes:
+ label: Is your feature request related to a problem? Please describe.
+ description: A clear and concise description of what the problem is.
+ placeholder: |
+ e.g. I'm always frustrated when ...
+- type: textarea
+ id: solution
+ attributes:
+ label: Describe the solution you'd like
+ description: A clear and concise description of what you want.
+ placeholder: |
+ e.g. I want a new event that ...
+- type: markdown
+ attributes:
+ value: |
+ Thank you again for your contribution to LiteLoader!
diff --git a/.github/ISSUE_TEMPLATE/feature_request_zh.yml b/.github/ISSUE_TEMPLATE/feature_request_zh.yml
new file mode 100644
index 0000000..2a311ec
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request_zh.yml
@@ -0,0 +1,26 @@
+name: "[简体中文] 新功能请求/建议"
+description: 提供建议/请求一个新的功能
+labels: ["enhancement"]
+body:
+- type: markdown
+ attributes:
+ value: |
+ 感谢您花时间填写这份新功能请求!
+- type: textarea
+ id: problem
+ attributes:
+ label: 您的建议是否与现存的某个问题相关?请描述问题?
+ description: 简明扼要地描述问题是什么
+ placeholder: |
+ e.g. LiteLoader的...API总是让我...
+- type: textarea
+ id: solution
+ attributes:
+ label: 您认为还缺少什么?如何解决您的问题?
+ description: 简明扼要地描述你想要什么
+ placeholder: |
+ e.g. 我想要一个新的监听事件...
+- type: markdown
+ attributes:
+ value: |
+ 再次感谢您对LiteLoader的贡献!
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..7d47bf4
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,22 @@
+## Description
+
+Please carefully read the [Contributing note](https://docs.litebds.com/#/zh_CN/Maintenance/README) before making any pull requests.
+And, **Do not make a pull request to merge into main branch unless it is a hotfix. Use the beta branch instead.**
+## Issues fixed by this PR
+
+
+## Type of changes
+
+
+
+- [ ] Bug fix
+- [ ] New feature
+- [ ] Enhancement
+- [ ] Documentation
+
+## Checklist:
+
+- [ ] My code follows the style guidelines of this project
+- [ ] My pull request is unique and no other pull requests have been opened for these changes
+- [ ] I have read the [Contributing note](https://docs.litebds.com/#/zh_CN/Maintenance/README)
+- [ ] I am responsible for any copyright issues with my code if it occurs in the future.
diff --git a/.github/workflows/cmake-self-hosted.yml b/.github/workflows/cmake-self-hosted.yml
new file mode 100644
index 0000000..94d5d3e
--- /dev/null
+++ b/.github/workflows/cmake-self-hosted.yml
@@ -0,0 +1,189 @@
+name: CMake(self-hosted)
+
+on:
+ push:
+ paths:
+ - '**.cpp'
+ - '**.cc'
+ - '**.cxx'
+ - '**.c'
+ - '**.hpp'
+ - '**.hh'
+ - '**.hxx'
+ - '**.h'
+ - '.github/workflows/cmake-self-hosted.yml'
+ - '**/CMakeLists.txt'
+
+env:
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+ runs-on: self-hosted
+
+ steps:
+ - uses: actions/checkout@v3.0.2
+ with:
+ fetch-depth: 1
+ submodules: 'true'
+
+ - name: Cache Bedrock Dedicated Server Library
+ id: cache-bds-lib
+ uses: actions/cache@v3
+ env:
+ cache-name: cache-bds-lib
+ with:
+ path: |
+ ${{ env.GITHUB_WORKSPACE }}LiteLoader/Lib/bedrock_server_api.lib
+ ${{ env.GITHUB_WORKSPACE }}LiteLoader/Lib/bedrock_server_var.lib
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Scripts/LINK.txt') }}
+
+ - name: Download Server
+ if: steps.cache-bds-lib.outputs.cache-hit != 'true'
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ mkdir Tools/Server
+ ServerLink=$(cat 'Scripts/LINK.txt')
+ curl -L -o Tools/Server/server.zip "$ServerLink"
+ unzip Tools/Server/server.zip -d Tools/Server/ > /dev/null
+ shell: bash
+
+ - name: Build Library
+ if: steps.cache-bds-lib.outputs.cache-hit != 'true'
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ cd Tools
+ LibraryBuilder.exe Server
+ shell: cmd
+
+ - name: Change LITELOADER_VERSION_STATUS_BETA
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ if: false == startsWith(github.ref, 'refs/tags/')
+ run: |
+ sed -r -i 's/#define\s+LITELOADER_VERSION_STATUS\s+LITELOADER_VERSION_\w+/#define LITELOADER_VERSION_STATUS LITELOADER_VERSION_BETA/' LiteLoader/Main/Version.h
+ sed -r -i 's/#define\s+LITELOADER_VERSION_ACTIONS\s+.*/#define LITELOADER_VERSION_ACTIONS ${{ github.run_number }}\r/' LiteLoader/Main/Version.h
+ cat LiteLoader/Main/Version.h
+ shell: bash
+
+ - name: Change LITELOADER_VERSION_STATUS_RELEASE
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ if: startsWith(github.ref, 'refs/tags/')
+ run: |
+ sed -r -i 's/#define\s+LITELOADER_VERSION_STATUS\s+LITELOADER_VERSION_\w+/#define LITELOADER_VERSION_STATUS LITELOADER_VERSION_RELEASE/' LiteLoader/Main/Version.h
+ sed -r -i 's/#define\s+LITELOADER_VERSION_ACTIONS\s+.*/#define LITELOADER_VERSION_ACTIONS ${{ github.run_number }}\r/' LiteLoader/Main/Version.h
+ shell: bash
+
+ - name: Get MSVC Path
+ working-directory: E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\
+ run: echo "MSVC_VER=$(ls | tail -n 1)" >> $GITHUB_ENV
+ shell: bash
+
+ #- name: Configure CMake(MSVC)
+ # run: |
+ # set CC="E:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/${{env.MSVC_VER}}/bin/Hostx64/x64/cl.exe"
+ # "E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 & cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -G Ninja -DCMAKE_TRY_COMPILE_TARGET_TYPE="STATIC_LIBRARY" -DCMAKE_MAKE_PROGRAM="E:\PROGRAM FILES\MICROSOFT VISUAL STUDIO\2022\Community\COMMON7\IDE\COMMONEXTENSIONS\Microsoft\CMake\Ninja\ninja.exe"
+ # shell: cmd
+
+ #- name: Build
+ # run: |
+ # "E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 & cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
+ # shell: cmd
+
+ - name: Configure CMake(MSVC)
+ run: |
+ cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+ shell: cmd
+
+ - name: Build
+ run: |
+ cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
+ shell: cmd
+
+ - name: Configure CMake(ClangCL)
+ run: |
+ cmake -B ${{github.workspace}}/build_clang -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -T clangcl
+ shell: cmd
+
+ - name: Build SymDBHelper
+ run: cmake --build ${{github.workspace}}/build_clang --config ${{env.BUILD_TYPE}} --target SymDBHelper
+
+ - name: Compress Resource Packs
+ run: |
+ cd RELEASE/plugins/LiteLoader/ResourcePacks
+ 7z a LiteLoaderBDS-CUI.tar LiteLoaderBDS-CUI
+ rm -r LiteLoaderBDS-CUI
+ shell: bash
+
+ - name: Pack Release
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ cd Scripts
+ ./PackRelease.cmd action
+ shell: cmd
+
+ - name: Move PDB to path
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ mkdir PDB
+ cp x64/Release/*.pdb PDB
+ shell: bash
+
+ - name: Pack PDB
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ if: startsWith(github.ref, 'refs/tags/')
+ run: 7z a -tzip PDB.zip PDB
+ shell: bash
+
+ - name: Update LiteLoaderSDK
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ git config --global user.name "github-actions[bot]"
+ git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ cd Scripts
+ ./UploadSDK.cmd action
+ env:
+ REPO_KEY: ${{secrets.PUSH_TOKEN}}
+ USERNAME: github-actions[bot]
+ shell: cmd
+
+ - name: Upload LiteLoader
+ uses: actions/upload-artifact@v3.1.0
+ with:
+ name: LiteLoader
+ path: ${{ github.workspace }}\RELEASE\
+
+ - name: Upload PDB
+ uses: actions/upload-artifact@v3.1.0
+ with:
+ name: PDB
+ path: ${{ github.workspace }}\PDB
+
+ - name: Prepare for creating Release
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ id: rel
+ if: startsWith(github.ref, 'refs/tags/')
+ run: |
+ echo ::set-output name=tag::${GITHUB_REF#refs/*/}
+ mv LiteLoader.zip LiteLoader-${GITHUB_REF#refs/*/}.zip
+ shell: bash
+
+ - name: Create New Release
+ uses: softprops/action-gh-release@v0.1.14
+ if: startsWith(github.ref, 'refs/tags/')
+ with:
+ body_path: ${{ github.workspace }}\CHANGELOG.md
+ files: |
+ ${{ github.workspace }}\LiteLoader-${{ steps.rel.outputs.tag }}.zip
+ ${{ github.workspace }}\PDB.zip
+ env:
+ GITHUB_REPOSITORY: LiteLDev/LiteLoaderBDS
+
+ #- name: Publish to MineBBS
+ # working-directory: ${{ env.GITHUB_WORKSPACE }}
+ # if: startsWith(github.ref, 'refs/tags/')
+ # run: |
+ # curl -X POST https://api.github.com/repos/LiteLDev/AutoUpdate-MineBBS/dispatches \
+ # -H "Accept: application/vnd.github.everest-preview+json" \
+ # -H "Authorization: Bearer ${{ secrets.MB_TOKEN }}" \
+ # --data '{"event_type": "webhook"}'
+ # shell: bash
\ No newline at end of file
diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
new file mode 100644
index 0000000..c721a58
--- /dev/null
+++ b/.github/workflows/cmake.yml
@@ -0,0 +1,124 @@
+name: CMake
+
+on:
+ workflow_dispatch:
+ pull_request:
+ paths:
+ - '**.cpp'
+ - '**.cc'
+ - '**.cxx'
+ - '**.c'
+ - '**.hpp'
+ - '**.hh'
+ - '**.hxx'
+ - '**.h'
+ - '.github/workflows/cmake.yml'
+ - '**/CMakeLists.txt'
+
+env:
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+ runs-on: windows-2022
+
+ steps:
+ - uses: actions/checkout@v3.0.2
+ with:
+ fetch-depth: 1
+ #submodules: 'true'
+
+ - name: Cache Bedrock Dedicated Server Library
+ id: cache-bds-lib
+ uses: actions/cache@v3
+ env:
+ cache-name: cache-bds-lib
+ with:
+ path: |
+ ${{ env.GITHUB_WORKSPACE }}LiteLoader/Lib/bedrock_server_api.lib
+ ${{ env.GITHUB_WORKSPACE }}LiteLoader/Lib/bedrock_server_var.lib
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('Scripts/LINK.txt') }}
+
+ - name: Download Server
+ if: steps.cache-bds-lib.outputs.cache-hit != 'true'
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ mkdir Tools/Server
+ ServerLink=$(cat 'Scripts/LINK.txt')
+ curl -L -o Tools/Server/server.zip "$ServerLink"
+ unzip Tools/Server/server.zip -d Tools/Server/ > /dev/null
+ shell: bash
+
+ - name: Build Library
+ if: steps.cache-bds-lib.outputs.cache-hit != 'true'
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ cd Tools
+ LibraryBuilder.exe Server
+ shell: cmd
+
+ - name: Change LITELOADER_VERSION_STATUS_BETA
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ if: false == startsWith(github.ref, 'refs/tags/')
+ run: |
+ sed -r -i 's/#define\s+LITELOADER_VERSION_STATUS\s+LITELOADER_VERSION_\w+/#define LITELOADER_VERSION_STATUS LITELOADER_VERSION_BETA/' LiteLoader/Main/Version.h
+ sed -r -i 's/#define\s+LITELOADER_VERSION_ACTIONS\s+.*/#define LITELOADER_VERSION_ACTIONS ${{ github.run_number }}\r/' LiteLoader/Main/Version.h
+ cat LiteLoader/Main/Version.h
+ shell: bash
+
+ - name: Change LITELOADER_VERSION_STATUS_RELEASE
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ if: startsWith(github.ref, 'refs/tags/')
+ run: |
+ sed -r -i 's/#define\s+LITELOADER_VERSION_STATUS\s+LITELOADER_VERSION_\w+/#define LITELOADER_VERSION_STATUS LITELOADER_VERSION_RELEASE/' LiteLoader/Main/Version.h
+ sed -r -i 's/#define\s+LITELOADER_VERSION_ACTIONS\s+.*/#define LITELOADER_VERSION_ACTIONS ${{ github.run_number }}\r/' LiteLoader/Main/Version.h
+ shell: bash
+
+ - name: Get MSVC Path
+ working-directory: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\
+ run: echo "MSVC_VER=$(ls | tail -n 1)" >> $GITHUB_ENV
+ shell: bash
+
+ - name: Configure CMake(MSVC)
+ run: |
+ cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+ shell: cmd
+
+ - name: Build
+ run: |
+ cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
+ shell: cmd
+
+ - name: Configure CMake(ClangCL)
+ run: |
+ cmake -B ${{github.workspace}}/build_clang -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -T clangcl
+ shell: cmd
+
+ - name: Build SymDBHelper
+ run: cmake --build ${{github.workspace}}/build_clang --config ${{env.BUILD_TYPE}} --target SymDBHelper
+
+ - name: Pack Release
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ cd Scripts
+ ./PackRelease.cmd action
+ shell: cmd
+
+ - name: Move PDB to path
+ working-directory: ${{ env.GITHUB_WORKSPACE }}
+ run: |
+ mkdir PDB
+ cp x64/Release/*.pdb PDB
+ shell: bash
+
+ - name: Upload LiteLoader
+ uses: actions/upload-artifact@v3.1.0
+ with:
+ name: LiteLoader
+ path: ${{ github.workspace }}\RELEASE\
+
+ - name: Upload PDB
+ uses: actions/upload-artifact@v3.1.0
+ with:
+ name: PDB
+ path: ${{ github.workspace }}\PDB
diff --git a/.github/workflows/issue-close-require.yml b/.github/workflows/issue-close-require.yml
new file mode 100644
index 0000000..58f4cc0
--- /dev/null
+++ b/.github/workflows/issue-close-require.yml
@@ -0,0 +1,19 @@
+name: Issue Close Require
+
+on:
+ schedule:
+ - cron: "0 0 * * *"
+
+jobs:
+ issue-close-require:
+ runs-on: ubuntu-latest
+ steps:
+ - name: need reproduce
+ uses: actions-cool/issues-helper@v2
+ with:
+ actions: 'close-issues'
+ labels: 'status: more information needed'
+ inactive-day: 30
+ body: |
+ 您超过 30 天未反馈信息,我们将关闭该 issue,如有需求您可以重新打开或者提交新的 issue。
+ If you haven't provided feedback for more than 30 days, we will close the issue. You can reopen or submit a new issue if necessary.
diff --git a/.github/workflows/issue-close.yml b/.github/workflows/issue-close.yml
new file mode 100644
index 0000000..4e604a1
--- /dev/null
+++ b/.github/workflows/issue-close.yml
@@ -0,0 +1,16 @@
+name: Issue Close Check
+
+on:
+ issues:
+ types: [closed]
+
+jobs:
+ issue-close-remove-labels:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Remove labels
+ uses: actions-cool/issues-helper@v2
+ if: ${{ !github.event.issue.pull_request }}
+ with:
+ actions: 'remove-labels'
+ labels: 'status: need review,status: more information needed'
diff --git a/.github/workflows/issue-comment.yml b/.github/workflows/issue-comment.yml
new file mode 100644
index 0000000..42c14f2
--- /dev/null
+++ b/.github/workflows/issue-comment.yml
@@ -0,0 +1,38 @@
+on:
+ issue_comment:
+ types: [created]
+
+name: Add issues workflow labels
+
+jobs:
+ add-label-if-is-author:
+ runs-on: ubuntu-latest
+ if: (github.event.issue.user.id == github.event.comment.user.id) && !github.event.issue.pull_request && (github.event.issue.state == 'open')
+ steps:
+ - name: Add require handle label
+ uses: actions-cool/issues-helper@v2
+ with:
+ actions: 'add-labels'
+ labels: 'status: need review'
+
+ - name: Remove require reply label
+ uses: actions-cool/issues-helper@v2
+ with:
+ actions: 'remove-labels'
+ labels: 'status: more information needed'
+
+ add-label-if-not-author:
+ runs-on: ubuntu-latest
+ if: (github.event.issue.user.id != github.event.comment.user.id) && !github.event.issue.pull_request && (github.event.issue.state == 'open')
+ steps:
+ - name: Add require replay label
+ uses: actions-cool/issues-helper@v2
+ with:
+ actions: 'add-labels'
+ labels: 'status: more information needed'
+
+ - name: Remove require handle label
+ uses: actions-cool/issues-helper@v2
+ with:
+ actions: 'remove-labels'
+ labels: 'status: need review'
diff --git a/.github/workflows/issue-open.yml b/.github/workflows/issue-open.yml
new file mode 100644
index 0000000..43e4426
--- /dev/null
+++ b/.github/workflows/issue-open.yml
@@ -0,0 +1,16 @@
+name: Issue Open Check
+
+on:
+ issues:
+ types: [opened]
+
+jobs:
+ issue-open-add-labels:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Add labels
+ uses: actions-cool/issues-helper@v2
+ if: ${{ !github.event.issue.pull_request }}
+ with:
+ actions: 'add-labels'
+ labels: 'status: need review'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8fed6d9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,36 @@
+x64/
+**/.vs/
+**/x64/
+**/*.user
+**/.vscode/
+
+**/bedrock_server_api.lib
+**/bedrock_server_var.lib
+**/SymDB_DelayLoadHelper.lib
+LiteLoader/Lib/SymDBHelper.lib
+**/*.lastcodeanalysissucceeded
+*.zip
+
+/LiteLoader/out/
+LiteLoaderSDK/
+
+RELEASE/*.dll
+RELEASE/LLPeEditor.exe
+!RELEASE/ChakraCore.dll
+RELEASE/plugins/LiteLoader/LiteLoader.*.dll
+RELEASE/plugins/LiteLoader/LiteLoader.*.pdb
+RELEASE/plugins/LiteLoader/pdb
+!RELEASE/plugins/LiteLoader/CrashLogger.dll
+
+.idea/**
+!.idea/cmake.xml
+
+cmake-build-release/
+/out
+LiteLoader/Lib/Demangler.pdb
+LiteLoader/Lib/Demangler.lib
+
+/build
+**/Directory.build.props
+RELEASE/plugins/lib/BaseLib.js
+RELEASE/plugins/lib/BaseLib.lua
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ec5c972
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "RELEASE/plugins/LiteLoader/ResourcePacks/LiteLoaderBDS-CUI"]
+ path = RELEASE/plugins/LiteLoader/ResourcePacks/LiteLoaderBDS-CUI
+ url = https://github.com/OEOTYAN/LiteLoaderBDS-CUI.git
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
new file mode 100644
index 0000000..14b7ea9
--- /dev/null
+++ b/.idea/cmake.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..49414ca
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+## [LiteLoader Release Note]
+LiteLoaderBDS-2.6.2 update has been released, adapted to BDS-1.19.21,BDS-1.19.22, ProtocolVersion 545
+
+## [Bug Fixes]
+- Fix LLSE `NetworkAPI`
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..be4fb73
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.21)
+project(LL-Parent)
+
+# Add sub directories
+add_subdirectory(PreLoader)
+add_subdirectory(LiteLoader)
+add_subdirectory(ScriptEngine/CMake/ScriptEngine-Lua)
+add_subdirectory(ScriptEngine/CMake/ScriptEngine-QuickJs)
+add_subdirectory(ScriptEngine/CMake/ScriptEngine-NodeJs)
+add_subdirectory(Tools/Demangler)
+add_subdirectory(ScriptEngine/third-party/ScriptX/CMake/ScriptX-Lua)
+add_subdirectory(ScriptEngine/third-party/ScriptX/CMake/ScriptX-QuickJs)
+add_subdirectory(ScriptEngine/third-party/ScriptX/CMake/ScriptX-NodeJs)
+add_subdirectory(Tools/SymDBHelper)
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..664263a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,188 @@
+[CHINESE VERSION]
+
+版权所有 © 2020-2022 [LiteLoaderBDS Developers](https://github.com/LiteLDev) 保留所有权利
+
+本项目使用 GNU Lesser General Public License version 3(LGPLv3许可证),例外情况如下:
+
+LiteLoaderSDK不受此许可证约束, LiteLoaderSDK使用MIT许可证开源。
+
+注: LiteLoaderSDK指本仓库的LiteLoader/Header目录下的文件
+ 许可证详见https://github.com/LiteLDev/LiteLoaderSDK/tree/main/LICENSE
+
+[ENGLISH VERSION]
+
+Copyright © 2020-2022 [LiteLoaderBDS Developers](https://github.com/LiteLDev) All rights reserved.
+
+This project is under the GNU Lesser General Public License version 3, with exceptions as follows:
+
+LiteLoaderSDK is not under the terms of this license, and is open source under the MIT license.
+
+Note: LiteLoaderSDK refers to the repository's LiteLoader/Header directory.
+ See https://github.com/LiteLDev/LiteLoaderSDK/tree/main/LICENSE for the license.
+
+========================================================================
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
\ No newline at end of file
diff --git a/LiteLoader/.gitignore b/LiteLoader/.gitignore
new file mode 100644
index 0000000..1127b8d
--- /dev/null
+++ b/LiteLoader/.gitignore
@@ -0,0 +1,5 @@
+*.user
+/cmake-build-release/
+/cmake-build-debug/
+/.idea/
+Lib/LiteLoader.lib
\ No newline at end of file
diff --git a/LiteLoader/CMakeLists.txt b/LiteLoader/CMakeLists.txt
new file mode 100644
index 0000000..d6ffaff
--- /dev/null
+++ b/LiteLoader/CMakeLists.txt
@@ -0,0 +1,70 @@
+cmake_minimum_required(VERSION 3.21)
+project(LiteLoader)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_BUILD_TYPE Release) # Always uses Release mode to build BDS plugin for ABI Compatibility
+
+set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/x64/)
+
+set(TARGET_DIR ${CMAKE_SOURCE_DIR}/x64/Release/)
+
+file(GLOB_RECURSE SRC_FILES
+ ${PROJECT_SOURCE_DIR}/Lib/third-party/nbt-cpp/*.cpp
+ ${PROJECT_SOURCE_DIR}/Header/*.cpp
+ ${PROJECT_SOURCE_DIR}/Header/*.hpp
+ ${PROJECT_SOURCE_DIR}/Header/*.h
+ ${PROJECT_SOURCE_DIR}/Kernel/*.cpp
+ ${PROJECT_SOURCE_DIR}/Kernel/*.hpp
+ ${PROJECT_SOURCE_DIR}/Kernel/*.h
+ ${PROJECT_SOURCE_DIR}/Main/*.cpp
+ ${PROJECT_SOURCE_DIR}/Main/*.hpp
+ ${PROJECT_SOURCE_DIR}/Main/*.h
+ ${PROJECT_SOURCE_DIR}/Resource/*.rc
+ ${PROJECT_SOURCE_DIR}/Resource/*.h
+)
+
+add_definitions(
+ -DUNICODE -DNDEBUG -DLITELOADER_EXPORTS -DWIN32_LEAN_AND_MEAN
+ -DCPPHTTPLIB_OPENSSL_SUPPORT -D_CRT_SECURE_NO_WARNINGS -D_WINDOWS
+ -D_USRDLL -D_AMD64_ -DNOMINMAX
+)
+
+add_compile_options(
+ /permissive /MP /analyze:external- /GS /GL /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Zc:inline /fp:precise
+ /external:W1 /errorReport:prompt /WX- /Zc:forScope /Gd /Oi /MD /FC /EHa /nologo /Ot /diagnostics:column
+ /utf-8
+)
+
+add_link_options(
+ /MANIFEST /LTCG:incremental /NXCOMPAT /DEBUG:FULL /DLL /MACHINE:X64 /OPT:REF /INCREMENTAL:NO /SUBSYSTEM:CONSOLE
+ /MANIFESTUAC:NO /OPT:ICF /ERRORREPORT:PROMPT /NOLOGO /TLBID:1
+ /DELAYLOAD:bedrock_server.dll # use delayload to import BDS APIs manually(bedrock_server.dll does not need to exist)
+)
+
+include_directories(${PROJECT_SOURCE_DIR})
+include_directories(${PROJECT_SOURCE_DIR}/Header)
+include_directories(${PROJECT_SOURCE_DIR}/Header/third-party)
+include_directories(${CMAKE_SOURCE_DIR})
+
+link_directories(${PROJECT_SOURCE_DIR})
+link_directories(${CMAKE_SOURCE_DIR}/x64/Release)
+
+add_library(LiteLoader SHARED ${SRC_FILES})
+
+target_link_libraries(LiteLoader LLPreLoader
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/leveldb/leveldb.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/openssl/libcrypto.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/openssl/libssl.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/SQLiteCpp/SQLiteCpp.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/SQLiteCpp/sqlite3.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/mysql/mysqlclient.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/compact_enc_det/ced.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/dyncall/dyncall_s.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/dyncall/dyncallback_s.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/dyncall/dynload_s.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/compact_enc_det/ced.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/third-party/dbghelp/dbghelp.lib"
+ # Please make sure that the BDS APIs ImportLibrary is at the bottom of the list to ensure proper linking.
+ "${PROJECT_SOURCE_DIR}/Lib/bedrock_server_api.lib"
+ "${PROJECT_SOURCE_DIR}/Lib/bedrock_server_var.lib"
+)
diff --git a/LiteLoader/Header/AllowListAPI.h b/LiteLoader/Header/AllowListAPI.h
new file mode 100644
index 0000000..84f4a43
--- /dev/null
+++ b/LiteLoader/Header/AllowListAPI.h
@@ -0,0 +1,51 @@
+#pragma once
+#include "Global.h"
+#include "third-party/Nlohmann/json.hpp"
+
+//////////////////////////////////////////////////////
+// For managing AllowList
+//
+// [Usage]
+//
+// AllowListManager().has("steve"[, "114514"]);
+// AllowListManager().add("alex"[, "11451419", true]).reload();
+// AllowListManager().remove("mojang"[, "233333"]).reload();
+// AllowListManager().add(...).remove(...).reload();
+//
+// [Note]
+//
+// If you don't call reload after add/remove, the changes will only write to file
+// but it will not take effect in the game.
+// This means the player you added won't be able to join the game until reloading
+//
+// 如果你在使用add/remove成员之后不调用reload函数, 这些更改将只会写入文件而不会在游戏中生效
+// 这意味着你添加到白名单的玩家将不能加入游戏, 直到重载白名单
+//
+//////////////////////////////////////////////////////
+
+// class AllowListManager {
+
+// void save();
+
+// public:
+// nlohmann::json allowList;
+
+// LIAPI AllowListManager();
+
+// LIAPI size_t size();
+
+// // @summary Return whether the player in the allowlist
+// LIAPI bool has(const std::string& name, const xuid_t& xuid = "");
+// // @summary Return whether the player in the allowlist
+// // @param index If exists, index will be set to the array index
+// LIAPI bool has(const std::string& name, const xuid_t& xuid, size_t& index);
+
+// // @summary Add a player to allowlist
+// LIAPI AllowListManager& add(const std::string& name, const xuid_t& xuid = "", bool ignore = false);
+
+// // @summary Remove a player from allowlist
+// LIAPI AllowListManager& remove(const std::string& name, const xuid_t& xuid = "");
+
+// // @summary Reload the allowlist(Making changes effective)
+// LIAPI void reload();
+// };
diff --git a/LiteLoader/Header/DB/Any.h b/LiteLoader/Header/DB/Any.h
new file mode 100644
index 0000000..f944d55
--- /dev/null
+++ b/LiteLoader/Header/DB/Any.h
@@ -0,0 +1,728 @@
+#pragma once
+#include "../Global.h"
+#include "Types.h"
+
+#pragma region AnyConversion
+
+namespace DB
+{
+// Declare Any class
+class Any;
+} // namespace DB
+
+/**
+ * @brief Function to convert Any to T.
+ *
+ * @tparam T The type to convert to
+ * @param v The Any object
+ * @return T The converted value
+ */
+template
+inline T any_to(const DB::Any& v)
+{
+ throw std::bad_cast();
+}
+
+template
+inline std::vector to_any_container(const std::vector& v)
+{
+ std::vector result;
+ for (auto& i : v)
+ {
+ result.push_back(DB::Any(i));
+ }
+ return result;
+}
+template
+inline std::set to_any_container(const std::set& v)
+{
+ std::set result;
+ for (auto& i : v)
+ {
+ result.insert(DB::Any(i));
+ }
+ return result;
+}
+template
+inline std::list to_any_container(const std::list& v)
+{
+ std::list result;
+ for (auto& i : v)
+ {
+ result.push_back(DB::Any(i));
+ }
+ return result;
+}
+template
+inline std::unordered_set to_any_container(const std::unordered_set& v)
+{
+ std::unordered_set result;
+ for (auto& i : v)
+ {
+ result.insert(DB::Any(i));
+ }
+ return result;
+}
+template
+inline std::map to_any_container(const std::map& v)
+{
+ std::map result;
+ for (auto& i : v)
+ {
+ result.insert(std::make_pair(i.first, DB::Any(i.second)));
+ }
+ return result;
+}
+template
+inline std::unordered_map to_any_unordered_map(const std::unordered_map& v)
+{
+ std::unordered_map result;
+ for (auto& i : v)
+ {
+ result.insert(std::make_pair(i.first, DB::Any(i.second)));
+ }
+ return result;
+}
+
+#pragma endregion
+
+namespace DB
+{
+
+/**
+ * @brief Any class to store some SQL basic types
+ *
+ */
+class Any
+{
+
+public:
+ union Value
+ {
+ bool boolean;
+ int64_t integer;
+ uint64_t uinteger;
+ double floating;
+ std::string* string;
+ Date* date;
+ Time* time;
+ DateTime* datetime;
+ ByteArray* blob;
+ } value; ///< Value
+
+ enum class Type : char
+ {
+ Null = 0,
+ Boolean = 1,
+ Integer = 2,
+ UInteger = 3,
+ Floating = 4,
+ String = 5,
+ Date = 6,
+ Time = 7,
+ DateTime = 8,
+ Blob = 9
+ } type = Type::Null; ///< Type of the value
+
+ /**
+ * @brief Construct a new Any object with null value.
+ *
+ */
+ LIAPI Any();
+ /**
+ * @brief Construct a new Any object with boolean value.
+ *
+ * @param v The boolean value
+ */
+ LIAPI Any(bool v);
+ /**
+ * @brief Construct a new Any object with int64 value.
+ *
+ * @param v The integer value
+ */
+ LIAPI Any(int64_t v);
+ /**
+ * @brief Construct a new Any object with uint64 value.
+ *
+ * @param v The unsigned integer value
+ */
+ LIAPI Any(uint64_t v);
+ /**
+ * @brief Construct a new Any object with double value.
+ *
+ * @param v The floating value
+ */
+ LIAPI Any(double v);
+ /**
+ * @brief Construct a new Any object with string value.
+ *
+ * @param v The string value
+ */
+ LIAPI Any(const std::string& v);
+ /**
+ * @brief Construct a new Any object with const char* value.
+ *
+ * @param v The const char* value
+ */
+ LIAPI Any(const char* v);
+ /**
+ * @brief Construct a new Any object with char* value.
+ *
+ * @param v The char* value
+ * @param len The length of the char* value
+ */
+ LIAPI Any(char* v, size_t len);
+ /**
+ * @brief Construct a new Any object with date value.
+ *
+ * @param v The Date object
+ */
+ LIAPI Any(const Date& v);
+ /**
+ * @brief Construct a new Any object with time value.
+ *
+ * @param v The Time object
+ */
+ LIAPI Any(const Time& v);
+ /**
+ * @brief Construct a new Any object with date time value.
+ *
+ * @param v The DateTime object
+ */
+ LIAPI Any(const DateTime& v);
+ /**
+ * @brief Construct a new Any object with int8(char) value.
+ *
+ * @param v The char value
+ */
+ LIAPI Any(char v);
+ /**
+ * @brief Construct a new Any object with uint8(unsigned char) value.
+ *
+ * @param v The unsigned char value
+ */
+ LIAPI Any(unsigned char v);
+ /**
+ * @brief Construct a new Any object with int16(short) value.
+ *
+ * @param v The short value
+ */
+ LIAPI Any(short v);
+ /**
+ * @brief Construct a new Any object with uint16(unsigned short) value.
+ *
+ * @param v The unsigned short value
+ */
+ LIAPI Any(unsigned short v);
+ /**
+ * @brief Construct a new Any object with int32(int) value.
+ *
+ * @param v The int value
+ */
+ LIAPI Any(int v);
+ /**
+ * @brief Construct a new Any object with uint32(unsigned int) value.
+ *
+ * @param v The unsigned int value
+ */
+ LIAPI Any(unsigned int v);
+ /**
+ * @brief Construct a new Any object with long value.
+ *
+ * @param v The long value
+ */
+ LIAPI Any(long v);
+ /**
+ * @brief Construct a new Any object with unsigned long value.
+ *
+ * @param v The unsigned long value
+ */
+ LIAPI Any(unsigned long v);
+ /**
+ * @brief Construct a new Any object with float value.
+ *
+ * @param v The float value
+ */
+ LIAPI Any(float v);
+ /**
+ * @brief Construct a new Any object with byte array value.
+ *
+ * @param v The byte array value
+ */
+ LIAPI Any(const ByteArray& v);
+ /// Copy constructor
+ LIAPI Any(const Any& v);
+ /// Copy assignment operator
+ LIAPI Any& operator=(const Any& v);
+
+ /// Destructor
+ LIAPI ~Any();
+
+ /**
+ * @brief Get if the value is null.
+ *
+ */
+ LIAPI bool is_null() const;
+ /**
+ * @brief Get if the value is boolean.
+ *
+ */
+ LIAPI bool is_boolean() const;
+ /**
+ * @brief Get if the value is (unsigned) integer.
+ *
+ */
+ LIAPI bool is_integer() const;
+ /**
+ * @brief Get if the value is unsigned integer.
+ *
+ */
+ LIAPI bool is_uinteger() const;
+ /**
+ * @brief Get if the value is floating.
+ *
+ */
+ LIAPI bool is_floating() const;
+ /**
+ * @brief Get if the value is string.
+ *
+ */
+ LIAPI bool is_string() const;
+ /**
+ * @brief Get if the value is date.
+ *
+ */
+ LIAPI bool is_date() const;
+ /**
+ * @brief Get if the value is time.
+ *
+ */
+ LIAPI bool is_time() const;
+ /**
+ * @brief Get if the value is date time.
+ *
+ */
+ LIAPI bool is_datetime() const;
+ /**
+ * @brief Get if the value is blob.
+ *
+ */
+ LIAPI bool is_blob() const;
+ /**
+ * @brief Get if the value is floating or (unsigned) integer.
+ *
+ */
+ LIAPI bool is_number() const;
+
+ /**
+ * @brief Get the number value as T
+ *
+ * @tparam T The C++ basic number type to convert to, such as int, long, double, etc.
+ * @return T The value
+ * @throws std::bad_cast If the value cannot be converted to T or the value is not a number
+ * @note You can use Any::is_number() to check if the value is a number before calling this function.
+ * @see is_number()
+ */
+ template
+ inline T get_number() const
+ {
+ switch (type)
+ {
+#if !defined(DBANY_NO_NULL_CONVERSION)
+ case Type::Null:
+ return 0;
+#endif
+ case Type::Boolean:
+ return static_cast(value.boolean);
+ case Type::Integer:
+ case Type::UInteger:
+ return static_cast(value.integer);
+ case Type::Floating:
+ return static_cast(value.floating);
+ case Type::String:
+ case Type::Date:
+ case Type::Time:
+ case Type::DateTime:
+ case Type::Blob:
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ /**
+ * @brief Get the value as T.
+ *
+ * @tparam T The type of the value
+ * @return T The value
+ * @throws std::bad_cast If the value cannot be converted to T
+ * @par Custom Type Conversion
+ * Define a custom type conversion function for the type T
+ * @code
+ * template <>
+ * MyClass any_to(const Any& v) {
+ * MyClass result;
+ * switch (v.type) {
+ * case Any::Type::String:
+ * result.a = *v.value.string;
+ * default:
+ * throw std::bad_cast();
+ * }
+ * return result;
+ * }
+ * @endcode
+ * @note You can use `#define DBANY_NO_NULL_CONVERSION` to disable null conversion.
+ * (throw an exception when trying converting from a null type value)
+ * @see any_to
+ */
+ template
+ inline T get() const
+ {
+ return any_to(*this);
+ }
+ /**
+ * @brief Get the value as string
+ *
+ * @tparam T = bool
+ * @return bool The value
+ * @throws std::bad_cast If the value cannot be converted to string
+ */
+ template <>
+ inline bool get() const
+ {
+ switch (type)
+ {
+#if !defined(DBANY_NO_NULL_CONVERSION)
+ case Type::Null:
+ return false;
+#endif
+ case Type::Boolean:
+ return value.boolean;
+ case Type::Integer:
+ case Type::UInteger:
+ case Type::Floating:
+ return (bool)value.integer;
+ case Type::String:
+ case Type::Date:
+ case Type::Time:
+ case Type::DateTime:
+ case Type::Blob:
+ default:
+ throw std::bad_cast();
+ }
+ }
+ /**
+ * @brief Get the value as char
+ *
+ * @tparam T = char
+ * @return char The value
+ * @throws std::bad_cast If the value cannot be converted to char
+ */
+ template <>
+ inline char get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as unsigned char
+ *
+ * @tparam T = unsigned char
+ * @return unsigned char The value
+ * @throws std::bad_cast If the value cannot be converted to unsigned char
+ */
+ template <>
+ inline unsigned char get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as short
+ *
+ * @tparam T = short
+ * @return short The value
+ * @throws std::bad_cast If the value cannot be converted to short
+ */
+ template <>
+ inline short get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as unsigned short
+ *
+ * @tparam T = unsigned short
+ * @return unsigned short The value
+ * @throws std::bad_cast If the value cannot be converted to unsigned short
+ */
+ template <>
+ inline unsigned short get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as int
+ *
+ * @tparam T = int
+ * @return int The value
+ * @throws std::bad_cast If the value cannot be converted to int
+ */
+ template <>
+ inline int get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as unsigned int
+ *
+ * @tparam T = unsigned int
+ * @return unsigned int The value
+ * @throws std::bad_cast If the value cannot be converted to unsigned int
+ */
+ template <>
+ inline unsigned int get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as long
+ *
+ * @tparam T = long
+ * @return long The value
+ * @throws std::bad_cast If the value cannot be converted to long
+ */
+ template <>
+ inline long get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as unsigned long
+ *
+ * @tparam T = unsigned long
+ * @return unsigned long The value
+ * @throws std::bad_cast If the value cannot be converted to unsigned long
+ */
+ template <>
+ inline unsigned long get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as long long
+ *
+ * @tparam T = long long
+ * @return long long The value
+ * @throws std::bad_cast If the value cannot be converted to long long
+ */
+ template <>
+ inline long long get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as unsigned long long
+ *
+ * @tparam T = unsigned long long
+ * @return unsigned long long The value
+ * @throws std::bad_cast If the value cannot be converted to unsigned long long
+ */
+ template <>
+ inline unsigned long long get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as double.
+ *
+ * @tparam T = double
+ * @return double The value
+ * @throws std::bad_cast If the value cannot be converted to double
+ */
+ template <>
+ inline double get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as float.
+ *
+ * @tparam T = float
+ * @return float The value
+ * @throws std::bad_cast If the value cannot be converted to float
+ */
+ template <>
+ inline float get() const
+ {
+ return get_number();
+ }
+ /**
+ * @brief Get the value as string.
+ *
+ * @tparam T = std::string
+ * @return std::string The value
+ * @throws std::bad_cast If the value cannot be converted to string
+ */
+ template <>
+ std::string get() const
+ {
+ switch (type)
+ {
+#if !defined(DBANY_NO_NULL_CONVERSION)
+ case Type::Null:
+ return "";
+#endif
+ case Type::Boolean:
+ return value.boolean ? "true" : "false";
+ case Type::Integer:
+ return std::to_string(value.integer);
+ case Type::UInteger:
+ return std::to_string(value.uinteger);
+ case Type::Floating:
+ return std::to_string(value.floating);
+ case Type::String:
+ return *value.string;
+ case Type::Date:
+ return std::to_string(value.date->year) + "-" +
+ std::to_string(value.date->month) + "-" +
+ std::to_string(value.date->day);
+ break;
+ case Type::Time:
+ return std::to_string(value.time->hour) + ":" +
+ std::to_string(value.time->minute) + ":" +
+ std::to_string(value.time->second);
+ case Type::DateTime:
+ return std::to_string(value.datetime->date.year) + "-" +
+ std::to_string(value.datetime->date.month) + "-" +
+ std::to_string(value.datetime->date.day) + " " +
+ std::to_string(value.datetime->time.hour) + ":" +
+ std::to_string(value.datetime->time.minute) + ":" +
+ std::to_string(value.datetime->time.second);
+ case Type::Blob:
+ return std::string(value.blob->begin(), value.blob->end());
+ default:
+ throw std::bad_cast();
+ }
+ }
+ /**
+ * @brief Get the value as Date
+ *
+ * @tparam T = DB::Date
+ * @return DB::Date The value
+ * @throws std::bad_cast If the value cannot be converted to DB::Date
+ */
+ template <>
+ Date get() const
+ {
+ switch (type)
+ {
+ case Type::Date:
+ return *value.date;
+ case Type::DateTime:
+ return value.datetime->date;
+ case Type::String:
+ case Type::Integer:
+ case Type::UInteger:
+ case Type::Floating:
+ case Type::Time:
+ case Type::Blob:
+ default:
+ throw std::bad_cast();
+ }
+ }
+ /**
+ * @brief Get the value as Time
+ *
+ * @tparam T = DB::Time
+ * @return DB::Time The value
+ * @throws std::bad_cast If the value cannot be converted to DB::Time
+ */
+ template <>
+ Time get() const
+ {
+ switch (type)
+ {
+ case Type::Time:
+ return *value.time;
+ case Type::DateTime:
+ return value.datetime->time;
+ case Type::String:
+ case Type::Integer:
+ case Type::UInteger:
+ case Type::Floating:
+ case Type::Date:
+ case Type::Blob:
+ default:
+ throw std::bad_cast();
+ }
+ }
+ /**
+ * @brief Get the value as DateTime
+ *
+ * @tparam T = DB::DateTime
+ * @return DB::DateTime The value
+ * @throws std::bad_cast If the value cannot be converted to DB::DateTime
+ */
+ template <>
+ DateTime get() const
+ {
+ switch (type)
+ {
+ case Type::DateTime:
+ return *value.datetime;
+ case Type::String:
+ case Type::Integer:
+ case Type::UInteger:
+ case Type::Floating:
+ case Type::Date:
+ case Type::Time:
+ case Type::Blob:
+ default:
+ throw std::bad_cast();
+ }
+ }
+ /**
+ * @brief Get the value as ByteArray
+ *
+ * @tparam T = DB::ByteArray
+ * @return DB::ByteArray The value
+ * @throws std::bad_cast If the value cannot be converted to DB::ByteArray
+ */
+ template <>
+ ByteArray get() const
+ {
+ switch (type)
+ {
+ case Type::Blob:
+ return *value.blob;
+ case Type::String:
+ return ByteArray((unsigned char*)value.string->data(),
+ (unsigned char*)value.string->data() + value.string->size());
+ case Type::Integer:
+ case Type::UInteger:
+ case Type::Floating:
+ case Type::Date:
+ case Type::Time:
+ case Type::DateTime:
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ /**
+ * @brief Convert Any::Type to string.
+ *
+ * @param type The Any::Type value
+ * @return std::string The string value
+ */
+ LIAPI static std::string type2str(Any::Type type);
+
+ /**
+ * @brief Convert string to Any.
+ *
+ * @param str The string
+ * @return Any The converted value
+ */
+ LIAPI static Any str2any(const std::string& str);
+
+};
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/ConnParams.h b/LiteLoader/Header/DB/ConnParams.h
new file mode 100644
index 0000000..c6dda03
--- /dev/null
+++ b/LiteLoader/Header/DB/ConnParams.h
@@ -0,0 +1,140 @@
+#pragma once
+#include "Any.h"
+#include
+
+namespace DB
+{
+
+/**
+ * @brief Connection parameters
+ *
+ */
+class ConnParams : public std::unordered_map
+{
+
+ std::string raw;
+
+public:
+ ConnParams() = default;
+ /**
+ * @brief Construct a new ConnParams object
+ *
+ * @param list An initializer list like `{"host", "localhost", "port", 3306}`
+ * @throw std::invalid_argument If the type of key is not supported
+ */
+ LIAPI ConnParams(const std::initializer_list& list);
+ /**
+ * @brief Construct a new ConnParams object
+ *
+ * @param list An initializer list like `{{"key1", "value1"}, {"key2", "value2"}}`
+ */
+ LIAPI ConnParams(const std::initializer_list>& list);
+ /**
+ * @brief Construct a new ConnParams object
+ *
+ * @param str Connection string like `mysql://localhost:3306?key1=value1&key2=value2`
+ */
+ LIAPI ConnParams(const std::string& str);
+ /**
+ * @brief Construct a new ConnParams object
+ *
+ * @param str Connection string like `mysql://localhost:3306?key1=value1&key2=value2`
+ */
+ LIAPI ConnParams(const char* str);
+
+ /**
+ * @brief Get the scheme.
+ *
+ * @return std::string The scheme
+ */
+ LIAPI std::string getScheme();
+ /**
+ * @brief Get the host.
+ *
+ * @return std::string The host name
+ */
+ LIAPI std::string getHost();
+ /**
+ * @brief Get the port.
+ *
+ * @return uint16_t The port number
+ */
+ LIAPI uint16_t getPort();
+ /**
+ * @brief Get the username.
+ *
+ * @return std::string The username
+ */
+ LIAPI std::string getUsername();
+ /**
+ * @brief Get the password.
+ *
+ * @return std::string The password
+ */
+ LIAPI std::string getPassword();
+ /**
+ * @brief Get the database.
+ *
+ * @return std::string The database name
+ */
+ LIAPI std::string getDatabase();
+ /**
+ * @brief Get the path.
+ *
+ * @return std::string The path
+ */
+ LIAPI std::string getPath();
+ /**
+ * @brief Get the raw connection string.
+ *
+ * @return std::string The connection string
+ * @note If this object is constructed by `ConnParams(const std::string& str)`
+ * or `ConnParams(const char* str)`,
+ * the return value is the same as the parameter `str`.
+ * Otherwise, the return value will be empty.
+ */
+ LIAPI std::string getRaw();
+
+ /**
+ * @brief Get the value of one of the keys.
+ *
+ * @tparam T The type of the value
+ * @param keys The keys (If ignoreCase is true, keys must be lowercase)
+ * @param ignoreCase Whether to ignore the case of the key
+ * @param defaultValue The default value
+ * @return T The value
+ * @note If there are multiple keys matched,
+ * the first one(keys[0]) will be returned.
+ */
+ template
+ inline T get(const std::vector& keys, bool ignoreCase = true, T defaultValue = T())
+ {
+ Any value;
+ int w = INT_MAX;
+ for (auto& [k, v] : *this)
+ {
+ std::string lowerKey = k;
+ if (ignoreCase)
+ {
+ std::transform(lowerKey.begin(), lowerKey.end(), lowerKey.begin(), ::tolower);
+ }
+ int i = 0;
+ for (auto& key : keys)
+ {
+ if (lowerKey == key && i < w)
+ {
+ value = v;
+ w = i;
+ if (w == 0) break;
+ }
+ i++;
+ }
+ if (w == 0) break;
+ }
+ if (value.is_null())
+ return defaultValue;
+ return value.get();
+ }
+};
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/Impl/MySQL/Session.h b/LiteLoader/Header/DB/Impl/MySQL/Session.h
new file mode 100644
index 0000000..ea5850f
--- /dev/null
+++ b/LiteLoader/Header/DB/Impl/MySQL/Session.h
@@ -0,0 +1,40 @@
+#pragma once
+#include "../../Session.h"
+
+struct MYSQL;
+
+namespace DB
+{
+
+class MySQLStmt;
+
+class MySQLSession : public Session
+{
+
+ MYSQL* conn = nullptr;
+
+ void setSSL(const ConnParams& params);
+
+public:
+ MySQLSession();
+ MySQLSession(const ConnParams& params);
+ ~MySQLSession();
+ void open(const ConnParams& params);
+ bool execute(const std::string& query);
+ bool relogin(const std::string& user, const std::string& password, const std::string& db = "");
+ Session& query(const std::string& query, std::function callback);
+ SharedPointer prepare(const std::string& query, bool autoExecute = false);
+ std::string getLastError() const;
+ uint64_t getAffectedRows() const;
+ uint64_t getLastInsertId() const;
+ void close();
+ bool isOpen();
+ DBType getType();
+
+ SharedPointer operator<<(const std::string& query);
+
+ friend class MySQLStmt;
+
+};
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/Impl/MySQL/Stmt.h b/LiteLoader/Header/DB/Impl/MySQL/Stmt.h
new file mode 100644
index 0000000..28b3e45
--- /dev/null
+++ b/LiteLoader/Header/DB/Impl/MySQL/Stmt.h
@@ -0,0 +1,71 @@
+#pragma once
+#include "../../Stmt.h"
+
+struct MYSQL_STMT;
+
+namespace DB
+{
+
+class MySQLSession;
+
+/**
+ * @brief Fetched data receiver(buffer)
+ *
+ */
+struct Receiver
+{
+ MYSQL_FIELD field;
+ std::shared_ptr buffer;
+ unsigned long length = 0;
+ bool isNull = false;
+ bool isUnsigned = false;
+ bool error = false;
+};
+
+class MySQLStmt : public Stmt
+{
+
+ MYSQL_STMT* stmt = nullptr;
+ MYSQL_RES* metadata = nullptr;
+ std::shared_ptr params = nullptr; ///< Parameters to bind
+ std::shared_ptr result = nullptr; ///< Result of query
+ std::shared_ptr resultHeader = nullptr;
+ std::vector boundIndexes;
+ std::vector paramValues;
+ std::vector resultValues;
+ std::unordered_map paramIndexes;
+ std::string query;
+ int boundParamsCount = 0;
+ int totalParamsCount = 0;
+ int steps = 0;
+ bool fetched = false;
+
+ MySQLStmt(MYSQL_STMT* stmt, const std::weak_ptr& parent, bool autoExecute = false);
+ int getNextParamIndex();
+ void bindResult();
+
+public:
+ ~MySQLStmt();
+ Stmt& bind(const Any& value, int index);
+ Stmt& bind(const Any& value, const std::string& name);
+ Stmt& bind(const Any& value);
+ Stmt& execute();
+ bool step();
+ bool next();
+ bool done();
+ Row _Fetch();
+ Stmt& reset();
+ Stmt& reexec();
+ Stmt& clear();
+ void close();
+ uint64_t getAffectedRows() const;
+ uint64_t getInsertId() const;
+ int getUnboundParams() const;
+ int getBoundParams() const;
+ int getParamsCount() const;
+ DBType getType() const;
+
+ LIAPI static SharedPointer create(const std::weak_ptr& sess, const std::string& sql, bool autoExecute = false);
+};
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/Impl/SQLite/Session.h b/LiteLoader/Header/DB/Impl/SQLite/Session.h
new file mode 100644
index 0000000..ee52a3c
--- /dev/null
+++ b/LiteLoader/Header/DB/Impl/SQLite/Session.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "../../Session.h"
+
+struct sqlite3;
+namespace DB
+{
+
+class SQLiteSession : public Session
+{
+
+ sqlite3* conn = nullptr;
+
+public:
+
+ SQLiteSession();
+ SQLiteSession(const ConnParams& params);
+ ~SQLiteSession();
+ void open(const ConnParams& params);
+ bool execute(const std::string& query);
+ Session& query(const std::string& query, std::function callback);
+ SharedPointer prepare(const std::string& query, bool autoExecute = false);
+ std::string getLastError() const;
+ uint64_t getAffectedRows() const;
+ uint64_t getLastInsertId() const;
+ void close();
+ bool isOpen();
+ DBType getType();
+
+ SharedPointer operator<<(const std::string& query);
+
+ friend class SQLiteStmt;
+};
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/Impl/SQLite/Stmt.h b/LiteLoader/Header/DB/Impl/SQLite/Stmt.h
new file mode 100644
index 0000000..64058f6
--- /dev/null
+++ b/LiteLoader/Header/DB/Impl/SQLite/Stmt.h
@@ -0,0 +1,57 @@
+#pragma once
+#include "../../Stmt.h"
+
+struct sqlite3_stmt;
+
+namespace DB
+{
+
+class SQLiteSession;
+
+class SQLiteStmt : public Stmt
+{
+
+ std::shared_ptr resultHeader;
+ sqlite3_stmt* stmt = nullptr;
+ int boundParamsCount = 0;
+ int totalParamsCount = 0;
+ int steps = 0;
+ uint64_t affectedRowCount = -1;
+ uint64_t insertRowId = -1;
+ bool stepped = false;
+ bool executed = false;
+ std::vector boundIndexes;
+
+ SQLiteStmt(sqlite3_stmt* stmt, const std::weak_ptr parent, bool autoExecute);
+ int getNextParamIndex();
+ void fetchResultHeader();
+
+public:
+ ~SQLiteStmt();
+ Stmt& bind(const Any& value, int index);
+ Stmt& bind(const Any& value, const std::string& name);
+ Stmt& bind(const Any& value);
+ Stmt& execute();
+ bool step();
+ bool next();
+ bool done();
+ Row _Fetch();
+ Stmt& reset();
+ /**
+ * @see Stmt::reexec for details
+ * @see https://www.sqlite.org/c3ref/reexec.html
+ */
+ Stmt& reexec();
+ Stmt& clear();
+ void close();
+ uint64_t getAffectedRows() const;
+ uint64_t getInsertId() const;
+ int getUnboundParams() const;
+ int getBoundParams() const;
+ int getParamsCount() const;
+ DBType getType() const;
+
+ LIAPI static SharedPointer create(const std::weak_ptr& sess, const std::string& sql, bool autoExecute = false);
+};
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/Pointer.h b/LiteLoader/Header/DB/Pointer.h
new file mode 100644
index 0000000..8021c19
--- /dev/null
+++ b/LiteLoader/Header/DB/Pointer.h
@@ -0,0 +1,68 @@
+#pragma once
+#include
+
+namespace DB
+{
+
+class Stmt;
+
+/**
+ * @brief A smart pointer class extended from std::shared_ptr
+ *
+ * @tparam T Type of the pointer
+ * @warning This class is only for internal use(Session, Stmt and so on).
+ * So do not use this class directly, use std::shared_ptr instead.
+ */
+template
+class SharedPointer : public std::shared_ptr
+{
+
+public:
+
+ SharedPointer(T* ptr = nullptr) : std::shared_ptr(ptr) {}
+ SharedPointer(const std::shared_ptr& ptr) : std::shared_ptr(ptr) {}
+ SharedPointer(std::shared_ptr&& ptr) : std::shared_ptr(ptr) {}
+ SharedPointer(const SharedPointer& other) : std::shared_ptr(other) {}
+ SharedPointer(SharedPointer&& other) : std::shared_ptr(other) {}
+ ~SharedPointer()
+ {
+ }
+ inline SharedPointer& operator=(const SharedPointer& other)
+ {
+ std::shared_ptr::operator=(other);
+ return *this;
+ }
+ inline SharedPointer& operator=(SharedPointer&& other) noexcept
+ {
+ std::shared_ptr::operator=(other);
+ return *this;
+ }
+
+ template
+ inline SharedPointer operator<<(const U& v)
+ {
+ auto ptr = std::shared_ptr::get();
+ if (!ptr) throw std::runtime_error("The pointer is nullptr");
+ //Logger("DBG").debug("operator<< {}", (void*)ptr);
+ return (*ptr) << v;
+ }
+
+ template
+ inline SharedPointer operator>>(U& v)
+ {
+ auto ptr = std::shared_ptr::get();
+ if (!ptr) throw std::runtime_error("The pointer is nullptr");
+ return (*ptr) >> v;
+ }
+
+ template
+ inline SharedPointer operator,(U v)
+ {
+ auto ptr = std::shared_ptr::get();
+ if (!ptr) throw std::runtime_error("The pointer is nullptr");
+ return ptr->operator,(v);
+ }
+
+};
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/Row.h b/LiteLoader/Header/DB/Row.h
new file mode 100644
index 0000000..0eab378
--- /dev/null
+++ b/LiteLoader/Header/DB/Row.h
@@ -0,0 +1,269 @@
+#pragma once
+#include "Any.h"
+#include
+#include
+
+namespace DB
+{
+
+class Row;
+
+/**
+ * @brief The header of a row
+ *
+ */
+class RowHeader : private std::vector
+{
+
+ using Base = std::vector;
+ std::vector hashes;
+
+public:
+ /**
+ * @brief Construct a new Row Header object.
+ *
+ */
+ RowHeader() = default;
+ /**
+ * @brief Construct a new Row Header object.
+ *
+ * @param list An initializer list like `{"col1", "col2", "col3"}`
+ */
+ LIAPI RowHeader(const std::initializer_list& list);
+ /// Move constructor
+ RowHeader(RowHeader&& other) noexcept = default;
+ /// Copy constructor
+ RowHeader(const RowHeader& other) = default;
+ /// Destructor
+ ~RowHeader();
+ /**
+ * @brief Add a column to the header.
+ *
+ * @param name The name of the column
+ * @return int The index of the column
+ */
+ LIAPI size_t add(const std::string& name);
+ /**
+ * @brief Get whether the header contains a column.
+ *
+ * @param name The name of the column
+ * @return bool True if the column exists
+ */
+ LIAPI bool contains(const std::string& name);
+ /**
+ * @brief Remove a column from the header.
+ *
+ * @param name The name of the column
+ * @throws std::out_of_range If the column does not exist
+ */
+ LIAPI void remove(const std::string& name);
+ /**
+ * @brief Get the size of the header.
+ *
+ * @return int The size of the header
+ */
+ LIAPI size_t size() const;
+ /**
+ * @brief Get weather the header is empty.
+ *
+ * @return bool True if the header is empty
+ */
+ LIAPI bool empty() const;
+ /**
+ * @brief Get the index of a column.
+ *
+ * @param name The name of the column
+ * @return int The index of the column
+ * @throws std::out_of_range If the column does not exist
+ */
+ LIAPI size_t at(const std::string& name);
+ /**
+ * @brief Get the index of a column.
+ *
+ * @param index The index of the column
+ * @return std::string& The name of the column
+ * @throws std::out_of_range If the column does not exist
+ */
+ LIAPI std::string& at(size_t index);
+ /**
+ * @brief Get the iterator to the first element
+ *
+ * @return std::unordered_map::iterator The iterator
+ */
+ LIAPI std::vector::iterator begin();
+ /**
+ * @brief Get the iterator to the last element.
+ *
+ * @return std::unordered_map::iterator The iterator
+ */
+ LIAPI std::vector::iterator end();
+ /**
+ * @brief Check whether the row can be adapted to the header.
+ *
+ * @param row The row to adapt
+ * @return bool True if the row can be adapted
+ */
+ LIAPI bool check(const Row& row) const;
+
+ /**
+ * @brief Get the index of a column.
+ *
+ * @param name The name of the column
+ * @return int The index of the column
+ * @note It will create the column(=add) if it does not exist
+ */
+ LIAPI size_t operator[](const std::string& name);
+ /**
+ * @brief Get the name of a column.
+ *
+ * @param index The index of the column
+ * @return std::string& The name of the column
+ */
+ LIAPI std::string& operator[](size_t index);
+
+ /// Move assignment operator
+ RowHeader& operator=(RowHeader&& other) noexcept = default;
+ /// Copy assignment operator
+ RowHeader& operator=(const RowHeader& other) = default;
+};
+
+/**
+ * @brief A row of data
+ *
+ */
+class Row : public std::vector
+{
+public:
+ std::shared_ptr header; //!< The header of the row
+
+ /**
+ * @brief Construct a new Row object.
+ *
+ * @param header The header(column names) of the row(shared_ptr)
+ */
+ LIAPI Row(const std::shared_ptr& header = nullptr);
+ /**
+ * @brief Construct a new Row object.
+ *
+ * @param header The header(column names) of the row
+ * @note This will create a shared_ptr of the header
+ */
+ LIAPI Row(const RowHeader& header);
+ /**
+ * @brief Construct a new Row object.
+ *
+ * @param list List of values
+ * @param header The header(column names) of the row
+ * @throw std::invalid_argument If the size of the list is not equal to the size of the header
+ * @par Example
+ * @code
+ * RowHeader header{"id", "age", "name"};
+ * Row row1({114, 24, "alex"}, header);
+ * Row row2({514, 24, "steve"}, {"id", "age", "name"});
+ * @endcode
+ */
+ LIAPI Row(const std::initializer_list& list, const RowHeader& header);
+ /**
+ * @brief Construct a new Row object.
+ *
+ * @param list List of values
+ * @param header The header(column names) of the row(shared_ptr)
+ * @throw std::invalid_argument If the size of the list is not equal to the size of the header
+ */
+ LIAPI Row(const std::initializer_list& list,
+ const std::shared_ptr& header = nullptr);
+ /**
+ * @brief Construct a new Row object(move).
+ *
+ * @param list Vector of values
+ * @param header The header(column names) of the row
+ * @throw std::invalid_argument If the size of the vector is not equal to the size of the header
+ */
+ LIAPI Row(std::vector&& list, const RowHeader& header);
+ /**
+ * @brief Construct a new Row object.
+ *
+ * @param list Vector of values
+ * @param header The header(column names) of the row
+ * @throw std::invalid_argument If the size of the vector is not equal to the size of the header
+ */
+ LIAPI Row(const std::vector& list, const RowHeader& header);
+ /**
+ * @brief Construct a new Row object(move).
+ *
+ * @param list Vector of column names(header) and values
+ * @par Example
+ * @code
+ * Row row({{"id", 114}, {"age", 2000}, {"name", "alex"}});
+ * @endcode
+ */
+ LIAPI Row(const std::initializer_list>& list);
+ /// Move constructor
+ LIAPI Row(Row&& other) noexcept;
+ /// Copy constructor
+ LIAPI Row(const Row& other);
+ /// Move assignment operator
+ LIAPI Row& operator=(Row&& other) noexcept;
+ /// Copy assignment operator
+ LIAPI Row& operator=(const Row& other);
+ /**
+ * @brief Get the value of a column
+ *
+ * @param column The name of the column
+ * @return Any& The value of the column
+ * @note It will create a new Any object if the column doesn't exist
+ */
+ LIAPI Any& operator[](const std::string& name);
+ /**
+ * @brief Get the value of a column
+ *
+ * @param column The name of the column
+ * @return Any& The value of the column
+ * @see Row::at
+ */
+ LIAPI const Any& operator[](const std::string& name) const;
+ /**
+ * @brief Get the value of a column
+ *
+ * @param column The name of the column
+ * @return Any& The value of the column
+ * @throw std::out_of_range If the column does not exist
+ */
+ LIAPI Any& at(const std::string& column);
+ LIAPI const Any& at(const std::string& column) const;
+ /**
+ * @brief Traverse the row(references)
+ *
+ * @param cb The function to call for each element
+ * @note Return false in callback function to stop the iteration
+ */
+ LIAPI void forEach_ref(std::function cb);
+ /**
+ * @brief Traverse the row
+ *
+ * @param cb The function to call for each element
+ * @note Return false in callback function to stop the iteration
+ */
+ LIAPI void forEach(std::function cb) const;
+};
+
+} // namespace DB
+
+/**
+ * @brief Function to convert a row to T.
+ *
+ * @tparam T The type to convert to
+ * @param row A row
+ * @return T The converted value
+ */
+template
+inline T row_to(const DB::Row& row)
+{
+ throw std::bad_cast();
+}
+
+template <>
+inline DB::Row row_to(const DB::Row& row)
+{
+ return row;
+}
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/RowSet.h b/LiteLoader/Header/DB/RowSet.h
new file mode 100644
index 0000000..5a6cc32
--- /dev/null
+++ b/LiteLoader/Header/DB/RowSet.h
@@ -0,0 +1,78 @@
+#pragma once
+#include "Row.h"
+#include
+
+#undef max
+
+namespace DB
+{
+
+class RowSet : public std::vector
+{
+
+ using Base = std::vector;
+
+public:
+ std::shared_ptr header; //!< The header of the rows
+
+ /**
+ * @brief Construct a new Row Set object
+ *
+ * @param header The header(column names) of rows(shared_ptr)
+ */
+ LIAPI RowSet(const std::shared_ptr& header = nullptr);
+ /**
+ * @brief Construct a new Row Set object
+ *
+ * @param header The header(column names) of rows
+ */
+ LIAPI RowSet(const RowHeader& header);
+ /// Move constructor
+ LIAPI RowSet(RowSet&& set) noexcept;
+ /// Copy constructor
+ LIAPI RowSet(const RowSet& set);
+ /// Move assignment operator
+ LIAPI RowSet& operator=(RowSet&& set) noexcept;
+ /// Copy assignment operator
+ LIAPI RowSet& operator=(const RowSet& set);
+
+ /**
+ * @brief Add a row to the set.
+ *
+ * @param row The row to add
+ */
+ LIAPI void add(const Row& row);
+ /**
+ * @brief Get if the set is valid
+ *
+ * @return bool True if valid
+ */
+ LIAPI bool valid();
+ /**
+ * @brief Add a row to the set.
+ *
+ * @param row The row to add
+ * @see add(const Row&)
+ */
+ LIAPI void push_back(const Row& row);
+ /**
+ * @brief Convert to the table string.
+ *
+ * @param nullPattern When the value is null, what to replace with(default '')
+ * @return std::string The result string
+ * @par sample
+ * @code
+ * | a | b |
+ * |=====|========|
+ * | awa | 114514 |
+ * | qwq | 233 |
+ * | ll | |
+ * |=====|========|
+ * @endcode
+ */
+ LIAPI std::string toTableString(const std::string& nullPattern = "") const;
+};
+
+using ResultSet = RowSet;
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/Session.h b/LiteLoader/Header/DB/Session.h
new file mode 100644
index 0000000..6d8af65
--- /dev/null
+++ b/LiteLoader/Header/DB/Session.h
@@ -0,0 +1,231 @@
+#pragma once
+#include "RowSet.h"
+#include "Stmt.h"
+#include "ConnParams.h"
+#include "Pointer.h"
+
+class Logger;
+
+namespace DB
+{
+
+extern Logger dbLogger;
+
+class Session
+{
+
+protected:
+#if defined(LLDB_DEBUG_MODE)
+ bool debugOutput = true;
+#else
+ bool debugOutput = false;
+#endif
+ std::weak_ptr self;
+ std::vector> stmtPool; ///< List of statements opened by prepare method.
+
+public:
+
+ /// Destructor
+ virtual ~Session() = default;
+ /**
+ * @brief Open the database connection.
+ *
+ * @par Implementation
+ * @see SQLiteSession::open
+ */
+ virtual void open(const ConnParams& params) = 0;
+ /**
+ * @brief Turn on/off debug output.
+ *
+ * @param enable Enable or not
+ */
+ LIAPI void setDebugOutput(bool enable);
+ /**
+ * @brief Change current user and database.
+ *
+ * @param user Username
+ * @param pass Password
+ * @param db Database name
+ * @return bool Success or not
+ * @throws std::runtime_error If not implemented
+ * @par Implementation
+ * None
+ */
+ virtual bool relogin(const std::string& user, const std::string& password, const std::string& db = "");
+ /**
+ * @brief Execute a query.
+ *
+ * @param query Query to execute
+ * @param callback Callback to process results
+ * @return *this
+ *
+ * @par Implementation
+ * @see SQLiteSession::query
+ */
+ virtual Session& query(const std::string& query, std::function callback) = 0;
+ /**
+ * @brief Execute a query.
+ *
+ * @param query The query to execute
+ * @return ResultSet Result set
+ */
+ virtual ResultSet query(const std::string& query);
+ /**
+ * @brief Execute a query without results.
+ *
+ * @param query The query to execute
+ * @return bool Success or not
+ */
+ virtual bool execute(const std::string& query) = 0;
+ /**
+ * @brief Prepare a query.
+ *
+ * @param query The query to execute
+ * @param autoExecute Whether to execute the statement automatically after binding all parameters
+ * @return SharedPointer The statement
+ * @par Example
+ * @code
+ * auto& stmt = session.prepare("SELECT * FROM table WHERE id = ?");
+ * stmt.bind(1);
+ * auto res = stmt.getResults();
+ * stmt.close();
+ * @endcode
+ */
+ virtual SharedPointer prepare(const std::string& query, bool autoExecute = false) = 0;
+ /**
+ * @brief Get the last error message
+ *
+ * @return std::string Error message
+ */
+ virtual std::string getLastError() const;
+ /**
+ * @brief Get the number of affected rows by the last query.
+ *
+ * @return uint64_t The number of affected rows
+ */
+ virtual uint64_t getAffectedRows() const = 0;
+ /**
+ * @brief Get the last insert id
+ *
+ * @return uint64_t The row id of the last inserted row
+ */
+ virtual uint64_t getLastInsertId() const = 0;
+ /**
+ * @brief Close the session.
+ *
+ */
+ virtual void close() = 0;
+ /**
+ * @brief Get whether the session is open.
+ *
+ */
+ virtual bool isOpen() = 0;
+ /**
+ * @brief Get the type of session
+ *
+ * @return DBType The database type
+ */
+ virtual DBType getType() = 0;
+ /**
+ * @brief Get or set the self pointer
+ *
+ * @return std::weak_ptr self
+ */
+ virtual std::weak_ptr getOrSetSelf();
+
+ /**
+ * @brief Operator<< to execute a query.
+ *
+ * @param query The query to execute
+ * @return SharedPointer The prepared statement
+ * @par Example
+ * @code
+ * ResultSet res;
+ * session << "SELECT * FROM table WHERE id = ?", bind(114514), into(res);
+ * @endcode
+ * @note It is not recommended to store the DB::Stmt reference returned by this method,
+ * it will be closed on the next execution.
+ */
+ virtual SharedPointer operator<<(const std::string& query);
+
+ /**
+ * @brief Create a new session.
+ *
+ * @param type Database type
+ * @return SharedPointer The session
+ */
+ LIAPI static SharedPointer create(DBType type);
+ /**
+ * @brief Create and open a new session.
+ *
+ * @param params Connection parameters
+ * @return SharedPointer The session
+ */
+ LIAPI static SharedPointer create(const ConnParams& params);
+ /**
+ * @brief Create and open a new session.
+ *
+ * @param type Database type
+ * @param params Connection parameters
+ * @return SharedPointer The session
+ */
+ LIAPI static SharedPointer create(DBType type, const ConnParams& params);
+ /**
+ * @brief Create and open a new session.
+ *
+ * @param type Database type
+ * @param host Hostname
+ * @param port Port
+ * @param user Username
+ * @param password Password
+ * @param database Database name
+ * @return SharedPointer The session
+ */
+ LIAPI static SharedPointer create(DBType type, const std::string& host, uint16_t port, const std::string& user, const std::string& password, const std::string& database);
+ /**
+ * @brief Create and open a new session.
+ *
+ * @param type Database type
+ * @param path Path to the database file
+ * @return SharedPointer The session
+ */
+ LIAPI static SharedPointer create(DBType type, const std::string& path);
+
+private:
+
+ /**
+ * @brief Create a new session(internal).
+ *
+ * @param type Database type
+ * @param params Connection parameters
+ * @return SharedPointer The session
+ */
+ static SharedPointer _Create(DBType type, const ConnParams& params = {});
+
+private:
+
+ static std::vector> sessionPool; ///< List of sessions(weak pointers)
+
+public:
+
+ /**
+ * @brief Get the Session ptr by the (this) pointer.
+ *
+ * @param session The (this) pointer
+ * @return std::shared_ptr The Session ptr
+ */
+ static std::shared_ptr getSession(Session* session)
+ {
+ for (auto& s : sessionPool)
+ {
+ if (s.expired()) continue;
+ auto ptr = s.lock();
+ if (ptr.get() == session)
+ return ptr;
+ }
+ throw std::runtime_error("Session::getSession: Session is not found or expired");
+ }
+
+};
+
+} // namespace DB
\ No newline at end of file
diff --git a/LiteLoader/Header/DB/Stmt.h b/LiteLoader/Header/DB/Stmt.h
new file mode 100644
index 0000000..214bb07
--- /dev/null
+++ b/LiteLoader/Header/DB/Stmt.h
@@ -0,0 +1,673 @@
+#pragma once
+#include "RowSet.h"
+#include "Pointer.h"
+
+#define IF_ENDBG if (debugOutput)
+
+class Logger;
+
+namespace DB
+{
+
+extern Logger dbLogger;
+
+class Session;
+
+/**
+ * @brief Structure to store a single value to bind to a prepared statement.
+ *
+ * @tparam T Type of sequence container, must have begin() and end() methods
+ * @tparam The value type of the container must be DB::Any.
+ */
+struct BindType
+{
+ Any value;
+ std::string name;
+ int idx = -1;
+};
+
+/**
+ * @brief Structure to store a sequential container
+ * to bind multiple parameters at once.
+ *
+ * @tparam T Type of sequence container, must have begin() and end() methods
+ * @tparam The value type of the container must be DB::Any.
+ */
+template
+struct BindSequenceType
+{
+ T values;
+ static_assert(std::is_same::value, "Container value type must be DB::Any");
+};
+/**
+ * @brief Structure to store a map(relevance) container
+ * to bind multiple parameters at once.
+ *
+ * @tparam T Type of map container, must have begin() and end() methods
+ * @note The key type of the map must be std::string,
+ * and the value type of the map must be DB::Any.
+ */
+template
+struct BindMapType
+{
+ T values;
+ static_assert(std::is_same::value, "Map key type must be std::string");
+ static_assert(std::is_same::value, "Map value type must be DB::Any");
+};
+
+template
+struct IntoType
+{
+ T& value;
+};
+
+class Stmt
+{
+
+protected:
+#if defined(LLDB_DEBUG_MODE)
+ bool debugOutput = true;
+#else
+ bool debugOutput = false;
+#endif
+ bool autoExecute = false; ///< Whether to automatically execute the statement on bind
+ std::weak_ptr parent; ///< Parent session
+ std::weak_ptr self;
+
+public:
+ Stmt(const std::weak_ptr& parent, bool autoExecute = false);
+
+ virtual ~Stmt();
+
+ /**
+ * @brief Turn on/off debug output.
+ *
+ * @param enable Enable or not
+ */
+ LIAPI void setDebugOutput(bool enable);
+
+ /**
+ * @brief Bind a value to a statement parameter.
+ *
+ * @param value Value to bind
+ * @param index Parameter index
+ * @throws std::runtime_error If error occurs
+ *
+ * @par Implementation
+ * @see SQLiteStmt::bind
+ */
+ virtual Stmt& bind(const Any& value, int index) = 0;
+
+ /**
+ * @brief Bind a value to a statement parameter.
+ *
+ * @param value Value to bind
+ * @param name Parameter name
+ * @throws std::runtime_error If error occurs
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::bind
+ */
+ virtual Stmt& bind(const Any& value, const std::string& name) = 0;
+
+ /**
+ * @brief Bind a value to the next statement parameter.
+ *
+ * @param value Value to bind
+ * @throws std::runtime_error If error occurs
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::bind
+ */
+ virtual Stmt& bind(const Any& value) = 0;
+
+ /**
+ * @brief Execute the statement(after binding all the parameters)
+ *
+ * @return Stmt& *this
+ * @note If `this->autoExecute` is true, there is no need to call this method
+ */
+ virtual Stmt& execute() = 0;
+
+ /**
+ * @brief Step to the next row(not fetch).
+ *
+ * @return bool True if there is a next row
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::step
+ */
+ virtual bool step() = 0;
+
+ /**
+ * @brief Step to the next row(=step).
+ *
+ * @return bool True if there is a next row
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::next
+ */
+ virtual bool next() = 0;
+
+ /**
+ * @brief Get weather all the rows have been fetched.
+ *
+ * @return bool True if all the rows have been fetched
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::done
+ */
+ virtual bool done() = 0;
+
+ /**
+ * @brief Fetch the current row.
+ *
+ * @tparam T The type of the value to return
+ * @return T The current row(converted)
+ * @throws std::runtime_error If there is no row to fetch
+ *
+ * @par Example
+ * @code
+ * auto stmt = sess->prepare("SELECT * FROM table");
+ * while (stmt->step()) {
+ * auto row = stmt->fetch();
+ * // Do something with the row
+ * }
+ * stmt->close();
+ * @endcode
+ */
+ template
+ inline T fetch()
+ {
+ return row_to(_Fetch());
+ }
+
+ /**
+ * @brief Fetch the current row.
+ *
+ * @param[out] row The current row
+ * @return Stmt& *this
+ */
+ template
+ inline Stmt& fetch(T& row)
+ {
+ row = row_to(_Fetch());
+ return *this;
+ }
+
+ /**
+ * @brief Fetch each of the result rows.
+ *
+ * @param cb Callback function to handle the result rows
+ * @return Stmt& *this
+ * @note Return false in callback to stop fetching
+ *
+ * @par Example
+ * @code
+ * sess->prepare("SELECT * FROM table")
+ * ->fetchEach([](const Row& row) {
+ * // Do something with the row
+ * return true;
+ * })
+ * ->close();
+ * @endcode
+ */
+ inline Stmt& fetchEach(std::function cb)
+ {
+ do {
+ auto res = _Fetch();
+ if (res.size() == 0) {
+ continue;
+ }
+ if (!cb(res)) {
+ break;
+ }
+ } while (step());
+ return *this;
+ }
+
+ /**
+ * @brief Fetch each of the result rows(For compatibility).
+ *
+ * @param cb Callback function to handle the result rows
+ * @return Stmt& *this
+ * @note Return false in callback to stop fetching
+ * @see Stmt::fetchEach
+ */
+ inline Stmt& fetchAll(std::function cb)
+ {
+ return fetchEach(cb);
+ }
+ //virtual Stmt& fetchAll(std::function cb);
+
+ /**
+ * @brief Fetch all the result rows.
+ *
+ * @tparam T The value type of vector
+ * @param[out] rows The result set
+ * @return Stmt& *this
+ */
+ template
+ inline Stmt& fetchAll(std::vector& rows) {
+ return fetchEach([&](const Row& row) {
+ rows.push_back(row_to(row));
+ return true;
+ });
+ return *this;
+ }
+
+ /**
+ * @brief Fetch all the result rows.
+ *
+ * @tparam T The value type of vector
+ * @return std::vector The result rows
+ */
+ template
+ inline std::vector fetchAll() {
+ std::vector result;
+ fetchAll(result);
+ return result;
+ }
+ //virtual ResultSet fetchAll() = 0;
+ //virtual Stmt& fetchAll(ResultSet& rows);
+
+ inline ResultSet fetchAll()
+ {
+ ResultSet set;
+ fetchAll(set);
+ return set;
+ }
+
+ inline Stmt& fetchAll(ResultSet& rows)
+ {
+ return fetchEach([&rows](const Row& row) {
+ rows.push_back(row);
+ return true;
+ });
+ }
+
+ /**
+ * @brief Reset the statement from executing state to perpared state
+ *
+ * @return Stmt& *this
+ *
+ * @par Note
+ * Different between `reset()`, `reexec` and `clear()`:
+ * - `reset()` : Reset the statement to the prepared state
+ * - `reexec()`: Reset the statement to the prepared state and execute it
+ * - `clear()` : Reset the statement to the prepared state and clear the parameters, but not execute it
+ */
+ virtual Stmt& reset() = 0;
+
+ /**
+ * @brief Re-execute the statement(keep the currently bound value to re-excute).
+ *
+ * @return Stmt& *this
+ * @note If you want to clear the bound value, use clear() instead.
+ * @see Stmt::reset
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::reexec
+ */
+ virtual Stmt& reexec() = 0;
+
+ /**
+ * @brief Clear all the bound values.
+ *
+ * @return Stmt& *this
+ * @see Stmt::reset
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::clear
+ */
+ virtual Stmt& clear() = 0;
+
+ /**
+ * @brief Close the statement.
+ *
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::close
+ */
+ virtual void close() = 0;
+
+ /**
+ * @brief Get the number of rows affected by the statement.
+ *
+ * @return int The number of rows affected
+ * @note It will return -1(ULLONG_MAX - 1) if the row count is not available
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::getAffectedRows
+ */
+ virtual uint64_t getAffectedRows() const = 0;
+
+ /**
+ * @brief Get the insert id of the statement
+ *
+ * @return uint64_t The insert id
+ * @throws std::runtime_error If error occurs
+ * @note It will return -1(ULLONG_MAX - 1) if the insert id is not available
+ *
+ * @par Implementation
+ * @see SQLiteStmt::getInsertId
+ */
+ virtual uint64_t getInsertId() const = 0;
+
+ /**
+ * @brief Get the number of the unbound parameters.
+ *
+ * @return int The number of the unbound parameters
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::getUnboundParams
+ */
+ virtual int getUnboundParams() const = 0;
+
+ /**
+ * @brief Get the number of the bound parameters.
+ *
+ * @return int The number of the bound parameters
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::getBoundParams
+ */
+ virtual int getBoundParams() const = 0;
+
+ /**
+ * @brief Get the number of parameters.
+ *
+ * @return int The number of parameters
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::getParamsCount
+ */
+ virtual int getParamsCount() const = 0;
+
+ /**
+ * @brief Get the session.
+ *
+ * @return std::weak_ptr The session ptr
+ */
+ virtual std::weak_ptr getParent() const;
+
+ /**
+ * @brief Get the shared pointer point to this
+ *
+ * @return SharedPointer The ptr
+ */
+ virtual SharedPointer getSharedPointer() const;
+
+ /**
+ * @brief Get the session type
+ *
+ * @return DB::DBType The database type
+ *
+ * @par Impletementation
+ * @see SQLiteStmt::getType
+ */
+ virtual DBType getType() const = 0;
+
+
+ /**
+ * @brief Fetch the current row(internal).
+ *
+ * @return Row The current row
+ */
+ virtual Row _Fetch() = 0;
+
+
+ /**
+ * @brief Operator<< to bind values.
+ *
+ * @param v The value
+ * @return SharedPointer this
+ */
+ inline SharedPointer operator<<(const Any& v)
+ {
+ bind(v);
+ return getSharedPointer();
+ }
+
+ /**
+ * @brief Operator>> to store the result.
+ *
+ * @tparam T The value type
+ * @param v Where to store
+ * @return SharedPointer this
+ */
+ template
+ inline SharedPointer operator>>(T& v)
+ {
+ fetch(v);
+ return getSharedPointer();
+ }
+ template <>
+ inline SharedPointer operator>>(ResultSet& v)
+ {
+ fetchAll(v);
+ return getSharedPointer();
+ }
+ template
+ inline SharedPointer operator>>(std::vector& v)
+ {
+ fetchAll(v);
+ return getSharedPointer();
+ }
+
+ /**
+ * @brief Operator, to bind single values.
+ *
+ * @param b The return value of DB::use
+ * @return SharedPointer this
+ */
+ virtual SharedPointer operator,(const BindType& b);
+ /**
+ * @brief Operator, to bind a sequence container.
+ *
+ * @param b The return value of DB::use
+ * @return SharedPointer this
+ */
+ template
+ inline SharedPointer operator,(const BindSequenceType& b)
+ {
+ for (auto& v : b.values)
+ {
+ bind(v);
+ }
+ return getSharedPointer();
+ }
+ /**
+ * @brief Operator, to bind a row.
+ *
+ * @param b The return value of DB::use
+ * @return SharedPointer this
+ */
+ template <>
+ inline SharedPointer operator,(const BindSequenceType& b)
+ {
+ if (b.values.header && b.values.header->size())
+ {
+ b.values.forEach([&](const std::string& name, const Any& value) {
+ bind(value, name);
+ return true;
+ });
+ }
+ else
+ {
+ for (auto& v : b.values)
+ {
+ bind(v);
+ }
+ }
+ return getSharedPointer();
+ }
+ /**
+ * @brief Operator, to bind a map container.
+ *
+ * @param b The return value of DB::bind
+ * @return SharedPointer this
+ */
+ template
+ inline SharedPointer operator,(const BindMapType& b)
+ {
+ for (auto& v : b.values)
+ {
+ bind(v.second, v.first);
+ }
+ return getSharedPointer();
+ }
+ /**
+ * @brief Operator, to store a row of results.
+ *
+ * @param i The return value of DB::into
+ * @return SharedPointer this
+ */
+ template
+ inline SharedPointer operator,(IntoType& i)
+ {
+ if (!done()) fetch(i.value);
+ return getSharedPointer();
+ }
+ /**
+ * @brief Operator, to store a set of results.
+ *
+ * @param i The return value of DB::into
+ * @return SharedPointer this
+ */
+ template
+ inline SharedPointer operator,(IntoType>& i)
+ {
+ fetchAll>(i.value);
+ return getSharedPointer();
+ }
+ /**
+ * @brief Operator, to store a set of results.
+ *
+ * @param i The return value of DB::into
+ * @return SharedPointer this
+ */
+ template <>
+ inline SharedPointer operator,(IntoType& i)
+ {
+ fetchAll(i.value);
+ return getSharedPointer();
+ }
+ /**
+ * @brief Operator, to store a row of results.
+ *
+ * @param i The return value of DB::into
+ * @return SharedPointer this
+ */
+ template <>
+ inline SharedPointer operator,(IntoType& i)
+ {
+ fetch(i.value);
+ return getSharedPointer();
+ }
+
+ /**
+ * @brief Operator-> to implement better API.
+ *
+ * @return Stmt* this
+ */
+ inline Stmt* operator->()
+ {
+ return this;
+ }
+};
+
+inline BindType use(const Any& value, int idx = -1)
+{
+ return BindType{value, std::string(), idx};
+}
+inline BindType use(const Any& value, const std::string& name)
+{
+ return BindType{value, name};
+}
+inline BindSequenceType use(const Row& values)
+{
+ return BindSequenceType{values};
+}
+
+
+template
+inline BindSequenceType> use(const std::vector& values)
+{
+ return BindSequenceType>{to_any_container(values)};
+}
+template
+inline BindSequenceType> use(const std::set& values)
+{
+ return BindSequenceType>{to_any_container(values)};
+}
+template
+inline BindSequenceType> use(const std::list& values)
+{
+ return BindSequenceType>{to_any_container(values)};
+}
+template
+inline BindSequenceType> use(const std::initializer_list& values)
+{
+ return BindSequenceType>{to_any_container(std::vector(values))};
+}
+template <>
+inline BindSequenceType> use(const std::vector& values)
+{
+ return BindSequenceType>{values};
+}
+template <>
+inline BindSequenceType> use(const std::set& values)
+{
+ return BindSequenceType>{values};
+}
+template <>
+inline BindSequenceType> use(const std::list& values)
+{
+ return BindSequenceType>{values};
+}
+template <>
+inline BindSequenceType> use(const std::initializer_list& values)
+{
+ return BindSequenceType>{std::vector(values)};
+}
+
+
+// Map
+template
+inline BindMapType> use(const std::map& values)
+{
+ return BindMapType>{values};
+}
+template