feat: 让Lib39跨加载器化,未完成
This commit is contained in:
parent
b616b4535e
commit
eac118e9a4
|
|
@ -26,14 +26,44 @@ jobs:
|
||||||
- name: Make gradlew executable
|
- name: Make gradlew executable
|
||||||
run: chmod +x ./gradlew
|
run: chmod +x ./gradlew
|
||||||
|
|
||||||
|
- name: Run Forge data generation
|
||||||
|
run: |
|
||||||
|
echo "=== 运行 Forge 数据生成 ==="
|
||||||
|
./gradlew runData --no-daemon
|
||||||
|
|
||||||
|
continue-on-error: false
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build --no-daemon
|
||||||
|
|
||||||
- name: Prepare release files
|
- name: Prepare release files
|
||||||
run: |
|
run: |
|
||||||
mkdir -p release-files
|
mkdir -p release-files
|
||||||
cp build/libs/*.jar release-files/ 2>/dev/null || true
|
|
||||||
echo "准备发布的文件:"
|
# 收集所有模块的构建产物
|
||||||
|
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/
|
ls -la release-files/
|
||||||
|
|
||||||
- name: Upload release artifacts
|
- name: Upload release artifacts
|
||||||
|
|
@ -60,6 +90,14 @@ jobs:
|
||||||
name: release-files
|
name: release-files
|
||||||
path: ./dist
|
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
|
||||||
|
|
||||||
- name: Generate CZ-compliant changelog
|
- name: Generate CZ-compliant changelog
|
||||||
id: generate_changelog
|
id: generate_changelog
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -177,19 +215,59 @@ jobs:
|
||||||
|
|
||||||
echo "- 发布日期: $(date '+%Y年%m月%d日')" >> $TEMP_FILE
|
echo "- 发布日期: $(date '+%Y年%m月%d日')" >> $TEMP_FILE
|
||||||
echo "- 当前版本: $CURRENT_TAG" >> $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 "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
echo "## 📦 下载文件" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
echo "本次发布包含以下平台的构建文件:" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
# 列出所有发布的文件
|
||||||
|
echo "### Fabric 版本" >> $TEMP_FILE
|
||||||
|
echo "\`\`\`" >> $TEMP_FILE
|
||||||
|
ls -1 dist/*fabric*.jar 2>/dev/null | grep -v "sources\|javadoc" | xargs -n1 basename || echo "无 Fabric 主文件" >> $TEMP_FILE
|
||||||
|
echo "\`\`\`" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
echo "### Forge 版本" >> $TEMP_FILE
|
||||||
|
echo "\`\`\`" >> $TEMP_FILE
|
||||||
|
ls -1 dist/*forge*.jar 2>/dev/null | grep -v "sources\|javadoc" | xargs -n1 basename || echo "无 Forge 主文件" >> $TEMP_FILE
|
||||||
|
echo "\`\`\`" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
echo "### 通用模块" >> $TEMP_FILE
|
||||||
|
echo "\`\`\`" >> $TEMP_FILE
|
||||||
|
ls -1 dist/*common*.jar 2>/dev/null | grep -v "sources\|javadoc" | xargs -n1 basename || echo "无通用模块" >> $TEMP_FILE
|
||||||
|
echo "\`\`\`" >> $TEMP_FILE
|
||||||
|
echo "" >> $TEMP_FILE
|
||||||
|
|
||||||
|
echo "### 源码和文档" >> $TEMP_FILE
|
||||||
|
echo "\`\`\`" >> $TEMP_FILE
|
||||||
|
ls -1 dist/*-sources.jar 2>/dev/null | xargs -n1 basename || echo "无源码文件" >> $TEMP_FILE
|
||||||
|
ls -1 dist/*-javadoc.jar 2>/dev/null | xargs -n1 basename || echo "无文档文件" >> $TEMP_FILE
|
||||||
|
echo "\`\`\`" >> $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
|
||||||
|
|
||||||
# 显示所有提交的详细列表,包含scope信息
|
# 显示所有提交的详细列表
|
||||||
if [ -z "$PREV_TAG" ]; then
|
if [ -z "$PREV_TAG" ]; then
|
||||||
git log --pretty=format:"- **%h** %s - %an (%ad)" --date=short --reverse | head -100 >> $TEMP_FILE
|
git log --pretty=format:"%h %s - %an (%ad)" --date=short --reverse >> $TEMP_FILE
|
||||||
else
|
else
|
||||||
git log --pretty=format:"- **%h** %s - %an (%ad)" --date=short $PREV_TAG..HEAD | head -100 >> $TEMP_FILE
|
git log --pretty=format:"%h %s - %an (%ad)" --date=short $PREV_TAG..HEAD >> $TEMP_FILE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "\`\`\`" >> $TEMP_FILE
|
||||||
|
echo "</details>" >> $TEMP_FILE
|
||||||
|
|
||||||
# 将文件内容输出到变量
|
# 将文件内容输出到变量
|
||||||
CHANGELOG_CONTENT=$(cat $TEMP_FILE)
|
CHANGELOG_CONTENT=$(cat $TEMP_FILE)
|
||||||
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
echo "changelog<<EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
@ -199,9 +277,13 @@ jobs:
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
artifacts: "dist/*.jar"
|
artifacts: |
|
||||||
|
dist/*.jar
|
||||||
tag: ${{ github.ref_name }}
|
tag: ${{ github.ref_name }}
|
||||||
name: "版本 ${{ github.ref_name }}"
|
name: "${{ steps.version_info.outputs.minecraft_version }} - ${{ github.ref_name }}"
|
||||||
body: ${{ steps.generate_changelog.outputs.changelog }}
|
body: ${{ steps.generate_changelog.outputs.changelog }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
allowUpdates: true
|
||||||
|
removeArtifacts: true
|
||||||
|
|
@ -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.
|
|
||||||
537
build.gradle
537
build.gradle
|
|
@ -1,535 +1,4 @@
|
||||||
//file:noinspection GroovyAssignabilityCheck
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'fabric-loom' version '1.9-SNAPSHOT' apply(false)
|
||||||
id 'idea'
|
id 'net.neoforged.moddev.legacyforge' version '2.0.103' apply(false)
|
||||||
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>
|
|
||||||
*/
|
|
||||||
3
buildSrc/build.gradle
Normal file
3
buildSrc/build.gradle
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
plugins {
|
||||||
|
id 'groovy-gradle-plugin'
|
||||||
|
}
|
||||||
211
buildSrc/src/main/groovy/multiloader-common.gradle
Normal file
211
buildSrc/src/main/groovy/multiloader-common.gradle
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
||||||
|
maven {
|
||||||
|
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
|
||||||
|
}
|
||||||
50
buildSrc/src/main/groovy/multiloader-loader.gradle
Normal file
50
buildSrc/src/main/groovy/multiloader-loader.gradle
Normal 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
43
common/build.gradle
Normal 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'
|
||||||
|
}
|
||||||
58
common/src/main/java/top/r3944realms/lib39/Lib39.java
Normal file
58
common/src/main/java/top/r3944realms/lib39/Lib39.java
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
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.platform.Services;
|
||||||
|
|
||||||
|
public class Lib39 {
|
||||||
|
public static final String MOD_ID = "lib39";
|
||||||
|
public static final String MOD_NAME = "3944Realms 's Lib Mod";
|
||||||
|
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";
|
||||||
|
public static void initialize() {
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isClientEnvironment() {
|
||||||
|
return Services.PLATFORM.isClientEnvironment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
package top.r3944realms.lib39.content.block;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
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.SoundSource;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
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.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.BooleanProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
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.Vec3;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
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.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.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public class DollBlock extends HorizontalDirectionalBlock implements SimpleWaterloggedBlock, EntityBlock {
|
||||||
|
private static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
|
||||||
|
/**
|
||||||
|
* The constant POSE.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
private static final float NOTE_COLOR_DIVISOR = 24.0F;
|
||||||
|
private static final int MAX_NOTE_COLORS = 4;
|
||||||
|
|
||||||
|
private static final float BASE_VOLUME = 1.0f;
|
||||||
|
private static final float PITCH_VARIANCE = 0.5f;
|
||||||
|
private static final float BASE_PITCH = 0.75f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Doll block.
|
||||||
|
*/
|
||||||
|
public DollBlock() {
|
||||||
|
super(properties);
|
||||||
|
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.SOUTH)
|
||||||
|
.setValue(POSE, DollPose.DEFAULT)
|
||||||
|
.setValue(WATERLOGGED, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeReplaced(@NotNull BlockState state, @NotNull BlockPlaceContext useContext) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull BlockState updateShape(@NotNull BlockState currentState, @NotNull Direction direction, @NotNull BlockState neighborState,
|
||||||
|
@NotNull LevelAccessor level, @NotNull BlockPos currentPos, @NotNull BlockPos neighborPos) {
|
||||||
|
if (currentState.getValue(WATERLOGGED)) {
|
||||||
|
level.scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
|
||||||
|
}
|
||||||
|
return super.updateShape(currentState, direction, neighborState, level, currentPos, neighborPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull FluidState getFluidState(@NotNull BlockState blockState) {
|
||||||
|
return blockState.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull InteractionResult use(@NotNull BlockState blockState, @NotNull Level level, @NotNull BlockPos blockPos, @NotNull Player player,
|
||||||
|
@NotNull InteractionHand hand, @NotNull BlockHitResult hitResult) {
|
||||||
|
if (level instanceof ServerLevel serverLevel) {
|
||||||
|
// 播放粒子效果
|
||||||
|
spawnNoteParticles(serverLevel, blockPos);
|
||||||
|
// 播放音效
|
||||||
|
playDollSound(serverLevel, blockPos);
|
||||||
|
}
|
||||||
|
return InteractionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在玩偶位置生成音符粒子效果
|
||||||
|
*/
|
||||||
|
private void spawnNoteParticles(ServerLevel serverLevel, BlockPos blockPos) {
|
||||||
|
Vec3 particlePosition = calculateParticlePosition(serverLevel, blockPos);
|
||||||
|
float noteColor = calculateNoteColor(serverLevel);
|
||||||
|
|
||||||
|
serverLevel.sendParticles(ParticleTypes.NOTE,
|
||||||
|
particlePosition.x(), particlePosition.y(), particlePosition.z(),
|
||||||
|
0, noteColor, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算粒子生成位置,添加随机偏移
|
||||||
|
*/
|
||||||
|
private @NotNull Vec3 calculateParticlePosition(@NotNull ServerLevel serverLevel, BlockPos blockPos) {
|
||||||
|
return Vec3.atBottomCenterOf(blockPos).add(
|
||||||
|
(serverLevel.getRandom().nextFloat() - 0.5) * PARTICLE_OFFSET_RANGE * 2,
|
||||||
|
PARTICLE_HEIGHT_OFFSET + serverLevel.getRandom().nextFloat() * PARTICLE_HEIGHT_VARIANCE,
|
||||||
|
(serverLevel.getRandom().nextFloat() - 0.5) * PARTICLE_OFFSET_RANGE * 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算音符粒子的颜色
|
||||||
|
*/
|
||||||
|
private float calculateNoteColor(@NotNull ServerLevel serverLevel) {
|
||||||
|
return serverLevel.getRandom().nextInt(MAX_NOTE_COLORS) / NOTE_COLOR_DIVISOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放玩偶音效
|
||||||
|
*/
|
||||||
|
private void playDollSound(@NotNull ServerLevel serverLevel, BlockPos blockPos) {
|
||||||
|
float pitch = BASE_PITCH + serverLevel.random.nextFloat() * PITCH_VARIANCE;
|
||||||
|
serverLevel.playSound(null, blockPos, Lib39SoundEvents.DUCK_TOY.get(),
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||||
|
return Lib39BlockEntities.DOLL_BLOCK_ENTITY.get().create(blockPos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
public @NotNull RenderShape getRenderShape(@NotNull BlockState state) {
|
||||||
|
return RenderShape.ENTITYBLOCK_ANIMATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getCloneItemStack(BlockGetter level, BlockPos pos, BlockState state) {
|
||||||
|
ItemStack stack = super.getCloneItemStack(level, pos, state);
|
||||||
|
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||||
|
if (blockEntity instanceof DollBlockEntity doll) {
|
||||||
|
GameProfile profile = doll.getOwnerProfile();
|
||||||
|
if (profile != null) {
|
||||||
|
GameProfileHelper.saveProfileToItemStack(stack, profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最重要的方法:重写掉落逻辑
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public List<ItemStack> getDrops(@NotNull BlockState state, @NotNull LootParams.Builder params) {
|
||||||
|
// 获取方块实体
|
||||||
|
BlockEntity blockEntity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY);
|
||||||
|
|
||||||
|
if (blockEntity instanceof DollBlockEntity dollEntity) {
|
||||||
|
List<ItemStack> customDrops = getCustomDrops(dollEntity, params);
|
||||||
|
if (customDrops != null) return customDrops;
|
||||||
|
}
|
||||||
|
return super.getDrops(state, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成自定义掉落物
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private List<ItemStack> getCustomDrops(DollBlockEntity dollEntity, LootParams.Builder params) {
|
||||||
|
if (params.getOptionalParameter(LootContextParams.THIS_ENTITY) instanceof Player player) {
|
||||||
|
if (player.isCreative()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameProfile profile = dollEntity.getOwnerProfile();
|
||||||
|
if (profile != null) {
|
||||||
|
ItemStack instance = Lib39Items.DOLL.get().getDefaultInstance();
|
||||||
|
GameProfileHelper.saveProfileToItemStack(instance, profile);
|
||||||
|
return List.of(instance);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package top.r3944realms.lib39.content.block.blockentity;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.NbtUtils;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
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.core.register.Lib39BlockEntities;
|
||||||
|
import top.r3944realms.lib39.util.GameProfileHelper;
|
||||||
|
import top.r3944realms.lib39.util.nbt.NBTReader;
|
||||||
|
import top.r3944realms.lib39.util.nbt.NBTWriter;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Doll block entity.
|
||||||
|
*/
|
||||||
|
public class DollBlockEntity extends BlockEntity {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private GameProfile owner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Doll block entity.
|
||||||
|
*
|
||||||
|
* @param pos the pos
|
||||||
|
* @param blockState the block state
|
||||||
|
*/
|
||||||
|
public DollBlockEntity(BlockPos pos, BlockState blockState) {
|
||||||
|
super(Lib39BlockEntities.DOLL_BLOCK_ENTITY.get(), pos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveAdditional(@NotNull CompoundTag tag) {
|
||||||
|
super.saveAdditional(tag);
|
||||||
|
NBTWriter.of(tag)
|
||||||
|
.compoundIf(GameProfileHelper.TAG_OWN_PROFILE, owner != null, () -> NbtUtils.writeGameProfile(new CompoundTag(), this.owner));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(@NotNull CompoundTag tag) {
|
||||||
|
super.load(tag);
|
||||||
|
NBTReader.of(tag)
|
||||||
|
.compound(GameProfileHelper.TAG_OWN_PROFILE, compoundTag -> setOwner(NbtUtils.readGameProfile(compoundTag)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets owner profile.
|
||||||
|
*
|
||||||
|
* @return the owner profile
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public GameProfile getOwnerProfile() {
|
||||||
|
return this.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ClientboundBlockEntityDataPacket getUpdatePacket() {
|
||||||
|
return ClientboundBlockEntityDataPacket.create(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull CompoundTag getUpdateTag() {
|
||||||
|
return this.saveWithoutMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets owner.
|
||||||
|
*
|
||||||
|
* @param owner the owner
|
||||||
|
*/
|
||||||
|
public void setOwner(@Nullable GameProfile owner) {
|
||||||
|
synchronized (this) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateOwnerProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets owner.
|
||||||
|
*
|
||||||
|
* @param ownerName the owner name
|
||||||
|
*/
|
||||||
|
public void setOwner(@Nullable String ownerName) {
|
||||||
|
setOwner(new GameProfile(Util.NIL_UUID, ownerName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOwnerProfile() {
|
||||||
|
SkullBlockEntity.updateGameprofile(this.owner, gameProfile -> {
|
||||||
|
this.owner = gameProfile;
|
||||||
|
this.setChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package top.r3944realms.lib39.content.block.property;
|
||||||
|
|
||||||
|
import net.minecraft.util.StringRepresentable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The enum Doll pose.
|
||||||
|
*/
|
||||||
|
public enum DollPose implements StringRepresentable {
|
||||||
|
/**
|
||||||
|
* Default doll pose.
|
||||||
|
*/
|
||||||
|
DEFAULT("default"),
|
||||||
|
/**
|
||||||
|
* further support
|
||||||
|
*/
|
||||||
|
FURTHER("further"),
|
||||||
|
;
|
||||||
|
private final String name;
|
||||||
|
DollPose(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String getSerializedName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package top.r3944realms.lib39.content.item;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.item.BlockItem;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
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.core.register.Lib39Blocks;
|
||||||
|
import top.r3944realms.lib39.util.GameProfileHelper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Doll item.
|
||||||
|
*/
|
||||||
|
public class DollItem extends BlockItem {
|
||||||
|
/**
|
||||||
|
* Instantiates a new Doll item.
|
||||||
|
*
|
||||||
|
* @param properties the properties
|
||||||
|
*/
|
||||||
|
public DollItem(Properties properties) {
|
||||||
|
super(Lib39Blocks.DOLL.get(), properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendHoverText(@NotNull ItemStack stack, @Nullable Level level, @NotNull List<Component> tooltip, @NotNull TooltipFlag flag) {
|
||||||
|
GameProfile profileFromItemStack = GameProfileHelper.getProfileFromItemStack(stack);
|
||||||
|
if (profileFromItemStack != null && profileFromItemStack.getName() != null) {
|
||||||
|
tooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.1", profileFromItemStack.getName()));
|
||||||
|
}
|
||||||
|
tooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
package top.r3944realms.lib39.datagen.provider;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public LanguageProvider(PackOutput output, String modid, String locale) {
|
||||||
|
this.output = output;
|
||||||
|
this.modid = modid;
|
||||||
|
this.locale = locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBlock(@NotNull Supplier<? extends Block> key, String name) {
|
||||||
|
this.add(key.get(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NotNull Block key, String name) {
|
||||||
|
this.add(key.getDescriptionId(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItem(@NotNull Supplier<? extends Item> key, String name) {
|
||||||
|
this.add(key.get(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NotNull Item key, String name) {
|
||||||
|
this.add(key.getDescriptionId(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItemStack(@NotNull Supplier<ItemStack> key, String name) {
|
||||||
|
this.add(key.get(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NotNull ItemStack key, String name) {
|
||||||
|
this.add(key.getDescriptionId(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEnchantment(@NotNull Supplier<? extends Enchantment> key, String name) {
|
||||||
|
this.add(key.get(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NotNull Enchantment key, String name) {
|
||||||
|
this.add(key.getDescriptionId(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEffect(@NotNull Supplier<? extends MobEffect> key, String name) {
|
||||||
|
this.add(key.get(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NotNull MobEffect key, String name) {
|
||||||
|
this.add(key.getDescriptionId(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntityType(@NotNull Supplier<? extends EntityType<?>> key, String name) {
|
||||||
|
this.add(key.get(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NotNull EntityType<?> key, String name) {
|
||||||
|
this.add(key.getDescriptionId(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String key, String value) {
|
||||||
|
if (this.data.put(key, value) != null) {
|
||||||
|
throw new IllegalStateException("Duplicate translation key " + key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package top.r3944realms.lib39.datagen.provider;
|
||||||
|
|
||||||
|
import net.minecraft.data.PackOutput;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Simple language provider.
|
||||||
|
*/
|
||||||
|
public class SimpleLanguageProvider extends LanguageProvider {
|
||||||
|
private final McLocale language;
|
||||||
|
private final ILangKeyValueCollection langKeyValueCollection;
|
||||||
|
@Nullable
|
||||||
|
private ILangKeyValueCollection[] langKeyValueCollections;
|
||||||
|
private final Map<String, String> translationMap; // Better naming
|
||||||
|
private final List<String> orderedKeys; // Better naming than "objects"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Simple language provider.
|
||||||
|
*
|
||||||
|
* @param output the output
|
||||||
|
* @param modId the mod id
|
||||||
|
* @param language the language
|
||||||
|
* @param langKeyValueCollection the lang key value collection
|
||||||
|
*/
|
||||||
|
public SimpleLanguageProvider(PackOutput output, String modId,
|
||||||
|
@NotNull McLocale language,
|
||||||
|
ILangKeyValueCollection langKeyValueCollection) {
|
||||||
|
super(output, modId, language.mcCode());
|
||||||
|
this.language = language;
|
||||||
|
this.langKeyValueCollection = langKeyValueCollection;
|
||||||
|
this.translationMap = new HashMap<>();
|
||||||
|
this.orderedKeys = new ArrayList<>();
|
||||||
|
initializeTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Simple language provider.
|
||||||
|
*
|
||||||
|
* @param output the output
|
||||||
|
* @param modId the mod id
|
||||||
|
* @param language the language
|
||||||
|
* @param langKeyValueCollection the lang key value collection
|
||||||
|
*/
|
||||||
|
public SimpleLanguageProvider(PackOutput output, String modId,
|
||||||
|
@NotNull McLocale language,
|
||||||
|
ILangKeyValueCollection... langKeyValueCollection) {
|
||||||
|
super(output, modId, language.mcCode());
|
||||||
|
this.language = language;
|
||||||
|
this.langKeyValueCollection = null;
|
||||||
|
this.langKeyValueCollections = langKeyValueCollection;
|
||||||
|
this.translationMap = new HashMap<>();
|
||||||
|
this.orderedKeys = new ArrayList<>();
|
||||||
|
initializeTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeTranslations() {
|
||||||
|
if (langKeyValueCollection != null) {
|
||||||
|
addToTranslationMap(langKeyValueCollection);
|
||||||
|
} else if (langKeyValueCollections != null) {
|
||||||
|
for (ILangKeyValueCollection keyValueCollection : langKeyValueCollections) {
|
||||||
|
if (keyValueCollection != null) {
|
||||||
|
addToTranslationMap(keyValueCollection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToTranslationMap(ILangKeyValueCollection keyValueCollection) {
|
||||||
|
for (ILangKeyValue langKeyValue : keyValueCollection.getValues()) {
|
||||||
|
String key = langKeyValue.getKey();
|
||||||
|
String value = langKeyValue.getLang(language);
|
||||||
|
|
||||||
|
if (!translationMap.containsKey(key)) {
|
||||||
|
orderedKeys.add(key);
|
||||||
|
}
|
||||||
|
translationMap.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addTranslations() {
|
||||||
|
orderedKeys.forEach(key -> add(key, translationMap.get(key)));
|
||||||
|
validateTranslations();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateTranslations() {
|
||||||
|
long addedCount = orderedKeys.stream()
|
||||||
|
.filter(translationMap::containsKey)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
Lib39.LOGGER.info("Added {}/{} translations for {}",
|
||||||
|
addedCount, orderedKeys.size(), language.mcCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package top.r3944realms.lib39.datagen.value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface Lang key value.
|
||||||
|
*/
|
||||||
|
public interface ILangKeyValue {
|
||||||
|
/**
|
||||||
|
* Gets key.
|
||||||
|
*
|
||||||
|
* @return the key
|
||||||
|
*/
|
||||||
|
String getKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets lang.
|
||||||
|
*
|
||||||
|
* @param locale the locale
|
||||||
|
* @return the lang
|
||||||
|
*/
|
||||||
|
String getLang(McLocale locale);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package top.r3944realms.lib39.datagen.value;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface Lang key value collection.
|
||||||
|
*/
|
||||||
|
public interface ILangKeyValueCollection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets values.
|
||||||
|
*
|
||||||
|
* @return the values
|
||||||
|
*/
|
||||||
|
List<? extends ILangKeyValue> getValues();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets lang.
|
||||||
|
*
|
||||||
|
* @param locale the locale
|
||||||
|
* @param key the key
|
||||||
|
* @return the lang
|
||||||
|
*/
|
||||||
|
static String getLang(McLocale locale, @NotNull ILangKeyValue key) {
|
||||||
|
return key.getLang(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package top.r3944realms.lib39.datagen.value;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface Locale entry.
|
||||||
|
*/
|
||||||
|
public interface ILocaleEntry {
|
||||||
|
/**
|
||||||
|
* Mc code string.
|
||||||
|
*
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
String mcCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java locale locale.
|
||||||
|
*
|
||||||
|
* @return the locale
|
||||||
|
*/
|
||||||
|
Locale javaLocale();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,617 @@
|
||||||
|
package top.r3944realms.lib39.datagen.value;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Lang key value.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class LangKeyValue implements ILangKeyValue {
|
||||||
|
/**
|
||||||
|
* The Supplier.
|
||||||
|
*/
|
||||||
|
protected final Supplier<?> supplier;
|
||||||
|
/**
|
||||||
|
* The Key.
|
||||||
|
*/
|
||||||
|
protected final String key;
|
||||||
|
/**
|
||||||
|
* The Us en.
|
||||||
|
*/
|
||||||
|
protected final String US_EN;
|
||||||
|
/**
|
||||||
|
* The Sim cn.
|
||||||
|
*/
|
||||||
|
protected final String SIM_CN;
|
||||||
|
/**
|
||||||
|
* The Tra cn.
|
||||||
|
*/
|
||||||
|
protected final String TRA_CN;
|
||||||
|
/**
|
||||||
|
* The Lzh.
|
||||||
|
*/
|
||||||
|
protected final String LZH;
|
||||||
|
/**
|
||||||
|
* The Default.
|
||||||
|
*/
|
||||||
|
protected final Boolean Default;
|
||||||
|
/**
|
||||||
|
* The Mpe.
|
||||||
|
*/
|
||||||
|
protected final ModPartEnum MPE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Lang key value.
|
||||||
|
*
|
||||||
|
* @param builder the builder
|
||||||
|
*/
|
||||||
|
protected LangKeyValue(Builder builder) {
|
||||||
|
this.supplier = builder.supplier;
|
||||||
|
this.key = builder.key;
|
||||||
|
this.MPE = builder.MPE;
|
||||||
|
this.US_EN = builder.US_EN;
|
||||||
|
this.SIM_CN = builder.SIM_CN;
|
||||||
|
this.TRA_CN = builder.TRA_CN;
|
||||||
|
this.LZH = builder.LZH;
|
||||||
|
this.Default = builder.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for LangKeyValue
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private Supplier<?> supplier;
|
||||||
|
private String key;
|
||||||
|
private ModPartEnum MPE;
|
||||||
|
private String US_EN;
|
||||||
|
private String SIM_CN;
|
||||||
|
private String TRA_CN;
|
||||||
|
private String LZH;
|
||||||
|
private Boolean Default = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set supplier
|
||||||
|
*
|
||||||
|
* @param supplier the supplier
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
public Builder supplier(Supplier<?> supplier) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set key
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
public Builder key(String key) {
|
||||||
|
this.key = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set mod part enum
|
||||||
|
*
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
public Builder MPE(ModPartEnum MPE) {
|
||||||
|
this.MPE = MPE;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set US English translation
|
||||||
|
*
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
public Builder US_EN(String US_EN) {
|
||||||
|
this.US_EN = US_EN;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Simplified Chinese translation
|
||||||
|
*
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
public Builder SIM_CN(String SIM_CN) {
|
||||||
|
this.SIM_CN = SIM_CN;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Traditional Chinese translation
|
||||||
|
*
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
public Builder TRA_CN(String TRA_CN) {
|
||||||
|
this.TRA_CN = TRA_CN;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Literary Chinese translation
|
||||||
|
*
|
||||||
|
* @param LZH the lzh
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
public Builder LZH(String LZH) {
|
||||||
|
this.LZH = LZH;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set as default
|
||||||
|
*
|
||||||
|
* @param isDefault the is default
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
public Builder isDefault(Boolean isDefault) {
|
||||||
|
this.Default = isDefault;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the LangKeyValue instance
|
||||||
|
*
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public LangKeyValue build() {
|
||||||
|
// Validate required fields
|
||||||
|
if (MPE == null) {
|
||||||
|
throw new IllegalStateException("MPE (ModPartEnum) is required");
|
||||||
|
}
|
||||||
|
if (US_EN == null) {
|
||||||
|
throw new IllegalStateException("US_EN translation is required");
|
||||||
|
}
|
||||||
|
if (SIM_CN == null) {
|
||||||
|
throw new IllegalStateException("SIM_CN translation is required");
|
||||||
|
}
|
||||||
|
if (TRA_CN == null) {
|
||||||
|
throw new IllegalStateException("TRA_CN translation is required");
|
||||||
|
}
|
||||||
|
// Either supplier or key must be provided, but not both
|
||||||
|
if (supplier == null && key == null) {
|
||||||
|
throw new IllegalStateException("Either supplier or key must be provided");
|
||||||
|
}
|
||||||
|
if (supplier != null && key != null) {
|
||||||
|
throw new IllegalStateException("Cannot provide both supplier and key");
|
||||||
|
}
|
||||||
|
return new LangKeyValue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new builder instance
|
||||||
|
*
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract(" -> new")
|
||||||
|
public static @NotNull Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create builder with supplier
|
||||||
|
*
|
||||||
|
* @param supplier the supplier
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull Builder withSupplier(Supplier<?> supplier) {
|
||||||
|
return new Builder().supplier(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create builder with key
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull Builder withKey(String key) {
|
||||||
|
return new Builder().key(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保持原有的静态工厂方法作为便捷方法
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of supplier lang key value.
|
||||||
|
*
|
||||||
|
* @param supplier the supplier
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@Contract(value = "_, _, _, _, _ -> new", pure = true)
|
||||||
|
public static @NotNull LangKeyValue ofSupplier(Supplier<?> supplier, ModPartEnum MPE,
|
||||||
|
String US_EN, String SIM_CN, String TRA_CN) {
|
||||||
|
return builder()
|
||||||
|
.supplier(supplier)
|
||||||
|
.MPE(MPE)
|
||||||
|
.US_EN(US_EN)
|
||||||
|
.SIM_CN(SIM_CN)
|
||||||
|
.TRA_CN(TRA_CN)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of supplier lang key value.
|
||||||
|
*
|
||||||
|
* @param supplier the supplier
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @param isDefault the is default
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@Contract(value = "_, _, _, _, _, _ -> new", pure = true)
|
||||||
|
public static @NotNull LangKeyValue ofSupplier(Supplier<?> supplier, ModPartEnum MPE,
|
||||||
|
String US_EN, String SIM_CN, String TRA_CN, boolean isDefault) {
|
||||||
|
return builder()
|
||||||
|
.supplier(supplier)
|
||||||
|
.MPE(MPE)
|
||||||
|
.US_EN(US_EN)
|
||||||
|
.SIM_CN(SIM_CN)
|
||||||
|
.TRA_CN(TRA_CN)
|
||||||
|
.isDefault(isDefault)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of supplier lang key value.
|
||||||
|
*
|
||||||
|
* @param supplier the supplier
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @param LZH the lzh
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@Contract(value = "_, _, _, _, _, _ -> new", pure = true)
|
||||||
|
public static @NotNull LangKeyValue ofSupplier(Supplier<?> supplier, ModPartEnum MPE,
|
||||||
|
String US_EN, String SIM_CN, String TRA_CN, String LZH) {
|
||||||
|
return builder()
|
||||||
|
.supplier(supplier)
|
||||||
|
.MPE(MPE)
|
||||||
|
.US_EN(US_EN)
|
||||||
|
.SIM_CN(SIM_CN)
|
||||||
|
.TRA_CN(TRA_CN)
|
||||||
|
.LZH(LZH)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of supplier lang key value.
|
||||||
|
*
|
||||||
|
* @param supplier the supplier
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @param LZH the lzh
|
||||||
|
* @param isDefault the is default
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@Contract(value = "_, _, _, _, _, _, _ -> new", pure = true)
|
||||||
|
public static @NotNull LangKeyValue ofSupplier(Supplier<?> supplier, ModPartEnum MPE,
|
||||||
|
String US_EN, String SIM_CN, String TRA_CN, String LZH, boolean isDefault) {
|
||||||
|
return builder()
|
||||||
|
.supplier(supplier)
|
||||||
|
.MPE(MPE)
|
||||||
|
.US_EN(US_EN)
|
||||||
|
.SIM_CN(SIM_CN)
|
||||||
|
.TRA_CN(TRA_CN)
|
||||||
|
.LZH(LZH)
|
||||||
|
.isDefault(isDefault)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of key lang key value.
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@Contract(value = "_, _, _, _, _ -> new", pure = true)
|
||||||
|
public static @NotNull LangKeyValue ofKey(String key, ModPartEnum MPE,
|
||||||
|
String US_EN, String SIM_CN, String TRA_CN) {
|
||||||
|
return builder()
|
||||||
|
.key(key)
|
||||||
|
.MPE(MPE)
|
||||||
|
.US_EN(US_EN)
|
||||||
|
.SIM_CN(SIM_CN)
|
||||||
|
.TRA_CN(TRA_CN)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of key lang key value.
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @param isDefault the is default
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@Contract(value = "_, _, _, _, _, _ -> new", pure = true)
|
||||||
|
public static @NotNull LangKeyValue ofKey(String key, ModPartEnum MPE,
|
||||||
|
String US_EN, String SIM_CN, String TRA_CN, boolean isDefault) {
|
||||||
|
return builder()
|
||||||
|
.key(key)
|
||||||
|
.MPE(MPE)
|
||||||
|
.US_EN(US_EN)
|
||||||
|
.SIM_CN(SIM_CN)
|
||||||
|
.TRA_CN(TRA_CN)
|
||||||
|
.isDefault(isDefault)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of key lang key value.
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @param LZH the lzh
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@Contract(value = "_, _, _, _, _, _ -> new", pure = true)
|
||||||
|
public static @NotNull LangKeyValue ofKey(String key, ModPartEnum MPE,
|
||||||
|
String US_EN, String SIM_CN, String TRA_CN, String LZH) {
|
||||||
|
return builder()
|
||||||
|
.key(key)
|
||||||
|
.MPE(MPE)
|
||||||
|
.US_EN(US_EN)
|
||||||
|
.SIM_CN(SIM_CN)
|
||||||
|
.TRA_CN(TRA_CN)
|
||||||
|
.LZH(LZH)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of key lang key value.
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @param MPE the mpe
|
||||||
|
* @param US_EN the us en
|
||||||
|
* @param SIM_CN the sim cn
|
||||||
|
* @param TRA_CN the tra cn
|
||||||
|
* @param LZH the lzh
|
||||||
|
* @param isDefault the is default
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
@Contract(value = "_, _, _, _, _, _, _ -> new", pure = true)
|
||||||
|
public static @NotNull LangKeyValue ofKey(String key, ModPartEnum MPE,
|
||||||
|
String US_EN, String SIM_CN, String TRA_CN, String LZH, boolean isDefault) {
|
||||||
|
return builder()
|
||||||
|
.key(key)
|
||||||
|
.MPE(MPE)
|
||||||
|
.US_EN(US_EN)
|
||||||
|
.SIM_CN(SIM_CN)
|
||||||
|
.TRA_CN(TRA_CN)
|
||||||
|
.LZH(LZH)
|
||||||
|
.isDefault(isDefault)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy of lang key value.
|
||||||
|
*
|
||||||
|
* @param supplier the supplier
|
||||||
|
* @param modPartEnum the mod part enum
|
||||||
|
* @param other the other
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
public static @NotNull LangKeyValue copyOf(Supplier<?> supplier, ModPartEnum modPartEnum, @NotNull LangKeyValue other) {
|
||||||
|
return builder()
|
||||||
|
.supplier(supplier)
|
||||||
|
.MPE(modPartEnum)
|
||||||
|
.US_EN(other.US_EN)
|
||||||
|
.SIM_CN(other.SIM_CN)
|
||||||
|
.TRA_CN(other.TRA_CN)
|
||||||
|
.LZH(other.LZH)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy of lang key value.
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @param modPartEnum the mod part enum
|
||||||
|
* @param other the other
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
public static @NotNull LangKeyValue copyOf(String key, ModPartEnum modPartEnum, @NotNull LangKeyValue other) {
|
||||||
|
return builder()
|
||||||
|
.key(key)
|
||||||
|
.MPE(modPartEnum)
|
||||||
|
.US_EN(other.US_EN)
|
||||||
|
.SIM_CN(other.SIM_CN)
|
||||||
|
.TRA_CN(other.TRA_CN)
|
||||||
|
.LZH(other.LZH)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy of lang key value.
|
||||||
|
*
|
||||||
|
* @param supplier the supplier
|
||||||
|
* @param modPartEnum the mod part enum
|
||||||
|
* @param other the other
|
||||||
|
* @param isDefault the is default
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
public static @NotNull LangKeyValue copyOf(Supplier<?> supplier, ModPartEnum modPartEnum, @NotNull LangKeyValue other, boolean isDefault) {
|
||||||
|
return builder()
|
||||||
|
.supplier(supplier)
|
||||||
|
.MPE(modPartEnum)
|
||||||
|
.US_EN(other.US_EN)
|
||||||
|
.SIM_CN(other.SIM_CN)
|
||||||
|
.TRA_CN(other.TRA_CN)
|
||||||
|
.LZH(other.LZH)
|
||||||
|
.isDefault(isDefault)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy of lang key value.
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @param modPartEnum the mod part enum
|
||||||
|
* @param other the other
|
||||||
|
* @param isDefault the is default
|
||||||
|
* @return the lang key value
|
||||||
|
*/
|
||||||
|
public static @NotNull LangKeyValue copyOf(String key, ModPartEnum modPartEnum, @NotNull LangKeyValue other, boolean isDefault) {
|
||||||
|
return builder()
|
||||||
|
.key(key)
|
||||||
|
.MPE(modPartEnum)
|
||||||
|
.US_EN(other.US_EN)
|
||||||
|
.SIM_CN(other.SIM_CN)
|
||||||
|
.TRA_CN(other.TRA_CN)
|
||||||
|
.LZH(other.LZH)
|
||||||
|
.isDefault(isDefault)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKey() {
|
||||||
|
return Objects.requireNonNullElseGet(key, () -> switch (MPE) {
|
||||||
|
case ITEM -> getItem().getDescriptionId();
|
||||||
|
case BLOCK -> getBlock().getDescriptionId();
|
||||||
|
default ->
|
||||||
|
throw new UnsupportedOperationException("The Key value is NULL! Please use the correct constructor and write the parameters correctly");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLang(@NotNull McLocale locale) {
|
||||||
|
return switch (locale) {
|
||||||
|
case EN_US, JA_JP, KO_KR, RU_RU, DE_DE, ES_ES, FR_FR -> US_EN;
|
||||||
|
case ZH_CN -> SIM_CN;
|
||||||
|
case ZH_TW -> TRA_CN;
|
||||||
|
case LZH -> LZH != null ? LZH : TRA_CN; // Fallback to TRA_CN if LZH is null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets supplier.
|
||||||
|
*
|
||||||
|
* @return the supplier
|
||||||
|
*/
|
||||||
|
// Getters for all fields
|
||||||
|
public Supplier<?> getSupplier() { return supplier; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets us en.
|
||||||
|
*
|
||||||
|
* @return the us en
|
||||||
|
*/
|
||||||
|
public String getUS_EN() { return US_EN; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets sim cn.
|
||||||
|
*
|
||||||
|
* @return the sim cn
|
||||||
|
*/
|
||||||
|
public String getSIM_CN() { return SIM_CN; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets tra cn.
|
||||||
|
*
|
||||||
|
* @return the tra cn
|
||||||
|
*/
|
||||||
|
public String getTRA_CN() { return TRA_CN; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets lzh.
|
||||||
|
*
|
||||||
|
* @return the lzh
|
||||||
|
*/
|
||||||
|
public String getLZH() { return LZH; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is default boolean.
|
||||||
|
*
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
public Boolean isDefault() { return Default; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets mpe.
|
||||||
|
*
|
||||||
|
* @return the mpe
|
||||||
|
*/
|
||||||
|
public ModPartEnum getMPE() { return MPE; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets item.
|
||||||
|
*
|
||||||
|
* @return the item
|
||||||
|
* @throws IllegalArgumentException the illegal argument exception
|
||||||
|
*/
|
||||||
|
public Item getItem() throws IllegalArgumentException {
|
||||||
|
if(MPE == ModPartEnum.ITEM) {
|
||||||
|
return (Item) supplier.get();
|
||||||
|
}
|
||||||
|
else throw new IllegalCallerException("Target's MPE is not ModPartEnum#ITEM.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets block.
|
||||||
|
*
|
||||||
|
* @return the block
|
||||||
|
* @throws IllegalArgumentException the illegal argument exception
|
||||||
|
*/
|
||||||
|
public Block getBlock() throws IllegalArgumentException {
|
||||||
|
if(MPE == ModPartEnum.BLOCK) {
|
||||||
|
return (Block) supplier.get();
|
||||||
|
}
|
||||||
|
else throw new IllegalCallerException("Target's MPE is not ModPartEnum#BLOCK.");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "LangKeyValue{" +
|
||||||
|
"key='" + key + '\'' +
|
||||||
|
", US_EN='" + US_EN + '\'' +
|
||||||
|
", SIM_CN='" + SIM_CN + '\'' +
|
||||||
|
", MPE=" + MPE +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package top.r3944realms.lib39.datagen.value;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The enum Mc locale.
|
||||||
|
*/
|
||||||
|
public enum McLocale implements ILocaleEntry {
|
||||||
|
/**
|
||||||
|
* En us mc locale.
|
||||||
|
*/
|
||||||
|
EN_US("en_us", Locale.US),
|
||||||
|
/**
|
||||||
|
* Zh cn mc locale.
|
||||||
|
*/
|
||||||
|
ZH_CN("zh_cn", Locale.SIMPLIFIED_CHINESE),
|
||||||
|
/**
|
||||||
|
* Zh tw mc locale.
|
||||||
|
*/
|
||||||
|
ZH_TW("zh_tw", Locale.TRADITIONAL_CHINESE),
|
||||||
|
/**
|
||||||
|
* The Lzh.
|
||||||
|
*/
|
||||||
|
LZH("lzh", new Locale("lzh", "ZH")),
|
||||||
|
/**
|
||||||
|
* Ja jp mc locale.
|
||||||
|
*/
|
||||||
|
JA_JP("ja_jp", Locale.JAPAN),
|
||||||
|
/**
|
||||||
|
* Ko kr mc locale.
|
||||||
|
*/
|
||||||
|
KO_KR("ko_kr", Locale.KOREA),
|
||||||
|
/**
|
||||||
|
* The Ru ru.
|
||||||
|
*/
|
||||||
|
RU_RU("ru_ru", new Locale("ru", "RU")),
|
||||||
|
/**
|
||||||
|
* Fr fr mc locale.
|
||||||
|
*/
|
||||||
|
FR_FR("fr_fr", Locale.FRANCE),
|
||||||
|
/**
|
||||||
|
* De de mc locale.
|
||||||
|
*/
|
||||||
|
DE_DE("de_de", Locale.GERMANY),
|
||||||
|
/**
|
||||||
|
* The Es es.
|
||||||
|
*/
|
||||||
|
ES_ES("es_es", new Locale("es", "ES"));
|
||||||
|
|
||||||
|
private final String mcCode;
|
||||||
|
private final Locale javaLocale;
|
||||||
|
|
||||||
|
McLocale(String mcCode, Locale javaLocale) {
|
||||||
|
this.mcCode = mcCode;
|
||||||
|
this.javaLocale = javaLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String mcCode() { return mcCode; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Locale javaLocale() { return javaLocale; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
package top.r3944realms.lib39.datagen.value;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模组各部分的类型枚举,用于数据生成与分类。
|
||||||
|
*/
|
||||||
|
public enum ModPartEnum {
|
||||||
|
/**
|
||||||
|
* 默认/未指定类型
|
||||||
|
*/
|
||||||
|
DEFAULT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物品
|
||||||
|
*/
|
||||||
|
ITEM(Item.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方块
|
||||||
|
*/
|
||||||
|
BLOCK(Block.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附魔
|
||||||
|
*/
|
||||||
|
ENCHANTMENT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进度标题
|
||||||
|
*/
|
||||||
|
ADVANCEMENT_TITLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就描述
|
||||||
|
*/
|
||||||
|
ADVANCEMENT_DESCRIPTION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创造模式物品栏
|
||||||
|
*/
|
||||||
|
CREATIVE_TAB,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置项
|
||||||
|
*/
|
||||||
|
CONFIG,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体(生物、载具等)
|
||||||
|
*/
|
||||||
|
ENTITY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图形界面
|
||||||
|
*/
|
||||||
|
GUI,
|
||||||
|
/**
|
||||||
|
* 容器
|
||||||
|
*/
|
||||||
|
CONTAINER,
|
||||||
|
/**
|
||||||
|
* 画作描述
|
||||||
|
*/
|
||||||
|
PAINTING_TITLE,
|
||||||
|
/**
|
||||||
|
* 画作作者
|
||||||
|
*/
|
||||||
|
PAINTING_AUTHOR,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题
|
||||||
|
*/
|
||||||
|
TITLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
NAME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 游戏规则(/gamerule)
|
||||||
|
*/
|
||||||
|
GAME_RULE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述文本
|
||||||
|
*/
|
||||||
|
DESCRIPTION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一般信息
|
||||||
|
*/
|
||||||
|
INFO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息(聊天、提示等)
|
||||||
|
*/
|
||||||
|
MESSAGE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生物群系
|
||||||
|
*/
|
||||||
|
BIOME,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令
|
||||||
|
*/
|
||||||
|
COMMAND,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 声音资源
|
||||||
|
*/
|
||||||
|
SOUND;
|
||||||
|
;
|
||||||
|
@Nullable
|
||||||
|
private final Class<?> clazz;
|
||||||
|
ModPartEnum() {
|
||||||
|
clazz = null;
|
||||||
|
}
|
||||||
|
ModPartEnum(@Nullable Class<?> clazz) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets full key.
|
||||||
|
*
|
||||||
|
* @param modId the mod id
|
||||||
|
* @param name the name
|
||||||
|
* @return the full key
|
||||||
|
*/
|
||||||
|
@Contract(pure = true)
|
||||||
|
public @NotNull String getFullKey(String modId, String name) {
|
||||||
|
return switch (this) {
|
||||||
|
case ITEM -> "item." + modId + "." + name;
|
||||||
|
case BLOCK -> "block." + modId + "." + name;
|
||||||
|
case ENCHANTMENT -> "enchantment." + modId + "." + name;
|
||||||
|
case ADVANCEMENT_TITLE -> "advancement." + modId + "." + name + ".title";
|
||||||
|
case ADVANCEMENT_DESCRIPTION -> "advancement." + modId + "." + name + ".description";
|
||||||
|
case CREATIVE_TAB -> "creativetab." + modId + "." + name;
|
||||||
|
case BIOME -> "biome." + modId + "." + name;
|
||||||
|
case CONFIG -> "config." + modId + "." + name;
|
||||||
|
case ENTITY -> "entity." + modId + "." + name;
|
||||||
|
case GUI -> "gui." + modId + "." + name;
|
||||||
|
case CONTAINER -> "container." + modId + "." + name;
|
||||||
|
case PAINTING_AUTHOR -> "painting." + modId + "." + name + ".author";
|
||||||
|
case PAINTING_TITLE -> "painting." + modId + "." + name + ".title";
|
||||||
|
case TITLE -> "title." + modId + "." + name;
|
||||||
|
case NAME -> "name." + modId + "." + name;
|
||||||
|
case GAME_RULE -> "gamerule."+ modId + "." + name;
|
||||||
|
case DESCRIPTION -> "description." + modId + "." + name;
|
||||||
|
case INFO -> "info." + modId + "." + name;
|
||||||
|
case MESSAGE -> "message." + modId + "." + name;
|
||||||
|
case COMMAND -> "command." + modId + "." + name;
|
||||||
|
case SOUND -> "sound." + modId + "." + name;
|
||||||
|
default -> modId + "." + name;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets clazz.
|
||||||
|
*
|
||||||
|
* @return the clazz
|
||||||
|
*/
|
||||||
|
public @Nullable Class<?> getClazz() {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package top.r3944realms.lib39.mixin.carryon;
|
||||||
|
|
||||||
|
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.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Pseudo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
import top.r3944realms.lib39.content.item.DollItem;
|
||||||
|
import top.r3944realms.lib39.util.GameProfileHelper;
|
||||||
|
import tschipp.carryon.client.render.CarriedObjectRender;
|
||||||
|
import tschipp.carryon.common.carry.CarryOnDataManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Mixin carried object render.
|
||||||
|
*/
|
||||||
|
@Pseudo
|
||||||
|
@Mixin(value = CarriedObjectRender.class, remap = false)
|
||||||
|
public class MixinCarriedObjectRender {
|
||||||
|
@ModifyVariable(
|
||||||
|
method = "drawFirstPersonBlock",
|
||||||
|
at = @At(
|
||||||
|
value = "LOAD",
|
||||||
|
target = "Ltschipp/carryon/client/render/CarryRenderHelper;renderBakedModel(Lnet/minecraft/world/item/ItemStack;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/client/resources/model/BakedModel;)V"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private static ItemStack warpDollItem$1(ItemStack stack, @Local(ordinal = 0, argsOnly = true) Player player) {
|
||||||
|
if (stack.getItem() instanceof DollItem) {
|
||||||
|
CompoundTag compound = CarryOnDataManager.getCarryData(player).getNbt().getCompound("tile").getCompound(GameProfileHelper.TAG_OWN_PROFILE);
|
||||||
|
stack.getOrCreateTag().put(GameProfileHelper.TAG_OWN_PROFILE, compound);
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
@ModifyVariable(
|
||||||
|
method = "drawThirdPerson",
|
||||||
|
at = @At(
|
||||||
|
value = "LOAD",
|
||||||
|
target = "Ltschipp/carryon/client/render/CarryRenderHelper;renderBakedModel(Lnet/minecraft/world/item/ItemStack;Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/client/resources/model/BakedModel;)V"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private static ItemStack warpDollItem$2(ItemStack stack, @Local(ordinal = 0) Player player) {
|
||||||
|
if (stack.getItem() instanceof DollItem) {
|
||||||
|
CompoundTag compound = CarryOnDataManager.getCarryData(player).getNbt().getCompound("tile").getCompound(GameProfileHelper.TAG_OWN_PROFILE);
|
||||||
|
stack.getOrCreateTag().put(GameProfileHelper.TAG_OWN_PROFILE, compound);
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package top.r3944realms.lib39.platform;
|
||||||
|
|
||||||
|
import top.r3944realms.lib39.Lib39;
|
||||||
|
import top.r3944realms.lib39.platform.services.IPlatformHelper;
|
||||||
|
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isClientEnvironment();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets mod version.
|
||||||
|
*
|
||||||
|
* @return the mod version
|
||||||
|
*/
|
||||||
|
String getModVersion();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,342 @@
|
||||||
|
package top.r3944realms.lib39.util;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.authlib.properties.Property;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.multiplayer.PlayerInfo;
|
||||||
|
import net.minecraft.client.player.AbstractClientPlayer;
|
||||||
|
import net.minecraft.client.resources.DefaultPlayerSkin;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.NbtUtils;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import top.r3944realms.lib39.util.nbt.NBTReader;
|
||||||
|
import top.r3944realms.lib39.util.nbt.NBTWriter;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type GameProfile helper.
|
||||||
|
*/
|
||||||
|
public class GameProfileHelper {
|
||||||
|
/**
|
||||||
|
* Client Only Class
|
||||||
|
*/
|
||||||
|
public static class ClientOpt implements IClientOnly {
|
||||||
|
public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) {
|
||||||
|
return IClientOnly.check(() ->
|
||||||
|
Minecraft.getInstance().getSkinManager()
|
||||||
|
.getInsecureSkinLocation(gameProfile));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) {
|
||||||
|
return IClientOnly.check(() -> {
|
||||||
|
if (gameProfile == null) {
|
||||||
|
return DefaultPlayerSkin.getDefaultSkin();
|
||||||
|
}
|
||||||
|
return resolveSkinTexture(gameProfile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public static final String TAG_BE = "BlockEntityTag";
|
||||||
|
/**
|
||||||
|
* The constant TAG_OWN_PROFILE.
|
||||||
|
*/
|
||||||
|
public static final String TAG_OWN_PROFILE = "OwnerProfile";
|
||||||
|
|
||||||
|
public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) {
|
||||||
|
return ClientOpt.getSkinTexture(gameProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) {
|
||||||
|
return ClientOpt.resolveSkinTexture(gameProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has slim arms boolean.
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
public static boolean hasSlimArms(@NotNull Player player) {
|
||||||
|
if (player.level().isClientSide) {
|
||||||
|
return hasSlimArmsClient(player);
|
||||||
|
} else {
|
||||||
|
return hasSlimArmsServer(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean hasSlimArmsClient(Player player) {
|
||||||
|
return ClientOpt.hasSlimArmsClient(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务器端判断
|
||||||
|
private static boolean hasSlimArmsServer(@NotNull Player player) {
|
||||||
|
GameProfile profile = player.getGameProfile();
|
||||||
|
for (Property property : profile.getProperties().get("textures")) {
|
||||||
|
try {
|
||||||
|
String json = new String(Base64.getDecoder().decode(property.getValue()));
|
||||||
|
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
|
||||||
|
JsonObject textures = obj.getAsJsonObject("textures");
|
||||||
|
JsonObject skin = textures.getAsJsonObject("SKIN");
|
||||||
|
|
||||||
|
if (skin.has("metadata")) {
|
||||||
|
JsonObject metadata = skin.getAsJsonObject("metadata");
|
||||||
|
if (metadata.has("model")) {
|
||||||
|
return "slim".equals(metadata.get("model").getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 解析失败,使用默认
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets skin model name.
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
* @return the skin model name
|
||||||
|
*/
|
||||||
|
public static @NotNull String getSkinModelName(@NotNull Player player) {
|
||||||
|
return ClientOpt.getSkinModelName(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断玩家是否为纤细手臂(Alex模型)
|
||||||
|
*
|
||||||
|
* @param profile 玩家的GameProfile
|
||||||
|
* @return true =纤细手臂,false=正常手臂
|
||||||
|
*/
|
||||||
|
public static boolean isSlimArms(GameProfile profile) {
|
||||||
|
if (profile == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取textures属性
|
||||||
|
Collection<Property> textures = profile.getProperties().get("textures");
|
||||||
|
if (textures.isEmpty()) {
|
||||||
|
return false; // 没有皮肤数据,使用默认
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取第一个texture属性(通常是皮肤)
|
||||||
|
Property textureProperty = textures.iterator().next();
|
||||||
|
String value = textureProperty.getValue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return isSlimFromTextureData(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 解析失败,使用默认
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Base64编码的皮肤数据判断
|
||||||
|
* @param encodedTexture Base64编码的皮肤数据
|
||||||
|
* @return true=纤细手臂
|
||||||
|
*/
|
||||||
|
private static boolean isSlimFromTextureData(String encodedTexture) {
|
||||||
|
if (encodedTexture == null || encodedTexture.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Base64解码
|
||||||
|
byte[] decodedBytes = Base64.getDecoder().decode(encodedTexture);
|
||||||
|
String jsonString = new String(decodedBytes, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
// 2. 解析JSON
|
||||||
|
JsonObject root = JsonParser.parseString(jsonString).getAsJsonObject();
|
||||||
|
|
||||||
|
// 3. 导航到textures -> SKIN
|
||||||
|
JsonObject textures = root.getAsJsonObject("textures");
|
||||||
|
if (textures == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject skin = textures.getAsJsonObject("SKIN");
|
||||||
|
if (skin == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 检查metadata -> model
|
||||||
|
JsonObject metadata = skin.getAsJsonObject("metadata");
|
||||||
|
if (metadata == null) {
|
||||||
|
return false; // 没有metadata,使用默认
|
||||||
|
}
|
||||||
|
|
||||||
|
String model = metadata.get("model").getAsString();
|
||||||
|
return "slim".equals(model);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 解析过程中出现任何错误,返回默认值
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取皮肤模型名称
|
||||||
|
*
|
||||||
|
* @param profile GameProfile
|
||||||
|
* @return "slim" 或 "default"
|
||||||
|
*/
|
||||||
|
public static String getSkinModelName(GameProfile profile) {
|
||||||
|
if (profile == null) {
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<Property> textures = profile.getProperties().get("textures");
|
||||||
|
if (textures.isEmpty()) {
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
Property textureProperty = textures.iterator().next();
|
||||||
|
String value = textureProperty.getValue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] decodedBytes = Base64.getDecoder().decode(value);
|
||||||
|
String jsonString = new String(decodedBytes, StandardCharsets.UTF_8);
|
||||||
|
JsonObject root = JsonParser.parseString(jsonString).getAsJsonObject();
|
||||||
|
JsonObject texturesObj = root.getAsJsonObject("textures");
|
||||||
|
JsonObject skin = texturesObj.getAsJsonObject("SKIN");
|
||||||
|
|
||||||
|
if (skin.has("metadata")) {
|
||||||
|
JsonObject metadata = skin.getAsJsonObject("metadata");
|
||||||
|
if (metadata.has("model")) {
|
||||||
|
return metadata.get("model").getAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略错误
|
||||||
|
}
|
||||||
|
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从ItemStack的NBT中读取GameProfile
|
||||||
|
*
|
||||||
|
* @param stack the stack
|
||||||
|
* @return the profile from item stack
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static GameProfile getProfileFromItemStack(ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AtomicReference<GameProfile> profileRef = new AtomicReference<>();
|
||||||
|
// 检查方块实体数据
|
||||||
|
NBTReader.of(tag)
|
||||||
|
.compound(TAG_BE, compoundTag ->
|
||||||
|
NBTReader.of(compoundTag)
|
||||||
|
.compound("OwnerProfile", ct -> profileRef.set(NbtUtils.readGameProfile(ct)))
|
||||||
|
)
|
||||||
|
.compound("OwnerProfile", ct -> {
|
||||||
|
if (profileRef.get() == null) { //兼容写法
|
||||||
|
profileRef.set(NbtUtils.readGameProfile(ct));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return profileRef.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将GameProfile保存到ItemStack的NBT
|
||||||
|
*
|
||||||
|
* @param stack the stack
|
||||||
|
* @param profile the profile
|
||||||
|
*/
|
||||||
|
public static void saveProfileToItemStack(@NotNull ItemStack stack, @Nullable GameProfile profile) {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag tag = stack.getOrCreateTag();
|
||||||
|
|
||||||
|
if (profile == null) {
|
||||||
|
// 移除现有数据
|
||||||
|
NBTReader.of(tag)
|
||||||
|
.compound(TAG_BE, ct -> tag.remove(TAG_OWN_PROFILE));
|
||||||
|
tag.remove(TAG_BE);
|
||||||
|
tag.remove(TAG_OWN_PROFILE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建方块实体数据
|
||||||
|
NBTWriter.of(tag)
|
||||||
|
.compound(TAG_BE, writer ->
|
||||||
|
writer
|
||||||
|
.compound(TAG_OWN_PROFILE, NbtUtils.writeGameProfile(new CompoundTag(), profile))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查ItemStack是否有保存的皮肤数据
|
||||||
|
*
|
||||||
|
* @param stack the stack
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
public static boolean hasProfileData(@NotNull ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag.contains(TAG_BE)) {
|
||||||
|
CompoundTag blockEntityTag = tag.getCompound(TAG_BE);
|
||||||
|
return blockEntityTag.contains(TAG_OWN_PROFILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag.contains(TAG_OWN_PROFILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package top.r3944realms.lib39.util;
|
||||||
|
|
||||||
|
import top.r3944realms.lib39.Lib39;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public interface IClientOnly {
|
||||||
|
static void check(Runnable runnable) {
|
||||||
|
if (Lib39.isClientEnvironment()) {
|
||||||
|
runnable.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("This method should be called in ClientEnvironment");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check(Runnable runnable, Runnable fallback) {
|
||||||
|
if (Lib39.isClientEnvironment()) {
|
||||||
|
runnable.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fallback.run();
|
||||||
|
}
|
||||||
|
static <T> T check(Supplier<T> supplier) {
|
||||||
|
if (Lib39.isClientEnvironment()) {
|
||||||
|
return supplier.get();
|
||||||
|
}
|
||||||
|
throw new RuntimeException("This method should be called in ClientEnvironment");
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> T check(Supplier<T> supplier, Supplier<T> fallback) {
|
||||||
|
if (Lib39.isClientEnvironment()) {
|
||||||
|
return supplier.get();
|
||||||
|
}
|
||||||
|
return fallback.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package top.r3944realms.lib39.util;
|
package top.r3944realms.lib39.util;
|
||||||
|
|
||||||
import net.minecraft.world.level.Level;
|
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.ApiStatus;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
|
@ -66,7 +64,6 @@ public interface ILevelHelper {
|
||||||
* @return the client level
|
* @return the client level
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@OnlyIn(Dist.CLIENT)
|
|
||||||
static Level getClientLevel() {
|
static Level getClientLevel() {
|
||||||
return LevelHelper.CLIENT.getLevel();
|
return LevelHelper.CLIENT.getLevel();
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
package top.r3944realms.lib39.util.block;
|
||||||
|
|
||||||
|
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 org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Block registry builder.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
||||||
|
public abstract class BlockRegistryBuilder {
|
||||||
|
private String registryName;
|
||||||
|
private Supplier<Block> blockObject;
|
||||||
|
private BiFunction<String, Supplier<Item>, Supplier<Item>> blockItemRegister;
|
||||||
|
private boolean needBuildItem;
|
||||||
|
private Item.Properties properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新的构建器实例
|
||||||
|
*
|
||||||
|
* @return the block registry builder
|
||||||
|
*/
|
||||||
|
@Contract(value = " -> new", pure = true)
|
||||||
|
public static @NotNull BlockRegistryBuilder create() {
|
||||||
|
return new BlockRegistryBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置注册名称
|
||||||
|
*
|
||||||
|
* @param name the name
|
||||||
|
* @return the block registry builder
|
||||||
|
*/
|
||||||
|
public BlockRegistryBuilder withName(String name) {
|
||||||
|
this.registryName = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册方块(不自动注册物品)
|
||||||
|
*
|
||||||
|
* @param blockRegister the block register
|
||||||
|
* @param blockSupplier the block supplier
|
||||||
|
* @return the block registry builder
|
||||||
|
*/
|
||||||
|
public BlockRegistryBuilder registerBlock(@NotNull BiFunction<String, Supplier<Block>,Supplier<Block>> blockRegister, Supplier<Block> blockSupplier) {
|
||||||
|
this.blockObject = blockRegister.apply(this.registryName, blockSupplier);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册对应的方块物品
|
||||||
|
*
|
||||||
|
* @param itemRegister the item deferred register
|
||||||
|
* @return the block registry builder
|
||||||
|
*/
|
||||||
|
public BlockRegistryBuilder registerItem(BiFunction<String, Supplier<Item>,Supplier<Item>> itemRegister) {
|
||||||
|
this.blockItemRegister = itemRegister;
|
||||||
|
needBuildItem = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册对应的方块带有对应属性物品
|
||||||
|
*
|
||||||
|
* @param itemRegister the item deferred register
|
||||||
|
* @param properties the item properties
|
||||||
|
* @return the block registry builder
|
||||||
|
*/
|
||||||
|
public BlockRegistryBuilder registerItemWithProperties(BiFunction<String, Supplier<Item>,Supplier<Item>> itemRegister, Item.Properties properties) {
|
||||||
|
this.blockItemRegister = itemRegister;
|
||||||
|
this.properties = properties;
|
||||||
|
needBuildItem = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应的方块物品属性
|
||||||
|
*
|
||||||
|
* @param properties the item properties
|
||||||
|
* @return the block registry builder
|
||||||
|
*/
|
||||||
|
public BlockRegistryBuilder ItemProperties(Item.Properties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部方法:注册对应的方块物品
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
private void registerBlockItem(Supplier<Block> blockObject, ResourceKey<CreativeModeTab>... creativeTabs) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册方块和物品到建筑标签页
|
||||||
|
*
|
||||||
|
* @param blockRegister the block register
|
||||||
|
* @param blockSupplier the block supplier
|
||||||
|
* @return the block registry builder
|
||||||
|
*/
|
||||||
|
public BlockRegistryBuilder registerWithBuildingTab(BiFunction<String, Supplier<Block>,Supplier<Block>> blockRegister, Supplier<Block> blockSupplier) {
|
||||||
|
registerBlock(blockRegister, blockSupplier);
|
||||||
|
registerBlockItem(this.blockObject, CreativeModeTabs.BUILDING_BLOCKS);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册方块和物品到功能标签页
|
||||||
|
*
|
||||||
|
* @param blockRegister the block register
|
||||||
|
* @param blockSupplier the block supplier
|
||||||
|
* @return the block registry builder
|
||||||
|
*/
|
||||||
|
public BlockRegistryBuilder registerWithFunctionalTab(BiFunction<String, Supplier<Block>,Supplier<Block>> blockRegister, Supplier<Block> blockSupplier) {
|
||||||
|
registerBlock(blockRegister, blockSupplier);
|
||||||
|
registerBlockItem(this.blockObject, CreativeModeTabs.FUNCTIONAL_BLOCKS);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注册的方块对象
|
||||||
|
*
|
||||||
|
* @return the registry object
|
||||||
|
*/
|
||||||
|
public Supplier<Block> build() {
|
||||||
|
if (needBuildItem) {
|
||||||
|
blockItemRegister.apply(this.registryName, () -> new BlockItem(this.blockObject.get(), properties == null ? new Item.Properties() : properties));
|
||||||
|
}
|
||||||
|
return this.blockObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ public abstract class EntityListResolve {
|
||||||
/**
|
/**
|
||||||
* The Result.
|
* The Result.
|
||||||
*/
|
*/
|
||||||
protected EntityListResolve.EntityResolveResult result;
|
protected EntityResolveResult result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type Entity resolve result.
|
* The type Entity resolve result.
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"format_version": "1.9.0",
|
||||||
|
"credit": "3D Model © 2025 LeisureTimeDock",
|
||||||
|
"parent": "builtin/entity",
|
||||||
|
"textures": {
|
||||||
|
"particle": "minecraft:block/white_wool"
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"thirdperson_righthand": {
|
||||||
|
"rotation": [75, 45, 0],
|
||||||
|
"translation": [0, 2.5, 0],
|
||||||
|
"scale": [0.375, 0.375, 0.375]
|
||||||
|
},
|
||||||
|
"thirdperson_lefthand": {
|
||||||
|
"rotation": [75, 45, 0],
|
||||||
|
"translation": [0, 2.5, 0],
|
||||||
|
"scale": [0.375, 0.375, 0.375]
|
||||||
|
},
|
||||||
|
"firstperson_righthand": {
|
||||||
|
"rotation": [0, 124, 0],
|
||||||
|
"translation": [2, 3, 0],
|
||||||
|
"scale": [0.4, 0.4, 0.4]
|
||||||
|
},
|
||||||
|
"firstperson_lefthand": {
|
||||||
|
"rotation": [0, 120, 0],
|
||||||
|
"translation": [1.5, 2.75, 0],
|
||||||
|
"scale": [0.4, 0.4, 0.4]
|
||||||
|
},
|
||||||
|
"ground": {
|
||||||
|
"translation": [0, 2, 0],
|
||||||
|
"scale": [0.5, 0.5, 0.5]
|
||||||
|
},
|
||||||
|
"gui": {
|
||||||
|
"rotation": [30, -135, 0],
|
||||||
|
"translation": [0.75, -1, 0],
|
||||||
|
"scale": [0.625, 0.625, 0.625]
|
||||||
|
},
|
||||||
|
"head": {
|
||||||
|
"translation": [0, 14, -0.75]
|
||||||
|
},
|
||||||
|
"fixed": {
|
||||||
|
"translation": [0, 0, -2.75],
|
||||||
|
"scale": [0.5, 0.5, 0.5]
|
||||||
|
},
|
||||||
|
"on_shelf": {
|
||||||
|
"rotation": [0, -180, 0],
|
||||||
|
"translation": [0, 0, 5.25]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
common/src/main/resources/assets/lib39/shaders/core/ring.fsh
Normal file
49
common/src/main/resources/assets/lib39/shaders/core/ring.fsh
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#version 150
|
||||||
|
|
||||||
|
in vec4 vertexColor;
|
||||||
|
|
||||||
|
uniform vec4 ColorModulator;
|
||||||
|
uniform vec2 Center;
|
||||||
|
uniform float InnerRadius;
|
||||||
|
uniform float OuterRadius;
|
||||||
|
uniform float AntiAliasing;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float dist = distance(gl_FragCoord.xy, Center);
|
||||||
|
float alpha = 0.0;
|
||||||
|
|
||||||
|
// 确保内外半径合理
|
||||||
|
if (OuterRadius <= InnerRadius) {
|
||||||
|
vec4 color = vertexColor;
|
||||||
|
color.a = 0;
|
||||||
|
fragColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算环形 alpha
|
||||||
|
if (dist >= InnerRadius && dist <= OuterRadius) {
|
||||||
|
alpha = 1.0;
|
||||||
|
|
||||||
|
// 内边缘抗锯齿
|
||||||
|
if (dist < InnerRadius + AntiAliasing) {
|
||||||
|
float fade = (dist - InnerRadius) / AntiAliasing;
|
||||||
|
alpha *= fade;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外边缘抗锯齿
|
||||||
|
if (dist > OuterRadius - AntiAliasing) {
|
||||||
|
float fade = 1.0 - (dist - (OuterRadius - AntiAliasing)) / AntiAliasing;
|
||||||
|
alpha *= fade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 color = vertexColor;
|
||||||
|
color.a *= alpha;
|
||||||
|
|
||||||
|
if (alpha > 0.0) {
|
||||||
|
fragColor = color * ColorModulator;
|
||||||
|
} else {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"vertex": "position_color",
|
||||||
|
"fragment": "lib39:ring",
|
||||||
|
"samplers": [
|
||||||
|
],
|
||||||
|
"uniforms": [
|
||||||
|
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||||
|
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||||
|
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
|
||||||
|
{
|
||||||
|
"name": "Center",
|
||||||
|
"type": "float",
|
||||||
|
"count": 2,
|
||||||
|
"values": [
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "InnerRadius",
|
||||||
|
"type": "float",
|
||||||
|
"count": 1,
|
||||||
|
"values": [
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OuterRadius",
|
||||||
|
"type": "float",
|
||||||
|
"count": 1,
|
||||||
|
"values": [
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AntiAliasing",
|
||||||
|
"type": "float",
|
||||||
|
"count": 1,
|
||||||
|
"values": [
|
||||||
|
1.25
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#version 150
|
||||||
|
|
||||||
|
in vec4 vertexColor;
|
||||||
|
|
||||||
|
uniform vec4 ColorModulator;
|
||||||
|
uniform vec2 FramebufferSize;
|
||||||
|
uniform vec2 Center;
|
||||||
|
uniform float Radius;
|
||||||
|
uniform float AntiAliasingRadius;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 fragPos = vec2(gl_FragCoord.x, FramebufferSize.y - gl_FragCoord.y);
|
||||||
|
float distance = distance(fragPos, Center);
|
||||||
|
vec4 color = vec4(0, 0, 0, 0);
|
||||||
|
if (distance <= Radius) {
|
||||||
|
color = vertexColor;
|
||||||
|
color.a = smoothstep(Radius, 0.0, distance) * vertexColor.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor = color * ColorModulator;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"vertex": "position_color",
|
||||||
|
"fragment": "lib39:selection",
|
||||||
|
"samplers": [
|
||||||
|
],
|
||||||
|
"uniforms": [
|
||||||
|
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||||
|
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||||
|
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
|
||||||
|
{
|
||||||
|
"name": "Center",
|
||||||
|
"type": "float",
|
||||||
|
"count": 2,
|
||||||
|
"values": [
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FramebufferSize",
|
||||||
|
"type": "float",
|
||||||
|
"count": 2,
|
||||||
|
"values": [
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radius",
|
||||||
|
"type": "float",
|
||||||
|
"count": 1,
|
||||||
|
"values": [
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AntiAliasingRadius",
|
||||||
|
"type": "float",
|
||||||
|
"count": 1,
|
||||||
|
"values": [
|
||||||
|
1.5
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
common/src/main/resources/assets/lib39/sounds/duck_toy.ogg
Normal file
BIN
common/src/main/resources/assets/lib39/sounds/duck_toy.ogg
Normal file
Binary file not shown.
BIN
common/src/main/resources/assets/lib39/textures/item/fabric.png
Normal file
BIN
common/src/main/resources/assets/lib39/textures/item/fabric.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
common/src/main/resources/assets/lib39/textures/item/forge.png
Normal file
BIN
common/src/main/resources/assets/lib39/textures/item/forge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 280 B |
18
common/src/main/resources/lib39.mixins.json
Normal file
18
common/src/main/resources/lib39.mixins.json
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "top.r3944realms.lib39.mixin",
|
||||||
|
"refmap": "${mod_id}.refmap.json",
|
||||||
|
"compatibilityLevel": "JAVA_17",
|
||||||
|
"mixins": [
|
||||||
|
"carryon.MixinCarriedObjectRender"
|
||||||
|
],
|
||||||
|
"client": [
|
||||||
|
],
|
||||||
|
"server": [
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BIN
common/src/main/resources/lib39_logo.png
Normal file
BIN
common/src/main/resources/lib39_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 649 KiB |
6
common/src/main/resources/pack.mcmeta
Normal file
6
common/src/main/resources/pack.mcmeta
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"pack": {
|
||||||
|
"description": "${mod_name}",
|
||||||
|
"pack_format": 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# JNI 头文件生成配置
|
|
||||||
# 每行一个类全限定名,例如:
|
|
||||||
# com.example.MyNativeClass
|
|
||||||
# com.example.NativeUtils
|
|
||||||
top.r3944realms.lib39.core.lang.ClassEncryptor
|
|
||||||
top.r3944realms.lib39.core.lang.EncryptedClassLoader
|
|
||||||
# 或者使用正则表达式自动匹配:
|
|
||||||
# #auto:.*Native.*
|
|
||||||
|
|
@ -1,241 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.28)
|
|
||||||
project(ENCRYPTED_CPP VERSION 1.0.0)
|
|
||||||
|
|
||||||
# 设置C++标准
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
||||||
|
|
||||||
# ========== 目录设置 ==========
|
|
||||||
# 项目根目录
|
|
||||||
set(PROJECT_ROOT "${CMAKE_SOURCE_DIR}")
|
|
||||||
|
|
||||||
# 源代码目录 - 使用cpp-src目录
|
|
||||||
if(EXISTS "${PROJECT_ROOT}/cpp-src")
|
|
||||||
set(SOURCE_DIR "${PROJECT_ROOT}/cpp-src")
|
|
||||||
set(HEADER_DIR "${PROJECT_ROOT}/cpp-src")
|
|
||||||
message(STATUS "Using cpp-src directory: ${CPP_SRC_DIR}")
|
|
||||||
else()
|
|
||||||
# 如果没有cpp-src目录,使用当前目录
|
|
||||||
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
|
||||||
set(HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
|
||||||
message(STATUS "Using current directory as source directory")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ========== 手动设置Java路径 ==========
|
|
||||||
# 查找Java HOME
|
|
||||||
if(DEFINED ENV{JAVA_HOME})
|
|
||||||
set(JAVA_HOME $ENV{JAVA_HOME})
|
|
||||||
message(STATUS "Found JAVA_HOME: ${JAVA_HOME}")
|
|
||||||
else()
|
|
||||||
# 尝试通过which或where命令查找java
|
|
||||||
if(WIN32)
|
|
||||||
execute_process(
|
|
||||||
COMMAND where java
|
|
||||||
OUTPUT_VARIABLE JAVA_PATH
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
execute_process(
|
|
||||||
COMMAND which java
|
|
||||||
OUTPUT_VARIABLE JAVA_PATH
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(JAVA_PATH)
|
|
||||||
# 从java路径推断JAVA_HOME
|
|
||||||
get_filename_component(JAVA_HOME "${JAVA_PATH}" DIRECTORY)
|
|
||||||
get_filename_component(JAVA_HOME "${JAVA_HOME}" DIRECTORY)
|
|
||||||
message(STATUS "Inferred JAVA_HOME: ${JAVA_HOME}")
|
|
||||||
else()
|
|
||||||
message(WARNING "Java not found in PATH")
|
|
||||||
set(JAVA_HOME "C:/Program Files/Java/jdk-21") # Windows默认路径
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 设置Java包含目录
|
|
||||||
set(JAVA_INCLUDE_DIRS
|
|
||||||
"${JAVA_HOME}/include"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 添加平台特定include目录
|
|
||||||
if(WIN32)
|
|
||||||
list(APPEND JAVA_INCLUDE_DIRS "${JAVA_HOME}/include/win32")
|
|
||||||
elseif(APPLE)
|
|
||||||
list(APPEND JAVA_INCLUDE_DIRS "${JAVA_HOME}/include/darwin")
|
|
||||||
else()
|
|
||||||
list(APPEND JAVA_INCLUDE_DIRS "${JAVA_HOME}/include/linux")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 验证Java包含目录是否存在
|
|
||||||
foreach(dir IN LISTS JAVA_INCLUDE_DIRS)
|
|
||||||
if(NOT EXISTS "${dir}")
|
|
||||||
message(WARNING "Java include directory not found: ${dir}")
|
|
||||||
else()
|
|
||||||
message(STATUS "Found Java include: ${dir}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# ========== 查找源文件和头文件 ==========
|
|
||||||
# 查找所有的C++源文件
|
|
||||||
file(GLOB SOURCE_FILES
|
|
||||||
"${SOURCE_DIR}/*.cpp"
|
|
||||||
"${SOURCE_DIR}/*.cxx"
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT SOURCE_FILES)
|
|
||||||
# 如果没找到,尝试递归查找
|
|
||||||
file(GLOB_RECURSE SOURCE_FILES
|
|
||||||
"${SOURCE_DIR}/*.cpp"
|
|
||||||
"${SOURCE_DIR}/*.cxx"
|
|
||||||
"${SOURCE_DIR}/*.cc"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(SOURCE_FILES)
|
|
||||||
message(STATUS "Found source files:")
|
|
||||||
foreach(file ${SOURCE_FILES})
|
|
||||||
message(STATUS " ${file}")
|
|
||||||
endforeach()
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "No source files found in ${SOURCE_DIR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 查找头文件
|
|
||||||
file(GLOB HEADER_FILES
|
|
||||||
"${HEADER_DIR}/*.h"
|
|
||||||
"${HEADER_DIR}/*.hpp"
|
|
||||||
)
|
|
||||||
|
|
||||||
# ========== 创建库目标 ==========
|
|
||||||
# 库名称
|
|
||||||
if(WIN32)
|
|
||||||
set(LIBRARY_NAME "ClassEncrypt")
|
|
||||||
else()
|
|
||||||
set(LIBRARY_NAME "ClassEncrypt")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 添加库目标
|
|
||||||
add_library(${LIBRARY_NAME} SHARED ${SOURCE_FILES}
|
|
||||||
"src/EnhancedEncryptionMagic.cpp"
|
|
||||||
src/guard/JByteArrayGuard.cpp)
|
|
||||||
|
|
||||||
# 设置输出名称和位置
|
|
||||||
set_target_properties(${LIBRARY_NAME} PROPERTIES
|
|
||||||
OUTPUT_NAME ${LIBRARY_NAME}
|
|
||||||
DEBUG_POSTFIX "d"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 设置输出目录
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
||||||
|
|
||||||
# ========== 包含目录 ==========
|
|
||||||
target_include_directories(${LIBRARY_NAME} PRIVATE
|
|
||||||
${JAVA_INCLUDE_DIRS}
|
|
||||||
${HEADER_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
# ========== 编译选项 ==========
|
|
||||||
if(MSVC)
|
|
||||||
target_compile_options(${LIBRARY_NAME} PRIVATE
|
|
||||||
/W3 # 警告级别3
|
|
||||||
/WX- # 不将警告视为错误
|
|
||||||
/EHsc # C++异常处理
|
|
||||||
$<$<CONFIG:Debug>:/MDd> # 调试版本使用MDd
|
|
||||||
$<$<CONFIG:Release>:/MD> # 发布版本使用MD
|
|
||||||
$<$<CONFIG:RelWithDebInfo>:/MD>
|
|
||||||
$<$<CONFIG:MinSizeRel>:/MD>
|
|
||||||
)
|
|
||||||
|
|
||||||
# MSVC预处理器定义
|
|
||||||
target_compile_definitions(${LIBRARY_NAME} PRIVATE
|
|
||||||
_CRT_SECURE_NO_WARNINGS
|
|
||||||
_WINSOCK_DEPRECATED_NO_WARNINGS
|
|
||||||
BUILDING_DLL
|
|
||||||
JNIEXPORT=__declspec(dllexport)
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
target_compile_options(${LIBRARY_NAME} PRIVATE
|
|
||||||
-Wall
|
|
||||||
-Wextra
|
|
||||||
-Wno-unused-parameter
|
|
||||||
-fPIC
|
|
||||||
$<$<CONFIG:Debug>:-g -O0>
|
|
||||||
$<$<CONFIG:Release>:-O2>
|
|
||||||
$<$<CONFIG:RelWithDebInfo>:-O2 -g>
|
|
||||||
$<$<CONFIG:MinSizeRel>:-Os>
|
|
||||||
)
|
|
||||||
|
|
||||||
# GCC/Clang预处理器定义
|
|
||||||
target_compile_definitions(${LIBRARY_NAME} PRIVATE
|
|
||||||
BUILDING_DLL
|
|
||||||
)
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
target_compile_options(${LIBRARY_NAME} PRIVATE
|
|
||||||
-stdlib=libc++
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ========== 链接库 ==========
|
|
||||||
if(WIN32)
|
|
||||||
# Windows链接库
|
|
||||||
target_link_libraries(${LIBRARY_NAME} PRIVATE
|
|
||||||
kernel32.lib
|
|
||||||
user32.lib
|
|
||||||
gdi32.lib
|
|
||||||
winspool.lib
|
|
||||||
shell32.lib
|
|
||||||
ole32.lib
|
|
||||||
oleaut32.lib
|
|
||||||
uuid.lib
|
|
||||||
comdlg32.lib
|
|
||||||
advapi32.lib
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
# Linux/macOS链接库
|
|
||||||
target_link_libraries(${LIBRARY_NAME} PRIVATE
|
|
||||||
pthread
|
|
||||||
dl
|
|
||||||
)
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
target_link_options(${LIBRARY_NAME} PRIVATE
|
|
||||||
-undefined dynamic_lookup
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ========== 安装配置(可选) ==========
|
|
||||||
if(NOT DEFINED CMAKE_SKIP_INSTALL_RULES)
|
|
||||||
install(TARGETS ${LIBRARY_NAME}
|
|
||||||
LIBRARY DESTINATION lib
|
|
||||||
RUNTIME DESTINATION bin
|
|
||||||
ARCHIVE DESTINATION lib
|
|
||||||
)
|
|
||||||
|
|
||||||
# 安装头文件
|
|
||||||
if(HEADER_FILES)
|
|
||||||
install(FILES ${HEADER_FILES} DESTINATION include)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ========== 输出总结信息 ==========
|
|
||||||
message(STATUS "")
|
|
||||||
message(STATUS "=========================================")
|
|
||||||
message(STATUS "Project Configuration Summary")
|
|
||||||
message(STATUS "=========================================")
|
|
||||||
message(STATUS "Project: ${PROJECT_NAME}")
|
|
||||||
message(STATUS "Version: ${PROJECT_VERSION}")
|
|
||||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
|
||||||
message(STATUS "Source directory: ${SOURCE_DIR}")
|
|
||||||
message(STATUS "Header directory: ${HEADER_DIR}")
|
|
||||||
message(STATUS "Java include dirs: ${JAVA_INCLUDE_DIRS}")
|
|
||||||
message(STATUS "Target library: ${LIBRARY_NAME}")
|
|
||||||
message(STATUS "C++ standard: ${CMAKE_CXX_STANDARD}")
|
|
||||||
message(STATUS "Output directory: ${CMAKE_BINARY_DIR}")
|
|
||||||
message(STATUS "=========================================")
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
# Config.cmake.in
|
|
||||||
@PACKAGE_INIT@
|
|
||||||
|
|
||||||
include(CMakeFindDependencyMacro)
|
|
||||||
|
|
||||||
# 查找依赖
|
|
||||||
find_dependency(Java COMPONENTS Development)
|
|
||||||
|
|
||||||
if(@USE_OPENSSL@)
|
|
||||||
find_dependency(OpenSSL REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 导入目标
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
|
||||||
|
|
||||||
# 检查目标是否存在
|
|
||||||
check_required_components(@PROJECT_NAME@)
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.28)
|
|
||||||
|
|
||||||
add_library( HEADER
|
|
||||||
top_r3944realms_lib39_core_lang_ClassEncryptor.h
|
|
||||||
top_r3944realms_lib39_core_lang_EncryptedClassLoader.h
|
|
||||||
)
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
|
||||||
#include <jni.h>
|
|
||||||
/* Header for class top_r3944realms_lib39_core_lang_ClassEncryptor */
|
|
||||||
|
|
||||||
#ifndef _Included_top_r3944realms_lib39_core_lang_ClassEncryptor
|
|
||||||
#define _Included_top_r3944realms_lib39_core_lang_ClassEncryptor
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
|
||||||
* Method: encryptClass
|
|
||||||
* Signature: ([BLjava/lang/String;)[B
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_encryptClass
|
|
||||||
(JNIEnv *, jobject, jbyteArray, jstring);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
|
||||||
* Method: decryptClass
|
|
||||||
* Signature: ([BLjava/lang/String;)[B
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_decryptClass
|
|
||||||
(JNIEnv *, jobject, jbyteArray, jstring);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
|
||||||
* Method: isEncryptedFile
|
|
||||||
* Signature: ([B)Z
|
|
||||||
*/
|
|
||||||
JNIEXPORT jboolean JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_isEncryptedFile
|
|
||||||
(JNIEnv *, jobject, jbyteArray);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
|
||||||
#include <jni.h>
|
|
||||||
/* Header for class top_r3944realms_lib39_core_lang_EncryptedClassLoader */
|
|
||||||
|
|
||||||
#ifndef _Included_top_r3944realms_lib39_core_lang_EncryptedClassLoader
|
|
||||||
#define _Included_top_r3944realms_lib39_core_lang_EncryptedClassLoader
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* Class: top_r3944realms_lib39_core_lang_EncryptedClassLoader
|
|
||||||
* Method: decryptClass
|
|
||||||
* Signature: ([BLjava/lang/String;)[B
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_EncryptedClassLoader_decryptClass
|
|
||||||
(JNIEnv *, jobject, jbyteArray, jstring);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.28)
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.28)
|
|
||||||
|
|
||||||
|
|
||||||
add_subdirectory(header)
|
|
||||||
add_subdirectory(lib)
|
|
||||||
|
|
||||||
set_target_properties(CONST_LIB PROPERTIES LINKER_LANGUAGE CXX)
|
|
||||||
|
|
@ -1,451 +0,0 @@
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions"
|
|
||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
#include <ctime>
|
|
||||||
#include <cstring>
|
|
||||||
#ifndef HEADER_JNI_H_
|
|
||||||
#define HEADER_JNI_H_
|
|
||||||
#include <jni.h>
|
|
||||||
#endif
|
|
||||||
#ifndef HEADER_P_H_
|
|
||||||
#define HEADER_P_H_
|
|
||||||
#include "guard/JByteArrayGuard.cpp"
|
|
||||||
#endif
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// 字节序转换宏(跨平台)
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma ide diagnostic ignored "UnreachableCallsOfFunction"
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <winsock2.h>
|
|
||||||
#pragma commen+t(lib, "ws2_32.lib")
|
|
||||||
|
|
||||||
#define htobe32(x) htonl(x)
|
|
||||||
#define be32toh(x) ntohl(x)
|
|
||||||
#define htobe16(x) htons(x)
|
|
||||||
#define be16toh(x) ntohs(x)
|
|
||||||
|
|
||||||
// Windows下64位字节序转换需要自己实现
|
|
||||||
static inline uint64_t htobe64(uint64_t x) {
|
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
||||||
return ((uint64_t)htonl(x & 0xFFFFFFFF) << 32) | htonl(x >> 32);
|
|
||||||
#else
|
|
||||||
return x;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t be64toh(uint64_t x) {
|
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
||||||
return ((uint64_t)ntohl(x & 0xFFFFFFFF) << 32) | ntohl(x >> 32);
|
|
||||||
#else
|
|
||||||
return x;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
|
||||||
#include <libkern/OSByteOrder.h>
|
|
||||||
#define htobe32(x) OSSwapHostToBigInt32(x)
|
|
||||||
#define be32toh(x) OSSwapBigToHostInt32(x)
|
|
||||||
#define htobe16(x) OSSwapHostToBigInt16(x)
|
|
||||||
#define be16toh(x) OSSwapBigToHostInt16(x)
|
|
||||||
#define htobe64(x) OSSwapHostToBigInt64(x)
|
|
||||||
#define be64toh(x) OSSwapBigToHostInt64(x)
|
|
||||||
|
|
||||||
#elif defined(__linux__) || defined(__ANDROID__)
|
|
||||||
#include <endian.h>
|
|
||||||
// Linux下endian.h已经定义了这些宏
|
|
||||||
|
|
||||||
#else
|
|
||||||
// 通用实现
|
|
||||||
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
||||||
// GCC/Clang内置函数
|
|
||||||
#define htobe32(x) __builtin_bswap32(x)
|
|
||||||
#define be32toh(x) __builtin_bswap32(x)
|
|
||||||
#define htobe16(x) __builtin_bswap16(x)
|
|
||||||
#define be16toh(x) __builtin_bswap16(x)
|
|
||||||
#define htobe64(x) __builtin_bswap64(x)
|
|
||||||
#define be64toh(x) __builtin_bswap64(x)
|
|
||||||
#else
|
|
||||||
#define htobe32(x) (x)
|
|
||||||
#define be32toh(x) (x)
|
|
||||||
#define htobe16(x) (x)
|
|
||||||
#define be16toh(x) (x)
|
|
||||||
#define htobe64(x) (x)
|
|
||||||
#define be64toh(x) (x)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 手动字节序转换函数(备用)
|
|
||||||
static inline uint64_t manual_htobe64(uint64_t x) {
|
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
||||||
return ((uint64_t)__builtin_bswap32(x & 0xFFFFFFFF) << 32) | __builtin_bswap32(x >> 32);
|
|
||||||
#else
|
|
||||||
return x;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t manual_be64toh(uint64_t x) {
|
|
||||||
return manual_htobe64(x); // 对称操作
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace EnhancedEncryptionMagic {
|
|
||||||
// 主魔数:0x4C494233 (ASCII: "LIB3")
|
|
||||||
static const uint32_t MAGIC = 0x4C494233; // "LIB3" in hex
|
|
||||||
|
|
||||||
// 文件头结构 - 调整为实际大小
|
|
||||||
struct EnhancedFileHeader {
|
|
||||||
uint32_t magic; // 魔数: 0x4C494233 "LIB3" (4字节)
|
|
||||||
uint16_t version_major; // 主版本号 (2字节)
|
|
||||||
uint16_t version_minor; // 次版本号 (2字节)
|
|
||||||
uint32_t flags; // 标志位 (4字节)
|
|
||||||
uint32_t original_size; // 原始数据大小 (4字节)
|
|
||||||
uint32_t encrypted_size; // 加密数据大小 (4字节)
|
|
||||||
uint64_t timestamp; // 时间戳 (8字节)
|
|
||||||
uint32_t checksum; // 校验和 (4字节)
|
|
||||||
uint32_t reserved; // 保留字段 (4字节)
|
|
||||||
// 总计: 4+2+2+4+4+4+8+4+4 = 36字节
|
|
||||||
|
|
||||||
// 编译器可能添加4字节填充到40字节,但我们应该按36字节处理
|
|
||||||
};
|
|
||||||
|
|
||||||
// 计算实际结构体大小
|
|
||||||
static const size_t CALCULATED_HEADER_SIZE =
|
|
||||||
sizeof(uint32_t) + // magic
|
|
||||||
sizeof(uint16_t) + // version_major
|
|
||||||
sizeof(uint16_t) + // version_minor
|
|
||||||
sizeof(uint32_t) + // flags
|
|
||||||
sizeof(uint32_t) + // original_size
|
|
||||||
sizeof(uint32_t) + // encrypted_size
|
|
||||||
sizeof(uint64_t) + // timestamp
|
|
||||||
sizeof(uint32_t) + // checksum
|
|
||||||
sizeof(uint32_t); // reserved
|
|
||||||
|
|
||||||
static const size_t HEADER_SIZE = CALCULATED_HEADER_SIZE;
|
|
||||||
|
|
||||||
// 标志位定义
|
|
||||||
namespace Flags {
|
|
||||||
static const uint32_t COMPRESSED = 0x00000001; // 是否压缩
|
|
||||||
static const uint32_t SIGNED = 0x00000002; // 是否签名
|
|
||||||
static const uint32_t ENCRYPTED = 0x00000004; // 是否加密
|
|
||||||
static const uint32_t VALIDATED = 0x00000008; // 是否验证
|
|
||||||
};
|
|
||||||
|
|
||||||
// 创建文件头
|
|
||||||
static inline EnhancedFileHeader createHeader(uint32_t originalSize, uint32_t encryptedSize) {
|
|
||||||
EnhancedFileHeader header;
|
|
||||||
memset(&header, 0, sizeof(header)); // 清零初始化
|
|
||||||
|
|
||||||
header.magic = MAGIC;
|
|
||||||
header.version_major = 1;
|
|
||||||
header.version_minor = 0;
|
|
||||||
header.flags = Flags::ENCRYPTED;
|
|
||||||
header.original_size = originalSize;
|
|
||||||
header.encrypted_size = encryptedSize;
|
|
||||||
header.timestamp = static_cast<uint64_t>(time(nullptr));
|
|
||||||
header.checksum = 0; // 将在之后计算
|
|
||||||
header.reserved = 0;
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字节序安全的内存复制函数
|
|
||||||
static inline void writeUint32(jbyte* buffer, uint32_t value, size_t offset) {
|
|
||||||
uint32_t networkValue = htobe32(value);
|
|
||||||
memcpy(buffer + offset, &networkValue, sizeof(uint32_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void writeUint16(jbyte* buffer, uint16_t value, size_t offset) {
|
|
||||||
uint16_t networkValue = htobe16(value);
|
|
||||||
memcpy(buffer + offset, &networkValue, sizeof(uint16_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void writeUint64(jbyte* buffer, uint64_t value, size_t offset) {
|
|
||||||
uint64_t networkValue = htobe64(value);
|
|
||||||
memcpy(buffer + offset, &networkValue, sizeof(uint64_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t readUint32(const jbyte* buffer, size_t offset) {
|
|
||||||
uint32_t networkValue;
|
|
||||||
memcpy(&networkValue, buffer + offset, sizeof(uint32_t));
|
|
||||||
return be32toh(networkValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t readUint16(const jbyte* buffer, size_t offset) {
|
|
||||||
uint16_t networkValue;
|
|
||||||
memcpy(&networkValue, buffer + offset, sizeof(uint16_t));
|
|
||||||
return be16toh(networkValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint64_t readUint64(const jbyte* buffer, size_t offset) {
|
|
||||||
uint64_t networkValue;
|
|
||||||
memcpy(&networkValue, buffer + offset, sizeof(uint64_t));
|
|
||||||
return be64toh(networkValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证文件头
|
|
||||||
static inline bool validateHeader(const EnhancedFileHeader& header) {
|
|
||||||
return header.magic == MAGIC &&
|
|
||||||
header.version_major == 1 &&
|
|
||||||
header.version_minor == 0 &&
|
|
||||||
(header.flags & Flags::ENCRYPTED) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算校验和(简单的CRC32替代)
|
|
||||||
static inline uint32_t calculateChecksum(const jbyte* data, jsize length) {
|
|
||||||
if (!data || length <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t crc = 0xFFFFFFFF;
|
|
||||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
|
|
||||||
|
|
||||||
for (jsize i = 0; i < length; i++) {
|
|
||||||
crc ^= bytes[i];
|
|
||||||
for (int j = 0; j < 8; j++) {
|
|
||||||
if (crc & 1) {
|
|
||||||
crc = (crc >> 1) ^ 0xEDB88320;
|
|
||||||
} else {
|
|
||||||
crc = crc >> 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ~crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新文件头的校验和
|
|
||||||
static inline void updateChecksum(EnhancedFileHeader& header, const jbyte* data, jsize length) {
|
|
||||||
header.checksum = calculateChecksum(data, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证数据校验和
|
|
||||||
static inline bool verifyChecksum(const EnhancedFileHeader& header, const jbyte* data, jsize length) {
|
|
||||||
uint32_t calculated = calculateChecksum(data, length);
|
|
||||||
return header.checksum == calculated;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将文件头写入字节数组(使用网络字节序)
|
|
||||||
static inline void writeHeaderToBytes(const EnhancedFileHeader& header, jbyte* buffer) {
|
|
||||||
if (!buffer) return;
|
|
||||||
|
|
||||||
size_t offset = 0;
|
|
||||||
writeUint32(buffer, header.magic, offset); offset += 4;
|
|
||||||
writeUint16(buffer, header.version_major, offset); offset += 2;
|
|
||||||
writeUint16(buffer, header.version_minor, offset); offset += 2;
|
|
||||||
writeUint32(buffer, header.flags, offset); offset += 4;
|
|
||||||
writeUint32(buffer, header.original_size, offset); offset += 4;
|
|
||||||
writeUint32(buffer, header.encrypted_size, offset); offset += 4;
|
|
||||||
writeUint64(buffer, header.timestamp, offset); offset += 8;
|
|
||||||
writeUint32(buffer, header.checksum, offset); offset += 4;
|
|
||||||
writeUint32(buffer, header.reserved, offset); offset += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从字节数组读取文件头
|
|
||||||
static inline EnhancedFileHeader readHeaderFromBytes(const jbyte* buffer) {
|
|
||||||
EnhancedFileHeader header{};
|
|
||||||
memset(&header, 0, sizeof(header));
|
|
||||||
|
|
||||||
if (!buffer) return header;
|
|
||||||
|
|
||||||
size_t offset = 0;
|
|
||||||
header.magic = readUint32(buffer, offset); offset += 4;
|
|
||||||
header.version_major = readUint16(buffer, offset); offset += 2;
|
|
||||||
header.version_minor = readUint16(buffer, offset); offset += 2;
|
|
||||||
header.flags = readUint32(buffer, offset); offset += 4;
|
|
||||||
header.original_size = readUint32(buffer, offset); offset += 4;
|
|
||||||
header.encrypted_size = readUint32(buffer, offset); offset += 4;
|
|
||||||
header.timestamp = readUint64(buffer, offset); offset += 8;
|
|
||||||
header.checksum = readUint32(buffer, offset); offset += 4;
|
|
||||||
header.reserved = readUint32(buffer, offset); offset += 4;
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将文件头格式化为可读字符串
|
|
||||||
static inline std::string headerToString(const EnhancedFileHeader& header) {
|
|
||||||
char magicStr[5] = {0};
|
|
||||||
memcpy(magicStr, &header.magic, 4);
|
|
||||||
|
|
||||||
char buffer[256];
|
|
||||||
snprintf(buffer, sizeof(buffer),
|
|
||||||
"Magic: %s (0x%08X)\n"
|
|
||||||
"Version: %d.%d\n"
|
|
||||||
"Flags: 0x%08X\n"
|
|
||||||
"Original Size: %u bytes\n"
|
|
||||||
"Encrypted Size: %u bytes\n"
|
|
||||||
"Timestamp: %llu\n"
|
|
||||||
"Checksum: 0x%08X\n"
|
|
||||||
"Reserved: 0x%08X",
|
|
||||||
magicStr, header.magic,
|
|
||||||
header.version_major, header.version_minor,
|
|
||||||
header.flags,
|
|
||||||
header.original_size,
|
|
||||||
header.encrypted_size,
|
|
||||||
(unsigned long long)header.timestamp,
|
|
||||||
header.checksum,
|
|
||||||
header.reserved);
|
|
||||||
|
|
||||||
return std::string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证文件是否完整
|
|
||||||
static inline bool validateFileIntegrity(const EnhancedFileHeader& header,
|
|
||||||
const jbyte* encryptedData,
|
|
||||||
jsize encryptedDataSize) {
|
|
||||||
// 检查大小是否匹配
|
|
||||||
if (header.encrypted_size != encryptedDataSize) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查校验和
|
|
||||||
return verifyChecksum(header, encryptedData, encryptedDataSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加密函数指针类型
|
|
||||||
typedef void (*EncryptFunc)(jbyte*, jsize, const char*, int);
|
|
||||||
|
|
||||||
// 创建完整的加密文件
|
|
||||||
static inline jbyteArray createEncryptedFile(JNIEnv* env,
|
|
||||||
const jbyte* originalData,
|
|
||||||
jsize originalSize,
|
|
||||||
const char* key,
|
|
||||||
size_t keyLen,
|
|
||||||
EncryptFunc encryptFunc) {
|
|
||||||
|
|
||||||
if (!env || !originalData || originalSize <= 0 || !key || keyLen <= 0 || !encryptFunc) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. 创建加密数据数组
|
|
||||||
jbyteArray encryptedDataArray = env->NewByteArray(originalSize);
|
|
||||||
if (!encryptedDataArray) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// 使用局部作用域确保 encryptedDataGuard 在加密后释放
|
|
||||||
JByteArrayGuard encryptedDataGuard(env, encryptedDataArray, false, JNI_ABORT);
|
|
||||||
if (!encryptedDataGuard.isValid()) {
|
|
||||||
env->DeleteLocalRef(encryptedDataArray);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 复制并加密数据
|
|
||||||
memcpy(encryptedDataGuard.get(), originalData, originalSize);
|
|
||||||
encryptFunc(encryptedDataGuard.get(), originalSize, key, keyLen);
|
|
||||||
|
|
||||||
// encryptedDataGuard 析构函数会自动以 JNI_ABORT 模式释放
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注意:这里已经释放了加密数据,需要重新获取
|
|
||||||
JByteArrayGuard encryptedDataGuard2(env, encryptedDataArray);
|
|
||||||
if (!encryptedDataGuard2.isValid()) {
|
|
||||||
env->DeleteLocalRef(encryptedDataArray);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 创建文件头
|
|
||||||
EnhancedFileHeader header = createHeader(originalSize, originalSize);
|
|
||||||
updateChecksum(header, encryptedDataGuard2.get(), originalSize);
|
|
||||||
|
|
||||||
// 3. 创建最终结果
|
|
||||||
jsize totalSize = HEADER_SIZE + originalSize;
|
|
||||||
jbyteArray result = env->NewByteArray(totalSize);
|
|
||||||
if (!result) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JByteArrayGuard resultGuard(env, result);
|
|
||||||
if (!resultGuard.isValid()) {
|
|
||||||
env->DeleteLocalRef(result);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 写入数据
|
|
||||||
writeHeaderToBytes(header, resultGuard.get());
|
|
||||||
memcpy(resultGuard.get() + HEADER_SIZE, encryptedDataGuard2.get(), originalSize);
|
|
||||||
|
|
||||||
// 5. 显式提交修改
|
|
||||||
resultGuard.commit(); // 提交修改到 Java 端
|
|
||||||
// resultGuard 析构时不会再释放
|
|
||||||
|
|
||||||
// 6. 清理中间数组
|
|
||||||
env->DeleteLocalRef(encryptedDataArray);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从加密文件中提取数据
|
|
||||||
static inline jbyteArray extractFromEncryptedFile(JNIEnv* env,
|
|
||||||
const jbyte* fileData,
|
|
||||||
jsize fileSize,
|
|
||||||
const char* key,
|
|
||||||
size_t keyLen,
|
|
||||||
EncryptFunc decryptFunc,
|
|
||||||
bool* isValid) {
|
|
||||||
|
|
||||||
if (isValid) *isValid = false;
|
|
||||||
|
|
||||||
if (!env || !fileData || fileSize < HEADER_SIZE || !key || keyLen <= 0 || !decryptFunc) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取文件头
|
|
||||||
EnhancedFileHeader header = readHeaderFromBytes(fileData);
|
|
||||||
|
|
||||||
// 验证文件头
|
|
||||||
if (!validateHeader(header)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查文件大小
|
|
||||||
jsize expectedSize = HEADER_SIZE + header.encrypted_size;
|
|
||||||
if (fileSize != expectedSize) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取加密数据
|
|
||||||
const jbyte* encryptedData = fileData + HEADER_SIZE;
|
|
||||||
|
|
||||||
// 验证完整性
|
|
||||||
if (!validateFileIntegrity(header, encryptedData, header.encrypted_size)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建结果数组
|
|
||||||
jbyteArray result = env->NewByteArray(header.original_size);
|
|
||||||
if (!result) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
JByteArrayGuard resultDataGuard(env, result);
|
|
||||||
|
|
||||||
if (!resultDataGuard.isValid()) {
|
|
||||||
env->DeleteLocalRef(result);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
jbyte* resultData = resultDataGuard.get();
|
|
||||||
|
|
||||||
// 复制加密数据
|
|
||||||
memcpy(resultData, encryptedData, header.original_size);
|
|
||||||
|
|
||||||
// 解密数据
|
|
||||||
decryptFunc(resultData, header.original_size, key, keyLen);
|
|
||||||
|
|
||||||
if (isValid) *isValid = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证是否为加密文件(不读取整个文件)
|
|
||||||
static inline bool isEncryptedFile(const jbyte* fileData, jsize fileSize) {
|
|
||||||
if (fileSize < HEADER_SIZE || !fileData) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnhancedFileHeader header = readHeaderFromBytes(fileData);
|
|
||||||
return validateHeader(header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
#include <header/top_r3944realms_lib39_core_lang_ClassEncryptor.h>
|
|
||||||
#include <header/top_r3944realms_lib39_core_lang_EncryptedClassLoader.h>
|
|
||||||
#include "EnhancedEncryptionMagic.cpp"
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wsign-compare"
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#include <wincrypt.h>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#define JNIEXPORT __declspec(dllexport)
|
|
||||||
#else
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#define JNIEXPORT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 简单的XOR加密/解密
|
|
||||||
*/
|
|
||||||
static void xorEncrypt(jbyte* data, jsize dataLen, const char* key, int keyLen) {
|
|
||||||
if (!data || !key || keyLen == 0) return;
|
|
||||||
|
|
||||||
for (jsize i = 0; i < dataLen; i++) {
|
|
||||||
data[i] ^= key[i % keyLen];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int safeStrlen(const char* str) {
|
|
||||||
return str ? (int)strlen(str) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证数据是否为有效的Java类文件
|
|
||||||
* @param data 字节码
|
|
||||||
* @param dataLen 字节码长度
|
|
||||||
* @return 是否有效
|
|
||||||
*/
|
|
||||||
static bool isValidJavaClass(const jbyte* data, jsize dataLen) {
|
|
||||||
// Java类文件魔数:0xCAFEBABE
|
|
||||||
return dataLen >= 4 &&
|
|
||||||
data[0] == (jbyte)0xCA &&
|
|
||||||
data[1] == (jbyte)0xFE &&
|
|
||||||
data[2] == (jbyte)0xBA &&
|
|
||||||
data[3] == (jbyte)0xBE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录错误信息
|
|
||||||
*/
|
|
||||||
void logError(const char* message) {
|
|
||||||
#ifdef DEBUG
|
|
||||||
std::cerr << "[JNI Error] " << message << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 安全的字符串转换
|
|
||||||
*/
|
|
||||||
std::string jstringToString(JNIEnv* env, jstring jstr) {
|
|
||||||
if (!jstr) return "";
|
|
||||||
|
|
||||||
const char* chars = env->GetStringUTFChars(jstr, nullptr);
|
|
||||||
if (!chars) return "";
|
|
||||||
|
|
||||||
std::string result(chars);
|
|
||||||
env->ReleaseStringUTFChars(jstr, chars);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t calculateChecksum(const jbyte* data, jsize length) {
|
|
||||||
uint32_t checksum = 0;
|
|
||||||
for (jsize i = 0; i < length; i++) {
|
|
||||||
checksum += static_cast<uint8_t>(data[i]);
|
|
||||||
checksum = (checksum << 1) | (checksum >> 31); // 简单旋转
|
|
||||||
}
|
|
||||||
return checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ==================== JNI函数实现 ====================
|
|
||||||
using namespace EnhancedEncryptionMagic;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
|
||||||
* Method: decryptClass
|
|
||||||
* Signature: ([BLjava/lang/String;)[B
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_decryptClass
|
|
||||||
(JNIEnv *env, jobject obj, jbyteArray encryptedData, jstring key) {
|
|
||||||
|
|
||||||
if (!encryptedData || !key) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
jsize fileSize = env->GetArrayLength(encryptedData);
|
|
||||||
if (fileSize == 0) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
JByteArrayGuard fileDataGuard(env, encryptedData);
|
|
||||||
jbyte* fileData = fileDataGuard.get();
|
|
||||||
if (!fileDataGuard.isValid()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
std::string keyStr = jstringToString(env, key);
|
|
||||||
size_t keyLen = keyStr.length();
|
|
||||||
bool isValid = keyLen > 0;
|
|
||||||
// 尝试从加密文件中提取数据
|
|
||||||
jbyteArray result = EnhancedEncryptionMagic::extractFromEncryptedFile(
|
|
||||||
env, fileData, fileSize, keyStr.c_str(), keyLen, xorEncrypt, &isValid);
|
|
||||||
if (!isValid || !result) {
|
|
||||||
// 如果不是有效的加密文件,返回原始数据
|
|
||||||
return encryptedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证解密后的数据是否为有效的Java类文件
|
|
||||||
JByteArrayGuard resultGuard(env, result);
|
|
||||||
jsize resultLen = resultGuard.size();
|
|
||||||
jbyte* resultData = resultGuard.get();
|
|
||||||
|
|
||||||
if (!resultGuard.isValid()) {
|
|
||||||
env->DeleteLocalRef(result);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool validClass = isValidJavaClass(resultData, resultLen);
|
|
||||||
|
|
||||||
if (!validClass) {
|
|
||||||
env->DeleteLocalRef(result);
|
|
||||||
return nullptr; // 解密后的数据不是有效的Java类
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
|
||||||
* Method: encryptClass
|
|
||||||
* Signature: ([BLjava/lang/String;)[B
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_encryptClass
|
|
||||||
(JNIEnv *env, jobject obj, jbyteArray classData, jstring key) {
|
|
||||||
|
|
||||||
if (!classData || !key) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
JByteArrayGuard jGuard(env, classData);
|
|
||||||
if (jGuard.isValid()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
jsize dataLen = jGuard.size();
|
|
||||||
jbyte* data = jGuard.get();
|
|
||||||
|
|
||||||
const char* keyStr = env->GetStringUTFChars(key, nullptr);
|
|
||||||
if (!keyStr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证输入数据是否为有效的Java类文件
|
|
||||||
if (!isValidJavaClass(data, dataLen)) {
|
|
||||||
env->ReleaseStringUTFChars(key, keyStr);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int keyLen = safeStrlen(keyStr);
|
|
||||||
|
|
||||||
// 使用增强版创建加密文件
|
|
||||||
jbyteArray result = EnhancedEncryptionMagic::createEncryptedFile(
|
|
||||||
env, data, dataLen, keyStr, keyLen, xorEncrypt);
|
|
||||||
|
|
||||||
env->ReleaseStringUTFChars(key, keyStr);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: top_r3944realms_lib39_core_lang_ClassEncryptor
|
|
||||||
* Method: isEncryptedFile
|
|
||||||
* Signature: ([B)Z
|
|
||||||
*/
|
|
||||||
JNIEXPORT jboolean JNICALL Java_top_r3944realms_lib39_core_lang_ClassEncryptor_isEncryptedFile
|
|
||||||
(JNIEnv *env, jobject obj, jbyteArray fileData) {
|
|
||||||
|
|
||||||
if (!fileData) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
JByteArrayGuard dataGuard(env, fileData);
|
|
||||||
if (!dataGuard.isValid()) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataGuard.size() < EnhancedEncryptionMagic::HEADER_SIZE) {
|
|
||||||
return JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证是否为加密文件
|
|
||||||
bool isEncrypted = EnhancedEncryptionMagic::isEncryptedFile(dataGuard.get(), dataGuard.size());
|
|
||||||
|
|
||||||
return isEncrypted ? JNI_TRUE : JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Class: top_r3944realms_lib39_core_lang_EncryptedClassLoader
|
|
||||||
* Method: decryptClass
|
|
||||||
* Signature: ([BLjava/lang/String;)[B
|
|
||||||
*/
|
|
||||||
JNIEXPORT jbyteArray JNICALL Java_top_r3944realms_lib39_core_lang_EncryptedClassLoader_decryptClass
|
|
||||||
(JNIEnv *env, jobject obj, jbyteArray encryptedData, jstring key) {
|
|
||||||
return Java_top_r3944realms_lib39_core_lang_ClassEncryptor_decryptClass(env, obj, encryptedData, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// JNI库初始化和卸载函数
|
|
||||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
// Windows下需要初始化Winsock
|
|
||||||
WSADATA wsaData;
|
|
||||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return JNI_VERSION_1_8;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
WSACleanup();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.28)
|
|
||||||
|
|
||||||
add_library(J_GUARD
|
|
||||||
JByteArrayGuard.cpp
|
|
||||||
)
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#ifndef HEADER_JNI_H_
|
|
||||||
#define HEADER_JNI_H_
|
|
||||||
#include <jni.h>
|
|
||||||
#endif
|
|
||||||
class JByteArrayGuard {
|
|
||||||
private:
|
|
||||||
JNIEnv* env;
|
|
||||||
jbyteArray array;
|
|
||||||
jbyte* data;
|
|
||||||
jsize length;
|
|
||||||
bool isCritical;
|
|
||||||
jint releaseMode;
|
|
||||||
public:
|
|
||||||
JByteArrayGuard(JNIEnv* env, jbyteArray array, bool critical = false, jint releaseMode = 0)
|
|
||||||
: env(env), array(array), data(nullptr), length(0), isCritical(critical), releaseMode(releaseMode)
|
|
||||||
{
|
|
||||||
if (array) {
|
|
||||||
length = env->GetArrayLength(array);
|
|
||||||
if (isCritical) {
|
|
||||||
data = (jbyte*) env->GetPrimitiveArrayCritical(array, nullptr);
|
|
||||||
} else {
|
|
||||||
data = env->GetByteArrayElements(array, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~JByteArrayGuard() {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
// 显式释放方法
|
|
||||||
void release() {
|
|
||||||
if (data && array) {
|
|
||||||
if (isCritical) {
|
|
||||||
env->ReleasePrimitiveArrayCritical(array, data, releaseMode);
|
|
||||||
} else {
|
|
||||||
env->ReleaseByteArrayElements(array, data, releaseMode);
|
|
||||||
}
|
|
||||||
data = nullptr; // 防止重复释放
|
|
||||||
array = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交修改但不释放(用于返回结果的情况)
|
|
||||||
void commit() {
|
|
||||||
if (data && array) {
|
|
||||||
if (isCritical) {
|
|
||||||
env->ReleasePrimitiveArrayCritical(array, data, 0);
|
|
||||||
} else {
|
|
||||||
env->ReleaseByteArrayElements(array, data, 0);
|
|
||||||
}
|
|
||||||
data = nullptr; // 标记为已释放,防止析构函数再次释放
|
|
||||||
array = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 丢弃修改
|
|
||||||
void abort() {
|
|
||||||
if (data && array) {
|
|
||||||
if (isCritical) {
|
|
||||||
env->ReleasePrimitiveArrayCritical(array, data, JNI_ABORT);
|
|
||||||
} else {
|
|
||||||
env->ReleaseByteArrayElements(array, data, JNI_ABORT);
|
|
||||||
}
|
|
||||||
data = nullptr;
|
|
||||||
array = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jbyte* get() { return data; }
|
|
||||||
jsize size() const { return length; }
|
|
||||||
bool isValid() { return data != nullptr; }
|
|
||||||
|
|
||||||
JByteArrayGuard(const JByteArrayGuard&) = delete;
|
|
||||||
JByteArrayGuard& operator=(const JByteArrayGuard&) = delete;
|
|
||||||
JByteArrayGuard(JByteArrayGuard&& other) noexcept
|
|
||||||
: env(other.env), array(other.array), data(other.data),
|
|
||||||
length(other.length), isCritical(other.isCritical),
|
|
||||||
releaseMode(other.releaseMode) {
|
|
||||||
other.array = nullptr;
|
|
||||||
other.data = nullptr;
|
|
||||||
other.length = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
170
fabric/build.gradle
Normal file
170
fabric/build.gradle
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
import net.fabricmc.loom.task.RemapJarTask
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'multiloader-loader'
|
||||||
|
id 'fabric-loom'
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
minecraft "com.mojang:minecraft:${minecraft_version}"
|
||||||
|
mappings loom.layered {
|
||||||
|
officialMojangMappings()
|
||||||
|
parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip")
|
||||||
|
}
|
||||||
|
modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}"
|
||||||
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}"
|
||||||
|
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1'
|
||||||
|
implementation project(":common")
|
||||||
|
}
|
||||||
|
|
||||||
|
loom {
|
||||||
|
if (project(":common").file("src/main/resources/${mod_id}.accesswidener").exists()) {
|
||||||
|
accessWidenerPath.set(project(":common").file("src/main/resources/${mod_id}.accesswidener"))
|
||||||
|
}
|
||||||
|
mixin {
|
||||||
|
defaultRefmapName.set("${mod_id}.refmap.json")
|
||||||
|
}
|
||||||
|
runs {
|
||||||
|
client {
|
||||||
|
client()
|
||||||
|
setConfigName("Fabric Client")
|
||||||
|
ideConfigGenerated(true)
|
||||||
|
runDir("run")
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
server()
|
||||||
|
setConfigName("Fabric Server")
|
||||||
|
ideConfigGenerated(true)
|
||||||
|
runDir("run")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.named('sourcesJar', Jar) {
|
||||||
|
dependsOn classes
|
||||||
|
dependsOn project(':common').tasks.named('sourcesJar') // 显式依赖common的source
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
archiveClassifier.set('sources')
|
||||||
|
from sourceSets.main.allSource
|
||||||
|
from project(':common').sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置javadoc任务
|
||||||
|
tasks.named('javadoc', Javadoc) {
|
||||||
|
source project(':common').sourceSets.main.allJava
|
||||||
|
dependsOn project(':common').tasks.named('javadoc') // 显式依赖common的javadoc
|
||||||
|
source sourceSets.main.allJava
|
||||||
|
classpath = configurations.compileClasspath
|
||||||
|
classpath += project(':common').sourceSets.main.compileClasspath
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
options.charSet = 'UTF-8'
|
||||||
|
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||||
|
options.memberLevel = JavadocMemberLevel.PUBLIC
|
||||||
|
options.addBooleanOption('Xdoclint:none', true)
|
||||||
|
options.addStringOption('doctitle', "${mod_id} ${minecraft_version} ${version} Javadoc")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置javadocJar任务
|
||||||
|
tasks.named('javadocJar', Jar) {
|
||||||
|
dependsOn javadoc
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
archiveClassifier.set('javadoc')
|
||||||
|
from javadoc.destinationDir
|
||||||
|
from project(':common').javadoc.destinationDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保build任务包含所有需要的jar
|
||||||
|
tasks.named('build') {
|
||||||
|
dependsOn tasks.named('sourcesJar')
|
||||||
|
dependsOn tasks.named('javadocJar')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置remap任务以包含sources和javadoc
|
||||||
|
remapJar {
|
||||||
|
dependsOn tasks.named('sourcesJar')
|
||||||
|
dependsOn tasks.named('javadocJar')
|
||||||
|
inputFile.set(tasks.named('jar').get().archiveFile)
|
||||||
|
addNestedDependencies = false
|
||||||
|
}
|
||||||
|
|
||||||
|
remapSourcesJar {
|
||||||
|
dependsOn tasks.named('sourcesJar')
|
||||||
|
inputFile.set(tasks.named('sourcesJar').get().archiveFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为javadocJar创建remap任务
|
||||||
|
tasks.register('remapJavadocJar', RemapJarTask) {
|
||||||
|
dependsOn tasks.named('javadocJar')
|
||||||
|
inputFile.set(tasks.named('javadocJar').get().archiveFile)
|
||||||
|
archiveClassifier.set('javadoc')
|
||||||
|
addNestedDependencies = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将remapped artifacts添加到发布配置
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
// 重置artifactsId
|
||||||
|
artifactId = "${mod_id}-fabric-${minecraft_version}"
|
||||||
|
artifacts.clear()
|
||||||
|
// 手动添加需要的artifacts
|
||||||
|
artifact(remapJar) {
|
||||||
|
builtBy remapJar
|
||||||
|
}
|
||||||
|
artifact(remapSourcesJar) {
|
||||||
|
builtBy remapSourcesJar
|
||||||
|
classifier = 'sources'
|
||||||
|
}
|
||||||
|
artifact(remapJavadocJar) {
|
||||||
|
builtBy remapJavadocJar
|
||||||
|
classifier = 'javadoc'
|
||||||
|
}
|
||||||
|
pom {
|
||||||
|
name = 'Lib39'
|
||||||
|
description = 'Lib39 is a general-purpose dependency library for Minecraft mods.'
|
||||||
|
url = 'https://github.com/3944Realms/lib39'
|
||||||
|
|
||||||
|
properties = [
|
||||||
|
'minecraft.version': project.minecraft_version,
|
||||||
|
'mod.version': project.version,
|
||||||
|
'fabric.version': project.fabric_version,
|
||||||
|
'java.version': '17'
|
||||||
|
]
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = 'MIT'
|
||||||
|
url = 'https://raw.githubusercontent.com/3944Realms/lib39/refs/heads/main/LICENSE'
|
||||||
|
distribution = 'repo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = 'R3944Realms'
|
||||||
|
name = "${mod_author}"
|
||||||
|
email = 'f256198830@hotmail.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scm {
|
||||||
|
connection = 'scm:git:https://github.com/3944Realms/lib39.git'
|
||||||
|
developerConnection = 'scm:git:ssh://git@github.com:3944Realms/lib39.git'
|
||||||
|
url = 'https://github.com/3944Realms/lib39'
|
||||||
|
tag = 'main'
|
||||||
|
}
|
||||||
|
|
||||||
|
issueManagement {
|
||||||
|
system = 'GitHub'
|
||||||
|
url = 'https://github.com/3944Realms/lib39/issues'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('generateMetadataFileForMavenJavaPublication') {
|
||||||
|
dependsOn tasks.named('remapJavadocJar')
|
||||||
|
dependsOn tasks.named('remapJar')
|
||||||
|
dependsOn tasks.named('remapSourcesJar')
|
||||||
|
}
|
||||||
|
|
||||||
18
fabric/src/main/java/top/r3944realms/lib39/Lib39Fabric.java
Normal file
18
fabric/src/main/java/top/r3944realms/lib39/Lib39Fabric.java
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package top.r3944realms.lib39;
|
||||||
|
|
||||||
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
|
||||||
|
public class Lib39Fabric implements ModInitializer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitialize() {
|
||||||
|
Lib39.initialize();
|
||||||
|
// This method is invoked by the Fabric mod loader when it is ready
|
||||||
|
// to load your mod. You can access Fabric and Common code in this
|
||||||
|
// project.
|
||||||
|
|
||||||
|
// Use Fabric to bootstrap the Common mod.
|
||||||
|
Lib39.LOGGER.info("Hello Fabric world!");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 FabricLib39BlockEntities {
|
||||||
|
/**
|
||||||
|
* The constant DOLL_BLOCK_ENTITY.
|
||||||
|
*/
|
||||||
|
public static Supplier<BlockEntityType<DollBlockEntity>> DOLL_BLOCK_ENTITY;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
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 FabricLib39Blocks {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constant DOLL.
|
||||||
|
*/
|
||||||
|
public static Supplier<Block> DOLL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 FabricLib39Items {
|
||||||
|
/**
|
||||||
|
* The constant DOLL.
|
||||||
|
*/
|
||||||
|
public static Supplier<Item> DOLL;
|
||||||
|
}
|
||||||
|
|
@ -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 FabricLib39SoundEvents {
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package top.r3944realms.lib39.platform;
|
||||||
|
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.loader.api.ModContainer;
|
||||||
|
import net.fabricmc.loader.api.metadata.ModMetadata;
|
||||||
|
import top.r3944realms.lib39.Lib39;
|
||||||
|
import top.r3944realms.lib39.platform.services.IPlatformHelper;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class FabricPlatformHelper implements IPlatformHelper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPlatformName() {
|
||||||
|
return "Fabric";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isModLoaded(String modId) {
|
||||||
|
return FabricLoader.getInstance().isModLoaded(modId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDevelopmentEnvironment() {
|
||||||
|
|
||||||
|
return FabricLoader.getInstance().isDevelopmentEnvironment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClientEnvironment() {
|
||||||
|
return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getModVersion() {
|
||||||
|
return FabricLoader.getInstance().getModContainer(Lib39.MOD_ID)
|
||||||
|
.map(ModContainer::getMetadata)
|
||||||
|
.map(ModMetadata::getVersion)
|
||||||
|
.map(Objects::toString)
|
||||||
|
.orElse("NONE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
top.r3944realms.lib39.platform.ForgePlatformHelper
|
||||||
36
fabric/src/main/resources/fabric.mod.json
Normal file
36
fabric/src/main/resources/fabric.mod.json
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "mod_id",
|
||||||
|
"version": "${version}",
|
||||||
|
"name": "${mod_name}",
|
||||||
|
"description": "${description}",
|
||||||
|
"authors": [
|
||||||
|
"${mod_author}"
|
||||||
|
],
|
||||||
|
"contact": {
|
||||||
|
"homepage": "https://fabricmc.net/",
|
||||||
|
"sources": "https://github.com/FabricMC/fabric-example-mod"
|
||||||
|
},
|
||||||
|
"license": "${license}",
|
||||||
|
"icon": "lib39_logo.png",
|
||||||
|
"environment": "*",
|
||||||
|
"entrypoints": {
|
||||||
|
"main": [
|
||||||
|
"top.r3944realms.lib39.Lib39Fabric"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mixins": [
|
||||||
|
"${mod_id}.mixins.json",
|
||||||
|
"${mod_id}.fabric.mixins.json"
|
||||||
|
],
|
||||||
|
"depends": {
|
||||||
|
"fabricloader": ">=0.14",
|
||||||
|
"fabric": "*",
|
||||||
|
"minecraft": "${minecraft_version}",
|
||||||
|
"java": ">=17"
|
||||||
|
},
|
||||||
|
"suggests": {
|
||||||
|
"another-mod": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
17
fabric/src/main/resources/lib39.fabric.mixins.json
Normal file
17
fabric/src/main/resources/lib39.fabric.mixins.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "top.r3944realms.lib39.mixin",
|
||||||
|
"refmap": "${mod_id}.refmap.json",
|
||||||
|
"compatibilityLevel": "JAVA_17",
|
||||||
|
"mixins": [
|
||||||
|
],
|
||||||
|
"client": [
|
||||||
|
],
|
||||||
|
"server": [
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
193
forge/build.gradle
Normal file
193
forge/build.gradle
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
plugins {
|
||||||
|
id 'multiloader-loader'
|
||||||
|
id 'net.neoforged.moddev.legacyforge'
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin {
|
||||||
|
add(sourceSets.main, "${mod_id}.refmap.json")
|
||||||
|
|
||||||
|
config("${mod_id}.mixins.json")
|
||||||
|
config("${mod_id}.forge.mixins.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
legacyForge {
|
||||||
|
version = "${minecraft_version}-${forge_version}"
|
||||||
|
|
||||||
|
validateAccessTransformers = true
|
||||||
|
|
||||||
|
def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||||
|
def generated = project(':common').file('src/generated/resources/')
|
||||||
|
if (at.exists()) {
|
||||||
|
accessTransformers = ["src/main/resources/META-INF/accesstransformer.cfg"]
|
||||||
|
}
|
||||||
|
parchment {
|
||||||
|
minecraftVersion = parchment_minecraft
|
||||||
|
mappingsVersion = parchment_version
|
||||||
|
}
|
||||||
|
runs {
|
||||||
|
client {
|
||||||
|
client()
|
||||||
|
}
|
||||||
|
data {
|
||||||
|
data()
|
||||||
|
programArguments.addAll '--mod', project.mod_id, '--all', '--output', generated.getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
server()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mods {
|
||||||
|
"${mod_id}" {
|
||||||
|
sourceSet sourceSets.main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets.main.resources.srcDir project(':common').file('src/generated/resources')
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly project(":common")
|
||||||
|
annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT:processor")
|
||||||
|
implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.2.0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
finalizedBy('reobfJar')
|
||||||
|
manifest.attributes([
|
||||||
|
"MixinConfigs": "${mod_id}.mixins.json,${mod_id}.forge.mixins.json"
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置sourceJar任务
|
||||||
|
tasks.named('sourcesJar', Jar) {
|
||||||
|
dependsOn classes
|
||||||
|
dependsOn project(':common').tasks.named('sourcesJar') // 显式依赖common的source
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
archiveClassifier.set('sources')
|
||||||
|
from sourceSets.main.allSource
|
||||||
|
from project(':common').sourceSets.main.allSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置javadoc任务
|
||||||
|
tasks.named('javadoc', Javadoc) {
|
||||||
|
source project(':common').sourceSets.main.allJava
|
||||||
|
source sourceSets.main.allJava
|
||||||
|
classpath = configurations.compileClasspath
|
||||||
|
classpath += project(':common').sourceSets.main.compileClasspath
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
options.charSet = 'UTF-8'
|
||||||
|
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||||
|
options.memberLevel = JavadocMemberLevel.PUBLIC
|
||||||
|
options.addBooleanOption('Xdoclint:none', true)
|
||||||
|
options.addStringOption('doctitle', "${mod_id} ${minecraft_version} ${version} Javadoc")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置javadocJar任务
|
||||||
|
tasks.named('javadocJar', Jar) {
|
||||||
|
dependsOn javadoc
|
||||||
|
dependsOn project(':common').tasks.named('javadoc') // 显式依赖common的javadoc
|
||||||
|
|
||||||
|
archiveClassifier.set('javadoc')
|
||||||
|
from javadoc.destinationDir
|
||||||
|
from project(':common').javadoc.destinationDir
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保build任务包含所有需要的jar
|
||||||
|
tasks.named('build') {
|
||||||
|
dependsOn tasks.named('sourcesJar')
|
||||||
|
dependsOn tasks.named('javadocJar')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理reobf
|
||||||
|
tasks.named('reobfJar') {
|
||||||
|
dependsOn tasks.named('sourcesJar')
|
||||||
|
dependsOn tasks.named('javadocJar')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布配置
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
artifactId = "${mod_id}-forge-${minecraft_version}"
|
||||||
|
artifacts.clear()
|
||||||
|
artifact(tasks.named('reobfJar').get()) {
|
||||||
|
builtBy tasks.named('reobfJar')
|
||||||
|
}
|
||||||
|
artifact(tasks.named('sourcesJar').get()) {
|
||||||
|
builtBy tasks.named('sourcesJar')
|
||||||
|
classifier = 'sources'
|
||||||
|
}
|
||||||
|
artifact(tasks.named('javadocJar').get()) {
|
||||||
|
builtBy tasks.named('javadocJar')
|
||||||
|
classifier = 'javadoc'
|
||||||
|
}
|
||||||
|
pom {
|
||||||
|
name = 'Lib39'
|
||||||
|
description = 'Lib39 is a general-purpose dependency library for Minecraft mods.'
|
||||||
|
url = 'https://github.com/3944Realms/lib39'
|
||||||
|
|
||||||
|
properties = [
|
||||||
|
'minecraft.version': project.minecraft_version,
|
||||||
|
'mod.version': project.version,
|
||||||
|
'forge.version': project.forge_version,
|
||||||
|
'java.version': '17'
|
||||||
|
]
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = 'MIT'
|
||||||
|
url = 'https://raw.githubusercontent.com/3944Realms/lib39/refs/heads/main/LICENSE'
|
||||||
|
distribution = 'repo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = 'R3944Realms'
|
||||||
|
name = "${mod_author}"
|
||||||
|
email = 'f256198830@hotmail.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scm {
|
||||||
|
connection = 'scm:git:https://github.com/3944Realms/lib39.git'
|
||||||
|
developerConnection = 'scm:git:ssh://git@github.com:3944Realms/lib39.git'
|
||||||
|
url = 'https://github.com/3944Realms/lib39'
|
||||||
|
tag = 'main'
|
||||||
|
}
|
||||||
|
|
||||||
|
issueManagement {
|
||||||
|
system = 'GitHub'
|
||||||
|
url = 'https://github.com/3944Realms/lib39/issues'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理资源
|
||||||
|
processResources {
|
||||||
|
from project(':common').sourceSets.main.resources
|
||||||
|
inputs.property "version", project.version
|
||||||
|
inputs.property "minecraft_version", minecraft_version
|
||||||
|
inputs.property "forge_version", forge_version
|
||||||
|
inputs.property "mod_id", mod_id
|
||||||
|
inputs.property "mod_name", mod_name
|
||||||
|
inputs.property "description", description
|
||||||
|
inputs.property "mod_author", mod_author
|
||||||
|
|
||||||
|
filesMatching(['META-INF/mods.toml', 'pack.mcmeta']) {
|
||||||
|
expand([
|
||||||
|
version: project.version,
|
||||||
|
minecraft_version: minecraft_version,
|
||||||
|
forge_version: forge_version,
|
||||||
|
mod_id: mod_id,
|
||||||
|
mod_name: mod_name,
|
||||||
|
description: description,
|
||||||
|
mod_author: mod_author
|
||||||
|
])
|
||||||
|
}
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
32
forge/src/main/java/top/r3944realms/lib39/Lib39Forge.java
Normal file
32
forge/src/main/java/top/r3944realms/lib39/Lib39Forge.java
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
package top.r3944realms.lib39;
|
||||||
|
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Lib 39.
|
||||||
|
*/
|
||||||
|
@Mod(top.r3944realms.lib39.Lib39.MOD_ID)
|
||||||
|
public class Lib39Forge {
|
||||||
|
/**
|
||||||
|
* Instantiates a new Lib 39.
|
||||||
|
*/
|
||||||
|
public Lib39Forge() {
|
||||||
|
Lib39.initialize();
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize.
|
||||||
|
*/
|
||||||
|
public static void initialize() {
|
||||||
|
Lib39.LOGGER.info("[Lib39] Initializing Lib39");
|
||||||
|
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||||
|
|
||||||
|
Lib39.LOGGER.info("[Lib39] Initialized Lib39");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package top.r3944realms.lib39.core.register;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus;
|
||||||
|
import net.minecraftforge.registries.DeferredRegister;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import net.minecraftforge.registries.RegistryObject;
|
||||||
|
import top.r3944realms.lib39.Lib39;
|
||||||
|
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Lib 39 block entities.
|
||||||
|
*/
|
||||||
|
public class ForgeLib39BlockEntities {
|
||||||
|
/**
|
||||||
|
* The constant BLOCK_ENTITY_TYPES.
|
||||||
|
*/
|
||||||
|
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITY_TYPES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, Lib39.MOD_ID);
|
||||||
|
/**
|
||||||
|
* The constant DOLL_BLOCK_ENTITY.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("DataFlowIssue")
|
||||||
|
public static final RegistryObject<BlockEntityType<DollBlockEntity>> DOLL_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("doll",
|
||||||
|
() -> BlockEntityType.Builder
|
||||||
|
.of(DollBlockEntity::new, ForgeLib39Blocks.DOLL.get())
|
||||||
|
.build(null)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register.
|
||||||
|
*
|
||||||
|
* @param bus the bus
|
||||||
|
*/
|
||||||
|
public static void register(IEventBus bus) {
|
||||||
|
BLOCK_ENTITY_TYPES.register(bus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package top.r3944realms.lib39.core.register;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus;
|
||||||
|
import net.minecraftforge.registries.DeferredRegister;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import net.minecraftforge.registries.RegistryObject;
|
||||||
|
import top.r3944realms.lib39.Lib39;
|
||||||
|
import top.r3944realms.lib39.content.block.DollBlock;
|
||||||
|
import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Lib 39 blocks.
|
||||||
|
*/
|
||||||
|
public class ForgeLib39Blocks {
|
||||||
|
/**
|
||||||
|
* The constant BLOCKS.
|
||||||
|
*/
|
||||||
|
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Lib39.MOD_ID);
|
||||||
|
|
||||||
|
static {
|
||||||
|
Lib39Blocks.DOLL = BlockRegistryBuilder
|
||||||
|
.create()
|
||||||
|
.withName("doll")
|
||||||
|
.registerBlock(BLOCKS, DollBlock::new)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Register.
|
||||||
|
*
|
||||||
|
* @param bus the bus
|
||||||
|
*/
|
||||||
|
public static void register(IEventBus bus) {
|
||||||
|
BLOCKS.register(bus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package top.r3944realms.lib39.core.register;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus;
|
||||||
|
import net.minecraftforge.registries.DeferredRegister;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import net.minecraftforge.registries.RegistryObject;
|
||||||
|
import top.r3944realms.lib39.Lib39;
|
||||||
|
import top.r3944realms.lib39.content.item.DollItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Ex lib 39 items.
|
||||||
|
*/
|
||||||
|
public class ForgeLib39Items {
|
||||||
|
/**
|
||||||
|
* The constant ITEMS.
|
||||||
|
*/
|
||||||
|
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Lib39.MOD_ID);
|
||||||
|
|
||||||
|
static {
|
||||||
|
Lib39Items.DOLL = ITEMS.register("doll", () -> new DollItem(new Item.Properties()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register.
|
||||||
|
*
|
||||||
|
* @param bus the bus
|
||||||
|
*/
|
||||||
|
public static void register(IEventBus bus) {
|
||||||
|
ITEMS.register(bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package top.r3944realms.lib39.core.register;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus;
|
||||||
|
import net.minecraftforge.registries.DeferredRegister;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import net.minecraftforge.registries.RegistryObject;
|
||||||
|
import top.r3944realms.lib39.Lib39;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Lib 39 sound events.
|
||||||
|
*/
|
||||||
|
public class ForgeLib39SoundEvents {
|
||||||
|
/**
|
||||||
|
* The constant SOUND_EVENTS.
|
||||||
|
*/
|
||||||
|
public static final DeferredRegister<SoundEvent> SOUND_EVENTS = DeferredRegister.create(ForgeRegistries.SOUND_EVENTS, Lib39.MOD_ID);
|
||||||
|
|
||||||
|
static {
|
||||||
|
Lib39SoundEvents.DUCK_TOY = SOUND_EVENTS.register("duck_toy",
|
||||||
|
() -> SoundEvent.createFixedRangeEvent(Lib39SoundEvents.RL_DUCK_TOY, 32.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register.
|
||||||
|
*
|
||||||
|
* @param bus the bus
|
||||||
|
*/
|
||||||
|
public static void register(IEventBus bus) {
|
||||||
|
SOUND_EVENTS.register(bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package top.r3944realms.lib39.datagen.provider;
|
||||||
|
|
||||||
|
import net.minecraft.data.PackOutput;
|
||||||
|
import net.minecraft.data.loot.LootTableProvider;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.level.storage.loot.LootTable;
|
||||||
|
import net.minecraft.world.level.storage.loot.ValidationContext;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Simple loot table provider.
|
||||||
|
*/
|
||||||
|
public class SimpleLootTableProvider extends LootTableProvider {
|
||||||
|
/**
|
||||||
|
* Instantiates a new Simple loot table provider.
|
||||||
|
*
|
||||||
|
* @param output the output
|
||||||
|
* @param subProvidersWrapper the sub providers wrapper
|
||||||
|
*/
|
||||||
|
public SimpleLootTableProvider(PackOutput output, @NotNull SubProvidersWrapper subProvidersWrapper) {
|
||||||
|
super(output, Set.of(), subProvidersWrapper.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Simple loot table provider.
|
||||||
|
*
|
||||||
|
* @param output the output
|
||||||
|
* @param requiredTables the required tables
|
||||||
|
* @param subProvidersWrapper the sub providers wrapper
|
||||||
|
*/
|
||||||
|
public SimpleLootTableProvider(PackOutput output, Set<ResourceLocation> requiredTables, @NotNull SubProvidersWrapper subProvidersWrapper) {
|
||||||
|
super(output, requiredTables, subProvidersWrapper.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void validate(@NotNull Map<ResourceLocation, LootTable> map, @NotNull ValidationContext validationcontext) {
|
||||||
|
map.forEach((id, table) -> table.validate(validationcontext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package top.r3944realms.lib39.datagen.provider;
|
||||||
|
|
||||||
|
import net.minecraft.data.loot.LootTableProvider;
|
||||||
|
import net.minecraft.data.loot.LootTableSubProvider;
|
||||||
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet;
|
||||||
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
||||||
|
import top.r3944realms.lib39.datagen.provider.subprovider.BlockLootTables;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Sub providers wrapper.
|
||||||
|
*/
|
||||||
|
public class SubProvidersWrapper {
|
||||||
|
/**
|
||||||
|
* The Entries.
|
||||||
|
*/
|
||||||
|
public List<LootTableProvider.SubProviderEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Sub providers wrapper.
|
||||||
|
*/
|
||||||
|
public SubProvidersWrapper() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add entry sub providers wrapper.
|
||||||
|
*
|
||||||
|
* @param entry the entry
|
||||||
|
* @return the sub providers wrapper
|
||||||
|
*/
|
||||||
|
public SubProvidersWrapper addEntry(LootTableProvider.SubProviderEntry entry) {
|
||||||
|
entries.add(entry);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add entry sub providers wrapper.
|
||||||
|
*
|
||||||
|
* @param subProvider the sub provider
|
||||||
|
* @param subParamSet the sub param set
|
||||||
|
* @return the sub providers wrapper
|
||||||
|
*/
|
||||||
|
public SubProvidersWrapper addEntry(LootTableSubProvider subProvider, LootContextParamSet subParamSet) {
|
||||||
|
entries.add(new LootTableProvider.SubProviderEntry(() -> subProvider, subParamSet));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add block entry sub providers wrapper.
|
||||||
|
*
|
||||||
|
* @param blockLootTables the block loot tables
|
||||||
|
* @return the sub providers wrapper
|
||||||
|
*/
|
||||||
|
public SubProvidersWrapper addBlockEntry(BlockLootTables blockLootTables) {
|
||||||
|
entries.add(new LootTableProvider.SubProviderEntry(() -> blockLootTables, LootContextParamSets.BLOCK));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,385 @@
|
||||||
|
package top.r3944realms.lib39.datagen.provider.subprovider;
|
||||||
|
|
||||||
|
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||||
|
import net.minecraft.data.loot.BlockLootSubProvider;
|
||||||
|
import net.minecraft.world.flag.FeatureFlags;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.enchantment.Enchantments;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.FlowerPotBlock;
|
||||||
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
|
import net.minecraft.world.level.storage.loot.LootPool;
|
||||||
|
import net.minecraft.world.level.storage.loot.LootTable;
|
||||||
|
import net.minecraft.world.level.storage.loot.entries.LootItem;
|
||||||
|
import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount;
|
||||||
|
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction;
|
||||||
|
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
|
||||||
|
import net.minecraft.world.level.storage.loot.predicates.MatchTool;
|
||||||
|
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
|
||||||
|
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
|
||||||
|
import net.minecraftforge.registries.DeferredRegister;
|
||||||
|
import net.minecraftforge.registries.RegistryObject;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type Block loot tables.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class BlockLootTables extends BlockLootSubProvider {
|
||||||
|
|
||||||
|
private final List<BlockEntry> blockEntries = new ArrayList<>();
|
||||||
|
private final DeferredRegister<Block> knowBlocks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Block loot tables.
|
||||||
|
*
|
||||||
|
* @param deferredRegister the deferred register
|
||||||
|
*/
|
||||||
|
public BlockLootTables(DeferredRegister<Block> deferredRegister) {
|
||||||
|
super(Set.of(), FeatureFlags.REGISTRY.allFlags());
|
||||||
|
knowBlocks = deferredRegister;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull Iterable<Block> getKnownBlocks() {
|
||||||
|
return knowBlocks.getEntries().stream().map(RegistryObject::get).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 流畅 API 构建方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加自掉落的方块
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
*/
|
||||||
|
public void dropSelf(RegistryObject<Block> block) {
|
||||||
|
addEntry(block, this::createSingleItemTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量添加自掉落的方块
|
||||||
|
*
|
||||||
|
* @param blocks the blocks
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
@Contract("_ -> this")
|
||||||
|
@SafeVarargs
|
||||||
|
public final BlockLootTables dropSelf(RegistryObject<Block> @NotNull ... blocks) {
|
||||||
|
for (RegistryObject<Block> block : blocks) {
|
||||||
|
dropSelf(block);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加需要丝绸之触才掉落的方块
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropWhenSilkTouch(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, BlockLootSubProvider::createSilkTouchOnlyTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加掉落其他物品的方块
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @param item the item
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropOther(RegistryObject<Block> block, RegistryObject<? extends ItemLike> item) {
|
||||||
|
return addEntry(block, pBlock -> this.createSingleItemTable(item.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加只能被剪子剪下的方块
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropWhenShears(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, BlockLootSubProvider::createShearsOnlyDrop);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加矿物的掉落表(支持时运附魔)
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @param oreItem the ore item
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropOre(RegistryObject<Block> block, RegistryObject<Item> oreItem) {
|
||||||
|
return addEntry(block, b -> this.createOreDrop(b, oreItem.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加红石矿石掉落表
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropRedstoneOre(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, this::createRedstoneOreDrops);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加青金石矿石掉落表
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropLapisOre(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, this::createLapisOreDrops);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加铜矿石掉落表
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropCopperOre(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, this::createCopperOreDrops);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加地毯类方块的掉落(一次掉落2个)
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropCarpet(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, b -> LootTable.lootTable()
|
||||||
|
.withPool(LootPool.lootPool()
|
||||||
|
.setRolls(ConstantValue.exactly(1))
|
||||||
|
.add(LootItem.lootTableItem(b)
|
||||||
|
.apply(SetItemCountFunction.setCount(ConstantValue.exactly(2.0F))))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加台阶方块的掉落
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropSlab(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, this::createSlabItemTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加门方块的掉落(只掉落下半部分)
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropDoor(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, this::createDoorTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加花盆的掉落
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropFlowerPot(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, (pBlock) -> this.createPotFlowerItemTable(((FlowerPotBlock)pBlock).getContent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加树叶的掉落
|
||||||
|
*
|
||||||
|
* @param leavesBlock the leaves block
|
||||||
|
* @param saplingBlock the sapling block
|
||||||
|
* @param chances the chances
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropLeaves(RegistryObject<Block> leavesBlock,
|
||||||
|
RegistryObject<Block> saplingBlock,
|
||||||
|
float... chances) {
|
||||||
|
return addEntry(leavesBlock, b -> this.createLeavesDrops(b, saplingBlock.get(), chances));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加橡树叶的掉落(包含苹果)
|
||||||
|
*
|
||||||
|
* @param leavesBlock the leaves block
|
||||||
|
* @param saplingBlock the sapling block
|
||||||
|
* @param chances the chances
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropOakLeaves(RegistryObject<Block> leavesBlock,
|
||||||
|
RegistryObject<Block> saplingBlock,
|
||||||
|
float... chances) {
|
||||||
|
return addEntry(leavesBlock, b -> this.createOakLeavesDrops(b, saplingBlock.get(), chances));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加农作物的掉落
|
||||||
|
*
|
||||||
|
* @param cropBlock the crop block
|
||||||
|
* @param cropItem the crop item
|
||||||
|
* @param seedsItem the seeds item
|
||||||
|
* @param ageProperty the age property
|
||||||
|
* @param maxAge the max age
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables dropCrop(RegistryObject<Block> cropBlock,
|
||||||
|
RegistryObject<Item> cropItem,
|
||||||
|
RegistryObject<Item> seedsItem,
|
||||||
|
Property<Integer> ageProperty,
|
||||||
|
int maxAge) {
|
||||||
|
return addEntry(cropBlock, b -> this.createCropDrops(
|
||||||
|
b,
|
||||||
|
cropItem.get(),
|
||||||
|
seedsItem.get(),
|
||||||
|
LootItemBlockStatePropertyCondition.hasBlockStateProperties(b)
|
||||||
|
.setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties()
|
||||||
|
.hasProperty(ageProperty, maxAge))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义掉落表
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @param factory the factory
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables custom(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {
|
||||||
|
return addEntry(block, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 没有掉落
|
||||||
|
*
|
||||||
|
* @param block the block
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables noDrop(RegistryObject<Block> block) {
|
||||||
|
return addEntry(block, b -> noDrop());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 批量操作方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量操作一系列方块
|
||||||
|
*
|
||||||
|
* @param blocks the blocks
|
||||||
|
* @param operation the operation
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables batch(@NotNull Iterable<RegistryObject<Block>> blocks,
|
||||||
|
Function<RegistryObject<Block>, BlockLootTables> operation) {
|
||||||
|
for (RegistryObject<Block> block : blocks) {
|
||||||
|
operation.apply(block);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对一组方块应用相同的操作
|
||||||
|
*
|
||||||
|
* @param operation the operation
|
||||||
|
* @param blocks the blocks
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
@Contract("_, _ -> this")
|
||||||
|
@SafeVarargs
|
||||||
|
public final BlockLootTables applyToAll(Function<RegistryObject<Block>, BlockLootTables> operation,
|
||||||
|
RegistryObject<Block> @NotNull ... blocks) {
|
||||||
|
for (RegistryObject<Block> block : blocks) {
|
||||||
|
operation.apply(block);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 构建方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建并返回自身(用于流畅API链式调用)
|
||||||
|
*
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
public BlockLootTables build() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generate() {
|
||||||
|
for (BlockEntry entry : blockEntries) {
|
||||||
|
this.add(entry.block.get(), entry.factory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 内部类和方法 ====================
|
||||||
|
|
||||||
|
private BlockLootTables addEntry(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {
|
||||||
|
blockEntries.add(new BlockEntry(block, factory));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record BlockEntry(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {}
|
||||||
|
|
||||||
|
// ==================== 静态工厂方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新的战利品表生成器
|
||||||
|
*
|
||||||
|
* @param deferredRegister the deferred register
|
||||||
|
* @return the block loot tables
|
||||||
|
*/
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull BlockLootTables create(DeferredRegister<Block> deferredRegister) {
|
||||||
|
return new BlockLootTables(deferredRegister);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速创建基本矿物的掉落表
|
||||||
|
*
|
||||||
|
* @param oreBlock the ore block
|
||||||
|
* @param oreItem the ore item
|
||||||
|
* @return the loot table .@ not null builder
|
||||||
|
*/
|
||||||
|
public static LootTable.@NotNull Builder simpleOreDrop(Block oreBlock, Item oreItem) {
|
||||||
|
return LootTable.lootTable()
|
||||||
|
.withPool(LootPool.lootPool()
|
||||||
|
.setRolls(ConstantValue.exactly(1))
|
||||||
|
.add(LootItem.lootTableItem(oreItem)
|
||||||
|
.when(MatchTool.toolMatches(ItemPredicate.Builder.item()
|
||||||
|
.hasEnchantment(new net.minecraft.advancements.critereon.EnchantmentPredicate(
|
||||||
|
Enchantments.SILK_TOUCH,
|
||||||
|
net.minecraft.advancements.critereon.MinMaxBounds.Ints.atLeast(1)
|
||||||
|
))).invert())
|
||||||
|
.apply(SetItemCountFunction.setCount(UniformGenerator.between(1, 1)))
|
||||||
|
.apply(ApplyBonusCount.addOreBonusCount(Enchantments.BLOCK_FORTUNE))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速创建石质方块的掉落表(需要镐子)
|
||||||
|
*
|
||||||
|
* @param stoneBlock the stone block
|
||||||
|
* @param dropItem the drop item
|
||||||
|
* @return the loot table .@ not null builder
|
||||||
|
*/
|
||||||
|
public static LootTable.@NotNull Builder stoneDrop(Block stoneBlock, Item dropItem) {
|
||||||
|
return LootTable.lootTable()
|
||||||
|
.withPool(LootPool.lootPool()
|
||||||
|
.setRolls(ConstantValue.exactly(1))
|
||||||
|
.add(LootItem.lootTableItem(dropItem)
|
||||||
|
.when(MatchTool.toolMatches(ItemPredicate.Builder.item()
|
||||||
|
.hasEnchantment(new net.minecraft.advancements.critereon.EnchantmentPredicate(
|
||||||
|
Enchantments.SILK_TOUCH,
|
||||||
|
net.minecraft.advancements.critereon.MinMaxBounds.Ints.atLeast(1)
|
||||||
|
))))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package top.r3944realms.lib39.platform;
|
||||||
|
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||||
|
import net.minecraftforge.fml.loading.FMLLoader;
|
||||||
|
import top.r3944realms.lib39.Lib39;
|
||||||
|
import top.r3944realms.lib39.platform.services.IPlatformHelper;
|
||||||
|
|
||||||
|
public class ForgePlatformHelper implements IPlatformHelper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPlatformName() {
|
||||||
|
return "Forge";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isModLoaded(String modId) {
|
||||||
|
return ModList.get().isLoaded(modId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDevelopmentEnvironment() {
|
||||||
|
return !FMLLoader.isProduction();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClientEnvironment() {
|
||||||
|
return FMLEnvironment.dist.isClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getModVersion() {
|
||||||
|
return ModList.get()
|
||||||
|
.getModContainerById(Lib39.MOD_ID)
|
||||||
|
.map(c -> c.getModInfo().getVersion().toString())
|
||||||
|
.orElse("UNKNOWN");
|
||||||
|
}
|
||||||
|
}
|
||||||
26
forge/src/main/resources/META-INF/mods.toml
Normal file
26
forge/src/main/resources/META-INF/mods.toml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
modLoader = "javafml" #mandatory
|
||||||
|
loaderVersion = "${forge_loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See https://files.minecraftforge.net/ for a list of versions.
|
||||||
|
license = "${license}" # Review your options at https://choosealicense.com/.
|
||||||
|
#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional
|
||||||
|
[[mods]] #mandatory
|
||||||
|
modId = "${mod_id}" #mandatory
|
||||||
|
version = "${version}" #mandatory
|
||||||
|
displayName = "${mod_name}" #mandatory
|
||||||
|
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional (see https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/)
|
||||||
|
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional (displayed in the mod UI)
|
||||||
|
logoFile = "lib39_logo.png" #optional (needs to be in the root of your mod jar (root of your 'resources' folder))
|
||||||
|
credits = "Thanks for this example mod goes to Java" #optional
|
||||||
|
authors = "${mod_author}" #optional
|
||||||
|
description = '''${description}''' #mandatory (Supports multiline text)
|
||||||
|
[[dependencies.${mod_id}]] #optional
|
||||||
|
modId = "forge" #mandatory
|
||||||
|
mandatory = true #mandatory
|
||||||
|
versionRange = "[${forge_version},)" #mandatory
|
||||||
|
ordering = "NONE" # The order that this dependency should load in relation to your mod, required to be either 'BEFORE' or 'AFTER' if the dependency is not mandatory
|
||||||
|
side = "BOTH" # Side this dependency is applied on - 'BOTH', 'CLIENT' or 'SERVER'
|
||||||
|
[[dependencies.${mod_id}]]
|
||||||
|
modId = "minecraft"
|
||||||
|
mandatory = true
|
||||||
|
versionRange = "${minecraft_version_range}"
|
||||||
|
ordering = "NONE"
|
||||||
|
side = "BOTH"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user