Compare commits

..

28 Commits

Author SHA1 Message Date
5fb2c614db build: 版本号更为正式版
Some checks failed
Build and Release / build (push) Failing after 34s
Build and Release / release (push) Has been skipped
2026-03-18 23:24:26 +08:00
c05c0bdfc4 build: 修改导致curseforge推送失败的json写法
Some checks failed
Build and Release / build (push) Failing after 34s
Build and Release / release (push) Has been skipped
2026-03-18 23:03:54 +08:00
5a1c2fdbc5 build: 修正CurseForge发布 2026-03-18 22:51:35 +08:00
c9b9e0eefe build: 工作流修正
Some checks failed
Build and Release / build (push) Failing after 1m33s
Build and Release / release (push) Has been skipped
2026-03-18 22:30:37 +08:00
f589cf27a2 build: 修复错误发布问题
Some checks failed
Build and Release / build (push) Failing after 33s
Build and Release / release (push) Has been skipped
2026-03-18 22:02:08 +08:00
35ceb69f0b build: 工作流脚本修复 2026-03-18 21:35:36 +08:00
dbae05691d build: 工作流
Some checks failed
Build and Release / build (push) Failing after 33s
Build and Release / release (push) Has been skipped
2026-03-18 21:19:01 +08:00
a435c3b272 build: 工作流修正 2026-03-18 21:15:24 +08:00
04640d2b12 build: 发布工作流更新
Some checks failed
Build and Release / build (push) Failing after 2h30m21s
Build and Release / release (push) Has been skipped
2026-03-18 20:55:17 +08:00
9dbeb0732f build: 修复发布脚本
Some checks failed
Build and Release / build (push) Failing after 16m59s
Build and Release / release (push) Has been skipped
2026-03-16 15:31:03 +08:00
408482759a Merge remote-tracking branch 'origin/MultiLoader_1_20_1' into MultiLoader_1_20_1 2026-03-16 15:08:07 +08:00
e4fe0f17f2 build: 移除加载DLL的行为(添加模组平台发布流程) 2026-03-16 15:07:46 +08:00
3944Realms
4cdbefc9c3
docs: Merge pull request #3 from 3944Realms/ML_1201_PR
docs: Update implementation section in README.md
2026-03-16 12:39:08 +08:00
3944Realms
c653832a9c
docs: Update implementation section in README.md
Clarified implementation instructions for version 0.5.0+.
2026-03-16 12:35:31 +08:00
85c4c47a72 build: 匹配src路径 2026-03-16 12:31:43 +08:00
80872ebbaa build: 格式检查配置 2026-03-16 12:16:25 +08:00
a5d00dad96 docs: 更新Readme,编写些提示 2026-03-16 11:29:27 +08:00
9eec9972f4 feat: 添加玩偶的Jade皮肤所属者的显示 2026-03-15 13:44:07 +08:00
6c6883b767 feat: 改为多角度的玩偶 2026-03-15 13:10:55 +08:00
0c2a589a0c build: 补充构建脚本
Some checks failed
Build and Release / build (push) Failing after 1h28m43s
Build and Release / release (push) Has been skipped
2026-03-14 22:29:27 +08:00
50856266d8 docs: 添加Javadoc 2026-03-14 22:21:18 +08:00
418e85394c fix: 修复fabric同步错误wt 2026-03-14 22:09:21 +08:00
af3e313fde feat: 完成跨版本化,修复fabric,forge同步错误逻辑 2026-03-14 18:13:25 +08:00
099f29e76f feat: 迁移到多加载器版本,还有点小BUG,待修 2026-03-13 04:19:23 +08:00
0c62d360d9 feat: 跨版本化,第三部分,未完成 2026-03-12 23:56:16 +08:00
5a6729aa31 feat: 跨版本化,第二部分,未完成 2026-03-11 21:02:57 +08:00
eac118e9a4 feat: 让Lib39跨加载器化,未完成 2026-03-09 19:44:09 +08:00
b616b4535e docs: 更新ReadMe,添加小图标 2026-03-01 22:49:17 +08:00
280 changed files with 7170 additions and 5541 deletions

View File

@ -1,207 +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: Build with Gradle
run: ./gradlew build
- name: Prepare release files
run: |
mkdir -p release-files
cp build/libs/*.jar release-files/ 2>/dev/null || true
echo "准备发布的文件:"
ls -la release-files/
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: release-files
path: release-files/
retention-days: 7
release:
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout with full history
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: release-files
path: ./dist
- name: Generate CZ-compliant changelog
id: generate_changelog
run: |
CURRENT_TAG="${{ github.ref_name }}"
PREV_TAG=$(git describe --tags --abbrev=0 $(git rev-list --tags --skip=1 --max-count=1) 2>/dev/null || echo "")
# 创建临时文件
TEMP_FILE=$(mktemp)
echo "# 🚀 版本 $CURRENT_TAG 发布" > $TEMP_FILE
echo "" >> $TEMP_FILE
echo "## 📋 变更摘要" >> $TEMP_FILE
echo "" >> $TEMP_FILE
if [ -z "$PREV_TAG" ]; then
echo "### 初始版本发布" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "这是项目的第一个正式版本。" >> $TEMP_FILE
echo "" >> $TEMP_FILE
# 获取所有提交并按类型分组
git log --pretty=format:"%s" --reverse | while read -r line; do
echo "- $line" >> $TEMP_FILE
done
else
echo "### 从 $PREV_TAG 到 $CURRENT_TAG 的变更" >> $TEMP_FILE
echo "" >> $TEMP_FILE
# 定义符合CZ规范的提交类型映射
declare -A commit_types
commit_types=(
["✨ 新功能"]="^(feat|feature)(\(.*\))?:"
["🐛 修复"]="^(fix|bugfix)(\(.*\))?:"
["📝 文档"]="^(docs|documentation)(\(.*\))?:"
["🎨 样式"]="^(style)(\(.*\))?:"
["🔨 重构"]="^(refactor)(\(.*\))?:"
["⚡️ 性能"]="^(perf|performance)(\(.*\))?:"
["✅ 测试"]="^(test)(\(.*\))?:"
["🔧 构建"]="^(build)(\(.*\))?:"
["👷 CI"]="^(ci)(\(.*\))?:"
["📦 依赖"]="^(chore|deps)(\(.*\))?:"
["⏪ 回退"]="^(revert)(\(.*\))?:"
)
# 获取所有提交
COMMITS=$(git log --pretty=format:"%s" $PREV_TAG..HEAD)
# 处理每种类型的提交
for type_name in "${!commit_types[@]}"; do
pattern="${commit_types[$type_name]}"
# 提取匹配的提交
matched_commits=$(echo "$COMMITS" | grep -E "$pattern" || true)
if [ -n "$matched_commits" ]; then
echo "#### $type_name" >> $TEMP_FILE
echo "" >> $TEMP_FILE
# 处理每条提交提取scope和subject
echo "$matched_commits" | while read -r commit; do
# 解析scope和subject
if [[ $commit =~ ^[a-z]+\((.*)\):\ (.*) ]]; then
scope="${BASH_REMATCH[1]}"
subject="${BASH_REMATCH[2]}"
echo "- **$scope**: $subject" >> $TEMP_FILE
elif [[ $commit =~ ^[a-z]+:\ (.*) ]]; then
subject="${BASH_REMATCH[1]}"
echo "- $subject" >> $TEMP_FILE
else
echo "- $commit" >> $TEMP_FILE
fi
done
echo "" >> $TEMP_FILE
fi
done
# 处理破坏性变更BREAKING CHANGE
breaking_changes=$(git log --pretty=format:"%b" $PREV_TAG..HEAD | grep -i "BREAKING CHANGE" || true)
if [ -n "$breaking_changes" ]; then
echo "#### ⚠️ 破坏性变更" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "$breaking_changes" | while read -r line; do
echo "- $line" >> $TEMP_FILE
done
echo "" >> $TEMP_FILE
fi
# 处理未分类的提交
uncategorized="$COMMITS"
for pattern in "${commit_types[@]}"; do
uncategorized=$(echo "$uncategorized" | grep -v -E "$pattern" || true)
done
if [ -n "$uncategorized" ]; then
echo "#### 📝 其他更改" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "$uncategorized" | while read -r commit; do
echo "- $commit" >> $TEMP_FILE
done
echo "" >> $TEMP_FILE
fi
fi
echo "## 📊 统计信息" >> $TEMP_FILE
echo "" >> $TEMP_FILE
if [ -z "$PREV_TAG" ]; then
TOTAL_COMMITS=$(git rev-list --count HEAD)
echo "- 总提交数: $TOTAL_COMMITS" >> $TEMP_FILE
echo "- 首次发布" >> $TEMP_FILE
else
COMMITS=$(git rev-list --count $PREV_TAG..HEAD)
echo "- 本次发布提交数: $COMMITS" >> $TEMP_FILE
echo "- 上一个版本: $PREV_TAG" >> $TEMP_FILE
fi
echo "- 发布日期: $(date '+%Y年%m月%d日')" >> $TEMP_FILE
echo "- 当前版本: $CURRENT_TAG" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "---" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "### 📜 详细提交历史" >> $TEMP_FILE
echo "" >> $TEMP_FILE
# 显示所有提交的详细列表包含scope信息
if [ -z "$PREV_TAG" ]; then
git log --pretty=format:"- **%h** %s - %an (%ad)" --date=short --reverse | head -100 >> $TEMP_FILE
else
git log --pretty=format:"- **%h** %s - %an (%ad)" --date=short $PREV_TAG..HEAD | head -100 >> $TEMP_FILE
fi
# 将文件内容输出到变量
CHANGELOG_CONTENT=$(cat $TEMP_FILE)
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*.jar"
tag: ${{ github.ref_name }}
name: "版本 ${{ github.ref_name }}"
body: ${{ steps.generate_changelog.outputs.changelog }}
draft: false
prerelease: false

454
.github/workflows/buildAndRelease.yml vendored Normal file
View File

@ -0,0 +1,454 @@
name: Build and Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Run Forge data generation
run: |
echo "=== 运行 Forge 数据生成 ==="
./gradlew runData --no-daemon
continue-on-error: false
- name: Build with Gradle
run: ./gradlew build --no-daemon
- name: Prepare release files
run: |
mkdir -p release-files
# 收集所有模块的构建产物
echo "=== 收集 common 模块构建产物 ==="
if [ -d "common/build/libs" ]; then
cp common/build/libs/*.jar release-files/ 2>/dev/null || echo "common 模块没有 jar 文件"
fi
echo "=== 收集 fabric 模块构建产物 ==="
if [ -d "fabric/build/libs" ]; then
cp fabric/build/libs/*-dev.jar release-files/ 2>/dev/null || true # 排除dev jar
cp fabric/build/libs/*-sources.jar release-files/ 2>/dev/null || true
cp fabric/build/libs/*-javadoc.jar release-files/ 2>/dev/null || true
# 只复制主jar没有sources/javadoc/dev classifier的jar
find fabric/build/libs -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" ! -name "*-dev.jar" -exec cp {} release-files/ \;
fi
echo "=== 收集 forge 模块构建产物 ==="
if [ -d "forge/build/libs" ]; then
cp forge/build/libs/*-sources.jar release-files/ 2>/dev/null || true
cp forge/build/libs/*-javadoc.jar release-files/ 2>/dev/null || true
# 只复制主jar没有sources/javadoc classifier的jar
find forge/build/libs -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" -exec cp {} release-files/ \;
fi
echo "=== 准备发布的文件 ==="
ls -la release-files/
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: release-files
path: release-files/
retention-days: 7
release:
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: Checkout with full history
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine version type
id: version_type
run: |
if [[ "${{ github.ref_name }}" == *"alpha"* ]]; then
echo "type=alpha" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref_name }}" == *"beta"* ]]; then
echo "type=beta" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref_name }}" == *"rc"* ]]; then
echo "type=beta" >> $GITHUB_OUTPUT
else
echo "type=release" >> $GITHUB_OUTPUT
fi
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: release-files
path: ./dist
- name: Extract version info
id: version_info
run: |
# 从tag中提取版本号去掉v前缀
VERSION="${GITHUB_REF_NAME#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "minecraft_version=$(grep "^minecraft_version=" gradle.properties | cut -d'=' -f2)" >> $GITHUB_OUTPUT
# 从 gradle.properties 提取 mod_id如果没有则尝试从文件名推断
MOD_ID=$(grep "^mod_id=" gradle.properties | cut -d'=' -f2 || echo "")
if [ -z "$MOD_ID" ]; then
# 尝试从现有的 jar 文件名提取 mod_id
SAMPLE_JAR=$(ls dist/ | grep -m 1 -E ".*-(fabric|forge)-.*\.jar" || echo "")
if [ -n "$SAMPLE_JAR" ]; then
MOD_ID=$(echo "$SAMPLE_JAR" | sed -E 's/-(fabric|forge)-.*//')
else
MOD_ID="mymod" # 默认值,请根据实际情况修改
fi
fi
echo "mod_id=$MOD_ID" >> $GITHUB_OUTPUT
# 从 gradle.properties 提取 mod_name用于显示
MOD_NAME=$(grep "^mod_name=" gradle.properties | cut -d'=' -f2 || echo "My Mod")
echo "mod_name=$MOD_NAME" >> $GITHUB_OUTPUT
# 从 gradle.properties 提取 modrinth_id
MODRINTH_ID=$(grep "^modrinth_id=" gradle.properties | cut -d'=' -f2 || echo "")
echo "modrinth_id=$MODRINTH_ID" >> $GITHUB_OUTPUT
# 从 gradle.properties 提取 curseforge_id
CURSEFORGE_ID=$(grep "^curseforge_id=" gradle.properties | cut -d'=' -f2 || echo "")
echo "curseforge_id=$CURSEFORGE_ID" >> $GITHUB_OUTPUT
# Java版本 - 使用简单格式不用JSON
JAVA_VERSIONS=$(grep "^java_versions=" gradle.properties | cut -d'=' -f2- || echo "21,17")
# 清理格式,移除无效字符
JAVA_VERSIONS=$(echo "$JAVA_VERSIONS" | sed 's/\[//g; s/\]//g; s/"//g; s/ //g; s/21a/21/g' | tr -d '\r')
echo "java_versions=$JAVA_VERSIONS" >> $GITHUB_OUTPUT
# 读取发布控制布尔值(默认都为 true
PUBLISH_GITHUB=$(grep "^publish_github=" gradle.properties | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]' | tr -d ' ' || echo "true")
if [ "$PUBLISH_GITHUB" = "true" ] || [ "$PUBLISH_GITHUB" = "1" ] || [ "$PUBLISH_GITHUB" = "yes" ]; then
echo "publish_github=true" >> $GITHUB_OUTPUT
else
echo "publish_github=false" >> $GITHUB_OUTPUT
fi
PUBLISH_MODRINTH=$(grep "^publish_modrinth=" gradle.properties | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]' | tr -d ' ' || echo "true")
if [ "$PUBLISH_MODRINTH" = "true" ] || [ "$PUBLISH_MODRINTH" = "1" ] || [ "$PUBLISH_MODRINTH" = "yes" ]; then
echo "publish_modrinth=true" >> $GITHUB_OUTPUT
else
echo "publish_modrinth=false" >> $GITHUB_OUTPUT
fi
PUBLISH_CURSEFORGE=$(grep "^publish_curseforge=" gradle.properties | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]' | tr -d ' ' || echo "true")
if [ "$PUBLISH_CURSEFORGE" = "true" ] || [ "$PUBLISH_CURSEFORGE" = "1" ] || [ "$PUBLISH_CURSEFORGE" = "yes" ]; then
echo "publish_curseforge=true" >> $GITHUB_OUTPUT
else
echo "publish_curseforge=false" >> $GITHUB_OUTPUT
fi
# 读取依赖配置 - 使用简单字符串不用JSON
FABRIC_MODRINTH_DEPS=$(grep "^fabric_modrinth_dependencies=" gradle.properties | cut -d'=' -f2- || echo "")
echo "fabric_modrinth_dependencies=$FABRIC_MODRINTH_DEPS" >> $GITHUB_OUTPUT
FORGE_MODRINTH_DEPS=$(grep "^forge_modrinth_dependencies=" gradle.properties | cut -d'=' -f2- || echo "")
echo "forge_modrinth_dependencies=$FORGE_MODRINTH_DEPS" >> $GITHUB_OUTPUT
FABRIC_CURSEFORGE_DEPS=$(grep "^fabric_curseforge_dependencies=" gradle.properties | cut -d'=' -f2- || echo "")
echo "fabric_curseforge_dependencies=$FABRIC_CURSEFORGE_DEPS" >> $GITHUB_OUTPUT
FORGE_CURSEFORGE_DEPS=$(grep "^forge_curseforge_dependencies=" gradle.properties | cut -d'=' -f2- || echo "")
echo "forge_curseforge_dependencies=$FORGE_CURSEFORGE_DEPS" >> $GITHUB_OUTPUT
- name: Generate CZ-compliant changelog
id: generate_changelog
run: |
CURRENT_TAG="${{ github.ref_name }}"
PREV_TAG=$(git describe --tags --abbrev=0 $(git rev-list --tags --skip=1 --max-count=1) 2>/dev/null || echo "")
# 创建临时文件
TEMP_FILE=$(mktemp)
echo "# 🚀 版本 $CURRENT_TAG 发布" > $TEMP_FILE
echo "" >> $TEMP_FILE
echo "## 📋 变更摘要" >> $TEMP_FILE
echo "" >> $TEMP_FILE
if [ -z "$PREV_TAG" ]; then
echo "### 初始版本发布" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "这是项目的第一个正式版本。" >> $TEMP_FILE
echo "" >> $TEMP_FILE
# 获取所有提交并按类型分组
git log --pretty=format:"%s" --reverse | while read -r line; do
echo "- $line" >> $TEMP_FILE
done
else
echo "### 从 $PREV_TAG 到 $CURRENT_TAG 的变更" >> $TEMP_FILE
echo "" >> $TEMP_FILE
# 定义符合CZ规范的提交类型映射
declare -A commit_types
commit_types=(
["✨ 新功能"]="^(feat|feature)(\(.*\))?:"
["🐛 修复"]="^(fix|bugfix)(\(.*\))?:"
["📝 文档"]="^(docs|documentation)(\(.*\))?:"
["🎨 样式"]="^(style)(\(.*\))?:"
["🔨 重构"]="^(refactor)(\(.*\))?:"
["⚡️ 性能"]="^(perf|performance)(\(.*\))?:"
["✅ 测试"]="^(test)(\(.*\))?:"
["🔧 构建"]="^(build)(\(.*\))?:"
["👷 CI"]="^(ci)(\(.*\))?:"
["📦 依赖"]="^(chore|deps)(\(.*\))?:"
["⏪ 回退"]="^(revert)(\(.*\))?:"
["🛠 合并"]="^Merge "
)
# 获取所有提交
COMMITS=$(git log --pretty=format:"%s" $PREV_TAG..HEAD)
# 处理每种类型的提交
for type_name in "${!commit_types[@]}"; do
pattern="${commit_types[$type_name]}"
# 提取匹配的提交
matched_commits=$(echo "$COMMITS" | grep -E "$pattern" || true)
if [ -n "$matched_commits" ]; then
echo "#### $type_name" >> $TEMP_FILE
echo "" >> $TEMP_FILE
# 处理每条提交提取scope和subject
echo "$matched_commits" | while read -r commit; do
# 解析scope和subject
if [[ $commit =~ ^[a-z]+\((.*)\):\ (.*) ]]; then
scope="${BASH_REMATCH[1]}"
subject="${BASH_REMATCH[2]}"
echo "- **$scope**: $subject" >> $TEMP_FILE
elif [[ $commit =~ ^[a-z]+:\ (.*) ]]; then
subject="${BASH_REMATCH[1]}"
echo "- $subject" >> $TEMP_FILE
else
echo "- $commit" >> $TEMP_FILE
fi
done
echo "" >> $TEMP_FILE
fi
done
# 处理破坏性变更BREAKING CHANGE
breaking_changes=$(git log --pretty=format:"%b" $PREV_TAG..HEAD | grep -i "BREAKING CHANGE" || true)
if [ -n "$breaking_changes" ]; then
echo "#### ⚠️ 破坏性变更" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "$breaking_changes" | while read -r line; do
echo "- $line" >> $TEMP_FILE
done
echo "" >> $TEMP_FILE
fi
# 处理未分类的提交
uncategorized="$COMMITS"
for pattern in "${commit_types[@]}"; do
uncategorized=$(echo "$uncategorized" | grep -v -E "$pattern" || true)
done
if [ -n "$uncategorized" ]; then
echo "#### 📝 其他更改" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "$uncategorized" | while read -r commit; do
echo "- $commit" >> $TEMP_FILE
done
echo "" >> $TEMP_FILE
fi
fi
echo "## 📊 统计信息" >> $TEMP_FILE
echo "" >> $TEMP_FILE
if [ -z "$PREV_TAG" ]; then
TOTAL_COMMITS=$(git rev-list --count HEAD)
echo "- 总提交数: $TOTAL_COMMITS" >> $TEMP_FILE
echo "- 首次发布" >> $TEMP_FILE
else
COMMITS=$(git rev-list --count $PREV_TAG..HEAD)
echo "- 本次发布提交数: $COMMITS" >> $TEMP_FILE
echo "- 上一个版本: $PREV_TAG" >> $TEMP_FILE
fi
echo "- 发布日期: $(date '+%Y年%m月%d日')" >> $TEMP_FILE
echo "- 当前版本: $CURRENT_TAG" >> $TEMP_FILE
echo "- Minecraft版本: ${{ steps.version_info.outputs.minecraft_version }}" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "---" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "### 📜 详细提交历史" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "<details>" >> $TEMP_FILE
echo "<summary>点击展开查看完整提交历史</summary>" >> $TEMP_FILE
echo "" >> $TEMP_FILE
echo "\`\`\`" >> $TEMP_FILE
if [ -z "$PREV_TAG" ]; then
# 使用 while 循环确保每条提交独立一行
git log --pretty=format:"%h %s - %an (%ad)" --date=short --reverse | while IFS= read -r line; do
echo "$line" >> $TEMP_FILE
done
else
git log --pretty=format:"%h %s - %an (%ad)" --date=short $PREV_TAG..HEAD | while IFS= read -r line; do
echo "$line" >> $TEMP_FILE
done
fi
# 确保文件末尾有换行
echo "" >> $TEMP_FILE
echo "\`\`\`" >> $TEMP_FILE
echo "</details>" >> $TEMP_FILE
# 将文件内容输出到变量
CHANGELOG_CONTENT=$(cat $TEMP_FILE)
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create Release
if: steps.version_info.outputs.publish_github == 'true'
uses: ncipollo/release-action@v1
with:
artifacts: |
dist/*.jar
tag: ${{ github.ref_name }}
name: "${{ steps.version_info.outputs.minecraft_version }} - ${{ github.ref_name }}"
body: ${{ steps.generate_changelog.outputs.changelog }}
draft: false
prerelease: ${{ contains(github.ref_name, 'rc') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }}
token: ${{ secrets.GITHUB_TOKEN }}
allowUpdates: true
removeArtifacts: true
# Fabric 发布到 Modrinth 和 CurseForge
- name: Publish Fabric to Modrinth & CurseForge
uses: Kir-Antipov/mc-publish@v3.3
if: success() && (steps.version_info.outputs.publish_modrinth == 'true' || steps.version_info.outputs.publish_curseforge == 'true')
continue-on-error: true
with:
# 文件匹配规则 - 只匹配 fabric 的文件
files: |
dist/${{ steps.version_info.outputs.mod_id }}-fabric-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}.jar
dist/${{ steps.version_info.outputs.mod_id }}-fabric-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}-javadoc.jar
dist/${{ steps.version_info.outputs.mod_id }}-fabric-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}-sources.jar
# 版本信息
name: ${{ steps.version_info.outputs.mod_name }} ${{ steps.version_info.outputs.version }} (Fabric/${{ steps.version_info.outputs.minecraft_version }})
version: "${{ steps.version_info.outputs.minecraft_version }}-fabric-${{ steps.version_info.outputs.version }}"
# 更新日志
changelog: ${{ steps.generate_changelog.outputs.changelog }}
# 版本类型
version-type: ${{ steps.version_type.outputs.type }}
# 只指定 Fabric 加载器
loaders: fabric
# 游戏版本
game-versions: |
${{ steps.version_info.outputs.minecraft_version }}
# Java版本
java: |
${{ steps.version_info.outputs.java_versions }}
# Modrinth 配置
modrinth-id: ${{ steps.version_info.outputs.modrinth_id }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: true
modrinth-unfeature-mode: any
modrinth-dependencies: ${{ steps.version_info.outputs.fabric_modrinth_dependencies }}
# CurseForge 配置
curseforge-id: ${{ steps.version_info.outputs.curseforge_id }}
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
curseforge-dependencies: ${{ steps.version_info.outputs.fabric_curseforge_dependencies }}
# 失败处理
fail-mode: skip
# Forge 发布到 Modrinth 和 CurseForge
- name: Publish Forge to Modrinth & CurseForge
uses: Kir-Antipov/mc-publish@v3.3
if: success() && (steps.version_info.outputs.publish_modrinth == 'true' || steps.version_info.outputs.publish_curseforge == 'true')
continue-on-error: true
with:
# 文件匹配规则 - 只匹配 forge 的文件
files: |
dist/${{ steps.version_info.outputs.mod_id }}-forge-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}.jar
dist/${{ steps.version_info.outputs.mod_id }}-forge-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}-javadoc.jar
dist/${{ steps.version_info.outputs.mod_id }}-forge-${{ steps.version_info.outputs.minecraft_version }}-${{ steps.version_info.outputs.version }}-sources.jar
# 版本信息
name: ${{ steps.version_info.outputs.mod_name }} ${{ steps.version_info.outputs.version }} (Forge/${{ steps.version_info.outputs.minecraft_version }})
version: "${{ steps.version_info.outputs.minecraft_version }}-forge-${{ steps.version_info.outputs.version }}"
# 更新日志
changelog: ${{ steps.generate_changelog.outputs.changelog }}
# 版本类型
version-type: ${{ steps.version_type.outputs.type }}
# 只指定 Forge 加载器
loaders: forge
# 游戏版本
game-versions: |
${{ steps.version_info.outputs.minecraft_version }}
# Java版本
java: |
${{ steps.version_info.outputs.java_versions }}
# Modrinth 配置
modrinth-id: ${{ steps.version_info.outputs.modrinth_id }}
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
modrinth-featured: true
modrinth-unfeature-mode: any
modrinth-dependencies: ${{ steps.version_info.outputs.forge_modrinth_dependencies }}
# CurseForge 配置
curseforge-id: ${{ steps.version_info.outputs.curseforge_id }}
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
curseforge-dependencies: ${{ steps.version_info.outputs.forge_curseforge_dependencies }}
# 失败处理
fail-mode: skip
# 发布完成后列出结果
- name: Summary
if: always()
run: |
echo "## 发布结果摘要" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### GitHub Release" >> $GITHUB_STEP_SUMMARY
echo "- 标签: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "- URL: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Modrinth" >> $GITHUB_STEP_SUMMARY
echo "- 项目ID: ${{ steps.version_info.outputs.modrinth_id }}" >> $GITHUB_STEP_SUMMARY
echo "- Fabric版本: ${{ steps.version_info.outputs.version }}-fabric" >> $GITHUB_STEP_SUMMARY
echo "- Forge版本: ${{ steps.version_info.outputs.version }}-forge" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### CurseForge" >> $GITHUB_STEP_SUMMARY
echo "- 项目ID: ${{ steps.version_info.outputs.curseforge_id }}" >> $GITHUB_STEP_SUMMARY
echo "- Fabric版本: ${{ steps.version_info.outputs.version }}-fabric" >> $GITHUB_STEP_SUMMARY
echo "- Forge版本: ${{ steps.version_info.outputs.version }}-forge" >> $GITHUB_STEP_SUMMARY

30
.github/workflows/styleCheck.yml vendored Normal file
View File

@ -0,0 +1,30 @@
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,6 +21,7 @@ build
# other
eclipse
run
generated
runs
run-data

View File

@ -1,10 +1,14 @@
# 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?
### How to implementation? ( Only for Version 0.5.0+ )
**In repositories:**
#### **In repositories:**
```groovy
maven {
@ -13,10 +17,41 @@ It provides utility methods and core functionality that other mods can build upo
}
```
**In dependencies:**
#### **In dependencies:**
##### General
**gradle.properties**
```properties
lib39_version=0.5.1
````
##### For Loom
**build.gradle**
```groovy
dependencies {
implementation("top.r3944realms.lib39:lib39:1.20.1-0.4.1")
modImplementation("top.r3944realms.lib39:lib39-fabric-1.20.1:${lib39_version}")
}
```
```
##### For ForgeGradle
**build.gradle**
```groovy
dependencies {
implementation fg.deof("top.r3944realms.lib39:lib39-forge-1.20.1:${lib39_version}")
}
```
##### 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}")
}
```

View File

@ -1,24 +0,0 @@
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,535 +1,4 @@
//file:noinspection GroovyAssignabilityCheck
plugins {
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>
*/
id 'fabric-loom' version '1.9-SNAPSHOT' apply(false)
id 'net.neoforged.moddev.legacyforge' version '2.0.103' apply(false)
}

3
buildSrc/build.gradle Normal file
View File

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

View File

@ -0,0 +1,207 @@
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

@ -0,0 +1,50 @@
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)
}

43
common/build.gradle Normal file
View File

@ -0,0 +1,43 @@
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

@ -0,0 +1,125 @@
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

@ -5,20 +5,26 @@ import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.command.SimpleCommandHelpManager;
/**
* The type Lib 39 command help manager.
* <pre>
* 命令帮助注册管理类
* 这是一个模组内置的示例
* </pre>
*/
public class Lib39CommandHelpManager extends SimpleCommandHelpManager {
/**
* The constant INSTANCE.
* 单例模式
*/
public static volatile Lib39CommandHelpManager INSTANCE = new Lib39CommandHelpManager();
/**
* The Id.
* 作为唯一标识符
*/
ResourceLocation ID = Lib39.rl("command_helper");
/**
* <pre>
* 一定要在构造器方法里调用 {@link #initialize 初始化方法}
* Instantiates a new Lib 39 command help manager.
* </pre>
*/
public Lib39CommandHelpManager() {
initialize();

View File

@ -6,13 +6,13 @@ import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.event.RegisterCommandsEvent;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.command.ICommandHelpManager;
@ -21,20 +21,27 @@ import top.r3944realms.lib39.core.command.SimpleHelpCommand;
import java.util.Map;
/**
* The type Lib 39 help command.
* <pre>
* 指令注册以及帮助编写类
* 这是一个模组内置的示例
* </pre>
*/
public class Lib39HelpCommand extends SimpleHelpCommand {
/**
* <pre>
* 需要{@link CommandDispatcher<CommandSourceStack> 指令注册调度器} {@link CommandBuildContext 指令上下文}
* </pre>
* Instantiates a new Lib 39 help command.
*
* @param event the event
* @param dispatcher the dispatcher
* @param context the context
*/
public Lib39HelpCommand(@NotNull RegisterCommandsEvent event) {
super(event);
public Lib39HelpCommand(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext context) {
super(dispatcher, context);
if(Lib39.shouldRegisterExamples()) {
// 在這裡註冊測試命令
registerTestCommands(event.getDispatcher());
registerTestCommands(dispatcher);
}
}

View File

@ -8,7 +8,7 @@ import net.minecraft.data.recipes.ShapelessRecipeBuilder;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.item.Items;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.content.register.Lib39Items;
import top.r3944realms.lib39.core.register.Lib39Items;
import java.util.function.Consumer;
@ -26,7 +26,7 @@ public class Lib39RecipeProvider extends RecipeProvider {
}
@Override
protected void buildRecipes(@NotNull Consumer<FinishedRecipe> consumer) {
public void buildRecipes(@NotNull Consumer<FinishedRecipe> consumer) {
ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, Lib39Items.DOLL.get())
.requires(ItemTags.WOOL)
.requires(Items.ARMOR_STAND)

View File

@ -3,9 +3,9 @@ package top.r3944realms.lib39.base.datagen.value;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Unmodifiable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.content.register.Lib39Blocks;
import top.r3944realms.lib39.content.register.Lib39Items;
import top.r3944realms.lib39.content.register.Lib39SoundEvents;
import top.r3944realms.lib39.core.register.Lib39Blocks;
import top.r3944realms.lib39.core.register.Lib39Items;
import top.r3944realms.lib39.core.register.Lib39SoundEvents;
import top.r3944realms.lib39.datagen.value.ILangKeyValueCollection;
import top.r3944realms.lib39.datagen.value.LangKeyValue;
import top.r3944realms.lib39.datagen.value.ModPartEnum;
@ -15,6 +15,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
/**
* The enum Lib 39 lang key.
@ -248,6 +249,13 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
addLang(LangKeyValue.copyOf(
Lib39Blocks.DOLL, ModPartEnum.BLOCK, dollName
));
addLang(LangKeyValue.copyOf(
Lib39Blocks.WALL_DOLL, ModPartEnum.BLOCK, dollName
));
addLang(
LangKeyValue.ofKey("config.jade.plugin_lib39.lib39", ModPartEnum.DEFAULT,
"Lib 39", "叁玖库", "叁玖庫", "叁玖庫"
));
addLang(LangKeyValue.ofKey(
"tooltip.lib39.content.doll.hover.1", ModPartEnum.DESCRIPTION,
"§eSkinOwner §7:§a %s ", "§e皮肤所有者§7:§a%s", "§e皮膚所有者§7:§a%s", "§e膚主§7:§a%s"
@ -315,7 +323,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_ROOT.
*/
// 根命令
public static final LangKeyValue LIB39_ROOT = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.root", ModPartEnum.MESSAGE,
@ -330,7 +337,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_TEST.
*/
// 測試命令
public static final LangKeyValue LIB39_TEST = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.test", ModPartEnum.MESSAGE,
@ -401,7 +407,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_GREET_BASIC.
*/
// 問候命令
public static final LangKeyValue LIB39_GREET_BASIC = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.greet.basic", ModPartEnum.MESSAGE,
@ -458,7 +463,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_CALCULATE.
*/
// 計算命令
public static final LangKeyValue LIB39_CALCULATE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.calculate", ModPartEnum.MESSAGE,
@ -487,7 +491,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_TELEPORT.
*/
// 傳送命令
public static final LangKeyValue LIB39_TELEPORT = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.teleport", ModPartEnum.MESSAGE,
@ -516,7 +519,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_INFO.
*/
// 信息命令
public static final LangKeyValue LIB39_INFO = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.info", ModPartEnum.MESSAGE,
@ -573,7 +575,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_TEAM.
*/
// 隊伍系統
public static final LangKeyValue LIB39_TEAM = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.team", ModPartEnum.MESSAGE,
@ -672,7 +673,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_GAME.
*/
// 遊戲系統
public static final LangKeyValue LIB39_GAME = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game", ModPartEnum.MESSAGE,
@ -799,7 +799,6 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* The constant LIB39_SETTINGS.
*/
// 設置命令
public static final LangKeyValue LIB39_SETTINGS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.settings", ModPartEnum.MESSAGE,
@ -854,7 +853,7 @@ public enum Lib39LangKey implements ILangKeyValueCollection {
);
// ===== 添加缺失的導入 =====
private static final java.util.function.Consumer<LangKeyValue> addConsumer = items::add;
private static final Consumer<LangKeyValue> addConsumer = items::add;
private static LangKeyValue addAndRet(LangKeyValue item) {
items.add(item);

View File

@ -353,8 +353,8 @@ public class WheelWidget extends AbstractWidget {
*
* @return the section circle diameter
*/
// 滚轮选择器中每个扇形的圆形直径
public float getSectionCircleDiameter() {
// 滚轮选择器中每个扇形的圆形直径
return this.ringOuterRadius + this.ringInnerRadius;
}
@ -635,8 +635,10 @@ public class WheelWidget extends AbstractWidget {
float pixelOuterRadius = outerRadius * guiScale;
float pixelAntiAliasing = 2.0f * guiScale; // 抗锯齿范围
System.out.println("Shader Params - Center: (" + pixelCenterX + ", " + pixelCenterY +
"), InnerRadius: " + pixelInnerRadius + ", OuterRadius: " + pixelOuterRadius);
// if (Services.PLATFORM.isDevelopmentEnvironment()) {
// System.out.println("Shader Params - Center: (" + pixelCenterX + ", " + pixelCenterY +
// "), InnerRadius: " + pixelInnerRadius + ", OuterRadius: " + pixelOuterRadius);
// }
ShaderInstance shader = Lib39Shaders.getRingShader();
shader.safeGetUniform("Center").set(pixelCenterX, pixelCenterY);

View File

@ -11,11 +11,15 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.SkullBlock;
import net.minecraft.world.level.block.WallSkullBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.RotationSegment;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.client.model.DollModel;
import top.r3944realms.lib39.client.renderer.item.DollItemRenderer;
import top.r3944realms.lib39.content.block.DollBlock;
import top.r3944realms.lib39.content.block.AbstractDollBlock;
import top.r3944realms.lib39.content.block.WallDollBlock;
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
import top.r3944realms.lib39.util.lang.Pair;
@ -37,14 +41,15 @@ public class DollBlockEntityRenderer implements BlockEntityRenderer<DollBlockEnt
@Override
public void render(@NotNull DollBlockEntity dollBlockEntity, float partialTick, @NotNull PoseStack poseStack, @NotNull MultiBufferSource buffer, int packedLight, int packedOverlay) {
BlockState blockState = dollBlockEntity.getBlockState();
if (blockState.getBlock() instanceof DollBlock) {
Direction facing = blockState.getValue(DollBlock.FACING);
if (blockState.getBlock() instanceof AbstractDollBlock dollBlock) {
boolean isWall = dollBlock instanceof WallDollBlock;
Direction direction = isWall ? blockState.getValue(WallSkullBlock.FACING) : null;
float rotation = isWall ? direction.toYRot() : RotationSegment.convertToDegrees(blockState.getValue(SkullBlock.ROTATION));
GameProfile profile = dollBlockEntity.getOwnerProfile();
Pair<ResourceLocation, Boolean> resourceLocationBooleanPair = DollItemRenderer.loadSkin(profile);
poseStack.pushPose();
poseStack.translate(0.5, 1.5, 0.5);
poseStack.scale(1.0F, -1.0F, -1.0F);
float rotation = facing.toYRot();
poseStack.mulPose(Axis.YP.rotationDegrees(rotation));
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityTranslucent(resourceLocationBooleanPair.first));
this.dollModel.slim = resourceLocationBooleanPair.second;

View File

@ -14,6 +14,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.client.model.DollModel;
import top.r3944realms.lib39.content.item.DollItem;
import top.r3944realms.lib39.util.GameProfileHelper;
@ -24,15 +25,29 @@ import top.r3944realms.lib39.util.lang.Pair;
*/
public class DollItemRenderer extends BlockEntityWithoutLevelRenderer {
private static DollItemRenderer instance;
private final DollModel dollModel;
private DollModel dollModel;
private boolean initialized = false;
private DollItemRenderer() {
super(
Minecraft.getInstance().getBlockEntityRenderDispatcher(),
Minecraft.getInstance().getEntityModels()
);
this.dollModel = new DollModel(
Minecraft.getInstance().getEntityModels().bakeLayer(DollModel.LAYER_LOCATION)
);
}
private void lazyInit() {
if (!initialized) {
try {
// 确保 Minecraft 实例已初始化
Minecraft mc = Minecraft.getInstance();
mc.getEntityModels();
this.dollModel = new DollModel(
mc.getEntityModels().bakeLayer(DollModel.LAYER_LOCATION)
);
initialized = true;
Lib39.LOGGER.info("Doll model initialized successfully");
} catch (Exception e) {
Lib39.LOGGER.error("Failed to initialize doll model", e);
}
}
}
/**
@ -52,6 +67,7 @@ public class DollItemRenderer extends BlockEntityWithoutLevelRenderer {
if (!(stack.getItem() instanceof DollItem)) {
return;
}
lazyInit();
GameProfile profile = GameProfileHelper.getProfileFromItemStack(stack);
Pair<ResourceLocation, Boolean> resourceLocationBooleanPair = loadSkin(profile);
ResourceLocation playerSkin = resourceLocationBooleanPair.first;

View File

@ -0,0 +1,55 @@
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

@ -5,7 +5,6 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
@ -17,7 +16,6 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.piston.PistonBaseBlock;
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;
@ -30,39 +28,36 @@ import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
import top.r3944realms.lib39.content.block.property.DollPose;
import top.r3944realms.lib39.content.register.Lib39BlockEntities;
import top.r3944realms.lib39.content.register.Lib39Items;
import top.r3944realms.lib39.content.register.Lib39SoundEvents;
import top.r3944realms.lib39.core.register.Lib39BlockEntities;
import top.r3944realms.lib39.core.register.Lib39Items;
import top.r3944realms.lib39.core.register.Lib39SoundEvents;
import top.r3944realms.lib39.util.GameProfileHelper;
import java.util.List;
/**
* The type Doll block.
* The type Abstract doll block.
*/
@SuppressWarnings("deprecation")
public class DollBlock extends HorizontalDirectionalBlock implements SimpleWaterloggedBlock, EntityBlock {
private static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
public abstract class AbstractDollBlock extends BaseEntityBlock implements SimpleWaterloggedBlock {
/**
* The constant POSE.
* The constant WATERLOGGED.
*/
public static final EnumProperty<DollPose> POSE = EnumProperty.create("pose", DollPose.class);
private static final VoxelShape DOLL_SHAPE = Block.box(2.0d, 0.0d, 2.0d, 14.0d, 12.0d, 14.0d);
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
private static final Properties properties = Properties.of()
.instrument(NoteBlockInstrument.BASEDRUM)
.sound(SoundType.WOOL)
.pushReaction(PushReaction.DESTROY)
.strength(0f, 10f)
.noOcclusion();
private static final double PARTICLE_OFFSET_RANGE = 0.25;
private static final double PARTICLE_HEIGHT_OFFSET = 1.0;
private static final double PARTICLE_HEIGHT_VARIANCE = 0.2;
@ -73,26 +68,23 @@ public class DollBlock extends HorizontalDirectionalBlock implements SimpleWater
private static final float PITCH_VARIANCE = 0.5f;
private static final float BASE_PITCH = 0.75f;
private static final VoxelShape DOLL_SHAPE = Block.box(2.0d, 0.0d, 2.0d, 14.0d, 12.0d, 14.0d);
/**
* Instantiates a new Doll block.
* The constant POSE.
*/
public DollBlock() {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.SOUTH)
.setValue(POSE, DollPose.DEFAULT)
.setValue(WATERLOGGED, false));
}
public static final EnumProperty<DollPose> POSE = EnumProperty.create("pose", DollPose.class);
/**
* Instantiates a new Abstract doll block.
*/
public AbstractDollBlock() {
super(properties);
}
@Override
public boolean canBeReplaced(@NotNull BlockState state, @NotNull BlockPlaceContext useContext) {
return false;
}
@Override
public @Nullable PushReaction getPistonPushReaction(BlockState state) {
return PushReaction.DESTROY;
}
@Override
public @NotNull BlockState updateShape(@NotNull BlockState currentState, @NotNull Direction direction, @NotNull BlockState neighborState,
@NotNull LevelAccessor level, @NotNull BlockPos currentPos, @NotNull BlockPos neighborPos) {
@ -118,7 +110,6 @@ public class DollBlock extends HorizontalDirectionalBlock implements SimpleWater
}
return InteractionResult.SUCCESS;
}
/**
* 在玩偶位置生成音符粒子效果
*/
@ -158,26 +149,13 @@ public class DollBlock extends HorizontalDirectionalBlock implements SimpleWater
SoundSource.BLOCKS, BASE_VOLUME, pitch);
}
@Override
public @Nullable BlockState getStateForPlacement(@NotNull BlockPlaceContext context) {
FluidState fluidState = context.getLevel().getFluidState(context.getClickedPos());
boolean isWaterlogged = fluidState.getType() == Fluids.WATER;
return this.defaultBlockState()
.setValue(FACING, context.getHorizontalDirection().getOpposite())
.setValue(WATERLOGGED, isWaterlogged)
.setValue(POSE, DollPose.DEFAULT);
}
@Override
public @NotNull VoxelShape getShape(@NotNull BlockState blockState, @NotNull BlockGetter level, @NotNull BlockPos blockPos, @NotNull CollisionContext context) {
return DOLL_SHAPE;
}
@Override
protected void createBlockStateDefinition(StateDefinition.@NotNull Builder<Block, BlockState> builder) {
super.createBlockStateDefinition(builder);
builder.add(FACING, WATERLOGGED, POSE);
public @NotNull VoxelShape getOcclusionShape(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos) {
return Shapes.empty();
}
@Nullable
@ -193,9 +171,8 @@ public class DollBlock extends HorizontalDirectionalBlock implements SimpleWater
}
@Override
public ItemStack getCloneItemStack(BlockState state, HitResult target, BlockGetter level, BlockPos pos, Player player) {
ItemStack stack = super.getCloneItemStack(state, target, level, pos, player);
public @NotNull ItemStack getCloneItemStack(@NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull BlockState state) {
ItemStack stack = super.getCloneItemStack(level, pos, state);
BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof DollBlockEntity doll) {
GameProfile profile = doll.getOwnerProfile();
@ -222,6 +199,12 @@ public class DollBlock extends HorizontalDirectionalBlock implements SimpleWater
return super.getDrops(state, params);
}
@Override
protected void createBlockStateDefinition(StateDefinition.@NotNull Builder<Block, BlockState> builder) {
super.createBlockStateDefinition(builder);
builder.add(WATERLOGGED, POSE);
}
/**
* 生成自定义掉落物
*/

View File

@ -0,0 +1,65 @@
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

@ -0,0 +1,56 @@
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

@ -10,7 +10,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.content.register.Lib39BlockEntities;
import top.r3944realms.lib39.core.register.Lib39BlockEntities;
import top.r3944realms.lib39.util.GameProfileHelper;
import top.r3944realms.lib39.util.nbt.NBTReader;
import top.r3944realms.lib39.util.nbt.NBTWriter;

View File

@ -1,51 +1,34 @@
package top.r3944realms.lib39.content.item;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Equipable;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.StandingAndWallBlockItem;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraftforge.client.extensions.common.IClientItemExtensions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.client.renderer.item.DollItemRenderer;
import top.r3944realms.lib39.content.register.Lib39Blocks;
import top.r3944realms.lib39.core.register.Lib39Blocks;
import top.r3944realms.lib39.util.GameProfileHelper;
import java.util.List;
import java.util.function.Consumer;
/**
* The type Doll item.
*/
public class DollItem extends BlockItem {
public class DollItem extends StandingAndWallBlockItem implements Equipable {
/**
* Instantiates a new Doll item.
*
* @param properties the properties
*/
public DollItem(Properties properties) {
super(Lib39Blocks.DOLL.get(), properties);
super(Lib39Blocks.DOLL.get(), Lib39Blocks.WALL_DOLL.get(), properties, Direction.DOWN);
}
@Override
public void initializeClient(@NotNull Consumer<IClientItemExtensions> consumer) {
consumer.accept(new IClientItemExtensions() {
@Override
public BlockEntityWithoutLevelRenderer getCustomRenderer() {
return DollItemRenderer.getInstance();
}
});
}
@Override
public boolean canEquip(ItemStack stack, EquipmentSlot armorType, Entity entity) {
return armorType == EquipmentSlot.HEAD;
}
@Override
public void appendHoverText(@NotNull ItemStack stack, @Nullable Level level, @NotNull List<Component> tooltip, @NotNull TooltipFlag flag) {
@ -55,4 +38,9 @@ public class DollItem extends BlockItem {
}
tooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.2"));
}
@Override
public @NotNull EquipmentSlot getEquipmentSlot() {
return EquipmentSlot.HEAD;
}
}

View File

@ -483,6 +483,7 @@ public interface ICommandHelpManager {
/**
* 获取命令树的字符串表示
*
* @param commandSourceStack the command source stack
* @return 命令树列表 command tree
*/
default List<MutableComponent> getCommandTree(CommandSourceStack commandSourceStack) {
@ -537,6 +538,7 @@ public interface ICommandHelpManager {
/**
* Build command tree help mutable component.
*
* @param commandSourceStack the command source stack
* @return the mutable component
*/
default MutableComponent buildCommandTreeHelp(CommandSourceStack commandSourceStack) {

View File

@ -9,10 +9,9 @@ import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraftforge.common.MinecraftForge;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.api.event.RegisterCommandHelpEvent;
import top.r3944realms.lib39.base.datagen.value.Lib39LangKey;
import top.r3944realms.lib39.platform.Services;
import javax.annotation.Nullable;
@ -63,8 +62,7 @@ public interface IHelpCommand {
.then(Commands.literal("toggle")
.then(Commands.argument("hash", IntegerArgumentType.integer()).executes(this::handleHelpToggle))
));
RegisterCommandHelpEvent registerHelpCommandEvent = new RegisterCommandHelpEvent(tree, getCommandHelpManager(), context);
MinecraftForge.EVENT_BUS.post(registerHelpCommandEvent);
Services.PLATFORM.getHelpCommandHook().onRegister(tree, getCommandHelpManager(), context);
dispatcher.register(head);
return head;
}

View File

@ -4,8 +4,6 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraftforge.event.RegisterCommandsEvent;
import org.jetbrains.annotations.NotNull;
/**
* The type Simple help command.
@ -17,15 +15,9 @@ public abstract class SimpleHelpCommand implements IHelpCommand {
protected final LiteralArgumentBuilder<CommandSourceStack> root;
/**
* Instantiates a new Simple help command.
*
* @param event the event
*/
public SimpleHelpCommand(@NotNull RegisterCommandsEvent event) {
root = buildCommand(event.getDispatcher(), event.getBuildContext());
}
/**
* <pre>
* 需要{@link CommandDispatcher<CommandSourceStack> 指令注册调度器} {@link CommandBuildContext 指令上下文}
* </pre>
* Instantiates a new Simple help command.
*
* @param dispatcher the dispatcher

View File

@ -35,9 +35,16 @@ public class CommandNode {
// 展开/闭合状态
private boolean expanded = true;
/**
* Test permission boolean.
*
* @param source the source
* @return the boolean
*/
public boolean testPermission(CommandSourceStack source) {
return testPermission.test(source);
}
/**
* Instantiates a new Command node.
*

View File

@ -0,0 +1,192 @@
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,7 +1,6 @@
package top.r3944realms.lib39.core.compat;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.eventbus.api.IEventBus;
import java.util.concurrent.Callable;
@ -9,8 +8,20 @@ import java.util.concurrent.Callable;
* The interface Compat.
*/
public interface ICompat {
/**
* Sets initialize.
*
* @param initialize the initialize
*/
void setInitialize(boolean initialize);
/**
* Is initialized boolean.
*
* @return the boolean
*/
boolean isInitialized();
/**
* Id resource location.
*
@ -75,58 +86,4 @@ public interface ICompat {
if (isModLoaded()) runnable.run(); else return false;
return true;
}
/**
* Add common game listener.
*
* @param gameBus the game bus
*/
default void addCommonGameListener(IEventBus gameBus) {
// 实现通用游戏事件监听器添加逻辑
}
/**
* Add common mod listener.
*
* @param modBus the mod bus
*/
default void addCommonModListener(IEventBus modBus) {
// 实现通用模组事件监听器添加逻辑
}
/**
* Add client game listener.
*
* @param gameBus the game bus
*/
default void addClientGameListener(IEventBus gameBus) {
// 实现客户端游戏事件监听器添加逻辑
}
/**
* Add client mod listener.
*
* @param modBus the mod bus
*/
default void addClientModListener(IEventBus modBus) {
// 实现客户端模组事件监听器添加逻辑
}
/**
* Add server game listener.
*
* @param gameBus the game bus
*/
default void addServerGameListener(IEventBus gameBus) {
// 实现服务端游戏事件监听器添加逻辑
}
/**
* Add server mod listener.
*
* @param modBus the mod bus
*/
default void addServerModListener(IEventBus modBus) {
// 实现服务端模组事件监听器添加逻辑
}
}

View File

@ -11,7 +11,7 @@ import java.util.stream.Stream;
*/
public class ClassEncryptor {
static {
System.loadLibrary("ClassEncrypt");
// System.loadLibrary("ClassEncrypt");
}
/**

View File

@ -13,7 +13,7 @@ import java.util.Map;
*/
public class EncryptedClassLoader extends ClassLoader {
static {
System.loadLibrary("ClassEncrypt");
// System.loadLibrary("ClassEncrypt");
}
private native byte[] decryptClass(byte[] encryptedData, String key);

View File

@ -0,0 +1,17 @@
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

@ -0,0 +1,21 @@
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

@ -0,0 +1,15 @@
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

@ -0,0 +1,31 @@
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

@ -0,0 +1,24 @@
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

@ -0,0 +1,18 @@
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

@ -2,15 +2,12 @@ package top.r3944realms.lib39.core.sync;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.util.INBTSerializable;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.core.network.NetworkHandler;
import top.r3944realms.lib39.core.network.toClient.SyncNBTCapDataEntityS2CPack;
/**
* The type Nbt sync data.
* The type Nbt entity sync data.
*/
public abstract class NBTEntitySyncData implements IEntity, ISyncData<NBTEntitySyncData>, INBTSerializable<CompoundTag> {
public abstract class NBTEntitySyncData implements IEntity, ISyncData<NBTEntitySyncData>, INBTSerializable<CompoundTag>, IUpdate {
/**
* The Dirty.
*/
@ -52,8 +49,13 @@ public abstract class NBTEntitySyncData implements IEntity, ISyncData<NBTEntityS
@Override
public void checkIfDirtyThenUpdate() {
if (isDirty()) {
NetworkHandler.sendToAllPlayer(new SyncNBTCapDataEntityS2CPack(entityId(), id(), serializeNBT()));
update();
}
dirty = false;
}
@Override
public NBTEntitySyncData getSyncData() {
return this;
}
}

View File

@ -1,6 +1,5 @@
package top.r3944realms.lib39.core.sync;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
@ -13,10 +12,17 @@ import java.util.function.Function;
/**
* The type Sync data 2 manager.
*
* @param <V> the type parameter
*/
@SuppressWarnings({"unused", "DuplicatedCode"})
public class SyncData2Manager {
private final Map<ResourceLocation, TypedSyncEntry<?, ?>> typedEntries = Maps.newConcurrentMap();
public abstract class SyncData2Manager<V extends SyncData2Manager.TypedSyncEntry<?, ?>> {
/**
* Gets typed entries.
*
* @return the typed entries
*/
protected abstract Map<ResourceLocation, V> getTypedEntries();
/**
* 数据提供者接口 - 用于通过键获取数据
@ -35,7 +41,13 @@ public class SyncData2Manager {
Optional<T> getData(K key);
}
private static class TypedSyncEntry<K, T extends ISyncData<?>> {
/**
* The type Typed sync entry.
*
* @param <K> the type parameter
* @param <T> the type parameter
*/
protected static class TypedSyncEntry<K, T extends ISyncData<?>> {
/**
* The Manager.
*/
@ -56,7 +68,7 @@ public class SyncData2Manager {
* @param manager the manager
* @param dataProvider the data provider
*/
TypedSyncEntry(ISyncManager<K, T> manager, @Nullable DataProvider<Entity, T> dataProvider) {
public TypedSyncEntry(ISyncManager<K, T> manager, @Nullable DataProvider<Entity, T> dataProvider) {
this.manager = manager;
this.dataProvider = dataProvider;
this.allowedClasses = Sets.newConcurrentHashSet();
@ -72,6 +84,7 @@ public class SyncData2Manager {
* @param manager the manager
* @param dataProvider the data provider
*/
@SuppressWarnings("unchecked")
public <K, T extends ISyncData<?>> void registerManagerWithProvider(
ResourceLocation key,
ISyncManager<K, T> manager,
@ -81,7 +94,7 @@ public class SyncData2Manager {
Objects.requireNonNull(manager, "Sync manager cannot be null");
Objects.requireNonNull(dataProvider, "Data provider cannot be null");
typedEntries.put(key, new TypedSyncEntry<>(manager, dataProvider));
getTypedEntries().put(key, (V) new TypedSyncEntry<>(manager, dataProvider));
}
/**
@ -93,6 +106,7 @@ public class SyncData2Manager {
* @param manager the manager
* @param getter the data getter function
*/
@SuppressWarnings("unchecked")
public <K, T extends ISyncData<?>> void registerManager(
ResourceLocation key,
ISyncManager<K, T> manager,
@ -102,7 +116,7 @@ public class SyncData2Manager {
Objects.requireNonNull(manager, "Sync manager cannot be null");
Objects.requireNonNull(getter, "Data getter function cannot be null");
typedEntries.put(key, new TypedSyncEntry<>(manager, getter::apply));
getTypedEntries().put(key, (V) new TypedSyncEntry<>(manager, getter::apply));
}
/**
@ -117,7 +131,7 @@ public class SyncData2Manager {
Objects.requireNonNull(manager, "Sync manager cannot be null");
// 创建一个没有数据提供者的 TypedSyncEntry
typedEntries.put(key, new TypedSyncEntry<>(
getTypedEntries().put(key, (V) new TypedSyncEntry<>(
(ISyncManager<?, ISyncData<?>>) manager,
null
));
@ -133,7 +147,7 @@ public class SyncData2Manager {
*/
@SuppressWarnings("unchecked")
public <K, T extends ISyncData<?>> Optional<ISyncManager<K, T>> getManager(ResourceLocation key) {
TypedSyncEntry<?,?> entry = typedEntries.get(key);
TypedSyncEntry<?,?> entry = getTypedEntries().get(key);
return entry != null ? Optional.of((ISyncManager<K,T>) entry.manager) : Optional.empty();
}
@ -146,7 +160,7 @@ public class SyncData2Manager {
*/
@SuppressWarnings("unchecked")
public <T extends ISyncData<?>> Optional<DataProvider<Entity, T>> getDataProvider(ResourceLocation key) {
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
if (entry != null && entry.dataProvider != null) {
return Optional.of((DataProvider<Entity, T>) entry.dataProvider);
}
@ -184,7 +198,7 @@ public class SyncData2Manager {
return;
}
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
if (entry != null) {
entry.allowedClasses.addAll(Arrays.asList(classes));
}
@ -200,7 +214,7 @@ public class SyncData2Manager {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(classes, "Classes array cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
if (entry != null && classes.length > 0) {
Arrays.asList(classes).forEach(entry.allowedClasses::remove);
}
@ -217,7 +231,7 @@ public class SyncData2Manager {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(dataProvider, "Data provider cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
if (entry != null) {
// 更新现有条目的数据提供者
updateDataProviderInEntry(key, entry, dataProvider);
@ -233,7 +247,7 @@ public class SyncData2Manager {
* @param key the key
* @param getter the data getter function
*/
public <T extends ISyncData<?>> void bindDataGetter(ResourceLocation key, Function<Entity, Optional<T>> getter) {
public <T extends ISyncData<?>> void bindDataGetter(ResourceLocation key, @NotNull Function<Entity, Optional<T>> getter) {
bindDataProvider(key, getter::apply);
}
@ -245,7 +259,7 @@ public class SyncData2Manager {
public void unbindDataProvider(ResourceLocation key) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
if (entry != null) {
// 将数据提供者设置为null但保留管理器和其他配置
updateDataProviderInEntry(key, entry, null);
@ -260,7 +274,7 @@ public class SyncData2Manager {
public void clearAllowedEntityClasses(ResourceLocation key) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
if (entry != null) {
entry.allowedClasses.clear();
}
@ -277,19 +291,17 @@ public class SyncData2Manager {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(entityClass, "Entity class cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry == null) {
return false;
TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
boolean isAllowed = false;
if (entry != null) {
for (Class<?> allowedClass : entry.allowedClasses) {
if (allowedClass.isAssignableFrom(entityClass)) {
isAllowed = true;
break;
}
}
}
// 如果没有设置允许的类则默认允许所有类
if (entry.allowedClasses.isEmpty()) {
return true;
}
// 检查实体类是否在允许的类中
return entry.allowedClasses.stream()
.anyMatch(allowedClass -> allowedClass.isAssignableFrom(entityClass));
return entry != null && isAllowed ;
}
/**
@ -300,7 +312,7 @@ public class SyncData2Manager {
*/
@SuppressWarnings("unchecked")
public void trackEntityForManager(Entity entity, ResourceLocation managerId) {
TypedSyncEntry<UUID, ?> entry = (TypedSyncEntry<UUID, ?>) typedEntries.get(managerId);
TypedSyncEntry<UUID, ?> entry = (TypedSyncEntry<UUID, ?>) getTypedEntries().get(managerId);
if (entry != null) {
trackEntityWithTypedEntry(entity, entry);
}
@ -321,7 +333,7 @@ public class SyncData2Manager {
*/
@SuppressWarnings("unchecked")
public void untrackEntityForManager(Entity entity, ResourceLocation managerId) {
TypedSyncEntry<UUID, ?> entry = (TypedSyncEntry<UUID, ?>) typedEntries.get(managerId);
TypedSyncEntry<UUID, ?> entry = (TypedSyncEntry<UUID, ?>) getTypedEntries().get(managerId);
if (entry != null) {
untrackEntityWithTypedEntry(entity, entry);
}
@ -376,7 +388,7 @@ public class SyncData2Manager {
* @param managerId the manager id
*/
public void clearAllTrackedData(ResourceLocation managerId) {
TypedSyncEntry<?, ?> entry = typedEntries.get(managerId);
TypedSyncEntry<?, ?> entry = getTypedEntries().get(managerId);
if (entry != null) {
clearTrackedDataForEntry(entry);
}
@ -398,9 +410,18 @@ public class SyncData2Manager {
}
}
// 辅助方法更新条目的数据提供者
/**
* Update data provider in entry.
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param id the id
* @param entry the entry
* @param newDataProvider the new data provider
*/
// 辅助方法更新条目的数据提供者
@SuppressWarnings("unchecked")
private <K, T extends ISyncData<?>> void updateDataProviderInEntry(
protected <K, T extends ISyncData<?>> void updateDataProviderInEntry(
ResourceLocation id,
TypedSyncEntry<?,?> entry,
DataProvider<Entity, T> newDataProvider
@ -412,7 +433,7 @@ public class SyncData2Manager {
);
newEntry.allowedClasses.addAll(entry.allowedClasses);
typedEntries.put(id, newEntry);
getTypedEntries().put(id, (V) newEntry);
}
/**
@ -421,7 +442,7 @@ public class SyncData2Manager {
* @return the registered keys
*/
public Set<ResourceLocation> getRegisteredKeys() {
return Collections.unmodifiableSet(typedEntries.keySet());
return Collections.unmodifiableSet(getTypedEntries().keySet());
}
/**
@ -431,7 +452,7 @@ public class SyncData2Manager {
*/
public void forEach(BiConsumer<ResourceLocation, ISyncManager<?,?>> consumer) {
Objects.requireNonNull(consumer, "Consumer cannot be null");
typedEntries.forEach((key, entry) -> consumer.accept(key, entry.manager));
getTypedEntries().forEach((key, entry) -> consumer.accept(key, entry.manager));
}
/**
@ -440,14 +461,14 @@ public class SyncData2Manager {
* @return the manager count
*/
public int getManagerCount() {
return typedEntries.size();
return getTypedEntries().size();
}
/**
* Clear all.
*/
public void clearAll() {
typedEntries.clear();
getTypedEntries().clear();
}
/**
@ -457,6 +478,6 @@ public class SyncData2Manager {
*/
public void removeManager(ResourceLocation key) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
typedEntries.remove(key);
getTypedEntries().remove(key);
}
}

View File

@ -0,0 +1,197 @@
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,9 +1,9 @@
package top.r3944realms.lib39.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.LanguageProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.datagen.value.ILangKeyValue;
import top.r3944realms.lib39.datagen.value.ILangKeyValueCollection;
import top.r3944realms.lib39.datagen.value.McLocale;
@ -98,7 +98,7 @@ public class SimpleLanguageProvider extends LanguageProvider {
.filter(translationMap::containsKey)
.count();
LOGGER.info("Added {}/{} translations for {}",
Lib39.LOGGER.info("Added {}/{} translations for {}",
addedCount, orderedKeys.size(), language.mcCode());
}
}

View File

@ -0,0 +1,33 @@
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

@ -13,6 +13,7 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector2f;
import top.r3944realms.lib39.client.gui.component.WheelWidget;
import top.r3944realms.lib39.mixin.minecraft.ScreenAccessor;
import top.r3944realms.lib39.util.lang.FourConsumer;
import top.r3944realms.lib39.util.lang.Pair;
@ -114,7 +115,7 @@ public class ForgeScreen extends Screen {
@Override
public void render(@NotNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
for (Renderable renderable : this.renderables) {
for (Renderable renderable : ((ScreenAccessor) this).getrRenderables()) {
renderable.render(guiGraphics, mouseX, mouseY, partialTick);
}
}

View File

@ -1,6 +1,8 @@
package top.r3944realms.lib39.example.content.capability;
package top.r3944realms.lib39.example.content.data;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
/**
@ -8,6 +10,27 @@ import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
*/
@SuppressWarnings("unused")
public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
/**
* The constant DEFAULT_TEST_STRING.
*/
public final static String DEFAULT_TEST_STRING = "default_value";
/**
* The constant DEFAULT_TEST_INT.
*/
public final static int DEFAULT_TEST_INT = 42;
/**
* The constant DEFAULT_TEST_BOOLEAN.
*/
public final static boolean DEFAULT_TEST_BOOLEAN = true;
/**
* The constant DEFAULT_TEST_DOUBLE.
*/
public final static double DEFAULT_TEST_DOUBLE = 3.14159;
/**
* The constant DEFAULT_TEST_DATA.
*/
public final static TestData DEFAULT_TEST_DATA = new TestData("default", 100, false);
/**
* Instantiates a new Nbt sync data.
*
@ -22,105 +45,157 @@ public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
*
* @return the test string
*/
abstract String getTestString();
public abstract String getTestString();
/**
* Sets test string.
*
* @param value the value
*/
abstract void setTestString(String value);
public abstract void setTestString(String value);
/**
* Gets test int.
*
* @return the test int
*/
abstract int getTestInt();
public abstract int getTestInt();
/**
* Sets test int.
*
* @param value the value
*/
abstract void setTestInt(int value);
public abstract void setTestInt(int value);
/**
* Is test boolean boolean.
*
* @return the boolean
*/
abstract boolean isTestBoolean();
public abstract boolean isTestBoolean();
/**
* Sets test boolean.
*
* @param value the value
*/
abstract void setTestBoolean(boolean value);
public abstract void setTestBoolean(boolean value);
/**
* Gets test double.
*
* @return the test double
*/
abstract double getTestDouble();
public abstract double getTestDouble();
/**
* Sets test double.
*
* @param value the value
*/
abstract void setTestDouble(double value);
public abstract void setTestDouble(double value);
/**
* Gets counter.
*
* @return the counter
*/
// 计数器用于测试数据变化
abstract int getCounter();
public abstract int getCounter();
/**
* Increment counter.
*/
abstract void incrementCounter();
public abstract void incrementCounter();
/**
* Clear counter.
*/
public abstract void clearCounter();
/**
* Gets last sync time.
*
* @return the last sync time
*/
// 时间戳用于测试同步时机
abstract long getLastSyncTime();
public abstract long getLastSyncTime();
/**
* Update sync time.
*/
abstract void updateSyncTime();
public abstract void updateSyncTime();
/**
* Clear sync time.
*/
public abstract void clearSyncTime();
/**
* Gets custom data.
*
* @return the custom data
*/
// 自定义对象测试
abstract TestData getCustomData();
public abstract TestData getCustomData();
/**
* Sets custom data.
*
* @param data the data
*/
abstract void setCustomData(TestData data);
public abstract void setCustomData(TestData data);
/**
* Validate data boolean.
*
* @return the boolean
*/
// 验证数据完整性
abstract boolean validateData();
public abstract boolean validateData();
/**
* Reset to defaults.
*/
public void resetToDefaults() {
setTestString(DEFAULT_TEST_STRING);
setTestInt(DEFAULT_TEST_INT);
setTestBoolean(DEFAULT_TEST_BOOLEAN);
setTestDouble(DEFAULT_TEST_DOUBLE);
setCustomData(DEFAULT_TEST_DATA);
clearCounter();
clearSyncTime();
markDirty();
}
/**
* Generate random data.
*/
public void generateRandomData() {
setTestString("random_" + System.currentTimeMillis());
setTestInt((int) (Math.random() * 1000));
setTestBoolean(Math.random() > 0.5);
setTestDouble(Math.random() * 100.0);
setCustomData(new TestData(
"custom_" + getCounter(),
(int) (Math.random() * 500),
Math.random() > 0.5
));
updateSyncTime();
incrementCounter();
markDirty();
}
/**
* To bytes.
*
* @param buf the buf
*/
public abstract void toBytes(FriendlyByteBuf buf);
/**
* From bytes.
*
* @param buf the buf
*/
public abstract void fromBytes(@NotNull FriendlyByteBuf buf);
/**
* 测试数据对象
@ -153,7 +228,6 @@ public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
*
* @return the name
*/
// getters and setters
public String getName() { return name; }
/**

View File

@ -17,11 +17,7 @@ import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.content.capability.AbstractedTestSyncData;
import top.r3944realms.lib39.example.content.capability.ExCapabilityHandler;
import top.r3944realms.lib39.example.content.capability.TestSyncData;
import top.r3944realms.lib39.example.core.network.ClientDataPacket;
import top.r3944realms.lib39.example.core.network.ExNetworkHandler;
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@ -31,14 +27,14 @@ import java.util.concurrent.CompletableFuture;
* Shift + 右键客户端与服务器双端同时查询检查同步
* 普通右键单端查询目标生物数据
*/
public class FabricItem extends Item {
public abstract class AbstractFabricItem extends Item {
/**
* Instantiates a new Fabric item.
*
* @param properties the properties
*/
public FabricItem(Properties properties) {
public AbstractFabricItem(Properties properties) {
super(properties);
}
@ -82,7 +78,7 @@ public class FabricItem extends Item {
if (targetEntity instanceof LivingEntity livingTarget) {
// 在客户端获取本地数据
TestSyncData clientData = getLocalClientData(livingTarget);
AbstractedTestSyncData clientData = getLocalClientData(livingTarget);
if (clientData != null) {
// 发送客户端数据到服务器
@ -107,7 +103,7 @@ public class FabricItem extends Item {
*/
private void handlePlayerSelfData(Player player) {
// 获取玩家自身的客户端数据
TestSyncData clientData = getLocalClientData(player);
AbstractedTestSyncData clientData = getLocalClientData(player);
if (clientData != null) {
// 发送玩家自身数据到服务器
@ -126,7 +122,7 @@ public class FabricItem extends Item {
Entity targetEntity = getClientTargetedEntity(player);
if (targetEntity instanceof LivingEntity livingTarget) {
TestSyncData clientData = getLocalClientData(livingTarget);
AbstractedTestSyncData clientData = getLocalClientData(livingTarget);
if (clientData != null) {
displayClientSideResults(player, livingTarget, clientData);
@ -157,14 +153,22 @@ public class FabricItem extends Item {
}
/**
* 在客户端获取本地数据
* Gets data.
*
* @param target the target
* @return the data
*/
private TestSyncData getLocalClientData(LivingEntity target) {
protected abstract AbstractedTestSyncData getData(Entity target);
/**
* 在客户端获取本地数据
*
* @param target the target
* @return the local client data
*/
protected AbstractedTestSyncData getLocalClientData(LivingEntity target) {
try {
AbstractedTestSyncData abstractData = target.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null);
if (abstractData instanceof TestSyncData) {
return (TestSyncData) abstractData;
}
return getData(target);
} catch (Exception e) {
Lib39.LOGGER.error("[FabricItem] 获取客户端数据失败", e);
}
@ -173,51 +177,11 @@ public class FabricItem extends Item {
/**
* 发送客户端数据到服务器
*/
private void sendClientDataToServer(TestSyncData clientData, int targetEntityId) {
// 使用网络系统发送数据包
ExNetworkHandler.INSTANCE.sendToServer(new ClientDataPacket(clientData, targetEntityId));
}
/**
* 服务器端处理客户端发送的数据从数据包调用
*
* @param player the player
* @param clientData the client data
* @param targetEntityId the target entity id
*/
public static void handleClientDataFromPacket(@NotNull ServerPlayer player, TestSyncData clientData, int targetEntityId) {
Entity target = player.level().getEntity(targetEntityId);
if (target instanceof LivingEntity livingTarget) {
// 获取服务器端数据
TestSyncData serverData = getServerSideData(livingTarget);
if (serverData != null) {
// 显示双端对比结果
displayDualEndComparison(player, livingTarget, serverData, clientData);
} else {
player.sendSystemMessage(Component.literal("§c无法获取服务器端数据"));
}
} else {
player.sendSystemMessage(Component.literal("§c目标生物不存在或已消失"));
}
}
/**
* 获取服务器端数据
*/
private static @Nullable TestSyncData getServerSideData(LivingEntity target) {
try {
AbstractedTestSyncData abstractData = target.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null);
if (abstractData instanceof TestSyncData) {
return (TestSyncData) abstractData;
}
} catch (Exception e) {
Lib39.LOGGER.error("[FabricItem] 获取服务器端数据失败", e);
}
return null;
}
protected abstract void sendClientDataToServer(AbstractedTestSyncData clientData, int targetEntityId);
/**
* 启动服务器单端查询
@ -255,9 +219,9 @@ public class FabricItem extends Item {
// 获取目标生物的数据
AbstractedTestSyncData abstractData = getTestSyncData(target);
if (abstractData instanceof TestSyncData testData) {
if (abstractData != null) {
// 显示详细数据
displayServerDetailedData(player, target, testData);
displayServerDetailedData(player, target, abstractData);
} else {
player.sendSystemMessage(Component.literal(
String.format("§c生物 §e%s§c 没有测试数据或数据无效", target.getName().getString())
@ -268,7 +232,7 @@ public class FabricItem extends Item {
/**
* 显示客户端查询结果
*/
private void displayClientSideResults(Player player, LivingEntity target, TestSyncData clientData) {
private void displayClientSideResults(Player player, LivingEntity target, AbstractedTestSyncData clientData) {
player.sendSystemMessage(Component.literal("§6=== 客户端数据查询结果 ==="));
player.sendSystemMessage(Component.literal("§7目标生物: §e" + target.getName().getString()));
player.sendSystemMessage(Component.literal("§7数据来源: §9客户端本地"));
@ -291,7 +255,7 @@ public class FabricItem extends Item {
/**
* 显示服务器详细数据单端查询
*/
private void displayServerDetailedData(ServerPlayer player, LivingEntity target, TestSyncData testData) {
private void displayServerDetailedData(ServerPlayer player, LivingEntity target, AbstractedTestSyncData testData) {
player.sendSystemMessage(Component.literal("§6=== 数据查询结果 ==="));
player.sendSystemMessage(Component.literal(
String.format("§7目标生物: §e%s", target.getName().getString())
@ -324,7 +288,7 @@ public class FabricItem extends Item {
player.sendSystemMessage(Component.literal(""));
// 显示自定义数据
TestSyncData.TestData customData = testData.getCustomData();
AbstractedTestSyncData.TestData customData = testData.getCustomData();
player.sendSystemMessage(Component.literal("§a自定义数据:"));
player.sendSystemMessage(Component.literal(
String.format("§7名称: §f%s", customData.getName())
@ -349,8 +313,13 @@ public class FabricItem extends Item {
/**
* 显示双端比较结果
*
* @param player the player
* @param target the target
* @param serverData the server data
* @param clientData the client data
*/
private static void displayDualEndComparison(ServerPlayer player, LivingEntity target, TestSyncData serverData, TestSyncData clientData) {
protected static void displayDualEndComparison(ServerPlayer player, LivingEntity target, AbstractedTestSyncData serverData, AbstractedTestSyncData clientData) {
player.sendSystemMessage(Component.literal("§6=== 客户端-服务器双端同步检查结果 ==="));
player.sendSystemMessage(Component.literal(
String.format("§7目标生物: §e%s", target.getName().getString())
@ -462,7 +431,7 @@ public class FabricItem extends Item {
/**
* 比较自定义数据
*/
private static boolean compareCustomData(TestSyncData.TestData first, TestSyncData.TestData second) {
private static boolean compareCustomData(AbstractedTestSyncData.TestData first, AbstractedTestSyncData.TestData second) {
return first.getName().equals(second.getName()) &&
first.getValue() == second.getValue() &&
first.isFlag() == second.isFlag();
@ -552,7 +521,7 @@ public class FabricItem extends Item {
*/
private AbstractedTestSyncData getTestSyncData(Entity entity) {
try {
return entity.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null);
return getData(entity);
} catch (Exception e) {
Lib39.LOGGER.debug("[FabricItem] 获取生物 {} 的 TestSyncData 失败: {}",
entity.getName().getString(), e.getMessage());

View File

@ -17,9 +17,7 @@ import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.content.capability.AbstractedTestSyncData;
import top.r3944realms.lib39.example.content.capability.ExCapabilityHandler;
import top.r3944realms.lib39.example.content.capability.TestSyncData;
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
import java.util.List;
import java.util.Random;
@ -29,7 +27,7 @@ import java.util.Random;
* Shift + 右键操作自己的数据
* 普通右键操作瞄准生物的数据
*/
public class NeoForgeItem extends Item {
public abstract class AbstractNeoForgeItem extends Item {
private static final Random RANDOM = new Random();
/**
@ -37,7 +35,7 @@ public class NeoForgeItem extends Item {
*
* @param properties the properties
*/
public NeoForgeItem(Properties properties) {
public AbstractNeoForgeItem(Properties properties) {
super(properties);
}
@ -139,10 +137,8 @@ public class NeoForgeItem extends Item {
*/
private boolean triggerRandomTransformation(LivingEntity entity) {
try {
AbstractedTestSyncData abstractData = getOrCreateTestSyncData(entity);
if (!(abstractData instanceof TestSyncData testData)) {
return false;
}
AbstractedTestSyncData testData = getOrCreateTestSyncData(entity);
// 随机选择一种变换方式
int transformationType = RANDOM.nextInt(6); // 增加更多变换类型
@ -170,7 +166,7 @@ public class NeoForgeItem extends Item {
}
case 3 -> {
// 修改自定义数据
TestSyncData.TestData newCustomData = new TestSyncData.TestData(
AbstractedTestSyncData.TestData newCustomData = new AbstractedTestSyncData.TestData(
"custom_" + RANDOM.nextInt(100),
RANDOM.nextInt(500),
RANDOM.nextBoolean()
@ -223,7 +219,7 @@ public class NeoForgeItem extends Item {
/**
* 显示数据预览给玩家
*/
private void displayDataPreview(Player player, TestSyncData testData) {
private void displayDataPreview(Player player, AbstractedTestSyncData testData) {
player.sendSystemMessage(Component.literal("§6数据预览:"));
player.sendSystemMessage(Component.literal(
String.format("§7字符串: §f%s", testData.getTestString())
@ -247,9 +243,23 @@ public class NeoForgeItem extends Item {
}
}
private AbstractedTestSyncData getOrCreateTestSyncData(Entity entity) {
/**
* Gets data.
*
* @param entity the entity
* @return the data
*/
protected abstract AbstractedTestSyncData getData(Entity entity);
/**
* Gets or create test sync data.
*
* @param entity the entity
* @return the or create test sync data
*/
protected AbstractedTestSyncData getOrCreateTestSyncData(Entity entity) {
try {
return entity.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElseThrow();
return getData(entity);
} catch (Exception e) {
Lib39.LOGGER.error("[NeoForgeItem] 获取 {} 的 TestSyncData 失败: {}",
getEntityName((LivingEntity) entity), e.getMessage());

View File

@ -7,10 +7,9 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.example.client.screen.ForgeScreen;
import top.r3944realms.lib39.util.IClientOnly;
/**
* The type Forge item.
@ -28,13 +27,17 @@ public class ForgeItem extends Item {
@Override
public @NotNull InteractionResultHolder<ItemStack> use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand usedHand) {
if (level.isClientSide() && usedHand == InteractionHand.MAIN_HAND) {
clientUse(usedHand);
ClientOpt.clientUse(usedHand);
}
return super.use(level, player, usedHand);
}
@OnlyIn(Dist.CLIENT)
private static void clientUse(@NotNull InteractionHand usedHand) {
Minecraft.getInstance().setScreen(new ForgeScreen(usedHand, 0));
}
/**
* The type Client opt.
*/
static class ClientOpt implements IClientOnly {
private static void clientUse(@NotNull InteractionHand usedHand) {
IClientOnly.check(() -> Minecraft.getInstance().setScreen(new ForgeScreen(usedHand, 0)));
}
}
}

View File

@ -0,0 +1,24 @@
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

@ -4,7 +4,6 @@ import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Debug;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;

View File

@ -0,0 +1,175 @@
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

@ -0,0 +1,22 @@
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

@ -0,0 +1,43 @@
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

@ -0,0 +1,21 @@
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

@ -0,0 +1,67 @@
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

@ -0,0 +1,15 @@
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

@ -13,8 +13,6 @@ import net.minecraft.nbt.NbtUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.util.nbt.NBTReader;
@ -30,6 +28,74 @@ import java.util.concurrent.atomic.AtomicReference;
* The type GameProfile helper.
*/
public class GameProfileHelper {
/**
* Client Only Class
*/
public static class ClientOpt implements IClientOnly {
/**
* Resolve skin texture resource location.
*
* @param gameProfile the game profile
* @return the resource location
*/
public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) {
return IClientOnly.check(() ->
Minecraft.getInstance().getSkinManager()
.getInsecureSkinLocation(gameProfile));
}
/**
* Gets skin texture.
*
* @param gameProfile the game profile
* @return the skin texture
*/
public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) {
return IClientOnly.check(() -> {
if (gameProfile == null) {
return DefaultPlayerSkin.getDefaultSkin();
}
return resolveSkinTexture(gameProfile);
});
}
/**
* Has slim arms client boolean.
*
* @param player the player
* @return the boolean
*/
public static boolean hasSlimArmsClient(Player player) {
return IClientOnly.check(() -> {
if (player instanceof AbstractClientPlayer clientPlayer) {
PlayerInfo playerInfo = Objects.requireNonNull(Minecraft.getInstance()
.getConnection())
.getPlayerInfo(clientPlayer.getUUID());
return playerInfo != null && "slim".equals(playerInfo.getModelName());
}
return false;
});
}
/**
* Gets skin model name.
*
* @param player the player
* @return the skin model name
*/
public static @NotNull String getSkinModelName(@NotNull Player player) {
return IClientOnly.check(() -> {
if (player.level().isClientSide && player instanceof AbstractClientPlayer) {
PlayerInfo info = Objects.requireNonNull(Minecraft.getInstance().getConnection())
.getPlayerInfo(player.getUUID());
return info != null ? info.getModelName() : "default";
}
return "default";
});
}
}
/**
* The constant TAG_BE.
*/
@ -39,32 +105,25 @@ public class GameProfileHelper {
*/
public static final String TAG_OWN_PROFILE = "OwnerProfile";
/**
* Gets skin texture.
*
* @param gameProfile the game profile
* @return the skin texture
*/
@OnlyIn(Dist.CLIENT)
public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) {
if (gameProfile == null) {
return DefaultPlayerSkin.getDefaultSkin();
}
return resolveSkinTexture(gameProfile);
return ClientOpt.getSkinTexture(gameProfile);
}
/**
* Resolve skin texture resource location.
*
* @param gameProfile the game profile
* @return the resource location
*/
@OnlyIn(Dist.CLIENT)
public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) {
Minecraft minecraft = Minecraft.getInstance();
return minecraft.getSkinManager()
.getInsecureSkinLocation(gameProfile);
return ClientOpt.resolveSkinTexture(gameProfile);
}
/**
@ -73,7 +132,6 @@ public class GameProfileHelper {
* @param player the player
* @return the boolean
*/
// 判断玩家是否使用纤细手臂
public static boolean hasSlimArms(@NotNull Player player) {
if (player.level().isClientSide) {
return hasSlimArmsClient(player);
@ -82,16 +140,9 @@ public class GameProfileHelper {
}
}
// 客户端判断
@OnlyIn(Dist.CLIENT)
private static boolean hasSlimArmsClient(Player player) {
if (player instanceof AbstractClientPlayer clientPlayer) {
PlayerInfo playerInfo = Objects.requireNonNull(Minecraft.getInstance()
.getConnection())
.getPlayerInfo(clientPlayer.getUUID());
return playerInfo != null && "slim".equals(playerInfo.getModelName());
}
return false;
return ClientOpt.hasSlimArmsClient(player);
}
// 服务器端判断
@ -123,15 +174,8 @@ public class GameProfileHelper {
* @param player the player
* @return the skin model name
*/
// 获取皮肤模型名称
@OnlyIn(Dist.CLIENT)
public static @NotNull String getSkinModelName(@NotNull Player player) {
if (player.level().isClientSide && player instanceof AbstractClientPlayer) {
PlayerInfo info = Objects.requireNonNull(Minecraft.getInstance().getConnection())
.getPlayerInfo(player.getUUID());
return info != null ? info.getModelName() : "default";
}
return "default";
return ClientOpt.getSkinModelName(player);
}
/**

View File

@ -0,0 +1,66 @@
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,8 +1,6 @@
package top.r3944realms.lib39.util;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
@ -66,7 +64,6 @@ public interface ILevelHelper {
* @return the client level
*/
@Nullable
@OnlyIn(Dist.CLIENT)
static Level getClientLevel() {
return LevelHelper.CLIENT.getLevel();
}

View File

@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
* <p><b>重要</b>由于使用了 weakValues()实现类必须被其他对象强引用
* 否则会被 GC 清理导致锁自动释放通常这意味着将实现类实例存储在
* 适当的管理器或容器中
*
* @author sch246
*/
public interface IUniPosOwner {
@ -67,8 +68,7 @@ public interface IUniPosOwner {
*
* @param level 维度 The level (dimension) of the position.
* @param pos 坐标 The position to lock.
* @return true if ownership was successfully claimed or was already held by this object,
* false if the position is owned by another object.
* @return true if ownership was successfully claimed or was already held by this object, false if the position is owned by another object.
*/
default boolean tryLock(Level level, BlockPos pos) {
return UniPosManager.INSTANCE.tryLock(level, pos, this);
@ -88,6 +88,7 @@ public interface IUniPosOwner {
/**
* 续租继续持有锁
*
* @param level 维度
* @param pos 位置
*/
@ -97,16 +98,19 @@ public interface IUniPosOwner {
}
/**
管理 IUniPosOwner 系统的单例存储
该类是后台实现大多数类不应直接使用
在同一维度键ResourceKey<Level范围内确保任意时刻一个 BlockPos 只能被一个对象拥有
注意锁的作用域是维度键级别因此在服务端同一维度的不同 Level 实例之间共享
缓存以 BlockPos long 值为键 IUniPosOwner 的弱值weak values为值
当所有者不再被强引用时条目会自动移除从而释放锁
该实现面向服务端使用
* 管理 IUniPosOwner 系统的单例存储
* 该类是后台实现大多数类不应直接使用
* 在同一维度键ResourceKey<Level范围内确保任意时刻一个 BlockPos 只能被一个对象拥有
* 注意锁的作用域是维度键级别因此在服务端同一维度的不同 Level 实例之间共享
* 缓存以 BlockPos long 值为键 IUniPosOwner 的弱值weak values为值
* 当所有者不再被强引用时条目会自动移除从而释放锁
* 该实现面向服务端使用
*/
final class UniPosManager {
/**
* The constant INSTANCE.
*/
public static final UniPosManager INSTANCE = new UniPosManager();
// 顶层映射维度键ResourceKey<Level> -> 每维度的坐标缓存
@ -139,7 +143,7 @@ final class UniPosManager {
*
* @param level 维度
* @param pos 坐标
* @return 如果存在所有者则返回所有者对象否则返回 null
* @return 如果存在所有者 则返回所有者对象否则返回 null
*/
@Nullable
public IUniPosOwner getOwner(Level level, BlockPos pos) {
@ -182,6 +186,7 @@ final class UniPosManager {
/**
* 续租继续持有锁
*
* @param level 维度
* @param pos 位置
* @param owner 对象

View File

@ -1,39 +1,35 @@
package top.r3944realms.lib39.util.block;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.RegistryObject;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.core.event.CommonEventHandler;
import top.r3944realms.lib39.mixin.minecraft.CreativeModeTabsAccessor;
import top.r3944realms.lib39.platform.Services;
import java.util.function.BiFunction;
import java.util.function.Supplier;
/**
* The type Block registry builder.
*/
@SuppressWarnings({"UnusedReturnValue", "unused"})
public class BlockRegistryBuilder {
public abstract class BlockRegistryBuilder {
private String registryName;
private RegistryObject<Block> blockObject;
private DeferredRegister<Item> items;
private Supplier<Block> blockObject;
private BiFunction<String, Supplier<Item>, Supplier<Item>> blockItemRegister;
private boolean needBuildItem;
private Item.Properties properties;
/**
* 创建新的构建器实例
* Create block registry builder.
*
* @return the block registry builder
*/
@Contract(value = " -> new", pure = true)
public static @NotNull BlockRegistryBuilder create() {
return new BlockRegistryBuilder();
public static BlockRegistryBuilder create() {
return Services.PLATFORM.getUtilHelper().getBlockRegistryBuilder();
}
/**
@ -54,8 +50,8 @@ public class BlockRegistryBuilder {
* @param blockSupplier the block supplier
* @return the block registry builder
*/
public BlockRegistryBuilder registerBlock(@NotNull DeferredRegister<Block> blockRegister, Supplier<? extends Block> blockSupplier) {
this.blockObject = blockRegister.register(this.registryName, blockSupplier);
public BlockRegistryBuilder registerBlock(@NotNull BiFunction<String, Supplier<Block>,Supplier<Block>> blockRegister, Supplier<Block> blockSupplier) {
this.blockObject = blockRegister.apply(this.registryName, blockSupplier);
return this;
}
@ -65,8 +61,8 @@ public class BlockRegistryBuilder {
* @param itemRegister the item deferred register
* @return the block registry builder
*/
public BlockRegistryBuilder registerItem(DeferredRegister<Item> itemRegister) {
this.items = itemRegister;
public BlockRegistryBuilder registerItem(BiFunction<String, Supplier<Item>,Supplier<Item>> itemRegister) {
this.blockItemRegister = itemRegister;
needBuildItem = true;
return this;
}
@ -78,8 +74,8 @@ public class BlockRegistryBuilder {
* @param properties the item properties
* @return the block registry builder
*/
public BlockRegistryBuilder registerItemWithProperties(DeferredRegister<Item> itemRegister, Item.Properties properties) {
this.items = itemRegister;
public BlockRegistryBuilder registerItemWithProperties(BiFunction<String, Supplier<Item>,Supplier<Item>> itemRegister, Item.Properties properties) {
this.blockItemRegister = itemRegister;
this.properties = properties;
needBuildItem = true;
return this;
@ -98,11 +94,11 @@ public class BlockRegistryBuilder {
/**
* 内部方法注册对应的方块物品
*
* @param blockObject the block object
* @param creativeTabs the creative tabs
*/
@SafeVarargs
private void registerBlockItem(RegistryObject<Block> blockObject, ResourceKey<CreativeModeTab>... creativeTabs) {
CommonEventHandler.Mod.addItemToTabs(blockObject, creativeTabs);
}
protected abstract void registerBlockItem(Supplier<Block> blockObject, ResourceKey<CreativeModeTab>... creativeTabs);
/**
* 注册方块和物品到建筑标签页
@ -111,9 +107,9 @@ public class BlockRegistryBuilder {
* @param blockSupplier the block supplier
* @return the block registry builder
*/
public BlockRegistryBuilder registerWithBuildingTab(DeferredRegister<Block> blockRegister, Supplier<? extends Block> blockSupplier) {
public BlockRegistryBuilder registerWithBuildingTab(BiFunction<String, Supplier<Block>,Supplier<Block>> blockRegister, Supplier<Block> blockSupplier) {
registerBlock(blockRegister, blockSupplier);
registerBlockItem(this.blockObject, CreativeModeTabs.BUILDING_BLOCKS);
registerBlockItem(this.blockObject, CreativeModeTabsAccessor.getBuildingBlocks());
return this;
}
@ -124,9 +120,9 @@ public class BlockRegistryBuilder {
* @param blockSupplier the block supplier
* @return the block registry builder
*/
public BlockRegistryBuilder registerWithFunctionalTab(DeferredRegister<Block> blockRegister, Supplier<? extends Block> blockSupplier) {
public BlockRegistryBuilder registerWithFunctionalTab(BiFunction<String, Supplier<Block>,Supplier<Block>> blockRegister, Supplier<Block> blockSupplier) {
registerBlock(blockRegister, blockSupplier);
registerBlockItem(this.blockObject, CreativeModeTabs.FUNCTIONAL_BLOCKS);
registerBlockItem(this.blockObject, CreativeModeTabsAccessor.getFunctionalBlocks());
return this;
}
@ -135,9 +131,9 @@ public class BlockRegistryBuilder {
*
* @return the registry object
*/
public RegistryObject<Block> build() {
public Supplier<Block> build() {
if (needBuildItem) {
items.register(this.registryName, () -> new BlockItem(this.blockObject.get(), properties == null ? new Item.Properties() : properties));
blockItemRegister.apply(this.registryName, () -> new BlockItem(this.blockObject.get(), properties == null ? new Item.Properties() : properties));
}
return this.blockObject;
}

View File

@ -17,7 +17,7 @@ public abstract class EntityListResolve {
/**
* The Result.
*/
protected EntityListResolve.EntityResolveResult result;
protected EntityResolveResult result;
/**
* The type Entity resolve result.

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