Compare commits

..

No commits in common. "MultiLoader_1_20_1" and "v0.4.1" have entirely different histories.

280 changed files with 5540 additions and 7169 deletions

207
.github/workflows/build.yml vendored Normal file
View 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

View File

@ -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

View File

@ -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
View File

@ -21,7 +21,6 @@ build
# other
eclipse
run
generated
runs
run-data

View File

@ -1,14 +1,10 @@
# Lib39
![GitHub Release](https://img.shields.io/github/v/release/3944Realms/Lib39) [![License](https://img.shields.io/github/license/3944realms/lib39)]()
[![CurseForge Download](https://img.shields.io/curseforge/dt/1445917?logo=curseforge&label=CurseForge)](https://www.curseforge.com/minecraft/mc-mods/lib-39)
[![Modrinth Download](https://img.shields.io/modrinth/dt/n65Vs1Vk?logo=modrinth&label=Modrinth)](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
View 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.

View File

@ -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>
*/

View File

@ -1,3 +0,0 @@
plugins {
id 'groovy-gradle-plugin'
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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'
}

View File

@ -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();
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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() {
}
}

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -1,2 +0,0 @@
accessWidener v2 named
# 不要用这个太垃圾了不支持parchment名

View File

@ -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
}
}

View File

@ -1,6 +0,0 @@
{
"pack": {
"description": "${mod_name}",
"pack_format": 8
}
}

8
config/jni-classes.txt Normal file
View 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
View 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()
# whichwherejava
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)
# javaJAVA_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
View 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@)

View 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
)

View 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

View File

@ -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
View File

@ -0,0 +1 @@
cmake_minimum_required(VERSION 3.28)

7
cpp/src/CMakeLists.txt Normal file
View 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)

View 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

View 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

View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.28)
add_library(J_GUARD
JByteArrayGuard.cpp
)

View 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;
}
};

View File

@ -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')
}

View File

@ -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);
}
}
}

View File

@ -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();
});
}
}

View File

@ -1,21 +0,0 @@
package top.r3944realms.lib39.api.callback;
/**
* 结果枚举
*/
public enum ActionResult {
/**
* 继续处理
*/
PASS,
/**
* 取消
*/
CANCEL,
/**
* 允许加入但停止后续处理
*/
SUCCESS
}

View File

@ -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(); // 继续默认处理
}
);
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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()));
}
}
}
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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
);
}
}

View File

@ -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
);
});
}
}

View File

@ -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;
}
}

View File

@ -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
);
}
}

View File

@ -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);
}
);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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()));
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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));
}
}

View File

@ -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() {
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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目标生物不存在或已消失"));
}
}
}

View File

@ -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);
}
}

View File

@ -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!");
}
}

View File

@ -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));
}
}

View File

@ -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;
});
}
}

View File

@ -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);
}
}

View File

@ -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
);
}
}

View File

@ -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()
));
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -1 +0,0 @@
top.r3944realms.lib39.platform.FabricPlatformHelper

View File

@ -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)"
]
}
}
}

View File

@ -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
}
}

View File

@ -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