Compare commits
No commits in common. "MultiLoader_1_20_1" and "v0.4.1" have entirely different histories.
MultiLoade
...
v0.4.1
207
.github/workflows/build.yml
vendored
Normal file
207
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Prepare release files
|
||||
run: |
|
||||
mkdir -p release-files
|
||||
cp build/libs/*.jar release-files/ 2>/dev/null || true
|
||||
echo "准备发布的文件:"
|
||||
ls -la release-files/
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-files
|
||||
path: release-files/
|
||||
retention-days: 7
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
||||
steps:
|
||||
- name: Checkout with full history
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: release-files
|
||||
path: ./dist
|
||||
|
||||
- name: Generate CZ-compliant changelog
|
||||
id: generate_changelog
|
||||
run: |
|
||||
CURRENT_TAG="${{ github.ref_name }}"
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 $(git rev-list --tags --skip=1 --max-count=1) 2>/dev/null || echo "")
|
||||
|
||||
# 创建临时文件
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
echo "# 🚀 版本 $CURRENT_TAG 发布" > $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "## 📋 变更摘要" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
echo "### 初始版本发布" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "这是项目的第一个正式版本。" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
# 获取所有提交并按类型分组
|
||||
git log --pretty=format:"%s" --reverse | while read -r line; do
|
||||
echo "- $line" >> $TEMP_FILE
|
||||
done
|
||||
else
|
||||
echo "### 从 $PREV_TAG 到 $CURRENT_TAG 的变更" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
# 定义符合CZ规范的提交类型映射
|
||||
declare -A commit_types
|
||||
commit_types=(
|
||||
["✨ 新功能"]="^(feat|feature)(\(.*\))?:"
|
||||
["🐛 修复"]="^(fix|bugfix)(\(.*\))?:"
|
||||
["📝 文档"]="^(docs|documentation)(\(.*\))?:"
|
||||
["🎨 样式"]="^(style)(\(.*\))?:"
|
||||
["🔨 重构"]="^(refactor)(\(.*\))?:"
|
||||
["⚡️ 性能"]="^(perf|performance)(\(.*\))?:"
|
||||
["✅ 测试"]="^(test)(\(.*\))?:"
|
||||
["🔧 构建"]="^(build)(\(.*\))?:"
|
||||
["👷 CI"]="^(ci)(\(.*\))?:"
|
||||
["📦 依赖"]="^(chore|deps)(\(.*\))?:"
|
||||
["⏪ 回退"]="^(revert)(\(.*\))?:"
|
||||
)
|
||||
|
||||
# 获取所有提交
|
||||
COMMITS=$(git log --pretty=format:"%s" $PREV_TAG..HEAD)
|
||||
|
||||
# 处理每种类型的提交
|
||||
for type_name in "${!commit_types[@]}"; do
|
||||
pattern="${commit_types[$type_name]}"
|
||||
|
||||
# 提取匹配的提交
|
||||
matched_commits=$(echo "$COMMITS" | grep -E "$pattern" || true)
|
||||
|
||||
if [ -n "$matched_commits" ]; then
|
||||
echo "#### $type_name" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
# 处理每条提交,提取scope和subject
|
||||
echo "$matched_commits" | while read -r commit; do
|
||||
# 解析scope和subject
|
||||
if [[ $commit =~ ^[a-z]+\((.*)\):\ (.*) ]]; then
|
||||
scope="${BASH_REMATCH[1]}"
|
||||
subject="${BASH_REMATCH[2]}"
|
||||
echo "- **$scope**: $subject" >> $TEMP_FILE
|
||||
elif [[ $commit =~ ^[a-z]+:\ (.*) ]]; then
|
||||
subject="${BASH_REMATCH[1]}"
|
||||
echo "- $subject" >> $TEMP_FILE
|
||||
else
|
||||
echo "- $commit" >> $TEMP_FILE
|
||||
fi
|
||||
done
|
||||
echo "" >> $TEMP_FILE
|
||||
fi
|
||||
done
|
||||
|
||||
# 处理破坏性变更(BREAKING CHANGE)
|
||||
breaking_changes=$(git log --pretty=format:"%b" $PREV_TAG..HEAD | grep -i "BREAKING CHANGE" || true)
|
||||
if [ -n "$breaking_changes" ]; then
|
||||
echo "#### ⚠️ 破坏性变更" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "$breaking_changes" | while read -r line; do
|
||||
echo "- $line" >> $TEMP_FILE
|
||||
done
|
||||
echo "" >> $TEMP_FILE
|
||||
fi
|
||||
|
||||
# 处理未分类的提交
|
||||
uncategorized="$COMMITS"
|
||||
for pattern in "${commit_types[@]}"; do
|
||||
uncategorized=$(echo "$uncategorized" | grep -v -E "$pattern" || true)
|
||||
done
|
||||
|
||||
if [ -n "$uncategorized" ]; then
|
||||
echo "#### 📝 其他更改" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "$uncategorized" | while read -r commit; do
|
||||
echo "- $commit" >> $TEMP_FILE
|
||||
done
|
||||
echo "" >> $TEMP_FILE
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "## 📊 统计信息" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
TOTAL_COMMITS=$(git rev-list --count HEAD)
|
||||
echo "- 总提交数: $TOTAL_COMMITS" >> $TEMP_FILE
|
||||
echo "- 首次发布" >> $TEMP_FILE
|
||||
else
|
||||
COMMITS=$(git rev-list --count $PREV_TAG..HEAD)
|
||||
echo "- 本次发布提交数: $COMMITS" >> $TEMP_FILE
|
||||
echo "- 上一个版本: $PREV_TAG" >> $TEMP_FILE
|
||||
fi
|
||||
|
||||
echo "- 发布日期: $(date '+%Y年%m月%d日')" >> $TEMP_FILE
|
||||
echo "- 当前版本: $CURRENT_TAG" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "---" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "### 📜 详细提交历史" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
# 显示所有提交的详细列表,包含scope信息
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
git log --pretty=format:"- **%h** %s - %an (%ad)" --date=short --reverse | head -100 >> $TEMP_FILE
|
||||
else
|
||||
git log --pretty=format:"- **%h** %s - %an (%ad)" --date=short $PREV_TAG..HEAD | head -100 >> $TEMP_FILE
|
||||
fi
|
||||
|
||||
# 将文件内容输出到变量
|
||||
CHANGELOG_CONTENT=$(cat $TEMP_FILE)
|
||||
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: "dist/*.jar"
|
||||
tag: ${{ github.ref_name }}
|
||||
name: "版本 ${{ github.ref_name }}"
|
||||
body: ${{ steps.generate_changelog.outputs.changelog }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
454
.github/workflows/buildAndRelease.yml
vendored
454
.github/workflows/buildAndRelease.yml
vendored
|
|
@ -1,454 +0,0 @@
|
|||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Run Forge data generation
|
||||
run: |
|
||||
echo "=== 运行 Forge 数据生成 ==="
|
||||
./gradlew runData --no-daemon
|
||||
|
||||
continue-on-error: false
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build --no-daemon
|
||||
|
||||
- name: Prepare release files
|
||||
run: |
|
||||
mkdir -p release-files
|
||||
|
||||
# 收集所有模块的构建产物
|
||||
echo "=== 收集 common 模块构建产物 ==="
|
||||
if [ -d "common/build/libs" ]; then
|
||||
cp common/build/libs/*.jar release-files/ 2>/dev/null || echo "common 模块没有 jar 文件"
|
||||
fi
|
||||
|
||||
echo "=== 收集 fabric 模块构建产物 ==="
|
||||
if [ -d "fabric/build/libs" ]; then
|
||||
cp fabric/build/libs/*-dev.jar release-files/ 2>/dev/null || true # 排除dev jar
|
||||
cp fabric/build/libs/*-sources.jar release-files/ 2>/dev/null || true
|
||||
cp fabric/build/libs/*-javadoc.jar release-files/ 2>/dev/null || true
|
||||
# 只复制主jar(没有sources/javadoc/dev classifier的jar)
|
||||
find fabric/build/libs -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" ! -name "*-dev.jar" -exec cp {} release-files/ \;
|
||||
fi
|
||||
|
||||
echo "=== 收集 forge 模块构建产物 ==="
|
||||
if [ -d "forge/build/libs" ]; then
|
||||
cp forge/build/libs/*-sources.jar release-files/ 2>/dev/null || true
|
||||
cp forge/build/libs/*-javadoc.jar release-files/ 2>/dev/null || true
|
||||
# 只复制主jar(没有sources/javadoc classifier的jar)
|
||||
find forge/build/libs -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" -exec cp {} release-files/ \;
|
||||
fi
|
||||
|
||||
echo "=== 准备发布的文件 ==="
|
||||
ls -la release-files/
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-files
|
||||
path: release-files/
|
||||
retention-days: 7
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
||||
steps:
|
||||
- name: Checkout with full history
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Determine version type
|
||||
id: version_type
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == *"alpha"* ]]; then
|
||||
echo "type=alpha" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.ref_name }}" == *"beta"* ]]; then
|
||||
echo "type=beta" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.ref_name }}" == *"rc"* ]]; then
|
||||
echo "type=beta" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "type=release" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: release-files
|
||||
path: ./dist
|
||||
|
||||
- name: Extract version info
|
||||
id: version_info
|
||||
run: |
|
||||
# 从tag中提取版本号(去掉v前缀)
|
||||
VERSION="${GITHUB_REF_NAME#v}"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "minecraft_version=$(grep "^minecraft_version=" gradle.properties | cut -d'=' -f2)" >> $GITHUB_OUTPUT
|
||||
# 从 gradle.properties 提取 mod_id,如果没有则尝试从文件名推断
|
||||
MOD_ID=$(grep "^mod_id=" gradle.properties | cut -d'=' -f2 || echo "")
|
||||
if [ -z "$MOD_ID" ]; then
|
||||
# 尝试从现有的 jar 文件名提取 mod_id
|
||||
SAMPLE_JAR=$(ls dist/ | grep -m 1 -E ".*-(fabric|forge)-.*\.jar" || echo "")
|
||||
if [ -n "$SAMPLE_JAR" ]; then
|
||||
MOD_ID=$(echo "$SAMPLE_JAR" | sed -E 's/-(fabric|forge)-.*//')
|
||||
else
|
||||
MOD_ID="mymod" # 默认值,请根据实际情况修改
|
||||
fi
|
||||
fi
|
||||
echo "mod_id=$MOD_ID" >> $GITHUB_OUTPUT
|
||||
|
||||
# 从 gradle.properties 提取 mod_name(用于显示)
|
||||
MOD_NAME=$(grep "^mod_name=" gradle.properties | cut -d'=' -f2 || echo "My Mod")
|
||||
echo "mod_name=$MOD_NAME" >> $GITHUB_OUTPUT
|
||||
# 从 gradle.properties 提取 modrinth_id
|
||||
MODRINTH_ID=$(grep "^modrinth_id=" gradle.properties | cut -d'=' -f2 || echo "")
|
||||
echo "modrinth_id=$MODRINTH_ID" >> $GITHUB_OUTPUT
|
||||
# 从 gradle.properties 提取 curseforge_id
|
||||
CURSEFORGE_ID=$(grep "^curseforge_id=" gradle.properties | cut -d'=' -f2 || echo "")
|
||||
echo "curseforge_id=$CURSEFORGE_ID" >> $GITHUB_OUTPUT
|
||||
|
||||
# Java版本 - 使用简单格式,不用JSON
|
||||
JAVA_VERSIONS=$(grep "^java_versions=" gradle.properties | cut -d'=' -f2- || echo "21,17")
|
||||
# 清理格式,移除无效字符
|
||||
JAVA_VERSIONS=$(echo "$JAVA_VERSIONS" | sed 's/\[//g; s/\]//g; s/"//g; s/ //g; s/21a/21/g' | tr -d '\r')
|
||||
echo "java_versions=$JAVA_VERSIONS" >> $GITHUB_OUTPUT
|
||||
|
||||
# 读取发布控制布尔值(默认都为 true)
|
||||
PUBLISH_GITHUB=$(grep "^publish_github=" gradle.properties | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]' | tr -d ' ' || echo "true")
|
||||
if [ "$PUBLISH_GITHUB" = "true" ] || [ "$PUBLISH_GITHUB" = "1" ] || [ "$PUBLISH_GITHUB" = "yes" ]; then
|
||||
echo "publish_github=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "publish_github=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
PUBLISH_MODRINTH=$(grep "^publish_modrinth=" gradle.properties | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]' | tr -d ' ' || echo "true")
|
||||
if [ "$PUBLISH_MODRINTH" = "true" ] || [ "$PUBLISH_MODRINTH" = "1" ] || [ "$PUBLISH_MODRINTH" = "yes" ]; then
|
||||
echo "publish_modrinth=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "publish_modrinth=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
PUBLISH_CURSEFORGE=$(grep "^publish_curseforge=" gradle.properties | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]' | tr -d ' ' || echo "true")
|
||||
if [ "$PUBLISH_CURSEFORGE" = "true" ] || [ "$PUBLISH_CURSEFORGE" = "1" ] || [ "$PUBLISH_CURSEFORGE" = "yes" ]; then
|
||||
echo "publish_curseforge=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "publish_curseforge=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
# 读取依赖配置 - 使用简单字符串,不用JSON
|
||||
FABRIC_MODRINTH_DEPS=$(grep "^fabric_modrinth_dependencies=" gradle.properties | cut -d'=' -f2- || echo "")
|
||||
echo "fabric_modrinth_dependencies=$FABRIC_MODRINTH_DEPS" >> $GITHUB_OUTPUT
|
||||
|
||||
FORGE_MODRINTH_DEPS=$(grep "^forge_modrinth_dependencies=" gradle.properties | cut -d'=' -f2- || echo "")
|
||||
echo "forge_modrinth_dependencies=$FORGE_MODRINTH_DEPS" >> $GITHUB_OUTPUT
|
||||
|
||||
FABRIC_CURSEFORGE_DEPS=$(grep "^fabric_curseforge_dependencies=" gradle.properties | cut -d'=' -f2- || echo "")
|
||||
echo "fabric_curseforge_dependencies=$FABRIC_CURSEFORGE_DEPS" >> $GITHUB_OUTPUT
|
||||
|
||||
FORGE_CURSEFORGE_DEPS=$(grep "^forge_curseforge_dependencies=" gradle.properties | cut -d'=' -f2- || echo "")
|
||||
echo "forge_curseforge_dependencies=$FORGE_CURSEFORGE_DEPS" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate CZ-compliant changelog
|
||||
id: generate_changelog
|
||||
run: |
|
||||
CURRENT_TAG="${{ github.ref_name }}"
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 $(git rev-list --tags --skip=1 --max-count=1) 2>/dev/null || echo "")
|
||||
|
||||
# 创建临时文件
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
echo "# 🚀 版本 $CURRENT_TAG 发布" > $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "## 📋 变更摘要" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
echo "### 初始版本发布" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "这是项目的第一个正式版本。" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
# 获取所有提交并按类型分组
|
||||
git log --pretty=format:"%s" --reverse | while read -r line; do
|
||||
echo "- $line" >> $TEMP_FILE
|
||||
done
|
||||
else
|
||||
echo "### 从 $PREV_TAG 到 $CURRENT_TAG 的变更" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
# 定义符合CZ规范的提交类型映射
|
||||
declare -A commit_types
|
||||
commit_types=(
|
||||
["✨ 新功能"]="^(feat|feature)(\(.*\))?:"
|
||||
["🐛 修复"]="^(fix|bugfix)(\(.*\))?:"
|
||||
["📝 文档"]="^(docs|documentation)(\(.*\))?:"
|
||||
["🎨 样式"]="^(style)(\(.*\))?:"
|
||||
["🔨 重构"]="^(refactor)(\(.*\))?:"
|
||||
["⚡️ 性能"]="^(perf|performance)(\(.*\))?:"
|
||||
["✅ 测试"]="^(test)(\(.*\))?:"
|
||||
["🔧 构建"]="^(build)(\(.*\))?:"
|
||||
["👷 CI"]="^(ci)(\(.*\))?:"
|
||||
["📦 依赖"]="^(chore|deps)(\(.*\))?:"
|
||||
["⏪ 回退"]="^(revert)(\(.*\))?:"
|
||||
["🛠 合并"]="^Merge "
|
||||
)
|
||||
|
||||
# 获取所有提交
|
||||
COMMITS=$(git log --pretty=format:"%s" $PREV_TAG..HEAD)
|
||||
|
||||
# 处理每种类型的提交
|
||||
for type_name in "${!commit_types[@]}"; do
|
||||
pattern="${commit_types[$type_name]}"
|
||||
|
||||
# 提取匹配的提交
|
||||
matched_commits=$(echo "$COMMITS" | grep -E "$pattern" || true)
|
||||
|
||||
if [ -n "$matched_commits" ]; then
|
||||
echo "#### $type_name" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
# 处理每条提交,提取scope和subject
|
||||
echo "$matched_commits" | while read -r commit; do
|
||||
# 解析scope和subject
|
||||
if [[ $commit =~ ^[a-z]+\((.*)\):\ (.*) ]]; then
|
||||
scope="${BASH_REMATCH[1]}"
|
||||
subject="${BASH_REMATCH[2]}"
|
||||
echo "- **$scope**: $subject" >> $TEMP_FILE
|
||||
elif [[ $commit =~ ^[a-z]+:\ (.*) ]]; then
|
||||
subject="${BASH_REMATCH[1]}"
|
||||
echo "- $subject" >> $TEMP_FILE
|
||||
else
|
||||
echo "- $commit" >> $TEMP_FILE
|
||||
fi
|
||||
done
|
||||
echo "" >> $TEMP_FILE
|
||||
fi
|
||||
done
|
||||
|
||||
# 处理破坏性变更(BREAKING CHANGE)
|
||||
breaking_changes=$(git log --pretty=format:"%b" $PREV_TAG..HEAD | grep -i "BREAKING CHANGE" || true)
|
||||
if [ -n "$breaking_changes" ]; then
|
||||
echo "#### ⚠️ 破坏性变更" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "$breaking_changes" | while read -r line; do
|
||||
echo "- $line" >> $TEMP_FILE
|
||||
done
|
||||
echo "" >> $TEMP_FILE
|
||||
fi
|
||||
|
||||
# 处理未分类的提交
|
||||
uncategorized="$COMMITS"
|
||||
for pattern in "${commit_types[@]}"; do
|
||||
uncategorized=$(echo "$uncategorized" | grep -v -E "$pattern" || true)
|
||||
done
|
||||
|
||||
if [ -n "$uncategorized" ]; then
|
||||
echo "#### 📝 其他更改" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "$uncategorized" | while read -r commit; do
|
||||
echo "- $commit" >> $TEMP_FILE
|
||||
done
|
||||
echo "" >> $TEMP_FILE
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "## 📊 统计信息" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
TOTAL_COMMITS=$(git rev-list --count HEAD)
|
||||
echo "- 总提交数: $TOTAL_COMMITS" >> $TEMP_FILE
|
||||
echo "- 首次发布" >> $TEMP_FILE
|
||||
else
|
||||
COMMITS=$(git rev-list --count $PREV_TAG..HEAD)
|
||||
echo "- 本次发布提交数: $COMMITS" >> $TEMP_FILE
|
||||
echo "- 上一个版本: $PREV_TAG" >> $TEMP_FILE
|
||||
fi
|
||||
|
||||
echo "- 发布日期: $(date '+%Y年%m月%d日')" >> $TEMP_FILE
|
||||
echo "- 当前版本: $CURRENT_TAG" >> $TEMP_FILE
|
||||
echo "- Minecraft版本: ${{ steps.version_info.outputs.minecraft_version }}" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "---" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
|
||||
echo "### 📜 详细提交历史" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "<details>" >> $TEMP_FILE
|
||||
echo "<summary>点击展开查看完整提交历史</summary>" >> $TEMP_FILE
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "\`\`\`" >> $TEMP_FILE
|
||||
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
# 使用 while 循环确保每条提交独立一行
|
||||
git log --pretty=format:"%h %s - %an (%ad)" --date=short --reverse | while IFS= read -r line; do
|
||||
echo "$line" >> $TEMP_FILE
|
||||
done
|
||||
else
|
||||
git log --pretty=format:"%h %s - %an (%ad)" --date=short $PREV_TAG..HEAD | while IFS= read -r line; do
|
||||
echo "$line" >> $TEMP_FILE
|
||||
done
|
||||
fi
|
||||
|
||||
# 确保文件末尾有换行
|
||||
echo "" >> $TEMP_FILE
|
||||
echo "\`\`\`" >> $TEMP_FILE
|
||||
echo "</details>" >> $TEMP_FILE
|
||||
|
||||
# 将文件内容输出到变量
|
||||
CHANGELOG_CONTENT=$(cat $TEMP_FILE)
|
||||
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Release
|
||||
if: steps.version_info.outputs.publish_github == 'true'
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
artifacts: |
|
||||
dist/*.jar
|
||||
tag: ${{ github.ref_name }}
|
||||
name: "${{ steps.version_info.outputs.minecraft_version }} - ${{ github.ref_name }}"
|
||||
body: ${{ steps.generate_changelog.outputs.changelog }}
|
||||
draft: false
|
||||
prerelease: ${{ contains(github.ref_name, 'rc') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowUpdates: true
|
||||
removeArtifacts: true
|
||||
# Fabric 发布到 Modrinth 和 CurseForge
|
||||
- name: Publish Fabric to Modrinth & CurseForge
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
if: success() && (steps.version_info.outputs.publish_modrinth == 'true' || steps.version_info.outputs.publish_curseforge == 'true')
|
||||
continue-on-error: true
|
||||
with:
|
||||
# 文件匹配规则 - 只匹配 fabric 的文件
|
||||
files: |
|
||||
dist/${{ steps.version_info.outputs.mod_id }}-fabric-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}.jar
|
||||
dist/${{ steps.version_info.outputs.mod_id }}-fabric-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}-javadoc.jar
|
||||
dist/${{ steps.version_info.outputs.mod_id }}-fabric-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}-sources.jar
|
||||
|
||||
# 版本信息
|
||||
name: ${{ steps.version_info.outputs.mod_name }} ${{ steps.version_info.outputs.version }} (Fabric/${{ steps.version_info.outputs.minecraft_version }})
|
||||
version: "${{ steps.version_info.outputs.minecraft_version }}-fabric-${{ steps.version_info.outputs.version }}"
|
||||
|
||||
# 更新日志
|
||||
changelog: ${{ steps.generate_changelog.outputs.changelog }}
|
||||
|
||||
# 版本类型
|
||||
version-type: ${{ steps.version_type.outputs.type }}
|
||||
|
||||
# 只指定 Fabric 加载器
|
||||
loaders: fabric
|
||||
|
||||
# 游戏版本
|
||||
game-versions: |
|
||||
${{ steps.version_info.outputs.minecraft_version }}
|
||||
|
||||
# Java版本
|
||||
java: |
|
||||
${{ steps.version_info.outputs.java_versions }}
|
||||
|
||||
# Modrinth 配置
|
||||
modrinth-id: ${{ steps.version_info.outputs.modrinth_id }}
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
modrinth-featured: true
|
||||
modrinth-unfeature-mode: any
|
||||
modrinth-dependencies: ${{ steps.version_info.outputs.fabric_modrinth_dependencies }}
|
||||
# CurseForge 配置
|
||||
curseforge-id: ${{ steps.version_info.outputs.curseforge_id }}
|
||||
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
curseforge-dependencies: ${{ steps.version_info.outputs.fabric_curseforge_dependencies }}
|
||||
|
||||
# 失败处理
|
||||
fail-mode: skip
|
||||
|
||||
# Forge 发布到 Modrinth 和 CurseForge
|
||||
- name: Publish Forge to Modrinth & CurseForge
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
if: success() && (steps.version_info.outputs.publish_modrinth == 'true' || steps.version_info.outputs.publish_curseforge == 'true')
|
||||
continue-on-error: true
|
||||
with:
|
||||
# 文件匹配规则 - 只匹配 forge 的文件
|
||||
files: |
|
||||
dist/${{ steps.version_info.outputs.mod_id }}-forge-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}.jar
|
||||
dist/${{ steps.version_info.outputs.mod_id }}-forge-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}-javadoc.jar
|
||||
dist/${{ steps.version_info.outputs.mod_id }}-forge-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}-sources.jar
|
||||
|
||||
# 版本信息
|
||||
name: ${{ steps.version_info.outputs.mod_name }} ${{ steps.version_info.outputs.version }} (Forge/${{ steps.version_info.outputs.minecraft_version }})
|
||||
version: "${{ steps.version_info.outputs.minecraft_version }}-forge-${{ steps.version_info.outputs.version }}"
|
||||
|
||||
# 更新日志
|
||||
changelog: ${{ steps.generate_changelog.outputs.changelog }}
|
||||
|
||||
# 版本类型
|
||||
version-type: ${{ steps.version_type.outputs.type }}
|
||||
|
||||
# 只指定 Forge 加载器
|
||||
loaders: forge
|
||||
|
||||
# 游戏版本
|
||||
game-versions: |
|
||||
${{ steps.version_info.outputs.minecraft_version }}
|
||||
|
||||
# Java版本
|
||||
java: |
|
||||
${{ steps.version_info.outputs.java_versions }}
|
||||
|
||||
# Modrinth 配置
|
||||
modrinth-id: ${{ steps.version_info.outputs.modrinth_id }}
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
modrinth-featured: true
|
||||
modrinth-unfeature-mode: any
|
||||
modrinth-dependencies: ${{ steps.version_info.outputs.forge_modrinth_dependencies }}
|
||||
|
||||
# CurseForge 配置
|
||||
curseforge-id: ${{ steps.version_info.outputs.curseforge_id }}
|
||||
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
curseforge-dependencies: ${{ steps.version_info.outputs.forge_curseforge_dependencies }}
|
||||
|
||||
# 失败处理
|
||||
fail-mode: skip
|
||||
|
||||
# 发布完成后列出结果
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## 发布结果摘要" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### GitHub Release" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- 标签: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- URL: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Modrinth" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- 项目ID: ${{ steps.version_info.outputs.modrinth_id }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Fabric版本: ${{ steps.version_info.outputs.version }}-fabric" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Forge版本: ${{ steps.version_info.outputs.version }}-forge" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### CurseForge" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- 项目ID: ${{ steps.version_info.outputs.curseforge_id }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Fabric版本: ${{ steps.version_info.outputs.version }}-fabric" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Forge版本: ${{ steps.version_info.outputs.version }}-forge" >> $GITHUB_STEP_SUMMARY
|
||||
30
.github/workflows/styleCheck.yml
vendored
30
.github/workflows/styleCheck.yml
vendored
|
|
@ -1,30 +0,0 @@
|
|||
name: Check Style in Pull Request
|
||||
on:
|
||||
pull_request_target:
|
||||
|
||||
jobs:
|
||||
checkstyle:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
checks: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.number }}/merge
|
||||
- name: Setup Java 17
|
||||
uses: actions/setup-java@v3.6.0
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 17
|
||||
- uses: reviewdog/action-setup@v1
|
||||
with:
|
||||
reviewdog_version: latest
|
||||
- name: download checkstyle
|
||||
run: curl -o checkstyle.jar -L https://github.com/checkstyle/checkstyle/releases/download/checkstyle-12.1.2/checkstyle-12.1.2-all.jar
|
||||
- name: checkstyle
|
||||
env:
|
||||
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: java -jar checkstyle.jar -c style.xml -f xml */src | reviewdog -f=checkstyle -name="Checkstyle" -reporter=github-pr-review -fail-level=any
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -21,7 +21,6 @@ build
|
|||
# other
|
||||
eclipse
|
||||
run
|
||||
generated
|
||||
runs
|
||||
run-data
|
||||
|
||||
|
|
|
|||
45
README.md
45
README.md
|
|
@ -1,14 +1,10 @@
|
|||
# Lib39
|
||||
 []()
|
||||
|
||||
[](https://www.curseforge.com/minecraft/mc-mods/lib-39)
|
||||
[](https://modrinth.com/mod/lib-39)
|
||||
|
||||
**Lib39** is a general-purpose dependency library for Minecraft mods.
|
||||
It provides utility methods and core functionality that other mods can build upon.
|
||||
### How to implementation? ( Only for Version 0.5.0+ )
|
||||
### How to implementation?
|
||||
|
||||
#### **In repositories:**
|
||||
**In repositories:**
|
||||
|
||||
```groovy
|
||||
maven {
|
||||
|
|
@ -17,41 +13,10 @@ It provides utility methods and core functionality that other mods can build upo
|
|||
}
|
||||
```
|
||||
|
||||
#### **In dependencies:**
|
||||
##### General
|
||||
**gradle.properties**
|
||||
```properties
|
||||
lib39_version=0.5.1
|
||||
````
|
||||
##### For Loom
|
||||
**build.gradle**
|
||||
```groovy
|
||||
dependencies {
|
||||
modImplementation("top.r3944realms.lib39:lib39-fabric-1.20.1:${lib39_version}")
|
||||
}
|
||||
```
|
||||
**In dependencies:**
|
||||
|
||||
##### For ForgeGradle
|
||||
**build.gradle**
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation fg.deof("top.r3944realms.lib39:lib39-forge-1.20.1:${lib39_version}")
|
||||
implementation("top.r3944realms.lib39:lib39:1.20.1-0.4.1")
|
||||
}
|
||||
```
|
||||
|
||||
##### For NeoForgeGradle / ModDevGradle
|
||||
**build.gradle**
|
||||
```groovy
|
||||
dependencies {
|
||||
modImplementation("top.r3944realms.lib39:lib39-forge-1.20.1:${lib39_version}")
|
||||
}
|
||||
```
|
||||
|
||||
##### For MultiLoader Project
|
||||
Add this in your common subproject.
|
||||
**build.gradle**
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation("top.r3944realms.lib39:lib39-common-1.20.1:${lib39_version}")
|
||||
}
|
||||
```
|
||||
```
|
||||
24
TEMPLATE_LICENSE.txt
Normal file
24
TEMPLATE_LICENSE.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 NeoForged project
|
||||
|
||||
This license applies to the template files as supplied by github.com/NeoForged/MDK
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
537
build.gradle
537
build.gradle
|
|
@ -1,4 +1,535 @@
|
|||
//file:noinspection GroovyAssignabilityCheck
|
||||
plugins {
|
||||
id 'fabric-loom' version '1.9-SNAPSHOT' apply(false)
|
||||
id 'net.neoforged.moddev.legacyforge' version '2.0.103' apply(false)
|
||||
}
|
||||
id 'java'
|
||||
id 'idea'
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
id 'net.neoforged.moddev.legacyforge' version '2.0.103'
|
||||
id 'com.dorongold.task-tree' version '2.1.1'
|
||||
}
|
||||
|
||||
apply from: 'gradle/jni-heads.gradle'
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
|
||||
tasks.named('wrapper', Wrapper).configure {
|
||||
distributionType = Wrapper.DistributionType.BIN
|
||||
}
|
||||
|
||||
version = "${minecraft_version}-${mod_version}"
|
||||
group = mod_group_id
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven { url = "https://libraries.minecraft.net/" }
|
||||
maven { url = "https://neoforged.forgecdn.net/releases" }
|
||||
maven { url = "https://neoforged.forgecdn.net/mojang-meta" }
|
||||
maven { url = "https://maven.neoforged.net/releases" }
|
||||
maven {
|
||||
name = "LTD Maven"
|
||||
url = "https://nexus.bot.leisuretimedock.top/repository/maven-public/"
|
||||
}
|
||||
flatDir {
|
||||
dir "libs"
|
||||
}
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = mod_id
|
||||
}
|
||||
|
||||
// Mojang ships Java 17 to end users in 1.20.1, so mods should target Java 17.
|
||||
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
|
||||
|
||||
legacyForge {
|
||||
// Specify the version of MinecraftForge to use.
|
||||
version = project.minecraft_version + '-' + project.forge_version
|
||||
|
||||
parchment {
|
||||
mappingsVersion = project.parchment_mappings_version
|
||||
minecraftVersion = project.parchment_minecraft_version
|
||||
}
|
||||
|
||||
// Default run configurations.
|
||||
runs {
|
||||
client {
|
||||
client()
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
}
|
||||
clientAuth{
|
||||
devLogin = true
|
||||
client()
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
}
|
||||
|
||||
server {
|
||||
server()
|
||||
programArgument '--nogui'
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
}
|
||||
|
||||
gameTestServer {
|
||||
type = "gameTestServer"
|
||||
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
|
||||
}
|
||||
|
||||
data {
|
||||
data()
|
||||
programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
|
||||
}
|
||||
|
||||
configureEach {
|
||||
systemProperty 'forge.logging.markers', 'REGISTRIES'
|
||||
logLevel = org.slf4j.event.Level.DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
mods {
|
||||
"${mod_id}" {
|
||||
sourceSet(sourceSets.main)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include resources generated by data generators.
|
||||
sourceSets.main.resources { srcDir 'src/generated/resources' }
|
||||
|
||||
configurations {
|
||||
runtimeClasspath.extendsFrom localRuntime
|
||||
}
|
||||
obfuscation {
|
||||
createRemappingConfiguration(configurations.localRuntime)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(jarJar("io.github.llamalad7:mixinextras-forge:[0.4.1,)"))
|
||||
|
||||
implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1"))
|
||||
modImplementation(jarJar("io.github.llamalad7:mixinextras-forge:0.4.1"))
|
||||
modImplementation("blank:carryon-1.20.1:2.1.2.7")
|
||||
|
||||
implementation 'org.joml:joml:1.10.5'
|
||||
// 单元测试依赖
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.3'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.3'
|
||||
|
||||
// 断言库
|
||||
testImplementation 'org.assertj:assertj-core:3.24.2'
|
||||
|
||||
// Mock库(可选)
|
||||
testImplementation 'org.mockito:mockito-core:5.3.1'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter:5.3.1'
|
||||
|
||||
// 测试工具
|
||||
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||
}
|
||||
mixin {
|
||||
add sourceSets.main, "${mod_id}.refmap.json"
|
||||
config "${mod_id}.mixins.json"
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest.attributes([
|
||||
"MixinConfigs": "${mod_id}.mixins.json"
|
||||
])
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
|
||||
// 显示测试输出
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
showStandardStreams = true
|
||||
}
|
||||
|
||||
// 设置类路径
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
}
|
||||
|
||||
// This block of code expands all declared replace properties in the specified resource targets.
|
||||
var generateModMetadata = tasks.register("generateModMetadata", ProcessResources) {
|
||||
var replaceProperties = [
|
||||
minecraft_version : minecraft_version,
|
||||
minecraft_version_range : minecraft_version_range,
|
||||
forge_version : forge_version,
|
||||
forge_version_range : forge_version_range,
|
||||
loader_version_range : loader_version_range,
|
||||
mod_id : mod_id,
|
||||
mod_name : mod_name,
|
||||
mod_license : mod_license,
|
||||
mod_version : mod_version,
|
||||
mod_authors : mod_authors,
|
||||
mod_description : mod_description,
|
||||
mod_credits : mod_credits
|
||||
]
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
inputs.properties replaceProperties
|
||||
expand replaceProperties
|
||||
from "src/main/templates"
|
||||
into "build/generated/sources/modMetadata"
|
||||
}
|
||||
|
||||
sourceSets.main.resources.srcDir generateModMetadata
|
||||
legacyForge.ideSyncTask generateModMetadata
|
||||
|
||||
// ==================== Javadoc 配置 ====================
|
||||
javadoc {
|
||||
options {
|
||||
encoding = 'UTF-8'
|
||||
charSet = 'UTF-8'
|
||||
author = true
|
||||
version = true
|
||||
windowTitle = "Lib39 ${project.mod_version} API"
|
||||
docTitle = "Lib39 ${project.mod_version} API"
|
||||
memberLevel = JavadocMemberLevel.PROTECTED
|
||||
links = [
|
||||
'https://docs.oracle.com/javase/8/docs/api/'
|
||||
]
|
||||
addBooleanOption('Xdoclint:none', true)
|
||||
addBooleanOption('html5', true)
|
||||
}
|
||||
|
||||
// 确保有源代码可供生成文档
|
||||
if (sourceSets.main.allJava.files.any { it.exists() }) {
|
||||
source = sourceSets.main.allJava
|
||||
}
|
||||
classpath = configurations.compileClasspath
|
||||
exclude '**/test/**'
|
||||
exclude '**/internal/**'
|
||||
|
||||
// 确保输出目录存在
|
||||
doFirst {
|
||||
destinationDir.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('javadocJar', Jar) {
|
||||
archiveFileName = "${mod_id}-${minecraft_version}-${mod_version}-javadoc.jar"
|
||||
archiveClassifier.set("javadoc")
|
||||
from tasks.javadoc
|
||||
dependsOn tasks.javadoc
|
||||
}
|
||||
|
||||
tasks.register('sourceJar', Jar) {
|
||||
from(sourceSets.main.allSource) // java
|
||||
archiveFileName = "${mod_id}-${minecraft_version}-${mod_version}-sources.jar"
|
||||
archiveClassifier.set("sources")
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
manifest {
|
||||
attributes([
|
||||
'Specification-Title' : mod_id,
|
||||
'Specification-Vendor' : mod_authors,
|
||||
'Specification-Version' : '1',
|
||||
'Implementation-Title' : project.name,
|
||||
'Implementation-Version' : archiveVersion,
|
||||
'Implementation-Vendor' : mod_authors,
|
||||
'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
'MixinConfigs' : "${mod_id}.mixins.json"
|
||||
])
|
||||
}
|
||||
dependsOn classes
|
||||
}
|
||||
|
||||
|
||||
tasks.named('publish') {
|
||||
dependsOn build
|
||||
}
|
||||
|
||||
|
||||
// ==================== 发布配置 ====================
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifactId = mod_id
|
||||
artifact reobfJar
|
||||
artifact sourceJar
|
||||
artifact javadocJar
|
||||
|
||||
pom {
|
||||
name = 'Lib39'
|
||||
description = 'Lib39 is a general-purpose dependency library for Minecraft mods.'
|
||||
url = 'https://github.com/3944Realms/lib39'
|
||||
|
||||
properties = [
|
||||
'minecraft.version': project.minecraft_version,
|
||||
'mod.version': project.mod_version,
|
||||
'forge.version': project.forge_version,
|
||||
'java.version': '17'
|
||||
]
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = 'MIT'
|
||||
url = 'https://raw.githubusercontent.com/3944Realms/lib39/refs/heads/main/LICENSE'
|
||||
distribution = 'repo'
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'R3944Realms'
|
||||
name = "${mod_authors}"
|
||||
email = 'f256198830@hotmail.com'
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection = 'scm:git:https://github.com/3944Realms/lib39.git'
|
||||
developerConnection = 'scm:git:ssh://git@github.com:3944Realms/lib39.git'
|
||||
url = 'https://github.com/3944Realms/lib39'
|
||||
tag = 'main'
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system = 'GitHub'
|
||||
url = 'https://github.com/3944Realms/lib39/issues'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
// 本地仓库
|
||||
maven {
|
||||
name = 'local'
|
||||
url = layout.buildDirectory.dir("repo")
|
||||
}
|
||||
|
||||
// Nexus 远程仓库
|
||||
maven {
|
||||
name = 'LTDNexus'
|
||||
url = 'https://nexus.bot.leisuretimedock.top/repository/maven-releases/'
|
||||
credentials {
|
||||
username = System.getenv('LTDNexusUsername') ?: ''
|
||||
password = System.getenv('LTDNexusPassword') ?: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ==================== 任务配置 ====================
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8'
|
||||
options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation']
|
||||
}
|
||||
|
||||
// 配置 Javadoc JAR - 使用标准 javadoc 任务输出
|
||||
tasks.named('javadocJar') {
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
// ==================== 验证任务 ====================
|
||||
tasks.register('verifyNexusCredentials') {
|
||||
doLast {
|
||||
def username = System.getenv('LTDNexusUsername')
|
||||
def password = System.getenv('LTDNexusPassword')
|
||||
|
||||
// 安全地显示用户名和密码(只显示最后两位)
|
||||
def displayUsername = username ? "***${username.length() > 2 ? username.substring(username.length() - 2) : '**'}" : 'NOT SET'
|
||||
def displayPassword = password ? "***${password.length() > 2 ? password.substring(password.length() - 2) : '**'}" : 'NOT SET'
|
||||
|
||||
println "Nexus Username: ${displayUsername}"
|
||||
println "Nexus Password: ${displayPassword}"
|
||||
|
||||
if (!username || !password) {
|
||||
throw new GradleException('LTDNexusUsername or LTDNexusPassword environment variables are not set')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('checkPublicationContents') {
|
||||
doLast {
|
||||
def publication = publishing.publications.mavenJava
|
||||
println "=== Publication Details ==="
|
||||
println "Group: ${publication.groupId}"
|
||||
println "Artifact: ${publication.artifactId}"
|
||||
println "Version: ${publication.version}"
|
||||
println "Artifacts:"
|
||||
publication.artifacts.each { artifact ->
|
||||
def file = artifact.file
|
||||
def exists = file.exists()
|
||||
println " - ${file.name} (${artifact.classifier ?: 'main'}) - Exists: ${exists}"
|
||||
|
||||
if (!exists) {
|
||||
throw new GradleException("Publication artifact missing: ${file.absolutePath}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 任务依赖 ====================
|
||||
tasks.named('publishMavenJavaPublicationToLTDNexusRepository') {
|
||||
dependsOn verifyNexusCredentials
|
||||
dependsOn checkPublicationContents
|
||||
}
|
||||
|
||||
tasks.withType(PublishToMavenRepository) {
|
||||
dependsOn assemble
|
||||
dependsOn javadocJar
|
||||
}
|
||||
|
||||
// ==================== 便捷任务 ====================
|
||||
tasks.register('publishToNexus') {
|
||||
group = 'publishing'
|
||||
description = 'Publishes all publications to LTD Nexus'
|
||||
dependsOn 'publishMavenJavaPublicationToLTDNexusRepository'
|
||||
}
|
||||
tasks.named('build') {
|
||||
dependsOn javadocJar, sourceJar
|
||||
}
|
||||
|
||||
|
||||
tasks.register('publishLocal') {
|
||||
group = 'publishing'
|
||||
description = 'Publishes all publications to the local Maven repository'
|
||||
dependsOn 'publishToMavenLocal'
|
||||
}
|
||||
|
||||
tasks.register('cleanRepo', Delete) {
|
||||
delete layout.buildDirectory.dir("repo")
|
||||
}
|
||||
|
||||
tasks.named('clean') {
|
||||
dependsOn cleanRepo
|
||||
}
|
||||
|
||||
// ==================== IDEA 配置 ====================
|
||||
idea {
|
||||
module {
|
||||
downloadSources = true
|
||||
downloadJavadoc = true
|
||||
}
|
||||
}
|
||||
|
||||
// 禁用模块元数据生成
|
||||
tasks.withType(GenerateModuleMetadata) {
|
||||
enabled = false
|
||||
}
|
||||
tasks.register('showTaskTree') {
|
||||
doLast {
|
||||
def showTaskDeps
|
||||
showTaskDeps = { task, prefix = '' ->
|
||||
println "${prefix}${task.name}"
|
||||
task.getTaskDependencies().getDependencies(task).each { dep ->
|
||||
showTaskDeps(dep, prefix + ' ')
|
||||
}
|
||||
}
|
||||
|
||||
def targetTask = tasks.findByName('build')
|
||||
if (targetTask) {
|
||||
println "构建任务依赖树:"
|
||||
showTaskDeps(targetTask)
|
||||
} else {
|
||||
println "未找到 build 任务"
|
||||
}
|
||||
}
|
||||
}
|
||||
/**<pre>
|
||||
build
|
||||
├── check
|
||||
│ └── test
|
||||
│ ├── compileTestJava
|
||||
│ │ ├── classes
|
||||
│ │ │ ├── compileJava
|
||||
│ │ │ │ └── createMcpToSrg
|
||||
│ │ │ │ └── extractSrg
|
||||
│ │ │ │ └── downloadMcpConfig
|
||||
│ │ │ └── processResources
|
||||
│ │ └── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ ├── testClasses
|
||||
│ │ ├── processTestResources
|
||||
│ │ └── compileTestJava
|
||||
│ │ ├── classes
|
||||
│ │ │ ├── compileJava
|
||||
│ │ │ │ └── createMcpToSrg
|
||||
│ │ │ │ └── extractSrg
|
||||
│ │ │ │ └── downloadMcpConfig
|
||||
│ │ │ └── processResources
|
||||
│ │ └── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ ├── classes
|
||||
│ │ ├── compileJava
|
||||
│ │ │ └── createMcpToSrg
|
||||
│ │ │ └── extractSrg
|
||||
│ │ │ └── downloadMcpConfig
|
||||
│ │ └── processResources
|
||||
│ └── compileJava
|
||||
│ └── createMcpToSrg
|
||||
│ └── extractSrg
|
||||
│ └── downloadMcpConfig
|
||||
└── assemble
|
||||
├── reobfJarJar
|
||||
│ ├── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ ├── configureReobfTaskForReobfJarJar
|
||||
│ └── proguard
|
||||
│ └── jarJar
|
||||
│ ├── classes
|
||||
│ │ ├── compileJava
|
||||
│ │ │ └── createMcpToSrg
|
||||
│ │ │ └── extractSrg
|
||||
│ │ │ └── downloadMcpConfig
|
||||
│ │ └── processResources
|
||||
│ ├── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ └── addMixinsToJarJar
|
||||
│ └── compileJava
|
||||
│ └── createMcpToSrg
|
||||
│ └── extractSrg
|
||||
│ └── downloadMcpConfig
|
||||
├── reobfJar
|
||||
│ ├── jar
|
||||
│ │ ├── classes
|
||||
│ │ │ ├── compileJava
|
||||
│ │ │ │ └── createMcpToSrg
|
||||
│ │ │ │ └── extractSrg
|
||||
│ │ │ │ └── downloadMcpConfig
|
||||
│ │ │ └── processResources
|
||||
│ │ ├── compileJava
|
||||
│ │ │ └── createMcpToSrg
|
||||
│ │ │ └── extractSrg
|
||||
│ │ │ └── downloadMcpConfig
|
||||
│ │ └── addMixinsToJar
|
||||
│ │ └── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ ├── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ └── configureReobfTaskForReobfJar
|
||||
└── jar
|
||||
├── classes
|
||||
│ ├── compileJava
|
||||
│ │ └── createMcpToSrg
|
||||
│ │ └── extractSrg
|
||||
│ │ └── downloadMcpConfig
|
||||
│ └── processResources
|
||||
├── compileJava
|
||||
│ └── createMcpToSrg
|
||||
│ └── extractSrg
|
||||
│ └── downloadMcpConfig
|
||||
└── addMixinsToJar
|
||||
└── compileJava
|
||||
└── createMcpToSrg
|
||||
└── extractSrg
|
||||
└── downloadMcpConfig
|
||||
</pre>
|
||||
*/
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
plugins {
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = "${mod_id}-${project.name}-${minecraft_version}"
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(java_version)
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
// https://docs.gradle.org/current/userguide/declaring_repositories.html#declaring_content_exclusively_found_in_one_repository
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = 'Sponge'
|
||||
url = 'https://repo.spongepowered.org/repository/maven-public'
|
||||
}
|
||||
}
|
||||
filter { includeGroupAndSubgroups('org.spongepowered') }
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepositories(
|
||||
maven {
|
||||
name = 'ParchmentMC'
|
||||
url = 'https://maven.parchmentmc.org/'
|
||||
},
|
||||
maven { url = "https://neoforged.forgecdn.net/releases" },
|
||||
maven { url = "https://neoforged.forgecdn.net/mojang-meta" }
|
||||
)
|
||||
filter { includeGroup('org.parchmentmc.data') }
|
||||
}
|
||||
maven { url = "https://libraries.minecraft.net/" }
|
||||
|
||||
|
||||
maven {
|
||||
url "https://cursemaven.com"
|
||||
content { includeGroup "curse.maven" }
|
||||
}
|
||||
maven {
|
||||
name = 'BlameJared'
|
||||
url = 'https://maven.blamejared.com'
|
||||
}
|
||||
}
|
||||
|
||||
// Declare capabilities on the outgoing configurations.
|
||||
// Read more about capabilities here: https://docs.gradle.org/current/userguide/component_capabilities.html#sec:declaring-additional-capabilities-for-a-local-component
|
||||
['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant ->
|
||||
configurations."$variant".outgoing {
|
||||
capability("$group:${project.name}:$version")
|
||||
capability("$group:${base.archivesName.get()}:$version")
|
||||
capability("$group:$mod_id-${project.name}-${minecraft_version}:$version")
|
||||
capability("$group:$mod_id:$version")
|
||||
}
|
||||
publishing.publications.configureEach {
|
||||
suppressPomMetadataWarningsFor(variant)
|
||||
}
|
||||
}
|
||||
|
||||
sourcesJar {
|
||||
from(rootProject.file('LICENSE')) {
|
||||
rename { "${it}_${mod_name}" }
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
from(rootProject.file('LICENSE')) {
|
||||
rename { "${it}_${mod_name}" }
|
||||
}
|
||||
|
||||
manifest {
|
||||
attributes([
|
||||
'Specification-Title' : mod_name,
|
||||
'Specification-Vendor' : mod_author,
|
||||
'Specification-Version' : project.jar.archiveVersion,
|
||||
'Implementation-Title' : project.name,
|
||||
'Implementation-Version': project.jar.archiveVersion,
|
||||
'Implementation-Vendor' : mod_author,
|
||||
'Built-On-Minecraft' : minecraft_version
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
var expandProps = [
|
||||
'version' : version,
|
||||
'group' : project.group, //Else we target the task's group.
|
||||
'minecraft_version' : minecraft_version,
|
||||
'minecraft_version_range' : minecraft_version_range,
|
||||
'fabric_version' : fabric_version,
|
||||
'fabric_loader_version' : fabric_loader_version,
|
||||
'mod_name' : mod_name,
|
||||
'mod_author' : mod_author,
|
||||
'mod_id' : mod_id,
|
||||
'license' : license,
|
||||
'description' : project.description,
|
||||
"forge_version" : forge_version,
|
||||
"forge_loader_version_range" : forge_loader_version_range,
|
||||
'credits' : credits,
|
||||
'java_version' : java_version
|
||||
]
|
||||
|
||||
var jsonExpandProps = expandProps.collectEntries {
|
||||
key, value -> [(key): value instanceof String ? value.replace("\n", "\\\\n") : value]
|
||||
}
|
||||
|
||||
filesMatching(['META-INF/mods.toml']) {
|
||||
expand expandProps
|
||||
}
|
||||
|
||||
filesMatching(['pack.mcmeta', 'fabric.mod.json', '*.mixins.json']) {
|
||||
expand jsonExpandProps
|
||||
}
|
||||
|
||||
inputs.properties(expandProps)
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
register('mavenJava', MavenPublication) {
|
||||
artifactId base.archivesName.get()
|
||||
from components.java
|
||||
pom {
|
||||
name = 'Lib39'
|
||||
description = 'Lib39 is a general-purpose dependency library for Minecraft mods.'
|
||||
url = 'https://github.com/3944Realms/lib39'
|
||||
|
||||
properties = [
|
||||
'minecraft.version': project.minecraft_version,
|
||||
'mod.version': project.version,
|
||||
'forge.version': project.forge_version,
|
||||
'java.version': '17'
|
||||
]
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = 'MIT'
|
||||
url = 'https://raw.githubusercontent.com/3944Realms/lib39/refs/heads/main/LICENSE'
|
||||
distribution = 'repo'
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'R3944Realms'
|
||||
name = "${mod_author}"
|
||||
email = 'f256198830@hotmail.com'
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection = 'scm:git:https://github.com/3944Realms/lib39.git'
|
||||
developerConnection = 'scm:git:ssh://git@github.com:3944Realms/lib39.git'
|
||||
url = 'https://github.com/3944Realms/lib39'
|
||||
tag = 'main'
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system = 'GitHub'
|
||||
url = 'https://github.com/3944Realms/lib39/issues'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
// 本地仓库
|
||||
maven {
|
||||
name = 'local'
|
||||
url = layout.buildDirectory.dir("repo")
|
||||
}
|
||||
// Nexus 远程仓库
|
||||
maven {
|
||||
name = 'LTDNexus'
|
||||
url = 'https://nexus.bot.leisuretimedock.top/repository/maven-releases/'
|
||||
credentials {
|
||||
username = System.getenv('LTDNexusUsername') ?: ''
|
||||
password = System.getenv('LTDNexusPassword') ?: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 任务依赖 ====================
|
||||
|
||||
tasks.withType(PublishToMavenRepository) {
|
||||
dependsOn assemble
|
||||
dependsOn javadoc
|
||||
}
|
||||
|
||||
tasks.named('build') {
|
||||
dependsOn javadoc, sourcesJar
|
||||
}
|
||||
|
||||
tasks.register('cleanRepo', Delete) {
|
||||
delete layout.buildDirectory.dir("repo")
|
||||
}
|
||||
|
||||
tasks.named('clean') {
|
||||
dependsOn cleanRepo
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
plugins {
|
||||
id 'multiloader-common'
|
||||
}
|
||||
|
||||
configurations {
|
||||
commonJava{
|
||||
canBeResolved = true
|
||||
}
|
||||
commonResources{
|
||||
canBeResolved = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(':common')) {
|
||||
capabilities {
|
||||
requireCapability "$group:$mod_id"
|
||||
}
|
||||
}
|
||||
commonJava project(path: ':common', configuration: 'commonJava')
|
||||
commonResources project(path: ':common', configuration: 'commonResources')
|
||||
}
|
||||
|
||||
tasks.named('compileJava', JavaCompile) {
|
||||
dependsOn(configurations.commonJava)
|
||||
source(configurations.commonJava)
|
||||
}
|
||||
|
||||
processResources {
|
||||
dependsOn(configurations.commonResources)
|
||||
from(configurations.commonResources)
|
||||
}
|
||||
|
||||
tasks.named('javadoc', Javadoc).configure {
|
||||
dependsOn(configurations.commonJava)
|
||||
source(configurations.commonJava)
|
||||
options.encoding = 'UTF-8'
|
||||
options.charSet = 'UTF-8'
|
||||
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||
options.memberLevel = JavadocMemberLevel.PUBLIC
|
||||
options.addBooleanOption('Xdoclint:none', true)
|
||||
options.addStringOption('doctitle', "${mod_id} ${minecraft_version} ${version} Javadoc")
|
||||
}
|
||||
|
||||
tasks.named('sourcesJar', Jar) {
|
||||
dependsOn(configurations.commonJava)
|
||||
from(configurations.commonJava)
|
||||
dependsOn(configurations.commonResources)
|
||||
from(configurations.commonResources)
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
plugins {
|
||||
id 'multiloader-common'
|
||||
id 'net.neoforged.moddev.legacyforge'
|
||||
}
|
||||
|
||||
legacyForge {
|
||||
mcpVersion = minecraft_version
|
||||
if (file("src/main/resources/META-INF/accesstransformer.cfg").exists()) {
|
||||
accessTransformers = ["src/main/resources/META-INF/accesstransformer.cfg"]
|
||||
}
|
||||
parchment {
|
||||
minecraftVersion = parchment_minecraft
|
||||
mappingsVersion = parchment_version
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(group: 'org.spongepowered', name: 'mixin', version: '0.8.5')
|
||||
implementation(group: 'tschipp.carryon', name: 'carryon-common-1.20.1', version: '2.1.2') {
|
||||
transitive = false
|
||||
}
|
||||
implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:0.2.0"))
|
||||
implementation(group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1')
|
||||
}
|
||||
configurations {
|
||||
commonJava {
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
}
|
||||
commonResources {
|
||||
canBeResolved = false
|
||||
canBeConsumed = true
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
commonJava sourceSets.main.java.sourceDirectories.singleFile
|
||||
commonResources sourceSets.main.resources.sourceDirectories.singleFile, file('src/generated/resources')
|
||||
}
|
||||
|
||||
clean {
|
||||
delete 'generated'
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
package top.r3944realms.lib39;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.r3944realms.lib39.example.Lib39Example;
|
||||
import top.r3944realms.lib39.platform.Services;
|
||||
|
||||
/**
|
||||
* The type Lib 39.
|
||||
*/
|
||||
public class Lib39 {
|
||||
/**
|
||||
* The constant MOD_ID.
|
||||
*/
|
||||
public static final String MOD_ID = "lib39";
|
||||
/**
|
||||
* The constant MOD_NAME.
|
||||
*/
|
||||
public static final String MOD_NAME = "3944Realms 's Lib Mod";
|
||||
/**
|
||||
* The constant LOGGER.
|
||||
*/
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME);
|
||||
/**
|
||||
* The constant ENABLE_EXAMPLES_PROPERTY_KEY.
|
||||
*/
|
||||
public static final String ENABLE_EXAMPLES_PROPERTY_KEY = "lib39.enable_examples";
|
||||
|
||||
/**
|
||||
* Initialize.
|
||||
*/
|
||||
public static void initialize() {
|
||||
Lib39.LOGGER.info("[Lib39-Common] Lib39-Common start initialization.");
|
||||
if (shouldRegisterExamples()) {
|
||||
LOGGER.info("[Lib39-Common] Registering Examples");
|
||||
registerExamples();
|
||||
}
|
||||
Lib39.LOGGER.info("[Lib39-Common] Finished Lib39-Common!.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Rl resource location.
|
||||
*
|
||||
* @param path the path
|
||||
* @return the resource location
|
||||
*/
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull ResourceLocation rl(String path) {
|
||||
return new ResourceLocation(Lib39.MOD_ID, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rl resource location.
|
||||
*
|
||||
* @param modId the mod id
|
||||
* @param path the path
|
||||
* @return the resource location
|
||||
*/
|
||||
@Contract("_, _ -> new")
|
||||
public static @NotNull ResourceLocation rl(String modId, String path) {
|
||||
return new ResourceLocation(modId, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mrl resource location.
|
||||
*
|
||||
* @param path the path
|
||||
* @return the resource location
|
||||
*/
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull ResourceLocation mrl(String path) {
|
||||
return new ResourceLocation(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is client environment boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean isClientEnvironment() {
|
||||
return Services.PLATFORM.isClientEnvironment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should register examples boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean shouldRegisterExamples() {
|
||||
return Services.PLATFORM.isDevelopmentEnvironment() || Boolean.getBoolean(ENABLE_EXAMPLES_PROPERTY_KEY);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register examples.
|
||||
*/
|
||||
static void registerExamples() {
|
||||
LOGGER.info("[Lib39-Common] Starting example demonstrations");
|
||||
try {
|
||||
// 创建示例实例并演示功能
|
||||
Lib39Example example = new Lib39Example();
|
||||
example.demonstrateFeature();
|
||||
|
||||
LOGGER.info("[Lib39-Common] Example demonstrations completed successfully");
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("[Lib39-Common] Failed to demonstrate examples", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Mod info.
|
||||
*/
|
||||
public static class ModInfo {
|
||||
/**
|
||||
* The constant VERSION.
|
||||
*/
|
||||
public static final String VERSION;
|
||||
static {
|
||||
VERSION = Services.PLATFORM.getModVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
package top.r3944realms.lib39.client.shader;
|
||||
|
||||
import net.minecraft.client.renderer.ShaderInstance;
|
||||
|
||||
/**
|
||||
* The type Lib 39 shaders.
|
||||
*/
|
||||
public class Lib39Shaders {
|
||||
/**
|
||||
* Gets ring shader.
|
||||
*
|
||||
* @return the ring shader
|
||||
*/
|
||||
public static ShaderInstance getRingShader() {
|
||||
return ringShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Ring shader.
|
||||
*/
|
||||
static ShaderInstance ringShader;
|
||||
|
||||
/**
|
||||
* Gets selection shader.
|
||||
*
|
||||
* @return the selection shader
|
||||
*/
|
||||
public static ShaderInstance getSelectionShader() {
|
||||
return selectionShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ring shader.
|
||||
*
|
||||
* @param ringShader the ring shader
|
||||
*/
|
||||
public static void setRingShader(ShaderInstance ringShader) {
|
||||
Lib39Shaders.ringShader = ringShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets selection shader.
|
||||
*
|
||||
* @param selectionShader the selection shader
|
||||
*/
|
||||
public static void setSelectionShader(ShaderInstance selectionShader) {
|
||||
Lib39Shaders.selectionShader = selectionShader;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Selection shader.
|
||||
*/
|
||||
static ShaderInstance selectionShader;
|
||||
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
package top.r3944realms.lib39.content.block;
|
||||
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.level.block.state.properties.RotationSegment;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.lib39.content.block.property.DollPose;
|
||||
|
||||
/**
|
||||
* The type Doll block.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class DollBlock extends AbstractDollBlock{
|
||||
/**
|
||||
* The constant MAX.
|
||||
*/
|
||||
public static final int MAX = RotationSegment.getMaxSegmentIndex();
|
||||
|
||||
private static final int ROTATIONS = MAX + 1;
|
||||
/**
|
||||
* The constant ROTATION.
|
||||
*/
|
||||
public static final IntegerProperty ROTATION = BlockStateProperties.ROTATION_16;
|
||||
|
||||
/**
|
||||
* Instantiates a new Doll block.
|
||||
*/
|
||||
public DollBlock() {
|
||||
super();
|
||||
this.registerDefaultState(
|
||||
this.stateDefinition.any()
|
||||
.setValue(POSE, DollPose.DEFAULT)
|
||||
.setValue(WATERLOGGED, false)
|
||||
.setValue(ROTATION, 0)
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public @Nullable BlockState getStateForPlacement(@NotNull BlockPlaceContext context) {
|
||||
BlockState stateForPlacement = super.getStateForPlacement(context);
|
||||
return stateForPlacement != null ? stateForPlacement.setValue(ROTATION, RotationSegment.convertToSegment((context.getRotation()+180) % 360)) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BlockState rotate(@NotNull BlockState state, @NotNull Rotation rotation) {
|
||||
return state.setValue(ROTATION, rotation.rotate(state.getValue(ROTATION), ROTATIONS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BlockState mirror(@NotNull BlockState state, @NotNull Mirror mirror) {
|
||||
return state.setValue(ROTATION, mirror.mirror(state.getValue(ROTATION), ROTATIONS));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.@NotNull Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(ROTATION);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package top.r3944realms.lib39.content.block;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.lib39.content.block.property.DollPose;
|
||||
|
||||
/**
|
||||
* The type Wall doll block.
|
||||
*/
|
||||
@SuppressWarnings({"deprecation"})
|
||||
public class WallDollBlock extends AbstractDollBlock {
|
||||
/**
|
||||
* The constant FACING.
|
||||
*/
|
||||
public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING;
|
||||
|
||||
/**
|
||||
* Instantiates a new Wall doll block.
|
||||
*/
|
||||
public WallDollBlock() {
|
||||
super();
|
||||
this.registerDefaultState(
|
||||
this.stateDefinition.any()
|
||||
.setValue(POSE, DollPose.DEFAULT)
|
||||
.setValue(WATERLOGGED, false)
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
);
|
||||
}
|
||||
public @NotNull BlockState rotate(@NotNull BlockState state, @NotNull Rotation rotation) {
|
||||
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
public @NotNull BlockState mirror(@NotNull BlockState state, @NotNull Mirror mirror) {
|
||||
return state.rotate(mirror.getRotation(state.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockState getStateForPlacement(@NotNull BlockPlaceContext context) {
|
||||
BlockState stateForPlacement = super.getStateForPlacement(context);
|
||||
return stateForPlacement != null ? stateForPlacement.setValue(FACING, context.getHorizontalDirection().getOpposite()) : null;
|
||||
}
|
||||
|
||||
protected void createBlockStateDefinition(StateDefinition.@NotNull Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(FACING);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
package top.r3944realms.lib39.core.compat;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The type Compat manager.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class CompatManager {
|
||||
/**
|
||||
* Gets id.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
public ResourceLocation getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Logger.
|
||||
*/
|
||||
protected final Logger logger;
|
||||
/**
|
||||
* The Id.
|
||||
*/
|
||||
protected final ResourceLocation id;
|
||||
/**
|
||||
* The Compats.
|
||||
*/
|
||||
protected final Map<ResourceLocation, ICompat> compats = new HashMap<>();
|
||||
/**
|
||||
* The Initialized.
|
||||
*/
|
||||
protected boolean initialized = false;
|
||||
/**
|
||||
* The Pending tasks.
|
||||
*/
|
||||
protected final List<Runnable> pendingTasks = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Initialize.
|
||||
*/
|
||||
public void initialize() {
|
||||
initializeAllCompat();
|
||||
onLoadComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Compat manager.
|
||||
*
|
||||
* @param id the id
|
||||
*/
|
||||
public CompatManager(@NotNull ResourceLocation id) {
|
||||
this.id = id;
|
||||
this.logger = LoggerFactory.getLogger(id.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register compat.
|
||||
*
|
||||
* @param id the id
|
||||
* @param compat the compat
|
||||
*/
|
||||
public void registerCompat(ResourceLocation id, ICompat compat) {
|
||||
if (initialized) {
|
||||
// 已初始化,直接注册
|
||||
doRegisterCompat(id, compat);
|
||||
} else {
|
||||
// 未初始化,缓存起来
|
||||
pendingTasks.add(() -> doRegisterCompat(id, compat));
|
||||
logger.debug("Cached compat registration for: {}", id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do register compat.
|
||||
*
|
||||
* @param id the id
|
||||
* @param compat the compat
|
||||
*/
|
||||
protected void doRegisterCompat(ResourceLocation id, ICompat compat) {
|
||||
if (compats.containsKey(id)) {
|
||||
logger.warn("Compat with id {} is already registered!", id);
|
||||
return;
|
||||
}
|
||||
compats.put(id, compat);
|
||||
logger.debug("Registered compat: {}", id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register compat.
|
||||
*
|
||||
* @param namespace the namespace
|
||||
* @param path the path
|
||||
* @param compat the compat
|
||||
*/
|
||||
public void registerCompat(String namespace, String path, ICompat compat) {
|
||||
registerCompat(Lib39.rl(namespace, path), compat);
|
||||
}
|
||||
|
||||
|
||||
// ===================== 初始化和管理 =====================
|
||||
|
||||
/**
|
||||
* 初始化所有兼容模块并应用事件监听器
|
||||
*/
|
||||
protected synchronized void initializeAllCompat() {
|
||||
logger.info("Initializing {} compatibility modules", compats.size());
|
||||
|
||||
// 先处理所有缓存的注册
|
||||
pendingTasks.forEach(Runnable::run);
|
||||
pendingTasks.clear();
|
||||
|
||||
// 初始化所有兼容模块
|
||||
for (Map.Entry<ResourceLocation, ICompat> entry : compats.entrySet()) {
|
||||
if (!entry.getValue().isInitialized() && entry.getValue().isModLoaded()) {
|
||||
try {
|
||||
entry.getValue().initialize();
|
||||
entry.getValue().setInitialize(true);
|
||||
logger.info("Initialized compat: {}", entry.getKey());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to initialize compat: {}", entry.getKey(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets compat.
|
||||
*
|
||||
* @param id the id
|
||||
* @return the compat
|
||||
*/
|
||||
public Optional<ICompat> getCompat(ResourceLocation id) {
|
||||
return Optional.ofNullable(compats.get(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Has compat boolean.
|
||||
*
|
||||
* @param id the id
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean hasCompat(ResourceLocation id) {
|
||||
return compats.containsKey(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister compat.
|
||||
*
|
||||
* @param id the id
|
||||
*/
|
||||
public void unregisterCompat(ResourceLocation id) {
|
||||
ICompat removed = compats.remove(id);
|
||||
if (removed != null) {
|
||||
logger.debug("Unregistered compat: {}", id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets loaded compats.
|
||||
*
|
||||
* @return the loaded compats
|
||||
*/
|
||||
public List<ICompat> getLoadedCompats() {
|
||||
return compats.values().stream()
|
||||
.filter(ICompat::isModLoaded)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* On load complete.
|
||||
*/
|
||||
public void onLoadComplete() {
|
||||
logger.info("Calling onLoadComplete for {} compatibility modules", compats.size());
|
||||
for (Map.Entry<ResourceLocation, ICompat> entry : compats.entrySet()) {
|
||||
try {
|
||||
entry.getValue().onLoadComplete();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error in onLoadComplete for compat: {}", entry.getKey(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package top.r3944realms.lib39.core.register;
|
||||
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Lib 39 block entities.
|
||||
*/
|
||||
public class Lib39BlockEntities {
|
||||
/**
|
||||
* The constant DOLL_BLOCK_ENTITY.
|
||||
*/
|
||||
public static Supplier<BlockEntityType<DollBlockEntity>> DOLL_BLOCK_ENTITY;
|
||||
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package top.r3944realms.lib39.core.register;
|
||||
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Lib 39 blocks.
|
||||
*/
|
||||
public class Lib39Blocks {
|
||||
|
||||
/**
|
||||
* The constant DOLL.
|
||||
*/
|
||||
public static Supplier<Block> DOLL;
|
||||
/**
|
||||
* The Wall doll.
|
||||
*/
|
||||
public static Supplier<Block> WALL_DOLL;
|
||||
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package top.r3944realms.lib39.core.register;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Ex lib 39 items.
|
||||
*/
|
||||
public class Lib39Items {
|
||||
/**
|
||||
* The constant DOLL.
|
||||
*/
|
||||
public static Supplier<Item> DOLL;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package top.r3944realms.lib39.core.register;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Lib 39 sound events.
|
||||
*/
|
||||
public class Lib39SoundEvents {
|
||||
/**
|
||||
* The constant RL_DUCK_TOY.
|
||||
*/
|
||||
public static final ResourceLocation RL_DUCK_TOY = Lib39.rl("duck_toy");
|
||||
/**
|
||||
* The constant DUCK_TOY.
|
||||
*/
|
||||
public static Supplier<SoundEvent> DUCK_TOY;
|
||||
|
||||
/**
|
||||
* Gets sub title translate key.
|
||||
*
|
||||
* @param name the name
|
||||
* @return the sub title translate key
|
||||
*/
|
||||
public static String getSubTitleTranslateKey(String name) {
|
||||
return "sound." + Lib39.MOD_ID + ".subtitle." + name;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package top.r3944realms.lib39.core.sync;
|
||||
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
/**
|
||||
* The interface Inbt serializable.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
*/
|
||||
public interface INBTSerializable <T extends Tag>{
|
||||
/**
|
||||
* Serialize nbt t.
|
||||
*
|
||||
* @return the t
|
||||
*/
|
||||
T serializeNBT();
|
||||
|
||||
/**
|
||||
* Deserialize nbt.
|
||||
*
|
||||
* @param var1 the var 1
|
||||
*/
|
||||
void deserializeNBT(T var1);
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package top.r3944realms.lib39.core.sync;
|
||||
|
||||
/**
|
||||
* The interface Update.
|
||||
*/
|
||||
public interface IUpdate {
|
||||
/**
|
||||
* Update.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Gets sync data.
|
||||
*
|
||||
* @return the sync data
|
||||
*/
|
||||
NBTEntitySyncData getSyncData();
|
||||
}
|
||||
|
|
@ -1,197 +0,0 @@
|
|||
package top.r3944realms.lib39.datagen.provider;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.PackOutput.Target;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.enchantment.Enchantment;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Language provider.
|
||||
*/
|
||||
public abstract class LanguageProvider implements DataProvider {
|
||||
private final Map<String, String> data = new TreeMap<>();
|
||||
private final PackOutput output;
|
||||
private final String modid;
|
||||
private final String locale;
|
||||
|
||||
/**
|
||||
* Instantiates a new Language provider.
|
||||
*
|
||||
* @param output the output
|
||||
* @param modid the modid
|
||||
* @param locale the locale
|
||||
*/
|
||||
public LanguageProvider(PackOutput output, String modid, String locale) {
|
||||
this.output = output;
|
||||
this.modid = modid;
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add translations.
|
||||
*/
|
||||
protected abstract void addTranslations();
|
||||
|
||||
public @NotNull CompletableFuture<?> run(@NotNull CachedOutput cache) {
|
||||
this.addTranslations();
|
||||
return !this.data.isEmpty() ? this.save(cache, this.output.getOutputFolder(Target.RESOURCE_PACK).resolve(this.modid).resolve("lang").resolve(this.locale + ".json")) : CompletableFuture.allOf();
|
||||
}
|
||||
|
||||
public @NotNull String getName() {
|
||||
return "Languages: " + this.locale;
|
||||
}
|
||||
|
||||
private @NotNull CompletableFuture<?> save(CachedOutput cache, Path target) {
|
||||
JsonObject json = new JsonObject();
|
||||
Objects.requireNonNull(json);
|
||||
this.data.forEach(json::addProperty);
|
||||
return DataProvider.saveStable(cache, json, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add block.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void addBlock(@NotNull Supplier<? extends Block> key, String name) {
|
||||
this.add(key.get(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void add(@NotNull Block key, String name) {
|
||||
this.add(key.getDescriptionId(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void addItem(@NotNull Supplier<? extends Item> key, String name) {
|
||||
this.add(key.get(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void add(@NotNull Item key, String name) {
|
||||
this.add(key.getDescriptionId(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item stack.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void addItemStack(@NotNull Supplier<ItemStack> key, String name) {
|
||||
this.add(key.get(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void add(@NotNull ItemStack key, String name) {
|
||||
this.add(key.getDescriptionId(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add enchantment.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void addEnchantment(@NotNull Supplier<? extends Enchantment> key, String name) {
|
||||
this.add(key.get(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void add(@NotNull Enchantment key, String name) {
|
||||
this.add(key.getDescriptionId(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add effect.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void addEffect(@NotNull Supplier<? extends MobEffect> key, String name) {
|
||||
this.add(key.get(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void add(@NotNull MobEffect key, String name) {
|
||||
this.add(key.getDescriptionId(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add entity type.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void addEntityType(@NotNull Supplier<? extends EntityType<?>> key, String name) {
|
||||
this.add(key.get(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
*
|
||||
* @param key the key
|
||||
* @param name the name
|
||||
*/
|
||||
public void add(@NotNull EntityType<?> key, String name) {
|
||||
this.add(key.getDescriptionId(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
*/
|
||||
public void add(String key, String value) {
|
||||
if (this.data.put(key, value) != null) {
|
||||
throw new IllegalStateException("Duplicate translation key " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package top.r3944realms.lib39.example;
|
||||
|
||||
/**
|
||||
* The type Lib 39 example.
|
||||
*/
|
||||
public class Lib39Example {
|
||||
private static boolean registered = false;
|
||||
|
||||
/**
|
||||
* Instantiates a new Lib 39 example.
|
||||
*/
|
||||
public Lib39Example() {
|
||||
if (!registered) {
|
||||
init();
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public void init() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Demonstrate feature.
|
||||
*/
|
||||
public void demonstrateFeature() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package top.r3944realms.lib39.example.core.register;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Ex lib 39 items.
|
||||
*/
|
||||
public class ExLib39Items {
|
||||
/**
|
||||
* The constant SUPER_LEAD_ROPE.
|
||||
*/
|
||||
public static Supplier<Item> FABRIC;
|
||||
/**
|
||||
* The constant ETERNAL_POTATO.
|
||||
*/
|
||||
public static Supplier<Item> NEOFORGE;
|
||||
/**
|
||||
* The constant FORGE.
|
||||
*/
|
||||
public static Supplier<Item> FORGE;
|
||||
|
||||
}
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
package top.r3944realms.lib39.mixin.minecraft;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* The interface Creative mode tabs accessor.
|
||||
*/
|
||||
@Mixin(CreativeModeTabs.class)
|
||||
public interface CreativeModeTabsAccessor {
|
||||
/**
|
||||
* Gets building blocks.
|
||||
*
|
||||
* @return the building blocks
|
||||
*/
|
||||
@Accessor("BUILDING_BLOCKS")
|
||||
static ResourceKey<CreativeModeTab> getBuildingBlocks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets colored blocks.
|
||||
*
|
||||
* @return the colored blocks
|
||||
*/
|
||||
@Accessor("COLORED_BLOCKS")
|
||||
static ResourceKey<CreativeModeTab> getColoredBlocks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets natural blocks.
|
||||
*
|
||||
* @return the natural blocks
|
||||
*/
|
||||
@Accessor("NATURAL_BLOCKS")
|
||||
static ResourceKey<CreativeModeTab> getNaturalBlocks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets functional blocks.
|
||||
*
|
||||
* @return the functional blocks
|
||||
*/
|
||||
@Accessor("FUNCTIONAL_BLOCKS")
|
||||
static ResourceKey<CreativeModeTab> getFunctionalBlocks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets redstone blocks.
|
||||
*
|
||||
* @return the redstone blocks
|
||||
*/
|
||||
@Accessor("REDSTONE_BLOCKS")
|
||||
static ResourceKey<CreativeModeTab> getRedstoneBlocks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets hotbar.
|
||||
*
|
||||
* @return the hotbar
|
||||
*/
|
||||
@Accessor("HOTBAR")
|
||||
static ResourceKey<CreativeModeTab> getHotbar() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets search.
|
||||
*
|
||||
* @return the search
|
||||
*/
|
||||
@Accessor("SEARCH")
|
||||
static ResourceKey<CreativeModeTab> getSearch() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets tools and utilities.
|
||||
*
|
||||
* @return the tools and utilities
|
||||
*/
|
||||
@Accessor("TOOLS_AND_UTILITIES")
|
||||
static ResourceKey<CreativeModeTab> getToolsAndUtilities() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets combat.
|
||||
*
|
||||
* @return the combat
|
||||
*/
|
||||
@Accessor("COMBAT")
|
||||
static ResourceKey<CreativeModeTab> getCombat() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets food and drinks.
|
||||
*
|
||||
* @return the food and drinks
|
||||
*/
|
||||
@Accessor("FOOD_AND_DRINKS")
|
||||
static ResourceKey<CreativeModeTab> getFoodAndDrinks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets ingredients.
|
||||
*
|
||||
* @return the ingredients
|
||||
*/
|
||||
@Accessor("INGREDIENTS")
|
||||
static ResourceKey<CreativeModeTab> getIngredients() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets spawn eggs.
|
||||
*
|
||||
* @return the spawn eggs
|
||||
*/
|
||||
@Accessor("SPAWN_EGGS")
|
||||
static ResourceKey<CreativeModeTab> getSpawnEggs() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets op blocks.
|
||||
*
|
||||
* @return the op blocks
|
||||
*/
|
||||
@Accessor("OP_BLOCKS")
|
||||
static ResourceKey<CreativeModeTab> getOpBlocks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets inventory.
|
||||
*
|
||||
* @return the inventory
|
||||
*/
|
||||
@Accessor("INVENTORY")
|
||||
static ResourceKey<CreativeModeTab> getInventory() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets cached parameters.
|
||||
*
|
||||
* @return the cached parameters
|
||||
*/
|
||||
@Accessor("CACHED_PARAMETERS")
|
||||
static CreativeModeTab.ItemDisplayParameters getCachedParameters() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets painting comparator.
|
||||
*
|
||||
* @return the painting comparator
|
||||
*/
|
||||
@Accessor("PAINTING_COMPARATOR")
|
||||
static Comparator<net.minecraft.world.item.CreativeModeTab> getPaintingComparator() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package top.r3944realms.lib39.mixin.minecraft;
|
||||
|
||||
import net.minecraft.client.gui.components.Renderable;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The interface Screen accessor.
|
||||
*/
|
||||
@Mixin(Screen.class)
|
||||
public interface ScreenAccessor {
|
||||
/**
|
||||
* Gets renderables.
|
||||
*
|
||||
* @return the renderables
|
||||
*/
|
||||
@Accessor("renderables")
|
||||
List<Renderable> getrRenderables();
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package top.r3944realms.lib39.platform;
|
||||
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.platform.services.IPlatformHelper;
|
||||
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* The type Services.
|
||||
*/
|
||||
// Service loaders are a built-in Java feature that allow us to locate implementations of an interface that vary from one
|
||||
// environment to another. In the context of MultiLoader we use this feature to access a mock API in the common code that
|
||||
// is swapped out for the platform specific implementation at runtime.
|
||||
public class Services {
|
||||
|
||||
/**
|
||||
* The constant PLATFORM.
|
||||
*/
|
||||
// In this example we provide a platform helper which provides information about what platform the mod is running on.
|
||||
// For example this can be used to check if the code is running on Forge vs Fabric, or to ask the modloader if another
|
||||
// mod is loaded.
|
||||
public static final IPlatformHelper PLATFORM = load(IPlatformHelper.class);
|
||||
|
||||
/**
|
||||
* Load t.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param clazz the clazz
|
||||
* @return the t
|
||||
*/
|
||||
// This code is used to load a service for the current environment. Your implementation of the service must be defined
|
||||
// manually by including a text file in META-INF/services named with the fully qualified class name of the service.
|
||||
// Inside the file you should write the fully qualified class name of the implementation to load for the platform. For
|
||||
// example our file on Forge points to ForgePlatformHelper while Fabric points to FabricPlatformHelper.
|
||||
public static <T> T load(Class<T> clazz) {
|
||||
|
||||
final T loadedService = ServiceLoader.load(clazz)
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName()));
|
||||
Lib39.LOGGER.debug("Loaded {} for service {}", loadedService, clazz);
|
||||
return loadedService;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package top.r3944realms.lib39.platform.services;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandBuildContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import top.r3944realms.lib39.core.command.ICommandHelpManager;
|
||||
|
||||
/**
|
||||
* The interface Help command hook.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IHelpCommandHook {
|
||||
/**
|
||||
* On register.
|
||||
*
|
||||
* @param tree the tree
|
||||
* @param manager the manager
|
||||
* @param context the context
|
||||
*/
|
||||
void onRegister(LiteralArgumentBuilder<CommandSourceStack> tree, ICommandHelpManager manager, CommandBuildContext context);
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package top.r3944realms.lib39.platform.services;
|
||||
|
||||
/**
|
||||
* The interface Platform helper.
|
||||
*/
|
||||
public interface IPlatformHelper {
|
||||
|
||||
/**
|
||||
* Gets the name of the current platform
|
||||
*
|
||||
* @return The name of the current platform.
|
||||
*/
|
||||
String getPlatformName();
|
||||
|
||||
/**
|
||||
* Checks if a mod with the given id is loaded.
|
||||
*
|
||||
* @param modId The mod to check if it is loaded.
|
||||
* @return True if the mod is loaded, false otherwise.
|
||||
*/
|
||||
boolean isModLoaded(String modId);
|
||||
|
||||
/**
|
||||
* Check if the game is currently in a development environment.
|
||||
*
|
||||
* @return True if in a development environment, false otherwise.
|
||||
*/
|
||||
boolean isDevelopmentEnvironment();
|
||||
|
||||
/**
|
||||
* Gets the name of the environment type as a string.
|
||||
*
|
||||
* @return The name of the environment type.
|
||||
*/
|
||||
default String getEnvironmentName() {
|
||||
return isDevelopmentEnvironment() ? "development" : "production";
|
||||
}
|
||||
|
||||
/**
|
||||
* Is client environment boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
boolean isClientEnvironment();
|
||||
|
||||
|
||||
/**
|
||||
* Gets mod version.
|
||||
*
|
||||
* @return the mod version
|
||||
*/
|
||||
String getModVersion();
|
||||
|
||||
/**
|
||||
* Gets util helper.
|
||||
*
|
||||
* @return the util helper
|
||||
*/
|
||||
IUtilHelper getUtilHelper();
|
||||
|
||||
/**
|
||||
* Gets help command hook.
|
||||
*
|
||||
* @return the help command hook
|
||||
*/
|
||||
IHelpCommandHook getHelpCommandHook();
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package top.r3944realms.lib39.platform.services;
|
||||
|
||||
import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
|
||||
|
||||
/**
|
||||
* The interface Util helper.
|
||||
*/
|
||||
public interface IUtilHelper {
|
||||
/**
|
||||
* Gets block registry builder.
|
||||
*
|
||||
* @return the block registry builder
|
||||
*/
|
||||
BlockRegistryBuilder getBlockRegistryBuilder();
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
package top.r3944realms.lib39.util;
|
||||
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The interface Client only.
|
||||
*/
|
||||
public interface IClientOnly {
|
||||
/**
|
||||
* Check.
|
||||
*
|
||||
* @param runnable the runnable
|
||||
*/
|
||||
static void check(Runnable runnable) {
|
||||
if (Lib39.isClientEnvironment()) {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("This method should be called in ClientEnvironment");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check.
|
||||
*
|
||||
* @param runnable the runnable
|
||||
* @param fallback the fallback
|
||||
*/
|
||||
static void check(Runnable runnable, Runnable fallback) {
|
||||
if (Lib39.isClientEnvironment()) {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
fallback.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check t.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param supplier the supplier
|
||||
* @return the t
|
||||
*/
|
||||
static <T> T check(Supplier<T> supplier) {
|
||||
if (Lib39.isClientEnvironment()) {
|
||||
return supplier.get();
|
||||
}
|
||||
throw new RuntimeException("This method should be called in ClientEnvironment");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check t.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param supplier the supplier
|
||||
* @param fallback the fallback
|
||||
* @return the t
|
||||
*/
|
||||
static <T> T check(Supplier<T> supplier, Supplier<T> fallback) {
|
||||
if (Lib39.isClientEnvironment()) {
|
||||
return supplier.get();
|
||||
}
|
||||
return fallback.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
public net.minecraft.world.item.CreativeModeTabs f_268496_ # CACHED_PARAMETERS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_268478_ # PAINTING_COMPARATOR
|
||||
public net.minecraft.world.item.CreativeModeTabs f_257039_ # INVENTORY
|
||||
public net.minecraft.world.item.CreativeModeTabs f_257028_ # REDSTONE_BLOCKS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256968_ # INGREDIENTS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256917_ # HOTBAR
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256869_ # TOOLS_AND_UTILITIES
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256839_ # FOOD_AND_DRINKS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256837_ # OP_BLOCKS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256797_ # COMBAT
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256791_ # FUNCTIONAL_BLOCKS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256788_ # BUILDING_BLOCKS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256776_ # NATURAL_BLOCKS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256750_ # SEARCH
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256731_ # SPAWN_EGGS
|
||||
public net.minecraft.world.item.CreativeModeTabs f_256725_ # COLORED_BLOCKS
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
accessWidener v2 named
|
||||
# 不要用这个,太垃圾了,不支持parchment名
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "top.r3944realms.lib39.mixin",
|
||||
"refmap": "${mod_id}.refmap.json",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"carryon.MixinCarriedObjectRender",
|
||||
"minecraft.CreativeModeTabsAccessor"
|
||||
],
|
||||
"client": [
|
||||
"minecraft.ScreenAccessor"
|
||||
],
|
||||
"server": [
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"pack": {
|
||||
"description": "${mod_name}",
|
||||
"pack_format": 8
|
||||
}
|
||||
}
|
||||
8
config/jni-classes.txt
Normal file
8
config/jni-classes.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# JNI 头文件生成配置
|
||||
# 每行一个类全限定名,例如:
|
||||
# com.example.MyNativeClass
|
||||
# com.example.NativeUtils
|
||||
top.r3944realms.lib39.core.lang.ClassEncryptor
|
||||
top.r3944realms.lib39.core.lang.EncryptedClassLoader
|
||||
# 或者使用正则表达式自动匹配:
|
||||
# #auto:.*Native.*
|
||||
241
cpp/CMakeLists.txt
Normal file
241
cpp/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
cmake_minimum_required(VERSION 3.28)
|
||||
project(ENCRYPTED_CPP VERSION 1.0.0)
|
||||
|
||||
# 设置C++标准
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# ========== 目录设置 ==========
|
||||
# 项目根目录
|
||||
set(PROJECT_ROOT "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
# 源代码目录 - 使用cpp-src目录
|
||||
if(EXISTS "${PROJECT_ROOT}/cpp-src")
|
||||
set(SOURCE_DIR "${PROJECT_ROOT}/cpp-src")
|
||||
set(HEADER_DIR "${PROJECT_ROOT}/cpp-src")
|
||||
message(STATUS "Using cpp-src directory: ${CPP_SRC_DIR}")
|
||||
else()
|
||||
# 如果没有cpp-src目录,使用当前目录
|
||||
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
message(STATUS "Using current directory as source directory")
|
||||
endif()
|
||||
|
||||
# ========== 手动设置Java路径 ==========
|
||||
# 查找Java HOME
|
||||
if(DEFINED ENV{JAVA_HOME})
|
||||
set(JAVA_HOME $ENV{JAVA_HOME})
|
||||
message(STATUS "Found JAVA_HOME: ${JAVA_HOME}")
|
||||
else()
|
||||
# 尝试通过which或where命令查找java
|
||||
if(WIN32)
|
||||
execute_process(
|
||||
COMMAND where java
|
||||
OUTPUT_VARIABLE JAVA_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND which java
|
||||
OUTPUT_VARIABLE JAVA_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endif()
|
||||
|
||||
if(JAVA_PATH)
|
||||
# 从java路径推断JAVA_HOME
|
||||
get_filename_component(JAVA_HOME "${JAVA_PATH}" DIRECTORY)
|
||||
get_filename_component(JAVA_HOME "${JAVA_HOME}" DIRECTORY)
|
||||
message(STATUS "Inferred JAVA_HOME: ${JAVA_HOME}")
|
||||
else()
|
||||
message(WARNING "Java not found in PATH")
|
||||
set(JAVA_HOME "C:/Program Files/Java/jdk-21") # Windows默认路径
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# 设置Java包含目录
|
||||
set(JAVA_INCLUDE_DIRS
|
||||
"${JAVA_HOME}/include"
|
||||
)
|
||||
|
||||
# 添加平台特定include目录
|
||||
if(WIN32)
|
||||
list(APPEND JAVA_INCLUDE_DIRS "${JAVA_HOME}/include/win32")
|
||||
elseif(APPLE)
|
||||
list(APPEND JAVA_INCLUDE_DIRS "${JAVA_HOME}/include/darwin")
|
||||
else()
|
||||
list(APPEND JAVA_INCLUDE_DIRS "${JAVA_HOME}/include/linux")
|
||||
endif()
|
||||
|
||||
# 验证Java包含目录是否存在
|
||||
foreach(dir IN LISTS JAVA_INCLUDE_DIRS)
|
||||
if(NOT EXISTS "${dir}")
|
||||
message(WARNING "Java include directory not found: ${dir}")
|
||||
else()
|
||||
message(STATUS "Found Java include: ${dir}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# ========== 查找源文件和头文件 ==========
|
||||
# 查找所有的C++源文件
|
||||
file(GLOB SOURCE_FILES
|
||||
"${SOURCE_DIR}/*.cpp"
|
||||
"${SOURCE_DIR}/*.cxx"
|
||||
)
|
||||
|
||||
if(NOT SOURCE_FILES)
|
||||
# 如果没找到,尝试递归查找
|
||||
file(GLOB_RECURSE SOURCE_FILES
|
||||
"${SOURCE_DIR}/*.cpp"
|
||||
"${SOURCE_DIR}/*.cxx"
|
||||
"${SOURCE_DIR}/*.cc"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(SOURCE_FILES)
|
||||
message(STATUS "Found source files:")
|
||||
foreach(file ${SOURCE_FILES})
|
||||
message(STATUS " ${file}")
|
||||
endforeach()
|
||||
else()
|
||||
message(FATAL_ERROR "No source files found in ${SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
# 查找头文件
|
||||
file(GLOB HEADER_FILES
|
||||
"${HEADER_DIR}/*.h"
|
||||
"${HEADER_DIR}/*.hpp"
|
||||
)
|
||||
|
||||
# ========== 创建库目标 ==========
|
||||
# 库名称
|
||||
if(WIN32)
|
||||
set(LIBRARY_NAME "ClassEncrypt")
|
||||
else()
|
||||
set(LIBRARY_NAME "ClassEncrypt")
|
||||
endif()
|
||||
|
||||
# 添加库目标
|
||||
add_library(${LIBRARY_NAME} SHARED ${SOURCE_FILES}
|
||||
"src/EnhancedEncryptionMagic.cpp"
|
||||
src/guard/JByteArrayGuard.cpp)
|
||||
|
||||
# 设置输出名称和位置
|
||||
set_target_properties(${LIBRARY_NAME} PROPERTIES
|
||||
OUTPUT_NAME ${LIBRARY_NAME}
|
||||
DEBUG_POSTFIX "d"
|
||||
)
|
||||
|
||||
# 设置输出目录
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# ========== 包含目录 ==========
|
||||
target_include_directories(${LIBRARY_NAME} PRIVATE
|
||||
${JAVA_INCLUDE_DIRS}
|
||||
${HEADER_DIR}
|
||||
)
|
||||
|
||||
# ========== 编译选项 ==========
|
||||
if(MSVC)
|
||||
target_compile_options(${LIBRARY_NAME} PRIVATE
|
||||
/W3 # 警告级别3
|
||||
/WX- # 不将警告视为错误
|
||||
/EHsc # C++异常处理
|
||||
$<$<CONFIG:Debug>:/MDd> # 调试版本使用MDd
|
||||
$<$<CONFIG:Release>:/MD> # 发布版本使用MD
|
||||
$<$<CONFIG:RelWithDebInfo>:/MD>
|
||||
$<$<CONFIG:MinSizeRel>:/MD>
|
||||
)
|
||||
|
||||
# MSVC预处理器定义
|
||||
target_compile_definitions(${LIBRARY_NAME} PRIVATE
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
_WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
BUILDING_DLL
|
||||
JNIEXPORT=__declspec(dllexport)
|
||||
)
|
||||
else()
|
||||
target_compile_options(${LIBRARY_NAME} PRIVATE
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
-fPIC
|
||||
$<$<CONFIG:Debug>:-g -O0>
|
||||
$<$<CONFIG:Release>:-O2>
|
||||
$<$<CONFIG:RelWithDebInfo>:-O2 -g>
|
||||
$<$<CONFIG:MinSizeRel>:-Os>
|
||||
)
|
||||
|
||||
# GCC/Clang预处理器定义
|
||||
target_compile_definitions(${LIBRARY_NAME} PRIVATE
|
||||
BUILDING_DLL
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
target_compile_options(${LIBRARY_NAME} PRIVATE
|
||||
-stdlib=libc++
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ========== 链接库 ==========
|
||||
if(WIN32)
|
||||
# Windows链接库
|
||||
target_link_libraries(${LIBRARY_NAME} PRIVATE
|
||||
kernel32.lib
|
||||
user32.lib
|
||||
gdi32.lib
|
||||
winspool.lib
|
||||
shell32.lib
|
||||
ole32.lib
|
||||
oleaut32.lib
|
||||
uuid.lib
|
||||
comdlg32.lib
|
||||
advapi32.lib
|
||||
)
|
||||
else()
|
||||
# Linux/macOS链接库
|
||||
target_link_libraries(${LIBRARY_NAME} PRIVATE
|
||||
pthread
|
||||
dl
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
target_link_options(${LIBRARY_NAME} PRIVATE
|
||||
-undefined dynamic_lookup
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ========== 安装配置(可选) ==========
|
||||
if(NOT DEFINED CMAKE_SKIP_INSTALL_RULES)
|
||||
install(TARGETS ${LIBRARY_NAME}
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
||||
# 安装头文件
|
||||
if(HEADER_FILES)
|
||||
install(FILES ${HEADER_FILES} DESTINATION include)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ========== 输出总结信息 ==========
|
||||
message(STATUS "")
|
||||
message(STATUS "=========================================")
|
||||
message(STATUS "Project Configuration Summary")
|
||||
message(STATUS "=========================================")
|
||||
message(STATUS "Project: ${PROJECT_NAME}")
|
||||
message(STATUS "Version: ${PROJECT_VERSION}")
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
message(STATUS "Source directory: ${SOURCE_DIR}")
|
||||
message(STATUS "Header directory: ${HEADER_DIR}")
|
||||
message(STATUS "Java include dirs: ${JAVA_INCLUDE_DIRS}")
|
||||
message(STATUS "Target library: ${LIBRARY_NAME}")
|
||||
message(STATUS "C++ standard: ${CMAKE_CXX_STANDARD}")
|
||||
message(STATUS "Output directory: ${CMAKE_BINARY_DIR}")
|
||||
message(STATUS "=========================================")
|
||||
17
cpp/Config.cmake.in
Normal file
17
cpp/Config.cmake.in
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Config.cmake.in
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
# 查找依赖
|
||||
find_dependency(Java COMPONENTS Development)
|
||||
|
||||
if(@USE_OPENSSL@)
|
||||
find_dependency(OpenSSL REQUIRED)
|
||||
endif()
|
||||
|
||||
# 导入目标
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
|
||||
# 检查目标是否存在
|
||||
check_required_components(@PROJECT_NAME@)
|
||||
6
cpp/header/CMakeLists.txt
Normal file
6
cpp/header/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.28)
|
||||
|
||||
add_library( HEADER
|
||||
top_r3944realms_lib39_core_lang_ClassEncryptor.h
|
||||
top_r3944realms_lib39_core_lang_EncryptedClassLoader.h
|
||||
)
|
||||
37
cpp/header/top_r3944realms_lib39_core_lang_ClassEncryptor.h
Normal file
37
cpp/header/top_r3944realms_lib39_core_lang_ClassEncryptor.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class top_r3944realms_lib39_core_lang_ClassEncryptor */
|
||||
|
||||
#ifndef _Included_top_r3944realms_lib39_core_lang_ClassEncryptor
|
||||
#define _Included_top_r3944realms_lib39_core_lang_ClassEncryptor
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
||||
* Method: encryptClass
|
||||
* Signature: ([BLjava/lang/String;)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_encryptClass
|
||||
(JNIEnv *, jobject, jbyteArray, jstring);
|
||||
|
||||
/*
|
||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
||||
* Method: decryptClass
|
||||
* Signature: ([BLjava/lang/String;)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_decryptClass
|
||||
(JNIEnv *, jobject, jbyteArray, jstring);
|
||||
|
||||
/*
|
||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
||||
* Method: isEncryptedFile
|
||||
* Signature: ([B)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_isEncryptedFile
|
||||
(JNIEnv *, jobject, jbyteArray);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class top_r3944realms_lib39_core_lang_EncryptedClassLoader */
|
||||
|
||||
#ifndef _Included_top_r3944realms_lib39_core_lang_EncryptedClassLoader
|
||||
#define _Included_top_r3944realms_lib39_core_lang_EncryptedClassLoader
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: top_r3944realms_lib39_core_lang_EncryptedClassLoader
|
||||
* Method: decryptClass
|
||||
* Signature: ([BLjava/lang/String;)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_EncryptedClassLoader_decryptClass
|
||||
(JNIEnv *, jobject, jbyteArray, jstring);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
1
cpp/lib/CMakeLists.txt
Normal file
1
cpp/lib/CMakeLists.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
cmake_minimum_required(VERSION 3.28)
|
||||
7
cpp/src/CMakeLists.txt
Normal file
7
cpp/src/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.28)
|
||||
|
||||
|
||||
add_subdirectory(header)
|
||||
add_subdirectory(lib)
|
||||
|
||||
set_target_properties(CONST_LIB PROPERTIES LINKER_LANGUAGE CXX)
|
||||
451
cpp/src/EnhancedEncryptionMagic.cpp
Normal file
451
cpp/src/EnhancedEncryptionMagic.cpp
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions"
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#ifndef HEADER_JNI_H_
|
||||
#define HEADER_JNI_H_
|
||||
#include <jni.h>
|
||||
#endif
|
||||
#ifndef HEADER_P_H_
|
||||
#define HEADER_P_H_
|
||||
#include "guard/JByteArrayGuard.cpp"
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
// 字节序转换宏(跨平台)
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "UnreachableCallsOfFunction"
|
||||
#if defined(_WIN32)
|
||||
#include <winsock2.h>
|
||||
#pragma commen+t(lib, "ws2_32.lib")
|
||||
|
||||
#define htobe32(x) htonl(x)
|
||||
#define be32toh(x) ntohl(x)
|
||||
#define htobe16(x) htons(x)
|
||||
#define be16toh(x) ntohs(x)
|
||||
|
||||
// Windows下64位字节序转换需要自己实现
|
||||
static inline uint64_t htobe64(uint64_t x) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return ((uint64_t)htonl(x & 0xFFFFFFFF) << 32) | htonl(x >> 32);
|
||||
#else
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t be64toh(uint64_t x) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return ((uint64_t)ntohl(x & 0xFFFFFFFF) << 32) | ntohl(x >> 32);
|
||||
#else
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
#define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
#define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
#define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
#define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
#define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
|
||||
#elif defined(__linux__) || defined(__ANDROID__)
|
||||
#include <endian.h>
|
||||
// Linux下endian.h已经定义了这些宏
|
||||
|
||||
#else
|
||||
// 通用实现
|
||||
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
// GCC/Clang内置函数
|
||||
#define htobe32(x) __builtin_bswap32(x)
|
||||
#define be32toh(x) __builtin_bswap32(x)
|
||||
#define htobe16(x) __builtin_bswap16(x)
|
||||
#define be16toh(x) __builtin_bswap16(x)
|
||||
#define htobe64(x) __builtin_bswap64(x)
|
||||
#define be64toh(x) __builtin_bswap64(x)
|
||||
#else
|
||||
#define htobe32(x) (x)
|
||||
#define be32toh(x) (x)
|
||||
#define htobe16(x) (x)
|
||||
#define be16toh(x) (x)
|
||||
#define htobe64(x) (x)
|
||||
#define be64toh(x) (x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// 手动字节序转换函数(备用)
|
||||
static inline uint64_t manual_htobe64(uint64_t x) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return ((uint64_t)__builtin_bswap32(x & 0xFFFFFFFF) << 32) | __builtin_bswap32(x >> 32);
|
||||
#else
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t manual_be64toh(uint64_t x) {
|
||||
return manual_htobe64(x); // 对称操作
|
||||
}
|
||||
|
||||
namespace EnhancedEncryptionMagic {
|
||||
// 主魔数:0x4C494233 (ASCII: "LIB3")
|
||||
static const uint32_t MAGIC = 0x4C494233; // "LIB3" in hex
|
||||
|
||||
// 文件头结构 - 调整为实际大小
|
||||
struct EnhancedFileHeader {
|
||||
uint32_t magic; // 魔数: 0x4C494233 "LIB3" (4字节)
|
||||
uint16_t version_major; // 主版本号 (2字节)
|
||||
uint16_t version_minor; // 次版本号 (2字节)
|
||||
uint32_t flags; // 标志位 (4字节)
|
||||
uint32_t original_size; // 原始数据大小 (4字节)
|
||||
uint32_t encrypted_size; // 加密数据大小 (4字节)
|
||||
uint64_t timestamp; // 时间戳 (8字节)
|
||||
uint32_t checksum; // 校验和 (4字节)
|
||||
uint32_t reserved; // 保留字段 (4字节)
|
||||
// 总计: 4+2+2+4+4+4+8+4+4 = 36字节
|
||||
|
||||
// 编译器可能添加4字节填充到40字节,但我们应该按36字节处理
|
||||
};
|
||||
|
||||
// 计算实际结构体大小
|
||||
static const size_t CALCULATED_HEADER_SIZE =
|
||||
sizeof(uint32_t) + // magic
|
||||
sizeof(uint16_t) + // version_major
|
||||
sizeof(uint16_t) + // version_minor
|
||||
sizeof(uint32_t) + // flags
|
||||
sizeof(uint32_t) + // original_size
|
||||
sizeof(uint32_t) + // encrypted_size
|
||||
sizeof(uint64_t) + // timestamp
|
||||
sizeof(uint32_t) + // checksum
|
||||
sizeof(uint32_t); // reserved
|
||||
|
||||
static const size_t HEADER_SIZE = CALCULATED_HEADER_SIZE;
|
||||
|
||||
// 标志位定义
|
||||
namespace Flags {
|
||||
static const uint32_t COMPRESSED = 0x00000001; // 是否压缩
|
||||
static const uint32_t SIGNED = 0x00000002; // 是否签名
|
||||
static const uint32_t ENCRYPTED = 0x00000004; // 是否加密
|
||||
static const uint32_t VALIDATED = 0x00000008; // 是否验证
|
||||
};
|
||||
|
||||
// 创建文件头
|
||||
static inline EnhancedFileHeader createHeader(uint32_t originalSize, uint32_t encryptedSize) {
|
||||
EnhancedFileHeader header;
|
||||
memset(&header, 0, sizeof(header)); // 清零初始化
|
||||
|
||||
header.magic = MAGIC;
|
||||
header.version_major = 1;
|
||||
header.version_minor = 0;
|
||||
header.flags = Flags::ENCRYPTED;
|
||||
header.original_size = originalSize;
|
||||
header.encrypted_size = encryptedSize;
|
||||
header.timestamp = static_cast<uint64_t>(time(nullptr));
|
||||
header.checksum = 0; // 将在之后计算
|
||||
header.reserved = 0;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
// 字节序安全的内存复制函数
|
||||
static inline void writeUint32(jbyte* buffer, uint32_t value, size_t offset) {
|
||||
uint32_t networkValue = htobe32(value);
|
||||
memcpy(buffer + offset, &networkValue, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static inline void writeUint16(jbyte* buffer, uint16_t value, size_t offset) {
|
||||
uint16_t networkValue = htobe16(value);
|
||||
memcpy(buffer + offset, &networkValue, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
static inline void writeUint64(jbyte* buffer, uint64_t value, size_t offset) {
|
||||
uint64_t networkValue = htobe64(value);
|
||||
memcpy(buffer + offset, &networkValue, sizeof(uint64_t));
|
||||
}
|
||||
|
||||
static inline uint32_t readUint32(const jbyte* buffer, size_t offset) {
|
||||
uint32_t networkValue;
|
||||
memcpy(&networkValue, buffer + offset, sizeof(uint32_t));
|
||||
return be32toh(networkValue);
|
||||
}
|
||||
|
||||
static inline uint16_t readUint16(const jbyte* buffer, size_t offset) {
|
||||
uint16_t networkValue;
|
||||
memcpy(&networkValue, buffer + offset, sizeof(uint16_t));
|
||||
return be16toh(networkValue);
|
||||
}
|
||||
|
||||
static inline uint64_t readUint64(const jbyte* buffer, size_t offset) {
|
||||
uint64_t networkValue;
|
||||
memcpy(&networkValue, buffer + offset, sizeof(uint64_t));
|
||||
return be64toh(networkValue);
|
||||
}
|
||||
|
||||
// 验证文件头
|
||||
static inline bool validateHeader(const EnhancedFileHeader& header) {
|
||||
return header.magic == MAGIC &&
|
||||
header.version_major == 1 &&
|
||||
header.version_minor == 0 &&
|
||||
(header.flags & Flags::ENCRYPTED) != 0;
|
||||
}
|
||||
|
||||
// 计算校验和(简单的CRC32替代)
|
||||
static inline uint32_t calculateChecksum(const jbyte* data, jsize length) {
|
||||
if (!data || length <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
|
||||
|
||||
for (jsize i = 0; i < length; i++) {
|
||||
crc ^= bytes[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (crc & 1) {
|
||||
crc = (crc >> 1) ^ 0xEDB88320;
|
||||
} else {
|
||||
crc = crc >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
// 更新文件头的校验和
|
||||
static inline void updateChecksum(EnhancedFileHeader& header, const jbyte* data, jsize length) {
|
||||
header.checksum = calculateChecksum(data, length);
|
||||
}
|
||||
|
||||
// 验证数据校验和
|
||||
static inline bool verifyChecksum(const EnhancedFileHeader& header, const jbyte* data, jsize length) {
|
||||
uint32_t calculated = calculateChecksum(data, length);
|
||||
return header.checksum == calculated;
|
||||
}
|
||||
|
||||
// 将文件头写入字节数组(使用网络字节序)
|
||||
static inline void writeHeaderToBytes(const EnhancedFileHeader& header, jbyte* buffer) {
|
||||
if (!buffer) return;
|
||||
|
||||
size_t offset = 0;
|
||||
writeUint32(buffer, header.magic, offset); offset += 4;
|
||||
writeUint16(buffer, header.version_major, offset); offset += 2;
|
||||
writeUint16(buffer, header.version_minor, offset); offset += 2;
|
||||
writeUint32(buffer, header.flags, offset); offset += 4;
|
||||
writeUint32(buffer, header.original_size, offset); offset += 4;
|
||||
writeUint32(buffer, header.encrypted_size, offset); offset += 4;
|
||||
writeUint64(buffer, header.timestamp, offset); offset += 8;
|
||||
writeUint32(buffer, header.checksum, offset); offset += 4;
|
||||
writeUint32(buffer, header.reserved, offset); offset += 4;
|
||||
}
|
||||
|
||||
// 从字节数组读取文件头
|
||||
static inline EnhancedFileHeader readHeaderFromBytes(const jbyte* buffer) {
|
||||
EnhancedFileHeader header{};
|
||||
memset(&header, 0, sizeof(header));
|
||||
|
||||
if (!buffer) return header;
|
||||
|
||||
size_t offset = 0;
|
||||
header.magic = readUint32(buffer, offset); offset += 4;
|
||||
header.version_major = readUint16(buffer, offset); offset += 2;
|
||||
header.version_minor = readUint16(buffer, offset); offset += 2;
|
||||
header.flags = readUint32(buffer, offset); offset += 4;
|
||||
header.original_size = readUint32(buffer, offset); offset += 4;
|
||||
header.encrypted_size = readUint32(buffer, offset); offset += 4;
|
||||
header.timestamp = readUint64(buffer, offset); offset += 8;
|
||||
header.checksum = readUint32(buffer, offset); offset += 4;
|
||||
header.reserved = readUint32(buffer, offset); offset += 4;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
// 将文件头格式化为可读字符串
|
||||
static inline std::string headerToString(const EnhancedFileHeader& header) {
|
||||
char magicStr[5] = {0};
|
||||
memcpy(magicStr, &header.magic, 4);
|
||||
|
||||
char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"Magic: %s (0x%08X)\n"
|
||||
"Version: %d.%d\n"
|
||||
"Flags: 0x%08X\n"
|
||||
"Original Size: %u bytes\n"
|
||||
"Encrypted Size: %u bytes\n"
|
||||
"Timestamp: %llu\n"
|
||||
"Checksum: 0x%08X\n"
|
||||
"Reserved: 0x%08X",
|
||||
magicStr, header.magic,
|
||||
header.version_major, header.version_minor,
|
||||
header.flags,
|
||||
header.original_size,
|
||||
header.encrypted_size,
|
||||
(unsigned long long)header.timestamp,
|
||||
header.checksum,
|
||||
header.reserved);
|
||||
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
// 验证文件是否完整
|
||||
static inline bool validateFileIntegrity(const EnhancedFileHeader& header,
|
||||
const jbyte* encryptedData,
|
||||
jsize encryptedDataSize) {
|
||||
// 检查大小是否匹配
|
||||
if (header.encrypted_size != encryptedDataSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查校验和
|
||||
return verifyChecksum(header, encryptedData, encryptedDataSize);
|
||||
}
|
||||
|
||||
// 加密函数指针类型
|
||||
typedef void (*EncryptFunc)(jbyte*, jsize, const char*, int);
|
||||
|
||||
// 创建完整的加密文件
|
||||
static inline jbyteArray createEncryptedFile(JNIEnv* env,
|
||||
const jbyte* originalData,
|
||||
jsize originalSize,
|
||||
const char* key,
|
||||
size_t keyLen,
|
||||
EncryptFunc encryptFunc) {
|
||||
|
||||
if (!env || !originalData || originalSize <= 0 || !key || keyLen <= 0 || !encryptFunc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 1. 创建加密数据数组
|
||||
jbyteArray encryptedDataArray = env->NewByteArray(originalSize);
|
||||
if (!encryptedDataArray) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
// 使用局部作用域确保 encryptedDataGuard 在加密后释放
|
||||
JByteArrayGuard encryptedDataGuard(env, encryptedDataArray, false, JNI_ABORT);
|
||||
if (!encryptedDataGuard.isValid()) {
|
||||
env->DeleteLocalRef(encryptedDataArray);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 复制并加密数据
|
||||
memcpy(encryptedDataGuard.get(), originalData, originalSize);
|
||||
encryptFunc(encryptedDataGuard.get(), originalSize, key, keyLen);
|
||||
|
||||
// encryptedDataGuard 析构函数会自动以 JNI_ABORT 模式释放
|
||||
}
|
||||
|
||||
// 注意:这里已经释放了加密数据,需要重新获取
|
||||
JByteArrayGuard encryptedDataGuard2(env, encryptedDataArray);
|
||||
if (!encryptedDataGuard2.isValid()) {
|
||||
env->DeleteLocalRef(encryptedDataArray);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 2. 创建文件头
|
||||
EnhancedFileHeader header = createHeader(originalSize, originalSize);
|
||||
updateChecksum(header, encryptedDataGuard2.get(), originalSize);
|
||||
|
||||
// 3. 创建最终结果
|
||||
jsize totalSize = HEADER_SIZE + originalSize;
|
||||
jbyteArray result = env->NewByteArray(totalSize);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JByteArrayGuard resultGuard(env, result);
|
||||
if (!resultGuard.isValid()) {
|
||||
env->DeleteLocalRef(result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 4. 写入数据
|
||||
writeHeaderToBytes(header, resultGuard.get());
|
||||
memcpy(resultGuard.get() + HEADER_SIZE, encryptedDataGuard2.get(), originalSize);
|
||||
|
||||
// 5. 显式提交修改
|
||||
resultGuard.commit(); // 提交修改到 Java 端
|
||||
// resultGuard 析构时不会再释放
|
||||
|
||||
// 6. 清理中间数组
|
||||
env->DeleteLocalRef(encryptedDataArray);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从加密文件中提取数据
|
||||
static inline jbyteArray extractFromEncryptedFile(JNIEnv* env,
|
||||
const jbyte* fileData,
|
||||
jsize fileSize,
|
||||
const char* key,
|
||||
size_t keyLen,
|
||||
EncryptFunc decryptFunc,
|
||||
bool* isValid) {
|
||||
|
||||
if (isValid) *isValid = false;
|
||||
|
||||
if (!env || !fileData || fileSize < HEADER_SIZE || !key || keyLen <= 0 || !decryptFunc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 读取文件头
|
||||
EnhancedFileHeader header = readHeaderFromBytes(fileData);
|
||||
|
||||
// 验证文件头
|
||||
if (!validateHeader(header)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 检查文件大小
|
||||
jsize expectedSize = HEADER_SIZE + header.encrypted_size;
|
||||
if (fileSize != expectedSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 提取加密数据
|
||||
const jbyte* encryptedData = fileData + HEADER_SIZE;
|
||||
|
||||
// 验证完整性
|
||||
if (!validateFileIntegrity(header, encryptedData, header.encrypted_size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 创建结果数组
|
||||
jbyteArray result = env->NewByteArray(header.original_size);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
JByteArrayGuard resultDataGuard(env, result);
|
||||
|
||||
if (!resultDataGuard.isValid()) {
|
||||
env->DeleteLocalRef(result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jbyte* resultData = resultDataGuard.get();
|
||||
|
||||
// 复制加密数据
|
||||
memcpy(resultData, encryptedData, header.original_size);
|
||||
|
||||
// 解密数据
|
||||
decryptFunc(resultData, header.original_size, key, keyLen);
|
||||
|
||||
if (isValid) *isValid = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 验证是否为加密文件(不读取整个文件)
|
||||
static inline bool isEncryptedFile(const jbyte* fileData, jsize fileSize) {
|
||||
if (fileSize < HEADER_SIZE || !fileData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EnhancedFileHeader header = readHeaderFromBytes(fileData);
|
||||
return validateHeader(header);
|
||||
}
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
#pragma clang diagnostic pop
|
||||
234
cpp/src/SimpleClassEncrypt.cpp
Normal file
234
cpp/src/SimpleClassEncrypt.cpp
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
#pragma once
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <header/top_r3944realms_lib39_core_lang_ClassEncryptor.h>
|
||||
#include <header/top_r3944realms_lib39_core_lang_EncryptedClassLoader.h>
|
||||
#include "EnhancedEncryptionMagic.cpp"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-compare"
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#include <cstdint>
|
||||
|
||||
#define JNIEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#define JNIEXPORT
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 简单的XOR加密/解密
|
||||
*/
|
||||
static void xorEncrypt(jbyte* data, jsize dataLen, const char* key, int keyLen) {
|
||||
if (!data || !key || keyLen == 0) return;
|
||||
|
||||
for (jsize i = 0; i < dataLen; i++) {
|
||||
data[i] ^= key[i % keyLen];
|
||||
}
|
||||
}
|
||||
|
||||
static int safeStrlen(const char* str) {
|
||||
return str ? (int)strlen(str) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数据是否为有效的Java类文件
|
||||
* @param data 字节码
|
||||
* @param dataLen 字节码长度
|
||||
* @return 是否有效
|
||||
*/
|
||||
static bool isValidJavaClass(const jbyte* data, jsize dataLen) {
|
||||
// Java类文件魔数:0xCAFEBABE
|
||||
return dataLen >= 4 &&
|
||||
data[0] == (jbyte)0xCA &&
|
||||
data[1] == (jbyte)0xFE &&
|
||||
data[2] == (jbyte)0xBA &&
|
||||
data[3] == (jbyte)0xBE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录错误信息
|
||||
*/
|
||||
void logError(const char* message) {
|
||||
#ifdef DEBUG
|
||||
std::cerr << "[JNI Error] " << message << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全的字符串转换
|
||||
*/
|
||||
std::string jstringToString(JNIEnv* env, jstring jstr) {
|
||||
if (!jstr) return "";
|
||||
|
||||
const char* chars = env->GetStringUTFChars(jstr, nullptr);
|
||||
if (!chars) return "";
|
||||
|
||||
std::string result(chars);
|
||||
env->ReleaseStringUTFChars(jstr, chars);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t calculateChecksum(const jbyte* data, jsize length) {
|
||||
uint32_t checksum = 0;
|
||||
for (jsize i = 0; i < length; i++) {
|
||||
checksum += static_cast<uint8_t>(data[i]);
|
||||
checksum = (checksum << 1) | (checksum >> 31); // 简单旋转
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
|
||||
// ==================== JNI函数实现 ====================
|
||||
using namespace EnhancedEncryptionMagic;
|
||||
|
||||
/*
|
||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
||||
* Method: decryptClass
|
||||
* Signature: ([BLjava/lang/String;)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_decryptClass
|
||||
(JNIEnv *env, jobject obj, jbyteArray encryptedData, jstring key) {
|
||||
|
||||
if (!encryptedData || !key) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jsize fileSize = env->GetArrayLength(encryptedData);
|
||||
if (fileSize == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
JByteArrayGuard fileDataGuard(env, encryptedData);
|
||||
jbyte* fileData = fileDataGuard.get();
|
||||
if (!fileDataGuard.isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string keyStr = jstringToString(env, key);
|
||||
size_t keyLen = keyStr.length();
|
||||
bool isValid = keyLen > 0;
|
||||
// 尝试从加密文件中提取数据
|
||||
jbyteArray result = EnhancedEncryptionMagic::extractFromEncryptedFile(
|
||||
env, fileData, fileSize, keyStr.c_str(), keyLen, xorEncrypt, &isValid);
|
||||
if (!isValid || !result) {
|
||||
// 如果不是有效的加密文件,返回原始数据
|
||||
return encryptedData;
|
||||
}
|
||||
|
||||
// 验证解密后的数据是否为有效的Java类文件
|
||||
JByteArrayGuard resultGuard(env, result);
|
||||
jsize resultLen = resultGuard.size();
|
||||
jbyte* resultData = resultGuard.get();
|
||||
|
||||
if (!resultGuard.isValid()) {
|
||||
env->DeleteLocalRef(result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool validClass = isValidJavaClass(resultData, resultLen);
|
||||
|
||||
if (!validClass) {
|
||||
env->DeleteLocalRef(result);
|
||||
return nullptr; // 解密后的数据不是有效的Java类
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
||||
* Method: encryptClass
|
||||
* Signature: ([BLjava/lang/String;)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_encryptClass
|
||||
(JNIEnv *env, jobject obj, jbyteArray classData, jstring key) {
|
||||
|
||||
if (!classData || !key) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JByteArrayGuard jGuard(env, classData);
|
||||
if (jGuard.isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jsize dataLen = jGuard.size();
|
||||
jbyte* data = jGuard.get();
|
||||
|
||||
const char* keyStr = env->GetStringUTFChars(key, nullptr);
|
||||
if (!keyStr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 验证输入数据是否为有效的Java类文件
|
||||
if (!isValidJavaClass(data, dataLen)) {
|
||||
env->ReleaseStringUTFChars(key, keyStr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int keyLen = safeStrlen(keyStr);
|
||||
|
||||
// 使用增强版创建加密文件
|
||||
jbyteArray result = EnhancedEncryptionMagic::createEncryptedFile(
|
||||
env, data, dataLen, keyStr, keyLen, xorEncrypt);
|
||||
|
||||
env->ReleaseStringUTFChars(key, keyStr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
||||
* Method: isEncryptedFile
|
||||
* Signature: ([B)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_isEncryptedFile
|
||||
(JNIEnv *env, jobject obj, jbyteArray fileData) {
|
||||
|
||||
if (!fileData) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
JByteArrayGuard dataGuard(env, fileData);
|
||||
if (!dataGuard.isValid()) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
if (dataGuard.size() < EnhancedEncryptionMagic::HEADER_SIZE) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
// 验证是否为加密文件
|
||||
bool isEncrypted = EnhancedEncryptionMagic::isEncryptedFile(dataGuard.get(), dataGuard.size());
|
||||
|
||||
return isEncrypted ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: top_r3944realms_lib39_core_lang_EncryptedClassLoader
|
||||
* Method: decryptClass
|
||||
* Signature: ([BLjava/lang/String;)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_EncryptedClassLoader_decryptClass
|
||||
(JNIEnv *env, jobject obj, jbyteArray encryptedData, jstring key) {
|
||||
return Java_top_r3944realms_lib39_core_lang_ClassEncryptor_decryptClass(env, obj, encryptedData, key);
|
||||
}
|
||||
|
||||
// JNI库初始化和卸载函数
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
#ifdef _WIN32
|
||||
// Windows下需要初始化Winsock
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
#endif
|
||||
|
||||
return JNI_VERSION_1_8;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
5
cpp/src/guard/CMakeLists.txt
Normal file
5
cpp/src/guard/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.28)
|
||||
|
||||
add_library(J_GUARD
|
||||
JByteArrayGuard.cpp
|
||||
)
|
||||
82
cpp/src/guard/JByteArrayGuard.cpp
Normal file
82
cpp/src/guard/JByteArrayGuard.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
#ifndef HEADER_JNI_H_
|
||||
#define HEADER_JNI_H_
|
||||
#include <jni.h>
|
||||
#endif
|
||||
class JByteArrayGuard {
|
||||
private:
|
||||
JNIEnv* env;
|
||||
jbyteArray array;
|
||||
jbyte* data;
|
||||
jsize length;
|
||||
bool isCritical;
|
||||
jint releaseMode;
|
||||
public:
|
||||
JByteArrayGuard(JNIEnv* env, jbyteArray array, bool critical = false, jint releaseMode = 0)
|
||||
: env(env), array(array), data(nullptr), length(0), isCritical(critical), releaseMode(releaseMode)
|
||||
{
|
||||
if (array) {
|
||||
length = env->GetArrayLength(array);
|
||||
if (isCritical) {
|
||||
data = (jbyte*) env->GetPrimitiveArrayCritical(array, nullptr);
|
||||
} else {
|
||||
data = env->GetByteArrayElements(array, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
~JByteArrayGuard() {
|
||||
release();
|
||||
}
|
||||
// 显式释放方法
|
||||
void release() {
|
||||
if (data && array) {
|
||||
if (isCritical) {
|
||||
env->ReleasePrimitiveArrayCritical(array, data, releaseMode);
|
||||
} else {
|
||||
env->ReleaseByteArrayElements(array, data, releaseMode);
|
||||
}
|
||||
data = nullptr; // 防止重复释放
|
||||
array = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 提交修改但不释放(用于返回结果的情况)
|
||||
void commit() {
|
||||
if (data && array) {
|
||||
if (isCritical) {
|
||||
env->ReleasePrimitiveArrayCritical(array, data, 0);
|
||||
} else {
|
||||
env->ReleaseByteArrayElements(array, data, 0);
|
||||
}
|
||||
data = nullptr; // 标记为已释放,防止析构函数再次释放
|
||||
array = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 丢弃修改
|
||||
void abort() {
|
||||
if (data && array) {
|
||||
if (isCritical) {
|
||||
env->ReleasePrimitiveArrayCritical(array, data, JNI_ABORT);
|
||||
} else {
|
||||
env->ReleaseByteArrayElements(array, data, JNI_ABORT);
|
||||
}
|
||||
data = nullptr;
|
||||
array = nullptr;
|
||||
}
|
||||
}
|
||||
jbyte* get() { return data; }
|
||||
jsize size() const { return length; }
|
||||
bool isValid() { return data != nullptr; }
|
||||
|
||||
JByteArrayGuard(const JByteArrayGuard&) = delete;
|
||||
JByteArrayGuard& operator=(const JByteArrayGuard&) = delete;
|
||||
JByteArrayGuard(JByteArrayGuard&& other) noexcept
|
||||
: env(other.env), array(other.array), data(other.data),
|
||||
length(other.length), isCritical(other.isCritical),
|
||||
releaseMode(other.releaseMode) {
|
||||
other.array = nullptr;
|
||||
other.data = nullptr;
|
||||
other.length = 0;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
import net.fabricmc.loom.task.RemapJarTask
|
||||
|
||||
plugins {
|
||||
id 'multiloader-loader'
|
||||
id 'fabric-loom'
|
||||
}
|
||||
repositories {
|
||||
maven { url 'https://maven.covers1624.net/' }
|
||||
}
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${minecraft_version}"
|
||||
mappings loom.layered {
|
||||
officialMojangMappings()
|
||||
parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip")
|
||||
}
|
||||
modImplementation(group: 'tschipp.carryon', name: 'carryon-fabric-1.20.1', version: '2.1.2.7') {
|
||||
transitive = false
|
||||
}
|
||||
modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}"
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}"
|
||||
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1'
|
||||
implementation project(":common")
|
||||
modImplementation "curse.maven:jade-324717:6291330"
|
||||
testImplementation "net.fabricmc:fabric-loader-junit:${fabric_loader_version}"
|
||||
localRuntime 'net.covers1624:DevLogin:0.1.0.5'
|
||||
}
|
||||
|
||||
loom {
|
||||
def commonResources = project(':common').file('src/main/resources/').getAbsolutePath().toString()
|
||||
def fabricResources = file('src/main/resources/').getAbsolutePath().toString()
|
||||
def fabricBuildResources = file('build/resources/main/').getAbsolutePath().toString()
|
||||
def commonBuildResources = project(':common').file('build/resources/main/').getAbsolutePath().toString()
|
||||
def generatedOutput = project(':common').file('src/generated/resources/').getAbsolutePath().toString()
|
||||
|
||||
if (project(":common").file("src/main/resources/${mod_id}.accesswidener").exists()) {
|
||||
accessWidenerPath.set(project(":common").file("src/main/resources/${mod_id}.accesswidener"))
|
||||
}
|
||||
mixin {
|
||||
defaultRefmapName.set("${mod_id}.refmap.json")
|
||||
}
|
||||
runs {
|
||||
client {
|
||||
client()
|
||||
ideConfigGenerated true
|
||||
programArgs '--launch_target', 'net.fabricmc.loader.impl.launch.knot.KnotClient'
|
||||
mainClass.set 'net.covers1624.devlogin.DevLogin'
|
||||
setConfigName("Fabric Client")
|
||||
ideConfigGenerated(true)
|
||||
runDir("run")
|
||||
def args = [
|
||||
"-Dlib39.modid=${mod_id}".toString(),
|
||||
"-Dlib39.output=${generatedOutput}".toString(),
|
||||
"-Dlib39.existing.fabric=${fabricResources}".toString(),
|
||||
"-Dlib39.existing.common=${commonResources}".toString(),
|
||||
"-Dlib39.existing.fabricBuild=${fabricBuildResources}".toString(),
|
||||
"-Dlib39.existing.commonBuild=${commonBuildResources}".toString()
|
||||
]
|
||||
vmArgs.addAll(args)
|
||||
|
||||
// 也可以添加JVM参数
|
||||
vmArgs.addAll(
|
||||
"-Dfabric.log.level=info",
|
||||
"-Dmixin.debug.export=true"
|
||||
)
|
||||
}
|
||||
|
||||
server {
|
||||
server()
|
||||
serverWithGui()
|
||||
setConfigName("Fabric Server")
|
||||
ideConfigGenerated(true)
|
||||
runDir("run")
|
||||
|
||||
def args = [
|
||||
"-Dlib39.modid=${mod_id}".toString(),
|
||||
"-Dlib39.output=${generatedOutput}".toString(),
|
||||
"-Dlib39.existing.fabric=${fabricResources}".toString(),
|
||||
"-Dlib39.existing.common=${commonResources}".toString(),
|
||||
"-Dlib39.existing.fabricBuild=${fabricBuildResources}".toString(),
|
||||
"-Dlib39.existing.commonBuild=${commonBuildResources}".toString()
|
||||
]
|
||||
vmArgs.addAll(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('sourcesJar', Jar) {
|
||||
dependsOn classes
|
||||
dependsOn project(':common').tasks.named('sourcesJar') // 显式依赖common的source
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
archiveClassifier.set('sources')
|
||||
from sourceSets.main.allSource
|
||||
from project(':common').sourceSets.main.allSource
|
||||
}
|
||||
|
||||
// 配置javadoc任务
|
||||
tasks.named('javadoc', Javadoc) {
|
||||
source project(':common').sourceSets.main.allJava
|
||||
dependsOn project(':common').tasks.named('javadoc') // 显式依赖common的javadoc
|
||||
source sourceSets.main.allJava
|
||||
classpath = configurations.compileClasspath
|
||||
classpath += project(':common').sourceSets.main.compileClasspath
|
||||
options.encoding = 'UTF-8'
|
||||
options.charSet = 'UTF-8'
|
||||
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||
options.memberLevel = JavadocMemberLevel.PUBLIC
|
||||
options.addBooleanOption('Xdoclint:none', true)
|
||||
options.addStringOption('doctitle', "${mod_id} ${minecraft_version} ${version} Javadoc")
|
||||
}
|
||||
|
||||
// 配置javadocJar任务
|
||||
tasks.named('javadocJar', Jar) {
|
||||
dependsOn javadoc
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
archiveClassifier.set('javadoc')
|
||||
from javadoc.destinationDir
|
||||
from project(':common').javadoc.destinationDir
|
||||
}
|
||||
|
||||
// 确保build任务包含所有需要的jar
|
||||
tasks.named('build') {
|
||||
dependsOn tasks.named('sourcesJar')
|
||||
dependsOn tasks.named('javadocJar')
|
||||
}
|
||||
|
||||
// 配置remap任务以包含sources和javadoc
|
||||
remapJar {
|
||||
dependsOn tasks.named('sourcesJar')
|
||||
dependsOn tasks.named('javadocJar')
|
||||
inputFile.set(tasks.named('jar').get().archiveFile)
|
||||
addNestedDependencies = false
|
||||
}
|
||||
|
||||
remapSourcesJar {
|
||||
dependsOn tasks.named('sourcesJar')
|
||||
inputFile.set(tasks.named('sourcesJar').get().archiveFile)
|
||||
}
|
||||
|
||||
// 为javadocJar创建remap任务
|
||||
tasks.register('remapJavadocJar', RemapJarTask) {
|
||||
dependsOn tasks.named('javadocJar')
|
||||
inputFile.set(tasks.named('javadocJar').get().archiveFile)
|
||||
archiveClassifier.set('javadoc')
|
||||
addNestedDependencies = false
|
||||
}
|
||||
|
||||
// 将remapped artifacts添加到发布配置
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
// 重置artifactsId
|
||||
artifactId = "${mod_id}-fabric-${minecraft_version}"
|
||||
artifacts.clear()
|
||||
// 手动添加需要的artifacts
|
||||
artifact(remapJar) {
|
||||
builtBy remapJar
|
||||
}
|
||||
artifact(remapSourcesJar) {
|
||||
builtBy remapSourcesJar
|
||||
classifier = 'sources'
|
||||
}
|
||||
artifact(remapJavadocJar) {
|
||||
builtBy remapJavadocJar
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
pom {
|
||||
name = 'Lib39'
|
||||
description = 'Lib39 is a general-purpose dependency library for Minecraft mods.'
|
||||
url = 'https://github.com/3944Realms/lib39'
|
||||
|
||||
properties = [
|
||||
'minecraft.version': project.minecraft_version,
|
||||
'mod.version': project.version,
|
||||
'fabric.version': project.fabric_version,
|
||||
'java.version': '17'
|
||||
]
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = 'MIT'
|
||||
url = 'https://raw.githubusercontent.com/3944Realms/lib39/refs/heads/main/LICENSE'
|
||||
distribution = 'repo'
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'R3944Realms'
|
||||
name = "${mod_author}"
|
||||
email = 'f256198830@hotmail.com'
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection = 'scm:git:https://github.com/3944Realms/lib39.git'
|
||||
developerConnection = 'scm:git:ssh://git@github.com:3944Realms/lib39.git'
|
||||
url = 'https://github.com/3944Realms/lib39'
|
||||
tag = 'main'
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system = 'GitHub'
|
||||
url = 'https://github.com/3944Realms/lib39/issues'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.named('generateMetadataFileForMavenJavaPublication') {
|
||||
dependsOn tasks.named('remapJavadocJar')
|
||||
dependsOn tasks.named('remapJar')
|
||||
dependsOn tasks.named('remapSourcesJar')
|
||||
}
|
||||
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package top.r3944realms.lib39;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
|
||||
import top.r3944realms.lib39.core.register.FabricLib39BlockEntities;
|
||||
import top.r3944realms.lib39.core.register.FabricLib39Blocks;
|
||||
import top.r3944realms.lib39.core.register.FabricLib39Items;
|
||||
import top.r3944realms.lib39.core.register.FabricLib39SoundEvents;
|
||||
import top.r3944realms.lib39.example.FabricLib39Example;
|
||||
|
||||
/**
|
||||
* The type Lib 39 fabric.
|
||||
*/
|
||||
public class Lib39Fabric implements ModInitializer {
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
Lib39.initialize();
|
||||
Lib39.LOGGER.info("[Lib39-Fabric] Hello Fabric!");
|
||||
FabricLib39Blocks.init();
|
||||
FabricLib39Items.init();
|
||||
FabricLib39BlockEntities.init();
|
||||
FabricLib39SoundEvents.init();
|
||||
FabricCommonEventHandler.initCommon();
|
||||
if (Lib39.shouldRegisterExamples()) {
|
||||
Lib39.LOGGER.info("[Lib39-Fabric] Registering Examples");
|
||||
registerExamples();
|
||||
}
|
||||
Lib39.LOGGER.info("[Lib39-Fabric] Finished Initializing.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Register examples.
|
||||
*/
|
||||
static void registerExamples() {
|
||||
Lib39.LOGGER.info("[Lib39-Fabric] Starting example demonstrations");
|
||||
try {
|
||||
// 创建示例实例并演示功能
|
||||
FabricLib39Example example = new FabricLib39Example();
|
||||
example.demonstrateFeature();
|
||||
|
||||
Lib39.LOGGER.info("[Lib39-Fabric] Example demonstrations completed successfully");
|
||||
} catch (Exception e) {
|
||||
Lib39.LOGGER.error("[Lib39-Fabric] Failed to demonstrate examples", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package top.r3944realms.lib39;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||
import top.r3944realms.lib39.client.BlockEntityRendererRegistry;
|
||||
import top.r3944realms.lib39.client.ItemRendererRegistry;
|
||||
import top.r3944realms.lib39.client.LayerDefinitionRegistry;
|
||||
import top.r3944realms.lib39.client.ShaderRegistry;
|
||||
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
|
||||
import top.r3944realms.lib39.core.network.FabricNetworkHandler;
|
||||
|
||||
/**
|
||||
* The type Lib 39 fabric client.
|
||||
*/
|
||||
public class Lib39FabricClient implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientLifecycleEvents.CLIENT_STARTED.register( r -> {
|
||||
ShaderRegistry.register();
|
||||
LayerDefinitionRegistry.register();
|
||||
BlockEntityRendererRegistry.register();
|
||||
ItemRendererRegistry.register();
|
||||
FabricNetworkHandler.registerClientReceivers();
|
||||
FabricCommonEventHandler.initClient();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package top.r3944realms.lib39.api.callback;
|
||||
|
||||
/**
|
||||
* 结果枚举
|
||||
*/
|
||||
public enum ActionResult {
|
||||
/**
|
||||
* 继续处理
|
||||
*/
|
||||
PASS,
|
||||
|
||||
/**
|
||||
* 取消
|
||||
*/
|
||||
CANCEL,
|
||||
|
||||
/**
|
||||
* 允许加入但停止后续处理
|
||||
*/
|
||||
SUCCESS
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
package top.r3944realms.lib39.api.callback;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Fabric版的铁砧更新事件,对应Forge的AnvilUpdateEvent
|
||||
*/
|
||||
public class AnvilUpdateCallback {
|
||||
|
||||
/**
|
||||
* 铁砧更新事件接口
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AnvilUpdate {
|
||||
/**
|
||||
* 当铁砧内容改变时调用
|
||||
*
|
||||
* @param left 左侧输入槽的物品
|
||||
* @param right 右侧输入槽的物品
|
||||
* @param outputSlot 输出槽的物品
|
||||
* @param name 输入的名字
|
||||
* @param baseCost 基础花费
|
||||
* @param player 玩家
|
||||
* @return AnvilUpdateResult 包含处理结果,如果返回null则继续默认处理
|
||||
*/
|
||||
AnvilUpdateResult onAnvilUpdate(ItemStack left, ItemStack right, ItemStack outputSlot,
|
||||
String name, int baseCost, Player player);
|
||||
}
|
||||
|
||||
/**
|
||||
* 铁砧更新结果
|
||||
*/
|
||||
public record AnvilUpdateResult(ItemStack output, int cost, int materialCost, boolean cancel) {
|
||||
|
||||
/**
|
||||
* Cancelled anvil update result.
|
||||
*
|
||||
* @return the anvil update result
|
||||
*/
|
||||
public static AnvilUpdateResult cancelled() {
|
||||
return new AnvilUpdateResult(ItemStack.EMPTY, 0, 0, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passed anvil update result.
|
||||
*
|
||||
* @return the anvil update result
|
||||
*/
|
||||
public static AnvilUpdateResult passed() {
|
||||
return new AnvilUpdateResult(ItemStack.EMPTY, 0, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* With output anvil update result.
|
||||
*
|
||||
* @param output the output
|
||||
* @param cost the cost
|
||||
* @param materialCost the material cost
|
||||
* @return the anvil update result
|
||||
*/
|
||||
public static AnvilUpdateResult withOutput(ItemStack output, int cost, int materialCost) {
|
||||
return new AnvilUpdateResult(output, cost, materialCost, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should override boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean shouldOverride() {
|
||||
return !output.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The constant EVENT.
|
||||
*/
|
||||
public static final Event<AnvilUpdate> EVENT = EventFactory.createArrayBacked(
|
||||
AnvilUpdate.class,
|
||||
(listeners) -> (left, right, outputSlot, name, baseCost, player) -> {
|
||||
for (AnvilUpdate listener : listeners) {
|
||||
AnvilUpdateResult result = listener.onAnvilUpdate(left, right, outputSlot, name, baseCost, player);
|
||||
if (result != null) {
|
||||
if (result.cancel()) {
|
||||
return result; // 取消事件
|
||||
}
|
||||
if (result.shouldOverride()) {
|
||||
return result; // 返回自定义结果
|
||||
}
|
||||
}
|
||||
}
|
||||
return AnvilUpdateResult.passed(); // 继续默认处理
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package top.r3944realms.lib39.api.callback;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.Services;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* The interface Minecraft set up service callback.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MinecraftSetUpServiceCallback {
|
||||
/**
|
||||
* The constant EVENT.
|
||||
*/
|
||||
Event<MinecraftSetUpServiceCallback> EVENT =
|
||||
EventFactory.createArrayBacked(
|
||||
MinecraftSetUpServiceCallback.class,
|
||||
(listeners) -> ((services, mainThreadExecutor) -> {
|
||||
for (MinecraftSetUpServiceCallback listener : listeners) {
|
||||
ActionResult result = listener.load(services, mainThreadExecutor);
|
||||
if (result != ActionResult.PASS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return ActionResult.PASS;
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Load boolean.
|
||||
*
|
||||
* @param services the services
|
||||
* @param mainThreadExecutor the main thread executor
|
||||
* @return 结果枚举 action result
|
||||
*/
|
||||
ActionResult load(Services services, Executor mainThreadExecutor);
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
package top.r3944realms.lib39.api.callback;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.commands.CommandBuildContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import top.r3944realms.lib39.core.command.ICommandHelpManager;
|
||||
import top.r3944realms.lib39.core.command.model.CommandNode;
|
||||
import top.r3944realms.lib39.core.command.model.CommandPath;
|
||||
import top.r3944realms.lib39.core.command.model.Parameter;
|
||||
|
||||
/**
|
||||
* The interface Register command help callback.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RegisterCommandHelpCallback {
|
||||
/**
|
||||
* Register action result.
|
||||
*
|
||||
* @param registrar the registrar
|
||||
* @return the action result
|
||||
*/
|
||||
ActionResult register(RegisterCommandHelpCallback.Registrar registrar);
|
||||
|
||||
/**
|
||||
* 创建 Event 实例
|
||||
*/
|
||||
Event<RegisterCommandHelpCallback> EVENT = EventFactory.createArrayBacked(
|
||||
RegisterCommandHelpCallback.class,
|
||||
(listeners) -> (registrar) -> {
|
||||
for (RegisterCommandHelpCallback listener : listeners) {
|
||||
ActionResult result = listener.register(registrar);
|
||||
if (result != ActionResult.PASS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* The interface Registrar.
|
||||
*/
|
||||
interface Registrar {
|
||||
/**
|
||||
* Gets id.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
ResourceLocation getID();
|
||||
|
||||
/**
|
||||
* Add child.
|
||||
*
|
||||
* @param child the child
|
||||
*/
|
||||
void addChild(LiteralArgumentBuilder<CommandSourceStack> child);
|
||||
|
||||
/**
|
||||
* Get context command build context.
|
||||
*
|
||||
* @return the command build context
|
||||
*/
|
||||
CommandBuildContext getContext();
|
||||
|
||||
/**
|
||||
* Get tree literal argument builder.
|
||||
*
|
||||
* @return the literal argument builder
|
||||
*/
|
||||
LiteralArgumentBuilder<CommandSourceStack> getTree();
|
||||
|
||||
/**
|
||||
* 注册命令帮助信息
|
||||
*
|
||||
* @param CommandNode 命令节点
|
||||
* @param description 命令描述
|
||||
*/
|
||||
void registerHelp(CommandNode CommandNode, MutableComponent description);
|
||||
|
||||
/**
|
||||
* 注册命令帮助信息
|
||||
*
|
||||
* @param CommandNode 命令节点
|
||||
* @param descriptionKey 命令描述的语言键
|
||||
*/
|
||||
void registerHelp(CommandNode CommandNode, String descriptionKey);
|
||||
|
||||
/**
|
||||
* 注册命令参数
|
||||
*
|
||||
* @param commandPath 命令节点
|
||||
* @param parametersBuilder 参数列表构造器
|
||||
*/
|
||||
void registerParameters(CommandPath commandPath, Parameter.Builder parametersBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Command help registrar.
|
||||
*/
|
||||
record CommandHelpRegistrar(LiteralArgumentBuilder<CommandSourceStack> builder, ICommandHelpManager helpManager, CommandBuildContext context) implements Registrar {
|
||||
@Override
|
||||
public ResourceLocation getID() {
|
||||
return helpManager.getID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChild(LiteralArgumentBuilder<CommandSourceStack> child) {
|
||||
this.builder.then(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandBuildContext getContext(){
|
||||
return this.context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiteralArgumentBuilder<CommandSourceStack> getTree(){
|
||||
return this.builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerHelp(CommandNode CommandNode, MutableComponent description) {
|
||||
this.helpManager.registerCommandHelp(CommandNode, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerHelp(CommandNode CommandNode, String descriptionKey) {
|
||||
this.helpManager.registerCommandHelp(CommandNode, descriptionKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerParameters(CommandPath commandPath, Parameter.Builder parametersBuilder) {
|
||||
this.helpManager.registerCommandParameters(commandPath, parametersBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
package top.r3944realms.lib39.api.callback;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.core.sync.ISyncData;
|
||||
import top.r3944realms.lib39.core.sync.ISyncManager;
|
||||
import top.r3944realms.lib39.core.sync.SyncData2LookupManager;
|
||||
|
||||
|
||||
/**
|
||||
* Fabric 的同步管理器注册回调
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SyncManagerRegisterCallback {
|
||||
|
||||
/**
|
||||
* 注册同步管理器的回调方法
|
||||
*
|
||||
* @param registrar 注册器实例
|
||||
* @return the action result
|
||||
*/
|
||||
ActionResult registerSyncManagers(Registrar registrar);
|
||||
|
||||
/**
|
||||
* 创建 Event 实例
|
||||
*/
|
||||
Event<SyncManagerRegisterCallback> EVENT = EventFactory.createArrayBacked(
|
||||
SyncManagerRegisterCallback.class,
|
||||
(listeners) -> (registrar) -> {
|
||||
for (SyncManagerRegisterCallback listener : listeners) {
|
||||
ActionResult result = listener.registerSyncManagers(registrar);
|
||||
if (result != ActionResult.PASS) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return ActionResult.PASS;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 注册器接口 - 提供注册同步管理器的方法
|
||||
*/
|
||||
interface Registrar {
|
||||
|
||||
/**
|
||||
* 获取 Fabric 同步管理器实例
|
||||
*
|
||||
* @return the sync data 2 lookup manager
|
||||
*/
|
||||
SyncData2LookupManager manager();
|
||||
|
||||
/**
|
||||
* 注册同步管理器
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param id the id
|
||||
* @param syncManager the sync manager
|
||||
* @param dataClass the data class
|
||||
*/
|
||||
<T extends ISyncData<?>> void register(
|
||||
ResourceLocation id,
|
||||
ISyncManager<Entity, T> syncManager,
|
||||
Class<T> dataClass
|
||||
);
|
||||
|
||||
/**
|
||||
* 允许实体类
|
||||
*
|
||||
* @param id the id
|
||||
* @param entityClasses the entity classes
|
||||
*/
|
||||
void allowEntityClass(ResourceLocation id, Class<?>... entityClasses);
|
||||
|
||||
/**
|
||||
* 移除允许的实体类
|
||||
*
|
||||
* @param id the id
|
||||
* @param entityClasses the entity classes
|
||||
*/
|
||||
void disallowEntityClass(ResourceLocation id, Class<?>... entityClasses);
|
||||
|
||||
/**
|
||||
* 绑定 EntityApiLookup(用于分离注册的情况)
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param id 必须先注册安全同步管理器,再绑定 Lookup,否则会抛出 {@link IllegalStateException}
|
||||
* @param apiLookup the EntityApiLookup
|
||||
*/
|
||||
<T extends ISyncData<?>> void bindApiLookup(
|
||||
ResourceLocation id,
|
||||
net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup<T, Void> apiLookup
|
||||
);
|
||||
|
||||
/**
|
||||
* 解绑 EntityApiLookup
|
||||
*
|
||||
* @param id the id
|
||||
*/
|
||||
void unbindApiLookup(ResourceLocation id);
|
||||
|
||||
/**
|
||||
* 完整的类型安全注册
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param id the id
|
||||
* @param syncManager the sync manager
|
||||
* @param dataClass the data class
|
||||
* @param allowedEntityClasses the allowed entity classes
|
||||
*/
|
||||
default <T extends ISyncData<?>> void registerComplete(
|
||||
ResourceLocation id,
|
||||
ISyncManager<Entity, T> syncManager,
|
||||
Class<T> dataClass,
|
||||
Class<?>... allowedEntityClasses
|
||||
) {
|
||||
register(id, syncManager, dataClass);
|
||||
if (allowedEntityClasses.length > 0) {
|
||||
allowEntityClass(id, allowedEntityClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Sync manager registrar.
|
||||
*/
|
||||
record SyncManagerRegistrar(
|
||||
SyncData2LookupManager manager) implements SyncManagerRegisterCallback.Registrar {
|
||||
|
||||
@Override
|
||||
public <T extends ISyncData<?>> void register(
|
||||
@NotNull ResourceLocation id,
|
||||
@NotNull ISyncManager<Entity, T> syncManager,
|
||||
@NotNull Class<T> dataClass
|
||||
) {
|
||||
manager.registerManager(id, syncManager, dataClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allowEntityClass(@NotNull ResourceLocation id, @NotNull Class<?>... entityClasses) {
|
||||
manager.allowEntityClass(id, entityClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disallowEntityClass(@NotNull ResourceLocation id, @NotNull Class<?>... entityClasses) {
|
||||
manager.disallowEntityClass(id, entityClasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ISyncData<?>> void bindApiLookup(
|
||||
@NotNull ResourceLocation id,
|
||||
@NotNull EntityApiLookup<T, Void> apiLookup
|
||||
) {
|
||||
manager.bindApiLookup(id, apiLookup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindApiLookup(@NotNull ResourceLocation id) {
|
||||
manager.unbindApiLookup(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
package top.r3944realms.lib39.api.callback.client;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
||||
/**
|
||||
* The interface Client world callback.
|
||||
*/
|
||||
public interface ClientWorldCallback {
|
||||
/**
|
||||
* The constant LOAD.
|
||||
*/
|
||||
Event<ClientWorldCallback.Load> LOAD = EventFactory.createArrayBacked(ClientWorldCallback.Load.class, (callbacks) -> (world) -> {
|
||||
for (Load callback : callbacks) {
|
||||
callback.onWorldLoad(world);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The constant UNLOAD.
|
||||
*/
|
||||
Event<ClientWorldCallback.Unload> UNLOAD = EventFactory.createArrayBacked(ClientWorldCallback.Unload.class, (callbacks) -> (world) -> {
|
||||
for (Unload callback : callbacks) {
|
||||
callback.onWorldUnload(world);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The interface Unload.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface Unload {
|
||||
/**
|
||||
* On world unload.
|
||||
*
|
||||
* @param clientLevel the client level
|
||||
*/
|
||||
void onWorldUnload(ClientLevel clientLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface Load.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface Load {
|
||||
/**
|
||||
* On world load.
|
||||
*
|
||||
* @param clientLevel the client level
|
||||
*/
|
||||
void onWorldLoad(ClientLevel clientLevel);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package top.r3944realms.lib39.base.compat.jade;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import snownee.jade.api.IWailaClientRegistration;
|
||||
import snownee.jade.api.IWailaPlugin;
|
||||
import snownee.jade.api.WailaPlugin;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.base.compat.jade.provider.FabricDollComponentProvider;
|
||||
import top.r3944realms.lib39.content.block.DollBlock;
|
||||
|
||||
/**
|
||||
* The type Fabric jade plugin.
|
||||
*/
|
||||
@WailaPlugin
|
||||
public class FabricJadePlugin implements IWailaPlugin {
|
||||
/**
|
||||
* The constant UID.
|
||||
*/
|
||||
public static final ResourceLocation UID = Lib39.rl("lib39");
|
||||
|
||||
@Override
|
||||
public void registerClient(IWailaClientRegistration registration) {
|
||||
registration.registerBlockComponent(new FabricDollComponentProvider(), DollBlock.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package top.r3944realms.lib39.base.compat.jade.provider;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import snownee.jade.api.BlockAccessor;
|
||||
import snownee.jade.api.IBlockComponentProvider;
|
||||
import snownee.jade.api.ITooltip;
|
||||
import snownee.jade.api.config.IPluginConfig;
|
||||
import top.r3944realms.lib39.base.compat.jade.FabricJadePlugin;
|
||||
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
|
||||
|
||||
/**
|
||||
* The type Fabric doll component provider.
|
||||
*/
|
||||
public class FabricDollComponentProvider implements IBlockComponentProvider {
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return FabricJadePlugin.UID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPluginConfig iPluginConfig) {
|
||||
if (blockAccessor.getBlockEntity() instanceof DollBlockEntity doll) {
|
||||
GameProfile ownerProfile = doll.getOwnerProfile();
|
||||
if (ownerProfile != null) {
|
||||
iTooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.1", ownerProfile.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package top.r3944realms.lib39.client;
|
||||
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
||||
import top.r3944realms.lib39.client.renderer.block.DollBlockEntityRenderer;
|
||||
import top.r3944realms.lib39.core.register.Lib39BlockEntities;
|
||||
|
||||
/**
|
||||
* The type Block entity renderer registry.
|
||||
*/
|
||||
public class BlockEntityRendererRegistry {
|
||||
/**
|
||||
* Register.
|
||||
*/
|
||||
public static void register() {
|
||||
BlockEntityRenderers.register(
|
||||
Lib39BlockEntities.DOLL_BLOCK_ENTITY.get(),
|
||||
DollBlockEntityRenderer::new
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package top.r3944realms.lib39.client;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry;
|
||||
import top.r3944realms.lib39.client.renderer.item.DollItemRenderer;
|
||||
import top.r3944realms.lib39.core.register.Lib39Items;
|
||||
|
||||
/**
|
||||
* The type Item renderer registry.
|
||||
*/
|
||||
public class ItemRendererRegistry {
|
||||
/**
|
||||
* Register.
|
||||
*/
|
||||
public static void register() {
|
||||
BuiltinItemRendererRegistry.INSTANCE.register(
|
||||
Lib39Items.DOLL.get(),
|
||||
DollItemRenderer.getInstance()::renderByItem
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package top.r3944realms.lib39.client;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry;
|
||||
import top.r3944realms.lib39.client.model.DollModel;
|
||||
|
||||
/**
|
||||
* The type Layer definition registry.
|
||||
*/
|
||||
public class LayerDefinitionRegistry {
|
||||
/**
|
||||
* Register.
|
||||
*/
|
||||
public static void register() {
|
||||
EntityModelLayerRegistry.registerModelLayer(
|
||||
DollModel.LAYER_LOCATION, DollModel::createBodyLayer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package top.r3944realms.lib39.client;
|
||||
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.CoreShaderRegistrationCallback;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.client.shader.Lib39Shaders;
|
||||
|
||||
/**
|
||||
* The type Shader registry.
|
||||
*/
|
||||
public class ShaderRegistry {
|
||||
/**
|
||||
* Register.
|
||||
*/
|
||||
public static void register() {
|
||||
CoreShaderRegistrationCallback.EVENT.register( context -> {
|
||||
context.register(
|
||||
Lib39.rl("ring"), DefaultVertexFormat.POSITION_COLOR, Lib39Shaders::setRingShader
|
||||
);
|
||||
context.register(
|
||||
Lib39.rl("selection"), DefaultVertexFormat.POSITION_COLOR, Lib39Shaders::setSelectionShader
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
package top.r3944realms.lib39.core.event;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
|
||||
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
|
||||
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.SkullBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.api.callback.AnvilUpdateCallback;
|
||||
import top.r3944realms.lib39.api.callback.SyncManagerRegisterCallback;
|
||||
import top.r3944realms.lib39.api.callback.client.ClientWorldCallback;
|
||||
import top.r3944realms.lib39.base.command.Lib39HelpCommand;
|
||||
import top.r3944realms.lib39.content.item.DollItem;
|
||||
import top.r3944realms.lib39.core.register.Lib39Items;
|
||||
import top.r3944realms.lib39.core.sync.ISyncData;
|
||||
import top.r3944realms.lib39.core.sync.SyncData2LookupManager;
|
||||
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
|
||||
import top.r3944realms.lib39.example.content.data.FabricTestSyncData;
|
||||
import top.r3944realms.lib39.example.content.data.FabricTestSyncLookupProvider;
|
||||
import top.r3944realms.lib39.util.GameProfileHelper;
|
||||
import top.r3944realms.lib39.util.IClientOnly;
|
||||
import top.r3944realms.lib39.util.ILevelHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Fabric common event handler.
|
||||
*/
|
||||
public class FabricCommonEventHandler {
|
||||
/**
|
||||
* The Sync data 2 manager.
|
||||
*/
|
||||
static volatile SyncData2LookupManager syncData2Manager;
|
||||
private static boolean isSync2MInitialized = false;
|
||||
private static ServerLevel sl;
|
||||
|
||||
/**
|
||||
* Gets server level.
|
||||
*
|
||||
* @return the server level
|
||||
*/
|
||||
public static ServerLevel getServerLevel() {
|
||||
return sl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets sync data 2 manager.
|
||||
*
|
||||
* @return the sync data 2 manager
|
||||
*/
|
||||
public static SyncData2LookupManager getSyncData2Manager() {
|
||||
return syncData2Manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* On server world load.
|
||||
*
|
||||
* @param server the server
|
||||
* @param serverLevel the server level
|
||||
*/
|
||||
public static void onServerWorldLoad(MinecraftServer server, ServerLevel serverLevel) {
|
||||
if (!serverLevel.dimension().equals(Level.OVERWORLD)) return;
|
||||
synchronized (FabricCommonEventHandler.class) {
|
||||
if (!isSync2MInitialized) {
|
||||
syncData2Manager = new SyncData2LookupManager();
|
||||
isSync2MInitialized = true;
|
||||
sl = serverLevel;
|
||||
ILevelHelper.LevelHelper.SERVER.setLevel(serverLevel);
|
||||
SyncManagerRegisterCallback.EVENT.invoker().registerSyncManagers(new SyncManagerRegisterCallback.SyncManagerRegistrar((syncData2Manager)));
|
||||
Lib39.LOGGER.info("SyncData2Manager initialized on Sever load");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* On server world un load.
|
||||
*
|
||||
* @param server the server
|
||||
* @param serverLevel the server level
|
||||
*/
|
||||
public static void onServerWorldUnLoad(MinecraftServer server, @NotNull ServerLevel serverLevel) {
|
||||
if (!serverLevel.dimension().equals(Level.OVERWORLD)) return;
|
||||
sl = null;
|
||||
ILevelHelper.LevelHelper.SERVER.setLevel(null);
|
||||
isSync2MInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* On server tick.
|
||||
*
|
||||
* @param server the server
|
||||
*/
|
||||
public static void onServerTick(MinecraftServer server) {
|
||||
if (syncData2Manager == null) return;
|
||||
if (server.getTickCount() % 10 == 0)
|
||||
syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::markDirty)));
|
||||
syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::checkIfDirtyThenUpdate)));
|
||||
}
|
||||
|
||||
/**
|
||||
* On entity join world.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @param level the level
|
||||
*/
|
||||
public static void onEntityJoinWorld(@NotNull Entity entity, ServerLevel level) {
|
||||
if (entity.level().isClientSide) return;
|
||||
|
||||
for (ResourceLocation id : syncData2Manager.getRegisteredKeys()) {
|
||||
if (syncData2Manager.isEntityClassAllowed(id, entity.getClass())) {
|
||||
syncData2Manager.trackEntityForManager(entity, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On entity leave world.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @param level the level
|
||||
*/
|
||||
public static void onEntityLeaveWorld(@NotNull Entity entity, ServerLevel level) {
|
||||
if (entity.level().isClientSide) return;
|
||||
|
||||
for (ResourceLocation id : syncData2Manager.getRegisteredKeys()) {
|
||||
if (syncData2Manager.isEntityClassAllowed(id, entity.getClass())) {
|
||||
syncData2Manager.untrackEntityForManager(entity, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static final Map<Supplier<Block>, ResourceKey<CreativeModeTab>[]> itemAddMap = new ConcurrentHashMap<>();
|
||||
private static final Map<ResourceKey<CreativeModeTab>, List<Supplier<Block>>> tabToItemsMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Add item to tabs.
|
||||
*
|
||||
* @param item the item
|
||||
* @param tabs the tabs
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static void addItemToTabs(Supplier<Block> item, ResourceKey<CreativeModeTab>... tabs) {
|
||||
itemAddMap.put(item, tabs);
|
||||
|
||||
// 更新反向映射
|
||||
for (ResourceKey<CreativeModeTab> tab : tabs) {
|
||||
tabToItemsMap.computeIfAbsent(tab, k -> new ArrayList<>()).add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init common.
|
||||
*/
|
||||
public static void initCommon() {
|
||||
try {
|
||||
Class.forName("top.r3944realms.lib39.base.command.Lib39CommandHelpManager");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
for (Map.Entry<ResourceKey<CreativeModeTab>, List<Supplier<Block>>> resourceKeyListEntry : tabToItemsMap.entrySet()) {
|
||||
ItemGroupEvents.modifyEntriesEvent(resourceKeyListEntry.getKey()).register(content -> resourceKeyListEntry.getValue().forEach(i -> content.accept(i.get())));
|
||||
}
|
||||
AnvilUpdateCallback.EVENT.register((left, right, outputSlot, name, baseCost, player) -> {
|
||||
if (left.getItem() instanceof DollItem && name != null && name.length() < 15) {
|
||||
GameProfile profile = new GameProfile(Util.NIL_UUID, name);
|
||||
ItemStack copied = Lib39Items.DOLL.get().getDefaultInstance();
|
||||
SkullBlockEntity.updateGameprofile(profile,
|
||||
profile1 -> GameProfileHelper.saveProfileToItemStack(copied, profile1)
|
||||
);
|
||||
copied.setCount(left.getCount());
|
||||
return AnvilUpdateCallback.AnvilUpdateResult.withOutput(copied, 1, 1);
|
||||
} else {
|
||||
ItemStack defaultInstance = Items.BARRIER.getDefaultInstance();
|
||||
defaultInstance.setHoverName(Component.translatable("invalid.player_name.too_long"));
|
||||
return AnvilUpdateCallback.AnvilUpdateResult.withOutput(defaultInstance, 0, 0);
|
||||
}
|
||||
});
|
||||
EntityApiLookup.get(FabricTestSyncData.ID, AbstractedTestSyncData.class, Void.class)
|
||||
.registerFallback(FabricTestSyncLookupProvider.INSTANCE);
|
||||
ServerTickEvents.END_SERVER_TICK.register(FabricCommonEventHandler::onServerTick);
|
||||
ServerWorldEvents.LOAD.register(FabricCommonEventHandler::onServerWorldLoad);
|
||||
ServerWorldEvents.UNLOAD.register(FabricCommonEventHandler::onServerWorldUnLoad);
|
||||
ServerEntityEvents.ENTITY_LOAD.register(FabricCommonEventHandler::onEntityJoinWorld);
|
||||
ServerEntityEvents.ENTITY_UNLOAD.register(FabricCommonEventHandler::onEntityLeaveWorld);
|
||||
CommandRegistrationCallback.EVENT.register((commandDispatcher, commandBuildContext, commandSelection) -> {
|
||||
new Lib39HelpCommand(commandDispatcher, commandBuildContext);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Client opt.
|
||||
*/
|
||||
public static class ClientOpt implements IClientOnly {
|
||||
/**
|
||||
* On client world load.
|
||||
*
|
||||
* @param clientLevel the client level
|
||||
*/
|
||||
public static void onClientWorldLoad(@NotNull ClientLevel clientLevel) {
|
||||
synchronized (FabricCommonEventHandler.ClientOpt.class) {
|
||||
IClientOnly.check(() -> {
|
||||
if (!clientLevel.dimension().equals(Level.OVERWORLD)) return;
|
||||
if (!isSync2MInitialized) {
|
||||
syncData2Manager = new SyncData2LookupManager();
|
||||
SyncManagerRegisterCallback.EVENT.invoker().registerSyncManagers(new SyncManagerRegisterCallback.SyncManagerRegistrar(syncData2Manager));
|
||||
Lib39.LOGGER.info("SyncData2Manager initialized on Client load");
|
||||
ILevelHelper.LevelHelper.CLIENT.setLevel(clientLevel);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On client world un load.
|
||||
*
|
||||
* @param clientLevel the client level
|
||||
*/
|
||||
public static void onClientWorldUnLoad(@NotNull ClientLevel clientLevel) {
|
||||
synchronized (FabricCommonEventHandler.ClientOpt.class) {
|
||||
IClientOnly.check(() -> ILevelHelper.LevelHelper.CLIENT.setLevel(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init client.
|
||||
*/
|
||||
public static void initClient() {
|
||||
ClientWorldCallback.LOAD.register(ClientOpt::onClientWorldLoad);
|
||||
ClientWorldCallback.LOAD.register(ClientOpt::onClientWorldUnLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets item add map.
|
||||
*
|
||||
* @return the item add map
|
||||
*/
|
||||
public static Map<Supplier<Block>, ResourceKey<CreativeModeTab>[]> getItemAddMap() {
|
||||
return itemAddMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package top.r3944realms.lib39.core.network;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import top.r3944realms.lib39.core.network.toClient.SyncNBTLookupDataEntityS2CPacket;
|
||||
|
||||
/**
|
||||
* The type Fabric network handler.
|
||||
*/
|
||||
public class FabricNetworkHandler {
|
||||
|
||||
/**
|
||||
* 注册客户端接收的数据包
|
||||
*/
|
||||
public static void registerClientReceivers() {
|
||||
ClientPlayNetworking.registerGlobalReceiver(
|
||||
SyncNBTLookupDataEntityS2CPacket.TYPE,
|
||||
SyncNBTLookupDataEntityS2CPacket::receive
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
package top.r3944realms.lib39.core.network.toClient;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
|
||||
import top.r3944realms.lib39.core.sync.ISyncData;
|
||||
import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
|
||||
import top.r3944realms.lib39.core.sync.SyncData2Manager;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The type Sync nbt lookup data entity s 2 c packet.
|
||||
*/
|
||||
public record SyncNBTLookupDataEntityS2CPacket(int entityId, ResourceLocation id, CompoundTag data) implements FabricPacket {
|
||||
/**
|
||||
* The constant SYNC_NBT_LOOKUP_PACKET_ID.
|
||||
*/
|
||||
public static final ResourceLocation SYNC_NBT_LOOKUP_PACKET_ID =
|
||||
Lib39.rl("sync_nbt_lookup_data_entity");
|
||||
/**
|
||||
* The constant TYPE.
|
||||
*/
|
||||
public static final PacketType<SyncNBTLookupDataEntityS2CPacket> TYPE = PacketType.create(
|
||||
SYNC_NBT_LOOKUP_PACKET_ID,
|
||||
buf -> new SyncNBTLookupDataEntityS2CPacket(buf.readInt(), buf.readResourceLocation(), buf.readNbt())
|
||||
);
|
||||
|
||||
/**
|
||||
* Instantiates a new Sync nbt data s 2 c pack.
|
||||
*
|
||||
* @param entityId the entity id
|
||||
* @param data the data
|
||||
*/
|
||||
public SyncNBTLookupDataEntityS2CPacket(int entityId, @NotNull NBTEntitySyncData data) {
|
||||
this(entityId, data.id(), data.serializeNBT());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull FriendlyByteBuf friendlyByteBuf) {
|
||||
friendlyByteBuf.writeInt(entityId);
|
||||
friendlyByteBuf.writeResourceLocation(id);
|
||||
friendlyByteBuf.writeNbt(data);
|
||||
}
|
||||
|
||||
@Contract(value = " -> new", pure = true)
|
||||
@Override
|
||||
public @NotNull PacketType<?> getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receive.
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param localPlayer the local player
|
||||
* @param packetSender the packet sender
|
||||
*/
|
||||
public static void receive(@NotNull SyncNBTLookupDataEntityS2CPacket packet, @NotNull LocalPlayer localPlayer, PacketSender packetSender) {
|
||||
Level level = localPlayer.level();
|
||||
Entity entity = level.getEntity(packet.entityId);
|
||||
if (entity != null) {
|
||||
Optional<SyncData2Manager.DataProvider<Entity, ISyncData<?>>> lookupOpt =
|
||||
FabricCommonEventHandler
|
||||
.getSyncData2Manager()
|
||||
.getDataProvider(packet.id);
|
||||
lookupOpt.flatMap(dataProvider -> dataProvider.getData(entity))
|
||||
.ifPresent(lookup -> {
|
||||
if (lookup instanceof NBTEntitySyncData nbtLookup) {
|
||||
CompoundTag current = nbtLookup.serializeNBT();
|
||||
if (!current.equals(packet.data)) {
|
||||
nbtLookup.deserializeNBT(packet.data);
|
||||
}
|
||||
} else Lib39.LOGGER.debug("Unhandled sync data: {}", packet.data);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package top.r3944realms.lib39.core.register;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Lib 39 block entities.
|
||||
*/
|
||||
public class FabricLib39BlockEntities {
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
public static void init() {
|
||||
Lib39BlockEntities.DOLL_BLOCK_ENTITY = register("doll", BlockEntityType.Builder
|
||||
.of(DollBlockEntity::new, Lib39Blocks.DOLL.get(), Lib39Blocks.WALL_DOLL.get())
|
||||
.build(null)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register @ not null supplier.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param path the path
|
||||
* @param blockEntityType the block entity type
|
||||
* @return the @ not null supplier
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static <T extends BlockEntityType<?>> @NotNull Supplier<T> register(String path, T blockEntityType) {
|
||||
T item = Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, Lib39.rl(path), blockEntityType);
|
||||
return () -> item;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
package top.r3944realms.lib39.core.register;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.content.block.DollBlock;
|
||||
import top.r3944realms.lib39.content.block.WallDollBlock;
|
||||
import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Lib 39 blocks.
|
||||
*/
|
||||
public class FabricLib39Blocks {
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public static void init() {
|
||||
Lib39Blocks.DOLL = BlockRegistryBuilder
|
||||
.create()
|
||||
.withName("doll")
|
||||
.registerBlock((name , block) -> register(name, block.get()), DollBlock::new)
|
||||
.build();
|
||||
Lib39Blocks.WALL_DOLL = BlockRegistryBuilder
|
||||
.create()
|
||||
.withName("wall_doll")
|
||||
.registerBlock((name , block) -> register(name, block.get()), WallDollBlock::new)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register @ not null supplier.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param path the path
|
||||
* @param block the block
|
||||
* @return the @ not null supplier
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static <T extends Block> @NotNull Supplier<T> register(String path, T block) {
|
||||
T item = Registry.register(BuiltInRegistries.BLOCK, Lib39.rl(path), block);
|
||||
return () -> item;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package top.r3944realms.lib39.core.register;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.content.item.DollItem;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Ex lib 39 items.
|
||||
*/
|
||||
public class FabricLib39Items {
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public static void init() {
|
||||
Lib39Items.DOLL = register("doll", new DollItem(new Item.Properties()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register @ not null supplier.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param path the path
|
||||
* @param item the item
|
||||
* @return the @ not null supplier
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static <T extends Item> @NotNull Supplier<T> register(String path, T item) {
|
||||
T register = Registry.register(BuiltInRegistries.ITEM, Lib39.rl(path), item);
|
||||
return () -> register;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package top.r3944realms.lib39.core.register;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Lib 39 sound events.
|
||||
*/
|
||||
public class FabricLib39SoundEvents {
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public static void init() {
|
||||
Lib39SoundEvents.DUCK_TOY = register("duck_toy", SoundEvent.createFixedRangeEvent(Lib39SoundEvents.RL_DUCK_TOY, 32.0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register @ not null supplier.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param path the path
|
||||
* @param soundEvent the sound event
|
||||
* @return the @ not null supplier
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public static <T extends SoundEvent> @NotNull Supplier<T> register(String path, T soundEvent) {
|
||||
T register = Registry.register(BuiltInRegistries.SOUND_EVENT, Lib39.rl(path), soundEvent);
|
||||
return () -> register;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package top.r3944realms.lib39.core.sync;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
/**
|
||||
* The interface Entity api look up impl extend.
|
||||
*
|
||||
* @param <A> the type parameter
|
||||
* @param <C> the type parameter
|
||||
*/
|
||||
public interface IEntityApiLookUpImplExtend<A, C> {
|
||||
/**
|
||||
* Lib 39 find without check a.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @param context the context
|
||||
* @return the a
|
||||
*/
|
||||
A lib39$findWithoutCheck(Entity entity, C context);
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package top.r3944realms.lib39.core.sync;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
|
||||
import top.r3944realms.lib39.core.network.toClient.SyncNBTLookupDataEntityS2CPacket;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The interface Fabric update.
|
||||
*/
|
||||
public interface IFabricUpdate extends IUpdate {
|
||||
default void update() {
|
||||
ServerLevel serverLevel = FabricCommonEventHandler.getServerLevel();
|
||||
if (serverLevel != null) {
|
||||
PlayerList playerList = serverLevel.getServer().getPlayerList();
|
||||
List<ServerPlayer> players = playerList.getPlayers();
|
||||
for (ServerPlayer player : players) {
|
||||
if (ServerPlayNetworking.canSend(player, SyncNBTLookupDataEntityS2CPacket.TYPE)) {
|
||||
ServerPlayNetworking.send(player, new SyncNBTLookupDataEntityS2CPacket(getSyncData().entityId(), getSyncData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package top.r3944realms.lib39.core.sync;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The interface Lib 39 sync data holder.
|
||||
*/
|
||||
public interface ILib39SyncDataHolder {
|
||||
/**
|
||||
* Lib 39 get sync data compound tag.
|
||||
*
|
||||
* @return the compound tag
|
||||
*/
|
||||
CompoundTag lib39$getSyncData();
|
||||
|
||||
/**
|
||||
* Lib 39 set sync data.
|
||||
*
|
||||
* @param tag the tag
|
||||
*/
|
||||
void lib39$setSyncData(CompoundTag tag);
|
||||
|
||||
/**
|
||||
* Lib 39 inject save sync data compound tag.
|
||||
*
|
||||
* @param tag the tag
|
||||
* @return the compound tag
|
||||
*/
|
||||
default CompoundTag lib39$injectSaveSyncData(CompoundTag tag) {
|
||||
if (lib39$getSyncData() != null && !lib39$getSyncData().isEmpty()) {
|
||||
tag.put("Lib39Data", lib39$getSyncData());
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lib 39 inject load sync data.
|
||||
*
|
||||
* @param tag the tag
|
||||
*/
|
||||
default void lib39$injectLoadSyncData(@NotNull CompoundTag tag) {
|
||||
if (tag.contains("Lib39Data", Tag.TAG_COMPOUND)) {
|
||||
lib39$setSyncData(tag.getCompound("Lib39Data"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save sync data.
|
||||
*
|
||||
* @param syncData the sync data
|
||||
*/
|
||||
default void saveSyncData(@NotNull NBTEntitySyncData syncData) {
|
||||
lib39$getSyncData().put(syncData.id.toDebugFileName(), syncData.serializeNBT());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load sync data.
|
||||
*
|
||||
* @param syncData the sync data
|
||||
*/
|
||||
default void loadSyncData(NBTEntitySyncData syncData) {
|
||||
if (syncData != null && lib39$getSyncData().contains(syncData.id.toDebugFileName())) {
|
||||
syncData.deserializeNBT(lib39$getSyncData().getCompound(syncData.id.toDebugFileName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets holder.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @return the holder
|
||||
*/
|
||||
static ILib39SyncDataHolder getHolder(Entity entity) {
|
||||
return (ILib39SyncDataHolder) entity;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
package top.r3944realms.lib39.core.sync;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The type Sync data 2 lookup manager.
|
||||
*/
|
||||
public class SyncData2LookupManager extends SyncData2Manager<SyncData2LookupManager.TypedSyncEntry<? extends ISyncData<?>>> {
|
||||
/**
|
||||
* The Typed entries.
|
||||
*/
|
||||
protected final Map<ResourceLocation, TypedSyncEntry<?>> typedEntries = Maps.newConcurrentMap();
|
||||
|
||||
@Override
|
||||
protected Map<ResourceLocation, TypedSyncEntry<?>> getTypedEntries() {
|
||||
return typedEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type Typed sync entry.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
*/
|
||||
protected static class TypedSyncEntry<T extends ISyncData<?>> extends SyncData2Manager.TypedSyncEntry<Entity, T> {
|
||||
/**
|
||||
* Instantiates a new Typed sync entry for Fabric
|
||||
*
|
||||
* @param manager the manager
|
||||
* @param apiLookup the EntityApiLookup instance
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public TypedSyncEntry(ISyncManager<Entity, T> manager, @Nullable EntityApiLookup<T, Void> apiLookup) {
|
||||
super(manager, key -> {
|
||||
if (apiLookup != null) {
|
||||
T data = ((IEntityApiLookUpImplExtend<T, Void>) apiLookup).lib39$findWithoutCheck(key, null);
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自定义提供器注册管理器
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param manager the manager
|
||||
* @param dataClass the data class
|
||||
*/
|
||||
public <T extends ISyncData<?>> void registerManager(
|
||||
ResourceLocation key,
|
||||
ISyncManager<Entity, T> manager,
|
||||
Class<T> dataClass
|
||||
) {
|
||||
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
|
||||
Objects.requireNonNull(manager, "Sync manager cannot be null");
|
||||
Objects.requireNonNull(dataClass, "Data class cannot be null");
|
||||
|
||||
// 创建 EntityApiLookup
|
||||
EntityApiLookup<T, Void> apiLookup = EntityApiLookup.get(key, dataClass, Void.class);
|
||||
typedEntries.put(key, new TypedSyncEntry<>(manager, apiLookup));
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定 EntityApiLookup(用于分离注册的情况)
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param key the key
|
||||
* @param apiLookup the EntityApiLookup
|
||||
*/
|
||||
public <T extends ISyncData<?>> void bindApiLookup(ResourceLocation key, EntityApiLookup<T, Void> apiLookup) {
|
||||
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
|
||||
Objects.requireNonNull(apiLookup, "EntityApiLookup cannot be null");
|
||||
|
||||
TypedSyncEntry<?> entry = typedEntries.get(key);
|
||||
if (entry != null) {
|
||||
updateApiLookupInEntry(key, entry, apiLookup);
|
||||
} else {
|
||||
throw new IllegalArgumentException("No manager found for " + key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑 EntityApiLookup
|
||||
*
|
||||
* @param key the key
|
||||
*/
|
||||
public void unbindApiLookup(ResourceLocation key) {
|
||||
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
|
||||
|
||||
TypedSyncEntry<?> entry = typedEntries.get(key);
|
||||
if (entry != null) {
|
||||
updateApiLookupInEntry(key, entry, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新条目的 EntityApiLookup
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
* @param id the id
|
||||
* @param entry the entry
|
||||
* @param newApiLookup the new api lookup
|
||||
*/
|
||||
protected <T extends ISyncData<?>> void updateApiLookupInEntry(
|
||||
ResourceLocation id,
|
||||
TypedSyncEntry<?> entry,
|
||||
EntityApiLookup<T, Void> newApiLookup
|
||||
) {
|
||||
updateDataProviderInEntry(id, entry, key -> newApiLookup != null ? Optional.ofNullable(newApiLookup.find(key, null)) : Optional.empty());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package top.r3944realms.lib39.core.sync;
|
||||
|
||||
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
|
||||
|
||||
/**
|
||||
* The type Sync lookup provider.
|
||||
*
|
||||
* @param <T> the type parameter
|
||||
*/
|
||||
public abstract class SyncLookupProvider<T extends NBTEntitySyncData> implements EntityApiLookup.EntityApiProvider<T, Void> {
|
||||
/**
|
||||
* Create empty lookup t.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @return the t
|
||||
*/
|
||||
protected abstract T createEmptyLookup(Entity entity);
|
||||
|
||||
/**
|
||||
* Gets id.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
protected abstract ResourceLocation getId();
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public @Nullable T find(Entity entity, Void context) {
|
||||
return FabricCommonEventHandler.getSyncData2Manager().getManager(getId()).map(
|
||||
objectISyncDataISyncManager -> {
|
||||
ISyncData<?> iSyncData = objectISyncDataISyncManager.getSyncMap().get(entity.getUUID());
|
||||
if (iSyncData instanceof NBTEntitySyncData syncData) {
|
||||
return (T) syncData;
|
||||
}
|
||||
T defaultLookup = createEmptyLookup(entity);
|
||||
ILib39SyncDataHolder.getHolder(entity).loadSyncData(defaultLookup);
|
||||
objectISyncDataISyncManager.getSyncMap().put(entity.getUUID(), defaultLookup);
|
||||
return defaultLookup;
|
||||
}
|
||||
).orElse(createEmptyLookup(entity));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package top.r3944realms.lib39.example;
|
||||
|
||||
import top.r3944realms.lib39.core.compat.CompatManager;
|
||||
import top.r3944realms.lib39.example.core.compat.FabricLib39Compat;
|
||||
import top.r3944realms.lib39.example.core.event.FabricExCommonEventHandler;
|
||||
import top.r3944realms.lib39.example.core.network.FabricExNetworkHandler;
|
||||
import top.r3944realms.lib39.example.core.register.FabricExLib39Items;
|
||||
|
||||
/**
|
||||
* The type Fabric lib 39 example.
|
||||
*/
|
||||
public class FabricLib39Example {
|
||||
private static boolean registered = false;
|
||||
|
||||
/**
|
||||
* Instantiates a new Lib 39 example.
|
||||
*/
|
||||
public FabricLib39Example() {
|
||||
if (!registered) {
|
||||
init();
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public void init() {
|
||||
FabricExCommonEventHandler.init();
|
||||
FabricExLib39Items.init();
|
||||
FabricExNetworkHandler.registerServerReceivers();
|
||||
CompatManager orCreateCompatManager = FabricExCommonEventHandler.getOrCreateCompatManager();
|
||||
orCreateCompatManager.registerCompat(FabricLib39Compat.ID, FabricLib39Compat.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Demonstrate feature.
|
||||
*/
|
||||
public void demonstrateFeature() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,360 +0,0 @@
|
|||
package top.r3944realms.lib39.example.content.data;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.core.sync.IFabricUpdate;
|
||||
import top.r3944realms.lib39.util.nbt.NBTReader;
|
||||
import top.r3944realms.lib39.util.nbt.NBTWriter;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 测试同步数据实现
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class FabricTestSyncData extends AbstractedTestSyncData implements IFabricUpdate {
|
||||
/**
|
||||
* The constant ID.
|
||||
*/
|
||||
public static final ResourceLocation ID = Lib39.rl(Lib39.MOD_ID, "test_sync_data");
|
||||
|
||||
// NBT 键常量
|
||||
private static final String NBT_KEY_STRING = "test_string";
|
||||
private static final String NBT_KEY_INT = "test_int";
|
||||
private static final String NBT_KEY_BOOLEAN = "test_boolean";
|
||||
private static final String NBT_KEY_DOUBLE = "test_double";
|
||||
private static final String NBT_KEY_COUNTER = "counter";
|
||||
private static final String NBT_KEY_SYNC_TIME = "last_sync_time";
|
||||
private static final String NBT_KEY_CUSTOM_DATA = "custom_data";
|
||||
private static final String NBT_KEY_CUSTOM_NAME = "name";
|
||||
private static final String NBT_KEY_CUSTOM_VALUE = "value";
|
||||
private static final String NBT_KEY_CUSTOM_FLAG = "flag";
|
||||
|
||||
// 数据字段
|
||||
private String testString = "default_value";
|
||||
private int testInt = 42;
|
||||
private boolean testBoolean = true;
|
||||
private double testDouble = 3.14159;
|
||||
private int counter = 0;
|
||||
private long lastSyncTime = 0L;
|
||||
private TestData customData = new TestData("default", 100, false);
|
||||
private Entity self;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param entity 关联的实体
|
||||
*/
|
||||
public FabricTestSyncData(Entity entity) {
|
||||
super(ID);
|
||||
this.self = entity;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数(用于测试)
|
||||
*
|
||||
* @param entityId 实体ID
|
||||
* @param self the self
|
||||
*/
|
||||
public FabricTestSyncData(int entityId, Entity self) {
|
||||
super(ID);
|
||||
this.self = self;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数(用于数据包反序列化)
|
||||
*
|
||||
* @param buf 字节缓冲区
|
||||
*/
|
||||
public FabricTestSyncData(FriendlyByteBuf buf) {
|
||||
super(ID);
|
||||
this.self = null; // 实体在从数据包重建时可能为null,需要在接收端设置
|
||||
fromBytes(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据写入字节缓冲区(用于网络传输)
|
||||
*
|
||||
* @param buf 字节缓冲区
|
||||
*/
|
||||
@Override
|
||||
public void toBytes(@NotNull FriendlyByteBuf buf) {
|
||||
// 写入基本类型字段
|
||||
buf.writeUtf(testString != null ? testString : "");
|
||||
buf.writeInt(testInt);
|
||||
buf.writeBoolean(testBoolean);
|
||||
buf.writeDouble(testDouble);
|
||||
buf.writeInt(counter);
|
||||
buf.writeLong(lastSyncTime);
|
||||
|
||||
// 写入自定义数据
|
||||
if (customData != null) {
|
||||
buf.writeUtf(customData.getName() != null ? customData.getName() : "");
|
||||
buf.writeInt(customData.getValue());
|
||||
buf.writeBoolean(customData.isFlag());
|
||||
} else {
|
||||
buf.writeUtf("");
|
||||
buf.writeInt(0);
|
||||
buf.writeBoolean(false);
|
||||
}
|
||||
|
||||
// 写入实体ID(如果实体存在)
|
||||
if (self != null) {
|
||||
buf.writeInt(self.getId());
|
||||
} else {
|
||||
buf.writeInt(-1);
|
||||
}
|
||||
|
||||
// 写入脏数据状态
|
||||
buf.writeBoolean(isDirty());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字节缓冲区读取数据(用于网络传输)
|
||||
*
|
||||
* @param buf 字节缓冲区
|
||||
*/
|
||||
@Override
|
||||
public void fromBytes(@NotNull FriendlyByteBuf buf) {
|
||||
// 读取基本类型字段
|
||||
this.testString = buf.readUtf(32767); // Minecraft字符串最大长度
|
||||
this.testInt = buf.readInt();
|
||||
this.testBoolean = buf.readBoolean();
|
||||
this.testDouble = buf.readDouble();
|
||||
this.counter = buf.readInt();
|
||||
this.lastSyncTime = buf.readLong();
|
||||
|
||||
// 读取自定义数据
|
||||
String customName = buf.readUtf();
|
||||
int customValue = buf.readInt();
|
||||
boolean customFlag = buf.readBoolean();
|
||||
this.customData = new TestData(customName, customValue, customFlag);
|
||||
|
||||
// 读取实体ID(在接收端可能需要额外处理)
|
||||
int entityId = buf.readInt();
|
||||
|
||||
// 读取脏数据状态
|
||||
boolean wasDirty = buf.readBoolean();
|
||||
if (wasDirty) {
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法:从字节缓冲区创建 TestSyncData 实例
|
||||
*
|
||||
* @param buf 字节缓冲区
|
||||
* @return 新的 TestSyncData 实例
|
||||
*/
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull FabricTestSyncData staticFromBytes(FriendlyByteBuf buf) {
|
||||
return new FabricTestSyncData(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTestString() {
|
||||
return testString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTestString(String value) {
|
||||
if (!java.util.Objects.equals(this.testString, value)) {
|
||||
this.testString = value;
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTestInt() {
|
||||
return testInt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTestInt(int value) {
|
||||
if (this.testInt != value) {
|
||||
this.testInt = value;
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTestBoolean() {
|
||||
return testBoolean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTestBoolean(boolean value) {
|
||||
if (this.testBoolean != value) {
|
||||
this.testBoolean = value;
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTestDouble() {
|
||||
return testDouble;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTestDouble(double value) {
|
||||
if (this.testDouble != value) {
|
||||
this.testDouble = value;
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementCounter() {
|
||||
this.counter++;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCounter() {
|
||||
this.counter = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastSyncTime() {
|
||||
return lastSyncTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSyncTime() {
|
||||
this.lastSyncTime = System.currentTimeMillis();
|
||||
markDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSyncTime() {
|
||||
this.lastSyncTime = 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestData getCustomData() {
|
||||
return customData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomData(TestData data) {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Custom data cannot be null");
|
||||
}
|
||||
if (!java.util.Objects.equals(this.customData, data)) {
|
||||
this.customData = data;
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateData() {
|
||||
return testString != null &&
|
||||
!testString.isEmpty() &&
|
||||
customData != null &&
|
||||
customData.getName() != null &&
|
||||
!customData.getName().isEmpty() &&
|
||||
counter >= 0 &&
|
||||
testInt >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeNBT() {
|
||||
return NBTWriter.builder()
|
||||
.string(NBT_KEY_STRING, testString)
|
||||
.intValue(NBT_KEY_INT, testInt)
|
||||
.booleanValue(NBT_KEY_BOOLEAN, testBoolean)
|
||||
.doubleValue(NBT_KEY_DOUBLE, testDouble)
|
||||
.intValue(NBT_KEY_COUNTER, counter)
|
||||
.longValue(NBT_KEY_SYNC_TIME, lastSyncTime)
|
||||
.compound(
|
||||
NBT_KEY_CUSTOM_DATA,
|
||||
NBTWriter.builder()
|
||||
.string(NBT_KEY_CUSTOM_NAME, customData.getName())
|
||||
.intValue(NBT_KEY_CUSTOM_VALUE, customData.getValue())
|
||||
.booleanValue(NBT_KEY_CUSTOM_FLAG, customData.isFlag())
|
||||
.build()
|
||||
).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(CompoundTag nbt) {
|
||||
NBTReader.of(nbt)
|
||||
.intValue(NBT_KEY_INT, integer -> testInt = integer)
|
||||
.string(NBT_KEY_STRING, string -> testString = string)
|
||||
.booleanValue(NBT_KEY_BOOLEAN, bool -> testBoolean = bool)
|
||||
.intValue(NBT_KEY_COUNTER, integer -> counter = integer)
|
||||
.doubleValue(NBT_KEY_DOUBLE, dou -> testDouble = dou)
|
||||
.longValue(NBT_KEY_SYNC_TIME, sync -> lastSyncTime = sync)
|
||||
.compound(NBT_KEY_CUSTOM_DATA, customDataTag -> {
|
||||
AtomicReference<String> name = new AtomicReference<>("");
|
||||
AtomicInteger value = new AtomicInteger(-1);
|
||||
AtomicBoolean flag = new AtomicBoolean(false);
|
||||
NBTReader.of(customDataTag)
|
||||
.string(NBT_KEY_CUSTOM_NAME, name::set)
|
||||
.intValue(NBT_KEY_CUSTOM_VALUE, value::set)
|
||||
.booleanValue(NBT_KEY_CUSTOM_FLAG, flag::set);
|
||||
this.customData = new TestData(name.get(), value.get(), flag.get());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int entityId() {
|
||||
return self != null ? self.getId() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置关联的实体
|
||||
*
|
||||
* @param entity 关联的实体
|
||||
*/
|
||||
public void setEntity(Entity entity) {
|
||||
this.self = entity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取所有数据的字符串表示(用于调试)
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"TestSyncData{id=%d, string='%s', int=%d, boolean=%s, double=%.2f, counter=%d, lastSync=%d, custom=%s}",
|
||||
self.getId(), testString, testInt, testBoolean, testDouble, counter, lastSyncTime, customData
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个不依赖实体的副本(用于网络传输)
|
||||
*
|
||||
* @return 不包含实体引用的副本 test sync data
|
||||
*/
|
||||
public FabricTestSyncData createNetworkCopy() {
|
||||
FabricTestSyncData copy = new FabricTestSyncData((Entity) null);
|
||||
copy.testString = this.testString;
|
||||
copy.testInt = this.testInt;
|
||||
copy.testBoolean = this.testBoolean;
|
||||
copy.testDouble = this.testDouble;
|
||||
copy.counter = this.counter;
|
||||
copy.lastSyncTime = this.lastSyncTime;
|
||||
copy.customData = new TestData(
|
||||
this.customData.getName(),
|
||||
this.customData.getValue(),
|
||||
this.customData.isFlag()
|
||||
);
|
||||
return copy;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package top.r3944realms.lib39.example.content.data;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import top.r3944realms.lib39.core.sync.SyncLookupProvider;
|
||||
|
||||
/**
|
||||
* The type Fabric test sync lookup provider.
|
||||
*/
|
||||
public class FabricTestSyncLookupProvider extends SyncLookupProvider<AbstractedTestSyncData> {
|
||||
/**
|
||||
* The constant INSTANCE.
|
||||
*/
|
||||
public static final FabricTestSyncLookupProvider INSTANCE = new FabricTestSyncLookupProvider();
|
||||
|
||||
@Override
|
||||
protected AbstractedTestSyncData createEmptyLookup(Entity entity) {
|
||||
return new FabricTestSyncData(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceLocation getId() {
|
||||
return FabricTestSyncData.ID;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
package top.r3944realms.lib39.example.content.item;
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
|
||||
import top.r3944realms.lib39.example.content.data.FabricTestSyncData;
|
||||
import top.r3944realms.lib39.example.core.network.FabricClientDataPacket;
|
||||
|
||||
/**
|
||||
* The type Fabric fabric item.
|
||||
*/
|
||||
public class FabricFabricItem extends AbstractFabricItem {
|
||||
|
||||
/**
|
||||
* Instantiates a new Fabric fabric item.
|
||||
*
|
||||
* @param properties the properties
|
||||
*/
|
||||
public FabricFabricItem(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractedTestSyncData getData(Entity target) {
|
||||
return getStaticData(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendClientDataToServer(AbstractedTestSyncData clientData, int targetEntityId) {
|
||||
if (ClientPlayNetworking.canSend(FabricClientDataPacket.TYPE)) {
|
||||
ClientPlayNetworking.send(new FabricClientDataPacket(clientData, targetEntityId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets static data.
|
||||
*
|
||||
* @param target the target
|
||||
* @return the static data
|
||||
*/
|
||||
public static @Nullable AbstractedTestSyncData getStaticData(Entity target) {
|
||||
try {
|
||||
AbstractedTestSyncData abstractData = EntityApiLookup.get(FabricTestSyncData.ID, AbstractedTestSyncData.class, Void.class).find(target, null);
|
||||
if (abstractData instanceof FabricTestSyncData) {
|
||||
return abstractData;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Lib39.LOGGER.error("[FabricItem] 获取服务器端数据失败", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client data from packet.
|
||||
*
|
||||
* @param player the player
|
||||
* @param clientData the client data
|
||||
* @param targetEntityId the target entity id
|
||||
*/
|
||||
public static void handleClientDataFromPacket(@NotNull ServerPlayer player, AbstractedTestSyncData clientData, int targetEntityId) {
|
||||
Entity target = player.level().getEntity(targetEntityId);
|
||||
|
||||
if (target instanceof LivingEntity livingTarget) {
|
||||
// 获取服务器端数据
|
||||
AbstractedTestSyncData serverData = getStaticData(livingTarget);
|
||||
|
||||
if (serverData != null) {
|
||||
// 显示双端对比结果
|
||||
displayDualEndComparison(player, livingTarget, serverData, clientData);
|
||||
} else {
|
||||
player.sendSystemMessage(Component.literal("§c无法获取服务器端数据"));
|
||||
}
|
||||
} else {
|
||||
player.sendSystemMessage(Component.literal("§c目标生物不存在或已消失"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package top.r3944realms.lib39.example.content.item;
|
||||
|
||||
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
|
||||
import top.r3944realms.lib39.example.content.data.FabricTestSyncData;
|
||||
|
||||
/**
|
||||
* The type Fabric neo forge item.
|
||||
*/
|
||||
public class FabricNeoForgeItem extends AbstractNeoForgeItem {
|
||||
|
||||
/**
|
||||
* Instantiates a new Fabric neo forge item.
|
||||
*
|
||||
* @param properties the properties
|
||||
*/
|
||||
public FabricNeoForgeItem(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractedTestSyncData getData(Entity entity) {
|
||||
return EntityApiLookup.get(FabricTestSyncData.ID, AbstractedTestSyncData.class, Void.class).find(entity, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
package top.r3944realms.lib39.example.core.compat;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.core.compat.ICompat;
|
||||
|
||||
/**
|
||||
* The type Lib 39 compat.
|
||||
*/
|
||||
public class FabricLib39Compat implements ICompat {
|
||||
/**
|
||||
* The Initialized.
|
||||
*/
|
||||
boolean initialized = false;
|
||||
/**
|
||||
* The constant INSTANCE.
|
||||
*/
|
||||
public static FabricLib39Compat INSTANCE = new FabricLib39Compat();
|
||||
/**
|
||||
* The constant ID.
|
||||
*/
|
||||
public static ResourceLocation ID = Lib39.rl("lib39");
|
||||
|
||||
@Override
|
||||
public void setInitialize(boolean initialize) {
|
||||
this.initialized = initialize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation id() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModLoaded() {
|
||||
return FabricLoader.getInstance().isModLoaded("lib39");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
Lib39.LOGGER.info("[Lib39 Compat] Init!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadComplete() {
|
||||
Lib39.LOGGER.info("[Lib39 Compat] Complete!");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package top.r3944realms.lib39.example.core.compat;
|
||||
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.core.compat.CompatManager;
|
||||
|
||||
/**
|
||||
* The type Fabric lib 39 compat manager.
|
||||
*/
|
||||
public class FabricLib39CompatManager extends CompatManager {
|
||||
/**
|
||||
* Instantiates a new Compat manager.
|
||||
*
|
||||
* @param path the path
|
||||
*/
|
||||
public FabricLib39CompatManager(String path) {
|
||||
super(Lib39.rl(path));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package top.r3944realms.lib39.example.core.event;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import top.r3944realms.lib39.api.callback.ActionResult;
|
||||
import top.r3944realms.lib39.api.callback.SyncManagerRegisterCallback;
|
||||
import top.r3944realms.lib39.core.compat.CompatManager;
|
||||
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
|
||||
import top.r3944realms.lib39.core.sync.CachedSyncManager;
|
||||
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
|
||||
import top.r3944realms.lib39.example.content.data.FabricTestSyncData;
|
||||
import top.r3944realms.lib39.example.core.compat.FabricLib39CompatManager;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* The type Fabric ex common event handler.
|
||||
*/
|
||||
public class FabricExCommonEventHandler {
|
||||
/**
|
||||
* Gets compat manager.
|
||||
*
|
||||
* @return the compat manager
|
||||
*/
|
||||
public static CompatManager getOrCreateCompatManager() {
|
||||
if (compatManager == null) {
|
||||
synchronized (FabricCommonEventHandler.class) {
|
||||
if (compatManager == null) {
|
||||
compatManager = new FabricLib39CompatManager("compat");
|
||||
}
|
||||
}
|
||||
}
|
||||
return compatManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Compat manager.
|
||||
*/
|
||||
static volatile CompatManager compatManager;
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public static void init() {
|
||||
SyncManagerRegisterCallback.EVENT.register(registrar -> {
|
||||
registrar.register(
|
||||
FabricTestSyncData.ID,
|
||||
new CachedSyncManager<>() {
|
||||
private final Map<Entity, AbstractedTestSyncData> syncDataMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Map<Entity, AbstractedTestSyncData> getSyncMap() {
|
||||
return syncDataMap;
|
||||
}
|
||||
},
|
||||
AbstractedTestSyncData.class
|
||||
);
|
||||
registrar.allowEntityClass(FabricTestSyncData.ID, LivingEntity.class);
|
||||
return ActionResult.PASS;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package top.r3944realms.lib39.example.core.network;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
|
||||
import top.r3944realms.lib39.example.content.data.FabricTestSyncData;
|
||||
import top.r3944realms.lib39.example.content.item.FabricFabricItem;
|
||||
|
||||
/**
|
||||
* The type Client data packet.
|
||||
*/
|
||||
public class FabricClientDataPacket implements FabricPacket {
|
||||
/**
|
||||
* The constant CLIENT_TEST_DATA.
|
||||
*/
|
||||
public static final ResourceLocation CLIENT_TEST_DATA =
|
||||
Lib39.rl("client_test_data");
|
||||
/**
|
||||
* The constant TYPE.
|
||||
*/
|
||||
public static final PacketType<FabricClientDataPacket> TYPE = PacketType.create(
|
||||
CLIENT_TEST_DATA,
|
||||
FabricClientDataPacket::new
|
||||
);
|
||||
private final AbstractedTestSyncData clientData;
|
||||
private final int targetEntityId;
|
||||
|
||||
/**
|
||||
* Instantiates a new Client data packet.
|
||||
*
|
||||
* @param clientData the client data
|
||||
* @param targetEntityId the target entity id
|
||||
*/
|
||||
public FabricClientDataPacket(AbstractedTestSyncData clientData, int targetEntityId) {
|
||||
this.clientData = clientData;
|
||||
this.targetEntityId = targetEntityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Client data packet.
|
||||
*
|
||||
* @param buf the buf
|
||||
*/
|
||||
public FabricClientDataPacket(FriendlyByteBuf buf) {
|
||||
this.clientData = FabricTestSyncData.staticFromBytes(buf);
|
||||
this.targetEntityId = buf.readInt();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
clientData.toBytes(buf);
|
||||
buf.writeInt(targetEntityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive.
|
||||
*
|
||||
* @param packet the packet
|
||||
* @param serverPlayer the server player
|
||||
* @param packetSender the packet sender
|
||||
*/
|
||||
public static void receive(@NotNull FabricClientDataPacket packet, @NotNull ServerPlayer serverPlayer, PacketSender packetSender) {
|
||||
FabricFabricItem.handleClientDataFromPacket(serverPlayer, packet.clientData, packet.targetEntityId);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package top.r3944realms.lib39.example.core.network;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
|
||||
/**
|
||||
* The type Fabric ex network handler.
|
||||
*/
|
||||
public class FabricExNetworkHandler {
|
||||
/**
|
||||
* 注册服务器接收的数据包
|
||||
*/
|
||||
public static void registerServerReceivers() {
|
||||
ServerPlayNetworking.registerGlobalReceiver(
|
||||
FabricClientDataPacket.TYPE,
|
||||
FabricClientDataPacket::receive
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package top.r3944realms.lib39.example.core.register;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
import top.r3944realms.lib39.core.register.FabricLib39Items;
|
||||
import top.r3944realms.lib39.example.content.item.FabricFabricItem;
|
||||
import top.r3944realms.lib39.example.content.item.FabricNeoForgeItem;
|
||||
import top.r3944realms.lib39.example.content.item.ForgeItem;
|
||||
|
||||
/**
|
||||
* The type Ex lib 39 items.
|
||||
*/
|
||||
public class FabricExLib39Items {
|
||||
/**
|
||||
* Init.
|
||||
*/
|
||||
public static void init() {
|
||||
ExLib39Items.FABRIC = FabricLib39Items.register("fabric", new FabricFabricItem(
|
||||
new Item.Properties()
|
||||
.stacksTo(1)
|
||||
.fireResistant()
|
||||
));
|
||||
ExLib39Items.NEOFORGE = FabricLib39Items.register("neoforge", new FabricNeoForgeItem(
|
||||
new Item.Properties()
|
||||
.stacksTo(1)
|
||||
.fireResistant()
|
||||
));
|
||||
ExLib39Items.FORGE = FabricLib39Items.register("forge", new ForgeItem(
|
||||
new Item.Properties()
|
||||
.stacksTo(1)
|
||||
.fireResistant()
|
||||
));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
package top.r3944realms.lib39.mixin;
|
||||
|
||||
import net.fabricmc.fabric.api.lookup.v1.custom.ApiProviderMap;
|
||||
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
|
||||
import net.fabricmc.fabric.impl.lookup.entity.EntityApiLookupImpl;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import top.r3944realms.lib39.core.sync.IEntityApiLookUpImplExtend;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The type Mixin api look up.
|
||||
*
|
||||
* @param <A> the type parameter
|
||||
* @param <C> the type parameter
|
||||
*/
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@Mixin(value = EntityApiLookupImpl.class, remap = false)
|
||||
public class MixinApiLookUpImpl<A, C> implements IEntityApiLookUpImplExtend<A, C> {
|
||||
@Shadow @Final private ApiProviderMap<EntityType<?>, EntityApiLookup.EntityApiProvider<A, C>> providerMap;
|
||||
|
||||
@Shadow @Final private List<EntityApiLookup.EntityApiProvider<A, C>> fallbackProviders;
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public A lib39$findWithoutCheck(Entity entity, C context) {
|
||||
Objects.requireNonNull(entity, "Entity may not be null.");
|
||||
EntityApiLookup.EntityApiProvider<A, C> provider = providerMap.get(entity.getType());
|
||||
|
||||
if (provider != null) {
|
||||
A instance = provider.find(entity, context);
|
||||
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
for (EntityApiLookup.EntityApiProvider<A, C> fallback : fallbackProviders) {
|
||||
A instance = fallback.find(entity, context);
|
||||
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package top.r3944realms.lib39.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
|
||||
import top.r3944realms.lib39.core.sync.ILib39SyncDataHolder;
|
||||
import top.r3944realms.lib39.core.sync.ISyncData;
|
||||
import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The type Mixin entity.
|
||||
*/
|
||||
@Mixin(Entity.class)
|
||||
public abstract class MixinEntity implements ILib39SyncDataHolder {
|
||||
/**
|
||||
* Gets uuid.
|
||||
*
|
||||
* @return the uuid
|
||||
*/
|
||||
@Shadow public abstract UUID getUUID();
|
||||
|
||||
@Unique
|
||||
private CompoundTag lib39$syncData;
|
||||
|
||||
@Override
|
||||
public CompoundTag lib39$getSyncData() {
|
||||
if (lib39$syncData == null) {
|
||||
lib39$syncData = new CompoundTag();
|
||||
}
|
||||
return lib39$syncData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lib39$setSyncData(CompoundTag tag) {
|
||||
this.lib39$syncData = tag;
|
||||
}
|
||||
|
||||
@WrapMethod(method = "saveWithoutId")
|
||||
private CompoundTag wrapSave(CompoundTag compound, @NotNull Operation<CompoundTag> original) {
|
||||
FabricCommonEventHandler.getSyncData2Manager().forEach((id, manager) -> {
|
||||
ISyncData<?> o = manager.getSyncMap().get(getUUID());
|
||||
if (o instanceof NBTEntitySyncData syncData) {
|
||||
saveSyncData(syncData);
|
||||
}
|
||||
});
|
||||
return lib39$injectSaveSyncData(original.call(compound));
|
||||
}
|
||||
|
||||
@WrapMethod(method = "load")
|
||||
private void warpLoad(CompoundTag compound, @NotNull Operation<Void> original) {
|
||||
original.call(compound);
|
||||
lib39$injectLoadSyncData(compound);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
package top.r3944realms.lib39.mixin.callback;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.*;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import top.r3944realms.lib39.api.callback.AnvilUpdateCallback;
|
||||
|
||||
/**
|
||||
* The type Mixin anvil menu.
|
||||
*/
|
||||
@Mixin(AnvilMenu.class)
|
||||
public abstract class MixinAnvilMenu extends AbstractContainerMenu {
|
||||
@Shadow
|
||||
private String itemName;
|
||||
@Final
|
||||
@Shadow private DataSlot cost;
|
||||
@Shadow
|
||||
private int repairItemCountCost;
|
||||
|
||||
@Unique
|
||||
private Player cachedPlayer;
|
||||
|
||||
/**
|
||||
* Instantiates a new Mixin anvil menu.
|
||||
*
|
||||
* @param menuType the menu type
|
||||
* @param containerId the container id
|
||||
*/
|
||||
protected MixinAnvilMenu(@Nullable MenuType<?> menuType, int containerId) {
|
||||
super(menuType, containerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获玩家引用
|
||||
*/
|
||||
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/inventory/ContainerLevelAccess;)V",
|
||||
at = @At("RETURN"))
|
||||
private void onInit(int containerId, net.minecraft.world.entity.player.Inventory playerInventory,
|
||||
ContainerLevelAccess access, CallbackInfo ci) {
|
||||
this.cachedPlayer = playerInventory.player;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在createResult方法开始时注入事件
|
||||
*/
|
||||
@Inject(
|
||||
method = "createResult",
|
||||
at = @At(value = "HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void onAnvilUpdate(CallbackInfo ci) {
|
||||
AnvilMenu menu = AnvilMenu.class.cast(this);
|
||||
ItemStack left = menu.getSlot(0).getItem();
|
||||
ItemStack right = menu.getSlot(1).getItem();
|
||||
ItemStack outputSlot = menu.getSlot(2).getItem();
|
||||
|
||||
// 如果左侧为空,不处理
|
||||
if (left.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用事件
|
||||
AnvilUpdateCallback.AnvilUpdateResult result = AnvilUpdateCallback.EVENT.invoker()
|
||||
.onAnvilUpdate(left, right, outputSlot, itemName, cost.get(), cachedPlayer);
|
||||
|
||||
if (result != null) {
|
||||
if (result.cancel()) {
|
||||
// 取消事件,清空输出
|
||||
menu.getSlot(2).set(ItemStack.EMPTY);
|
||||
cost.set(0);
|
||||
repairItemCountCost = 0;
|
||||
broadcastChanges();
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.shouldOverride()) {
|
||||
// 设置自定义结果
|
||||
menu.getSlot(2).set(result.output());
|
||||
cost.set(result.cost());
|
||||
repairItemCountCost = result.materialCost();
|
||||
broadcastChanges();
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package top.r3944realms.lib39.mixin.callback;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.ServerInterface;
|
||||
import net.minecraft.server.Services;
|
||||
import net.minecraft.server.WorldStem;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
|
||||
import net.minecraft.server.packs.repository.PackRepository;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import top.r3944realms.lib39.api.callback.MinecraftSetUpServiceCallback;
|
||||
|
||||
import java.net.Proxy;
|
||||
|
||||
/**
|
||||
* The type Mixin dedicate server.
|
||||
*/
|
||||
@Mixin(DedicatedServer.class)
|
||||
public abstract class MixinDedicateServer extends MinecraftServer implements ServerInterface {
|
||||
/**
|
||||
* Instantiates a new Mixin dedicate server.
|
||||
*
|
||||
* @param serverThread the server thread
|
||||
* @param storageSource the storage source
|
||||
* @param packRepository the pack repository
|
||||
* @param worldStem the world stem
|
||||
* @param proxy the proxy
|
||||
* @param fixerUpper the fixer upper
|
||||
* @param services the services
|
||||
* @param progressListenerFactory the progress listener factory
|
||||
*/
|
||||
public MixinDedicateServer(Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, ChunkProgressListenerFactory progressListenerFactory) {
|
||||
super(serverThread, storageSource, packRepository, worldStem, proxy, fixerUpper, services, progressListenerFactory);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "initServer",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/players/GameProfileCache;setUsesAuthentication(Z)V",
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
private void initServer$setup(CallbackInfoReturnable<Boolean> cir) {
|
||||
MinecraftSetUpServiceCallback.EVENT.invoker().load(this.services,this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
package top.r3944realms.lib39.mixin.callback;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.platform.WindowEventHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.server.Services;
|
||||
import net.minecraft.server.WorldStem;
|
||||
import net.minecraft.server.packs.repository.PackRepository;
|
||||
import net.minecraft.util.thread.ReentrantBlockableEventLoop;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import top.r3944realms.lib39.api.callback.MinecraftSetUpServiceCallback;
|
||||
import top.r3944realms.lib39.api.callback.client.ClientWorldCallback;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The type Mixin minecraft.
|
||||
*/
|
||||
@Mixin(Minecraft.class)
|
||||
public abstract class MixinMinecraft extends ReentrantBlockableEventLoop<Runnable> implements WindowEventHandler {
|
||||
/**
|
||||
* The Level.
|
||||
*/
|
||||
@Shadow @Nullable public ClientLevel level;
|
||||
|
||||
/**
|
||||
* Instantiates a new Mixin minecraft.
|
||||
*
|
||||
* @param name the name
|
||||
*/
|
||||
public MixinMinecraft(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set level callback.
|
||||
*
|
||||
* @param levelClient the level client
|
||||
* @param original the original
|
||||
*/
|
||||
@WrapMethod(method = "setLevel")
|
||||
public void setLevel$callback(ClientLevel levelClient, Operation<Void> original) {
|
||||
if (levelClient != null) ClientWorldCallback.UNLOAD.invoker().onWorldUnload(levelClient);
|
||||
original.call(levelClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear level callback.
|
||||
*
|
||||
* @param original the original
|
||||
*/
|
||||
@WrapMethod(method = "clearLevel()V")
|
||||
public void clearLevel$callback(Operation<Void> original) {
|
||||
if (level != null) ClientWorldCallback.UNLOAD.invoker().onWorldUnload(level);
|
||||
original.call();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set level setup.
|
||||
*
|
||||
* @param levelClient the level client
|
||||
* @param ci the ci
|
||||
* @param services the services
|
||||
*/
|
||||
@Inject(
|
||||
method = "setLevel",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/players/GameProfileCache;setUsesAuthentication(Z)V",
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
public void setLevel$setup(ClientLevel levelClient, CallbackInfo ci,
|
||||
@Local(ordinal = 0) Services services) {
|
||||
MinecraftSetUpServiceCallback.EVENT.invoker().load(services,this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do world load setup.
|
||||
*
|
||||
* @param levelId the level id
|
||||
* @param level the level
|
||||
* @param packRepository the pack repository
|
||||
* @param worldStem the world stem
|
||||
* @param newWorld the new world
|
||||
* @param ci the ci
|
||||
* @param services the services
|
||||
*/
|
||||
@Inject(
|
||||
method = "doWorldLoad",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/players/GameProfileCache;setUsesAuthentication(Z)V",
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
public void doWorldLoad$setup(String levelId, LevelStorageSource.LevelStorageAccess level, PackRepository packRepository, WorldStem worldStem, boolean newWorld, CallbackInfo ci,
|
||||
@Local(ordinal = 0) Services services) {
|
||||
MinecraftSetUpServiceCallback.EVENT.invoker().load(services,this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package top.r3944realms.lib39.mixin.callback.client;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.storage.WritableLevelData;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import top.r3944realms.lib39.api.callback.client.ClientWorldCallback;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Mixin client level.
|
||||
*/
|
||||
@Mixin(ClientLevel.class)
|
||||
public abstract class MixinClientLevel extends Level {
|
||||
/**
|
||||
* Instantiates a new Mixin client level.
|
||||
*
|
||||
* @param levelData the level data
|
||||
* @param dimension the dimension
|
||||
* @param registryAccess the registry access
|
||||
* @param dimensionTypeRegistration the dimension type registration
|
||||
* @param profiler the profiler
|
||||
* @param isClientSide the is client side
|
||||
* @param isDebug the is debug
|
||||
* @param biomeZoomSeed the biome zoom seed
|
||||
* @param maxChainedNeighborUpdates the max chained neighbor updates
|
||||
*/
|
||||
protected MixinClientLevel(WritableLevelData levelData, ResourceKey<Level> dimension, RegistryAccess registryAccess, Holder<DimensionType> dimensionTypeRegistration, Supplier<ProfilerFiller> profiler, boolean isClientSide, boolean isDebug, long biomeZoomSeed, int maxChainedNeighborUpdates) {
|
||||
super(levelData, dimension, registryAccess, dimensionTypeRegistration, profiler, isClientSide, isDebug, biomeZoomSeed, maxChainedNeighborUpdates);
|
||||
}
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void $init(ClientPacketListener connection, ClientLevel.ClientLevelData clientLevelData, ResourceKey<Level> dimension, Holder<DimensionType> dimensionType, int viewDistance, int serverSimulationDistance, Supplier<ProfilerFiller> profiler, LevelRenderer levelRenderer, boolean isDebug, long biomeZoomSeed, CallbackInfo ci) {
|
||||
ClientWorldCallback.LOAD.invoker().onWorldLoad(ClientLevel.class.cast(this));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package top.r3944realms.lib39.platform;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandBuildContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import top.r3944realms.lib39.api.callback.RegisterCommandHelpCallback;
|
||||
import top.r3944realms.lib39.core.command.ICommandHelpManager;
|
||||
import top.r3944realms.lib39.platform.services.IHelpCommandHook;
|
||||
|
||||
/**
|
||||
* The enum Fabric help command hook.
|
||||
*/
|
||||
public enum FabricHelpCommandHook implements IHelpCommandHook {
|
||||
/**
|
||||
* Instance fabric help command hook.
|
||||
*/
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public void onRegister(LiteralArgumentBuilder<CommandSourceStack> tree, ICommandHelpManager manager, CommandBuildContext context) {
|
||||
RegisterCommandHelpCallback.EVENT.invoker().register(new RegisterCommandHelpCallback.CommandHelpRegistrar(tree, manager, context));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package top.r3944realms.lib39.platform;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import net.fabricmc.loader.api.metadata.ModMetadata;
|
||||
import top.r3944realms.lib39.Lib39;
|
||||
import top.r3944realms.lib39.platform.services.IHelpCommandHook;
|
||||
import top.r3944realms.lib39.platform.services.IPlatformHelper;
|
||||
import top.r3944realms.lib39.platform.services.IUtilHelper;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The type Fabric platform helper.
|
||||
*/
|
||||
public class FabricPlatformHelper implements IPlatformHelper {
|
||||
|
||||
@Override
|
||||
public String getPlatformName() {
|
||||
return "Fabric";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModLoaded(String modId) {
|
||||
return FabricLoader.getInstance().isModLoaded(modId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDevelopmentEnvironment() {
|
||||
|
||||
return FabricLoader.getInstance().isDevelopmentEnvironment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClientEnvironment() {
|
||||
return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModVersion() {
|
||||
return FabricLoader.getInstance().getModContainer(Lib39.MOD_ID)
|
||||
.map(ModContainer::getMetadata)
|
||||
.map(ModMetadata::getVersion)
|
||||
.map(Objects::toString)
|
||||
.orElse("NONE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IUtilHelper getUtilHelper() {
|
||||
return FabricUtilHelper.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IHelpCommandHook getHelpCommandHook() {
|
||||
return FabricHelpCommandHook.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package top.r3944realms.lib39.platform;
|
||||
|
||||
import top.r3944realms.lib39.platform.services.IUtilHelper;
|
||||
import top.r3944realms.lib39.util.FabricBlockRegistryBuilder;
|
||||
import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
|
||||
|
||||
/**
|
||||
* The enum Fabric util helper.
|
||||
*/
|
||||
public enum FabricUtilHelper implements IUtilHelper {
|
||||
/**
|
||||
* Instance fabric util helper.
|
||||
*/
|
||||
INSTANCE;
|
||||
@Override
|
||||
public BlockRegistryBuilder getBlockRegistryBuilder() {
|
||||
return new FabricBlockRegistryBuilder();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package top.r3944realms.lib39.util;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
|
||||
import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* The type Fabric block registry builder.
|
||||
*/
|
||||
public class FabricBlockRegistryBuilder extends BlockRegistryBuilder {
|
||||
@SafeVarargs
|
||||
@Override
|
||||
protected final void registerBlockItem(Supplier<Block> blockObject, ResourceKey<CreativeModeTab>... creativeTabs) {
|
||||
FabricCommonEventHandler.addItemToTabs(blockObject, creativeTabs);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
top.r3944realms.lib39.platform.FabricPlatformHelper
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "${mod_id}",
|
||||
"version": "${version}",
|
||||
"name": "${mod_name}",
|
||||
"description": "${description}",
|
||||
"authors": [
|
||||
"${mod_author}"
|
||||
],
|
||||
"contact": {
|
||||
"homepage": "https://github.com/3944Realms",
|
||||
"sources": "https://github.com/3944Realms/Lib39"
|
||||
},
|
||||
"license": "${license}",
|
||||
"icon": "lib39_logo.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"top.r3944realms.lib39.Lib39Fabric"
|
||||
],
|
||||
"client": [
|
||||
"top.r3944realms.lib39.Lib39FabricClient"
|
||||
],
|
||||
"jade": [
|
||||
"top.r3944realms.lib39.base.compat.jade.FabricJadePlugin"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"${mod_id}.mixins.json",
|
||||
"${mod_id}.fabric.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.14",
|
||||
"fabric": "*",
|
||||
"minecraft": "${minecraft_version}",
|
||||
"java": ">=17"
|
||||
},
|
||||
"suggests": {
|
||||
"another-mod": "*"
|
||||
},
|
||||
"custom": {
|
||||
"mc-publish": {
|
||||
"dependencies": [
|
||||
"fabric-api(required)"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "top.r3944realms.lib39.mixin",
|
||||
"refmap": "${mod_id}.fabric.refmap.json",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"MixinApiLookUpImpl",
|
||||
"MixinEntity",
|
||||
"callback.MixinAnvilMenu",
|
||||
"callback.MixinDedicateServer"
|
||||
],
|
||||
"client": [
|
||||
"callback.MixinMinecraft",
|
||||
"callback.client.MixinClientLevel"
|
||||
],
|
||||
"server": [
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
plugins {
|
||||
id 'multiloader-loader'
|
||||
id 'net.neoforged.moddev.legacyforge'
|
||||
}
|
||||
|
||||
mixin {
|
||||
add(sourceSets.main, "${mod_id}.refmap.json")
|
||||
|
||||
config("${mod_id}.mixins.json")
|
||||
config("${mod_id}.forge.mixins.json")
|
||||
}
|
||||
|
||||
def commonResources = project(':common').file('src/main/resources/').getAbsolutePath()
|
||||
def forgeResources = file('src/main/resources/').getAbsolutePath()
|
||||
def forgeBuildResources = file('build/resources/main/').getAbsolutePath()
|
||||
def commonBuildResources = project(':common').file('build/resources/main/').getAbsolutePath()
|
||||
def generatedOutput = project(':common').file('src/generated/resources/').getAbsolutePath()
|
||||
|
||||
legacyForge {
|
||||
version = "${minecraft_version}-${forge_version}"
|
||||
|
||||
validateAccessTransformers = true
|
||||
|
||||
def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
if (at.exists()) {
|
||||
accessTransformers = ["src/main/resources/META-INF/accesstransformer.cfg"]
|
||||
}
|
||||
parchment {
|
||||
minecraftVersion = parchment_minecraft
|
||||
mappingsVersion = parchment_version
|
||||
}
|
||||
runs {
|
||||
client {
|
||||
client()
|
||||
devLogin = true
|
||||
programArguments.addAll(
|
||||
'--mod', project.mod_id,
|
||||
'--all',
|
||||
'--existing', forgeResources,
|
||||
'--existing', commonResources,
|
||||
'--existing', forgeBuildResources,
|
||||
'--existing', commonBuildResources
|
||||
)
|
||||
}
|
||||
data {
|
||||
data()
|
||||
|
||||
// 使用之前定义的变量
|
||||
programArguments.addAll(
|
||||
'--mod', project.mod_id,
|
||||
'--all',
|
||||
'--output', generatedOutput,
|
||||
'--existing', forgeResources,
|
||||
'--existing', commonResources,
|
||||
'--existing', forgeBuildResources,
|
||||
'--existing', commonBuildResources
|
||||
)
|
||||
}
|
||||
server {
|
||||
server()
|
||||
}
|
||||
}
|
||||
|
||||
mods {
|
||||
"${mod_id}" {
|
||||
sourceSet sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.main.resources.srcDir project(':common').file('src/generated/resources')
|
||||
|
||||
dependencies {
|
||||
compileOnly project(":common")
|
||||
annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT:processor")
|
||||
implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:0.2.0"))
|
||||
modImplementation(group: 'tschipp.carryon', name: 'carryon-forge-1.20.1', version: '2.1.2.7') {
|
||||
transitive = false
|
||||
}
|
||||
modImplementation ("curse.maven:jade-324717:6855440")
|
||||
implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.2.0"))
|
||||
}
|
||||
|
||||
jar {
|
||||
finalizedBy('reobfJar')
|
||||
manifest.attributes([
|
||||
"MixinConfigs": "${mod_id}.mixins.json,${mod_id}.forge.mixins.json"
|
||||
])
|
||||
}
|
||||
|
||||
// 配置sourceJar任务
|
||||
tasks.named('sourcesJar', Jar) {
|
||||
dependsOn classes
|
||||
dependsOn project(':common').tasks.named('sourcesJar') // 显式依赖common的source
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
archiveClassifier.set('sources')
|
||||
from sourceSets.main.allSource
|
||||
from project(':common').sourceSets.main.allSource
|
||||
}
|
||||
|
||||
// 配置javadoc任务
|
||||
tasks.named('javadoc', Javadoc) {
|
||||
source project(':common').sourceSets.main.allJava
|
||||
source sourceSets.main.allJava
|
||||
classpath = configurations.compileClasspath
|
||||
classpath += project(':common').sourceSets.main.compileClasspath
|
||||
options.encoding = 'UTF-8'
|
||||
options.charSet = 'UTF-8'
|
||||
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||
options.memberLevel = JavadocMemberLevel.PUBLIC
|
||||
options.addBooleanOption('Xdoclint:none', true)
|
||||
options.addStringOption('doctitle', "${mod_id} ${minecraft_version} ${version} Javadoc")
|
||||
}
|
||||
|
||||
// 配置javadocJar任务
|
||||
tasks.named('javadocJar', Jar) {
|
||||
dependsOn javadoc
|
||||
dependsOn project(':common').tasks.named('javadoc') // 显式依赖common的javadoc
|
||||
|
||||
archiveClassifier.set('javadoc')
|
||||
from javadoc.destinationDir
|
||||
from project(':common').javadoc.destinationDir
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
// 确保build任务包含所有需要的jar
|
||||
tasks.named('build') {
|
||||
dependsOn tasks.named('sourcesJar')
|
||||
dependsOn tasks.named('javadocJar')
|
||||
}
|
||||
|
||||
// 处理reobf
|
||||
tasks.named('reobfJar') {
|
||||
dependsOn tasks.named('sourcesJar')
|
||||
dependsOn tasks.named('javadocJar')
|
||||
}
|
||||
|
||||
// 发布配置
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifactId = "${mod_id}-forge-${minecraft_version}"
|
||||
artifacts.clear()
|
||||
artifact(tasks.named('reobfJar').get()) {
|
||||
builtBy tasks.named('reobfJar')
|
||||
}
|
||||
artifact(tasks.named('sourcesJar').get()) {
|
||||
builtBy tasks.named('sourcesJar')
|
||||
classifier = 'sources'
|
||||
}
|
||||
artifact(tasks.named('javadocJar').get()) {
|
||||
builtBy tasks.named('javadocJar')
|
||||
classifier = 'javadoc'
|
||||
}
|
||||
pom {
|
||||
name = 'Lib39'
|
||||
description = 'Lib39 is a general-purpose dependency library for Minecraft mods.'
|
||||
url = 'https://github.com/3944Realms/lib39'
|
||||
|
||||
properties = [
|
||||
'minecraft.version': project.minecraft_version,
|
||||
'mod.version': project.version,
|
||||
'forge.version': project.forge_version,
|
||||
'java.version': '17'
|
||||
]
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = 'MIT'
|
||||
url = 'https://raw.githubusercontent.com/3944Realms/lib39/refs/heads/main/LICENSE'
|
||||
distribution = 'repo'
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'R3944Realms'
|
||||
name = "${mod_author}"
|
||||
email = 'f256198830@hotmail.com'
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection = 'scm:git:https://github.com/3944Realms/lib39.git'
|
||||
developerConnection = 'scm:git:ssh://git@github.com:3944Realms/lib39.git'
|
||||
url = 'https://github.com/3944Realms/lib39'
|
||||
tag = 'main'
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system = 'GitHub'
|
||||
url = 'https://github.com/3944Realms/lib39/issues'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
// 处理资源
|
||||
processResources {
|
||||
from project(':common').sourceSets.main.resources
|
||||
|
||||
inputs.property "version", project.version
|
||||
inputs.property "minecraft_version", minecraft_version
|
||||
inputs.property "forge_version", forge_version
|
||||
inputs.property "mod_id", mod_id
|
||||
inputs.property "mod_name", mod_name
|
||||
inputs.property "description", description
|
||||
inputs.property "mod_author", mod_author
|
||||
|
||||
filesMatching(['META-INF/mods.toml', 'pack.mcmeta', "*.mixins.json"]) {
|
||||
expand([
|
||||
version: project.version,
|
||||
minecraft_version: minecraft_version,
|
||||
forge_version: forge_version,
|
||||
mod_id: mod_id,
|
||||
mod_name: mod_name,
|
||||
description: description,
|
||||
mod_author: mod_author
|
||||
])
|
||||
}
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user