将库更新到1.21.10的版本

This commit is contained in:
叁玖领域 2025-11-01 23:23:28 +08:00
parent 8919d7ddae
commit eff8d64aed
57 changed files with 2449 additions and 1984 deletions

View File

@ -1,25 +1,24 @@
//file:noinspection GroovyAssignabilityCheck
plugins {
id 'java'
id 'idea'
id 'java-library'
id 'maven-publish'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'net.neoforged.moddev.legacyforge' version '2.0.103'
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(17)
id 'net.neoforged.moddev' version '2.0.116'
id 'idea'
}
tasks.named('wrapper', Wrapper).configure {
// Define wrapper values here so as to not have to always do so when updating gradlew.properties.
// Switching this to Wrapper.DistributionType.ALL will download the full gradle sources that comes with
// documentation attached on cursor hover of gradle classes and methods. However, this comes with increased
// file size for Gradle. If you do switch this to ALL, run the Gradle wrapper task twice afterwards.
// (Verify by checking gradle/wrapper/gradle-wrapper.properties to see if distributionUrl now points to `-all`)
distributionType = Wrapper.DistributionType.BIN
}
version = "${minecraft_version}-${mod_version}"
version = mod_version
group = mod_group_id
repositories {
// Add here additional repositories if required by some of the dependencies below.
mavenLocal()
maven { url = "https://libraries.minecraft.net/" }
maven { url = "https://neoforged.forgecdn.net/releases" }
@ -38,24 +37,31 @@ 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)
// Mojang ships Java 21 to end users in 1.21.10, so mods should target Java 21.
java.toolchain.languageVersion = JavaLanguageVersion.of(21)
legacyForge {
// Specify the version of MinecraftForge to use.
version = project.minecraft_version + '-' + project.forge_version
neoForge {
// Specify the version of NeoForge to use.
version = project.neo_version
parchment {
mappingsVersion = project.parchment_mappings_version
minecraftVersion = project.parchment_minecraft_version
}
// This line is optional. Access Transformers are automatically detected
accessTransformers = project.files('src/main/templates/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
client {
client()
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
clientAuth{
devLogin = true
client()
@ -64,27 +70,47 @@ legacyForge {
server {
server()
programArgument '--nogui'
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
// This run config launches GameTestServer and runs all registered gametests, then exits.
// By default, the server will crash when no gametests are provided.
// The gametest system is also enabled by default for other run configs under the /test command.
gameTestServer {
type = "gameTestServer"
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
data {
data()
clientData()
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
// gameDirectory = project.file('run-data')
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
}
// applies to all the run configs above
configureEach {
// Recommended logging data for a userdev environment
// The markers can be added/remove as needed separated by commas.
// "SCAN": For mods scan.
// "REGISTRIES": For firing of registry events.
// "REGISTRYDUMP": For getting the contents of all registries.
systemProperty 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
// You can set various levels here.
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
logLevel = org.slf4j.event.Level.DEBUG
}
}
mods {
// define mod <-> source bindings
// these are used to tell the game which sources are for which mod
// multi mod projects should define one per mod
"${mod_id}" {
sourceSet(sourceSets.main)
}
@ -94,267 +120,86 @@ legacyForge {
// Include resources generated by data generators.
sourceSets.main.resources { srcDir 'src/generated/resources' }
// Sets up a dependency configuration called 'localRuntime'.
// This configuration should be used instead of 'runtimeOnly' to declare
// a dependency that will be present for runtime testing but that is
// "optional", meaning it will not be pulled by dependents of this mod.
configurations {
runtimeClasspath.extendsFrom localRuntime
}
obfuscation {
createRemappingConfiguration(configurations.localRuntime)
}
dependencies {
//
// Example optional mod dependency with JEI
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
// compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}"
// compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}"
// We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it
// localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}"
// Example mod dependency using a mod jar from ./libs with a flat dir repository
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
// The group id is ignored when searching -- in this case, it is "blank"
// implementation "blank:coolmod-${mc_version}:${coolmod_version}"
// Example mod dependency using a file as dependency
// implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar")
// Example project dependency using a sister or child project:
// implementation project(":myproject")
// For more info:
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
}
// This block of code expands all declared replace properties in the specified resource targets.
// A missing property will result in an error. Properties are expanded using ${} Groovy notation.
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
minecraft_version : minecraft_version,
minecraft_version_range: minecraft_version_range,
neo_version : neo_version,
mod_id : mod_id,
mod_name : mod_name,
mod_license : mod_license,
mod_version : mod_version,
mod_authors : mod_authors,
mod_credits : mod_credits,
mod_description : mod_description
]
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
inputs.properties replaceProperties
expand replaceProperties
from "src/main/templates"
into "build/generated/sources/modMetadata"
}
// Include the output of "generateModMetadata" as an input directory for the build
// this works with both building through Gradle and the IDE.
sourceSets.main.resources.srcDir generateModMetadata
legacyForge.ideSyncTask generateModMetadata
// To avoid having to run "generateModMetadata" manually, make it run on every project reload
neoForge.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)
}
source = sourceSets.main.allJava
classpath = configurations.compileClasspath
exclude '**/test/**'
exclude '**/internal/**'
}
tasks.register('javadocJar', Jar) {
archiveClassifier.set('javadoc')
from tasks.javadoc
dependsOn tasks.javadoc
}
// ===================== Jarclass + java =====================
tasks.register('deobfJar', Jar) {
archiveFileName = "${mod_id}-${minecraft_version}-${mod_version}.jar"
from(sourceSets.main.output)
// === 使 jar ===
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
attributes(
'Specification-Title': mod_id,
'Specification-Vendor': mod_authors,
'Implementation-Title': project.name,
'Implementation-Version': archiveVersion,
'Implementation-Vendor': mod_authors,
'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
)
}
dependsOn classes
}
// ==================== ====================
// Example configuration to allow publishing using the maven-publish plugin
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = mod_id
artifact deobfJar
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'
}
}
register('mavenJava', MavenPublication) {
from components.java
}
}
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') ?: ''
}
url "file://${project.projectDir}/repo"
}
}
}
// ==================== ====================
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation']
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
}
// 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.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 no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
idea {
module {
downloadSources = true
downloadJavadoc = true
}
}
//
tasks.withType(GenerateModuleMetadata) {
enabled = false
}
afterEvaluate {
tasks.named('deobfJar') {
doLast {
def jar = file(layout.buildDirectory.dir("repo") + "${mod_id}-${minecraft_version}-${mod_version}.jar")
if (jar.exists()) ant.delete(jar)
}
}
}

View File

@ -7,22 +7,18 @@ org.gradle.configuration-cache=false
#read more on this at https://github.com/neoforged/ModDevGradle?tab=readme-ov-file#better-minecraft-parameter-names--javadoc-parchment
# you can also find the latest versions at: https://parchmentmc.org/docs/getting-started
parchment_minecraft_version=1.20.1
parchment_mappings_version=2023.09.03
parchment_minecraft_version=1.21.10
parchment_mappings_version=2025.10.12
# Environment Properties
# You can find the latest versions here: https://files.minecraftforge.net/net/minecraftforge/forge/index_1.20.1.html
# The Minecraft version must agree with the Forge version to get a valid artifact
minecraft_version=1.20.1
minecraft_version=1.21.10
# The Minecraft version range can use any release version of Minecraft as bounds.
# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly
# as they do not follow standard versioning conventions.
minecraft_version_range=[1.20.1, 1.21)
# The Forge version must agree with the Minecraft version to get a valid artifact
forge_version=47.1.3
# The Forge version range can use any version of Forge as bounds
forge_version_range=[47.1.3,)
# The loader version range can only use the major version of FML as bounds
loader_version_range=[47,)
minecraft_version_range=[1.21.10]
neo_version=21.10.42-beta
## Mod Properties
# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63}

View File

@ -1,12 +1,11 @@
//file:noinspection GroovyAssignabilityCheck
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
maven { url = 'https://maven.neoforged.net/releases' }
maven { url = 'https://maven.parchmentmc.org' } // Add this line
maven { url = 'https://maven.parchmentmc.org' }
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}
}

View File

@ -1,2 +0,0 @@
// 1.20.1 2025-10-25T19:14:21.1829335 Languages: zh_tw
4cb94c651f6aa74538a2ab25cb183cffd75be688 assets/lib39/lang/zh_tw.json

View File

@ -1,2 +0,0 @@
// 1.20.1 2025-10-25T19:14:21.1764262 Languages: zh_cn
a1db601a0fca923c5434b8b0843773a3115d0b59 assets/lib39/lang/zh_cn.json

View File

@ -1,3 +0,0 @@
// 1.20.1 2025-10-25T18:42:29.3405259 Item Models: lib39
14f581c8f8e7f0de004c57a180f371e60e7b12ae assets/lib39/models/item/fabric.json
447b36747d0aa8748dcd86715f4cce2cff19aca7 assets/lib39/models/item/neoforge.json

View File

@ -1,2 +0,0 @@
// 1.20.1 2025-10-25T19:14:21.1804258 Languages: lzh
098df025475d3f4d9d2abefa91a7d38c44644ba4 assets/lib39/lang/lzh.json

View File

@ -1,2 +0,0 @@
// 1.20.1 2025-10-25T19:14:21.1784269 Languages: en_us
c6b6aadca0a922823a8c949ebde93f8f999737f9 assets/lib39/lang/en_us.json

View File

@ -1,4 +0,0 @@
{
"item.lib39.fabric": "Fabric",
"item.lib39.neoforge": "NeoForge"
}

View File

@ -1,4 +0,0 @@
{
"item.lib39.fabric": "織",
"item.lib39.neoforge": "狸"
}

View File

@ -1,4 +0,0 @@
{
"item.lib39.fabric": "织布",
"item.lib39.neoforge": "小狐狸"
}

View File

@ -1,4 +0,0 @@
{
"item.lib39.fabric": "織布",
"item.lib39.neoforge": "狐狸"
}

View File

@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "lib39:item/fabric"
}
}

View File

@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "lib39:item/neoforge"
}
}

View File

@ -1,11 +1,12 @@
package top.r3944realms.lib39;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.loading.FMLEnvironment;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModList;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.r3944realms.lib39.core.network.NetworkHandler;
import top.r3944realms.lib39.example.Lib39Example;
/**
@ -25,19 +26,18 @@ public class Lib39 {
/**
* Instantiates a new Lib 39.
*/
public Lib39() {
initialize();
public Lib39(IEventBus event) {
initialize(event);
}
/**
* Initialize.
*/
public static void initialize() {
public static void initialize(IEventBus event) {
LOGGER.info("[Lib39] Initializing Lib39");
NetworkHandler.register();
if (shouldRegisterExamples()) {
LOGGER.info("[Lib39] Registering Examples");
registerExamples();
registerExamples(event);
}
LOGGER.info("[Lib39] Initialized Lib39");
@ -66,17 +66,17 @@ public class Lib39 {
* @return the boolean
*/
static boolean shouldRegisterExamples() {
return !FMLEnvironment.production;
return !FMLEnvironment.isProduction();
}
/**
* Register examples.
*/
static void registerExamples() {
static void registerExamples(IEventBus event) {
LOGGER.info("[Lib39] Starting example demonstrations");
try {
// 创建示例实例并演示功能
Lib39Example example = new Lib39Example();
Lib39Example example = new Lib39Example(event);
example.demonstrateFeature();
LOGGER.info("[Lib39] Example demonstrations completed successfully");

View File

@ -1,8 +1,8 @@
package top.r3944realms.lib39.api.event;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import net.neoforged.bus.api.Event;
import net.neoforged.fml.event.IModBusEvent;
import top.r3944realms.lib39.core.compat.CompatManager;
import top.r3944realms.lib39.core.compat.ICompat;

View File

@ -2,8 +2,7 @@ package top.r3944realms.lib39.api.event;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.eventbus.api.Event;
import net.neoforged.bus.api.Event;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.ISyncManager;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
@ -39,22 +38,6 @@ public class SyncManagerRegisterEvent extends Event {
return syncs2Manager;
}
/**
* 类型安全的同步管理器注册
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param id the id
* @param syncManager the sync manager
* @param capability the capability
*/
public <K, T extends ISyncData<?>> void registerSyncManager(
ResourceLocation id,
ISyncManager<K, T> syncManager,
Capability<T> capability
) {
syncs2Manager.registerManager(id, syncManager, entity -> entity.getCapability(capability).resolve());
}
/**
* 类型安全的同步管理器注册
@ -103,16 +86,6 @@ public class SyncManagerRegisterEvent extends Event {
syncs2Manager.disallowEntityClass(id, entityClasses);
}
/**
* 绑定能力用于分离注册的情况
*
* @param <T> the type parameter
* @param id 必须先注册安全同步管理器再绑定Cap否则会抛出{@link IllegalStateException 未找到对应安全同步管理器}
* @param capability the capability
*/
public <T extends ISyncData<?>> void bindCapability(ResourceLocation id, Capability<T> capability) {
syncs2Manager.bindDataGetter(id, entity -> entity.getCapability(capability).resolve());
}
/**
* 解绑数据提供者
@ -123,27 +96,6 @@ public class SyncManagerRegisterEvent extends Event {
syncs2Manager.unbindDataProvider(id);
}
/**
* 完整的类型安全注册
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param id the id
* @param syncManager the sync manager
* @param capability the capability
* @param allowedEntityClasses the allowed entity classes
*/
public <K, T extends ISyncData<?>> void registerComplete(
ResourceLocation id,
ISyncManager<K, T> syncManager,
Capability<T> capability,
Class<?>... allowedEntityClasses
) {
registerSyncManager(id, syncManager, capability);
if (allowedEntityClasses.length > 0) {
addAllowEntityClass(id, allowedEntityClasses);
}
}
/**
* 完整的类型安全注册

View File

@ -1,9 +1,8 @@
package top.r3944realms.lib39.core.compat;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.common.Mod;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.IEventBus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
@ -58,7 +57,7 @@ public class CompatManager {
* @param compat the compat
*/
public void registerCompat(String namespace, String path, ICompat compat) {
registerCompat(new ResourceLocation(namespace, path), compat);
registerCompat(ResourceLocation.fromNamespaceAndPath(namespace, path), compat);
}
/**
@ -67,8 +66,8 @@ public class CompatManager {
* @param dists the dists
* @param bus the bus
*/
public void addListenerForAll(@Nullable Dist dists, Mod.EventBusSubscriber.Bus bus) {
listenerConfigs.add(new ListenerConfig(null, dists, bus));
public void addListenerForAll(@Nullable Dist dists, boolean isModbus) {
listenerConfigs.add(new ListenerConfig(null, dists, isModbus));
}
/**
@ -78,8 +77,8 @@ public class CompatManager {
* @param dists the dists
* @param bus the bus
*/
public void addListenerForCompat(ResourceLocation compatId, @Nullable Dist dists, Mod.EventBusSubscriber.Bus bus) {
listenerConfigs.add(new ListenerConfig(compatId, dists, bus));
public void addListenerForCompat(ResourceLocation compatId, @Nullable Dist dists, boolean isModbus) {
listenerConfigs.add(new ListenerConfig(compatId, dists, isModbus));
}
/**
@ -89,8 +88,8 @@ public class CompatManager {
* @param bus the bus
* @param consumer the consumer
*/
public void addListenerForLoaded(@Nullable Dist dists, Mod.EventBusSubscriber.Bus bus, Consumer<IEventBus> consumer) {
listenerConfigs.add(new ListenerConfig(null, dists, bus) {
public void addListenerForLoaded(@Nullable Dist dists, boolean isModbus, Consumer<IEventBus> consumer) {
listenerConfigs.add(new ListenerConfig(null, dists, isModbus) {
@Override
boolean shouldApply(@NotNull ICompat compat) {
return super.shouldApply(compat);
@ -167,14 +166,14 @@ public class CompatManager {
if (config.dists != null) {
switch (config.dists) {
case CLIENT -> {
if (config.bus == Mod.EventBusSubscriber.Bus.FORGE) {
if (!config.isModBus) {
compat.addClientGameListener(gameEventBus);
} else {
compat.addClientModListener(modEventBus);
}
}
case DEDICATED_SERVER -> {
if (config.bus == Mod.EventBusSubscriber.Bus.FORGE) {
if (!config.isModBus) {
compat.addServerGameListener(gameEventBus);
} else {
compat.addServerModListener(modEventBus);
@ -183,7 +182,7 @@ public class CompatManager {
}
} else {
// 通用监听器
if (config.bus == Mod.EventBusSubscriber.Bus.FORGE) {
if (!config.isModBus) {
compat.addCommonGameListener(gameEventBus);
} else {
compat.addCommonModListener(modEventBus);
@ -204,9 +203,9 @@ public class CompatManager {
private @NotNull String getListenerTypeName(@NotNull ListenerConfig config) {
if (config.dists != null) {
return config.dists.name().toLowerCase() + " " +
(config.bus == Mod.EventBusSubscriber.Bus.FORGE ? "game" : "mod");
(!config.isModBus ? "game" : "mod");
} else {
return "common " + (config.bus == Mod.EventBusSubscriber.Bus.FORGE ? "game" : "mod");
return "common " + (!config.isModBus ? "game" : "mod");
}
}
@ -217,8 +216,8 @@ public class CompatManager {
* @param compatId the compat id
* @param bus the bus
*/
public void addListenerForCompat(ResourceLocation compatId, Mod.EventBusSubscriber.Bus bus) {
addListenerForCompat(compatId, null, bus);
public void addListenerForCompat(ResourceLocation compatId, boolean isModbus) {
addListenerForCompat(compatId, null, isModbus);
}
private static class ListenerConfig {
@ -233,7 +232,7 @@ public class CompatManager {
/**
* The Bus.
*/
final Mod.EventBusSubscriber.Bus bus;
final boolean isModBus;
/**
* Instantiates a new Listener config.
@ -242,10 +241,10 @@ public class CompatManager {
* @param dists the dists
* @param bus the bus
*/
ListenerConfig(ResourceLocation compatId, Dist dists, Mod.EventBusSubscriber.Bus bus) {
ListenerConfig(ResourceLocation compatId, Dist dists, boolean isModbus) {
this.compatId = compatId;
this.dists = dists;
this.bus = bus;
this.isModBus = isModbus;
}
/**

View File

@ -1,7 +1,7 @@
package top.r3944realms.lib39.core.compat;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.eventbus.api.IEventBus;
import net.neoforged.bus.api.IEventBus;
import java.util.concurrent.Callable;

View File

@ -7,21 +7,24 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
import net.minecraftforge.event.entity.EntityLeaveLevelEvent;
import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.RegistryObject;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModLoadingContext;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
import net.neoforged.neoforge.event.entity.EntityLeaveLevelEvent;
import net.neoforged.neoforge.event.level.LevelEvent;
import net.neoforged.neoforge.event.tick.ServerTickEvent;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import net.neoforged.neoforge.registries.DeferredHolder;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.api.event.RegisterCompatEvent;
import top.r3944realms.lib39.api.event.SyncManagerRegisterEvent;
import top.r3944realms.lib39.core.compat.CompatManager;
import top.r3944realms.lib39.core.network.NetworkHandler;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
@ -38,7 +41,7 @@ public class CommonEventHandler {
* The type Game.
*/
@SuppressWarnings("unused")
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = Lib39.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.FORGE)
@EventBusSubscriber(modid = Lib39.MOD_ID)
public static class Game extends CommonEventHandler {
private static ServerLevel sl;
@ -79,7 +82,7 @@ public class CommonEventHandler {
synchronized (Game.class) {
if (!isSync2MInitialized) {
syncData2Manager = new SyncData2Manager();
MinecraftForge.EVENT_BUS.post(new SyncManagerRegisterEvent(syncData2Manager));
NeoForge.EVENT_BUS.post(new SyncManagerRegisterEvent(syncData2Manager));
isSync2MInitialized = true;
sl = serverLevel;
Lib39.LOGGER.info("SyncData2Manager initialized on world load");
@ -107,13 +110,11 @@ public class CommonEventHandler {
* @param event the event
*/
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.END) {
if (syncData2Manager == null) return;
if (event.getServer().getTickCount() % 10 == 0)
syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::markDirty)));
syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::checkIfDirtyThenUpdate)));
}
public static void onServerTick(ServerTickEvent.Post event) {
if (syncData2Manager == null) return;
if (event.getServer().getTickCount() % 10 == 0)
syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::markDirty)));
syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::checkIfDirtyThenUpdate)));
}
/**
@ -124,7 +125,7 @@ public class CommonEventHandler {
@SubscribeEvent
public static void onEntityJoinWorld(EntityJoinLevelEvent event) {
Entity entity = event.getEntity();
if (entity.level().isClientSide) return;
if (entity.level().isClientSide()) return;
for (ResourceLocation id : syncData2Manager.getRegisteredKeys()) {
if (syncData2Manager.isEntityClassAllowed(id, entity.getClass())) {
@ -141,7 +142,7 @@ public class CommonEventHandler {
@SubscribeEvent
public static void onEntityLeaveWorld(EntityLeaveLevelEvent event) {
Entity entity = event.getEntity();
if (entity.level().isClientSide) return;
if (entity.level().isClientSide()) return;
for (ResourceLocation id : syncData2Manager.getRegisteredKeys()) {
if (syncData2Manager.isEntityClassAllowed(id, entity.getClass())) {
@ -155,10 +156,10 @@ public class CommonEventHandler {
* The type Mod.
*/
@SuppressWarnings("unused")
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = Lib39.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD)
@EventBusSubscriber(modid = Lib39.MOD_ID)
public static class Mod extends CommonEventHandler {
private static final Map<RegistryObject<Block>, ResourceKey<CreativeModeTab>[]> itemAddMap = new ConcurrentHashMap<>();
private static final Map<ResourceKey<CreativeModeTab>, List<RegistryObject<Block>>> tabToItemsMap = new ConcurrentHashMap<>();
private static final Map<DeferredHolder<Block, Block>, ResourceKey<CreativeModeTab>[]> itemAddMap = new ConcurrentHashMap<>();
private static final Map<ResourceKey<CreativeModeTab>, List<DeferredHolder<Block, Block>>> tabToItemsMap = new ConcurrentHashMap<>();
/**
* Gets compat manager.
@ -182,12 +183,16 @@ public class CommonEventHandler {
@SubscribeEvent
public static void onFMLCommonSetup (FMLCommonSetupEvent event) {
event.enqueueWork(() -> {
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
IEventBus gameBus = MinecraftForge.EVENT_BUS;
IEventBus modBus = ModLoadingContext.get().getActiveContainer().getEventBus();
IEventBus gameBus = NeoForge.EVENT_BUS;
compatManager = new CompatManager(modBus, gameBus);
MinecraftForge.EVENT_BUS.post(new RegisterCompatEvent(compatManager));
gameBus.post(new RegisterCompatEvent(compatManager));
});
}
@SubscribeEvent
public static void onPacketRegister (RegisterPayloadHandlersEvent event) {
NetworkHandler.registerPackets(event);
}
/**
* Add item to tabs.
@ -196,7 +201,7 @@ public class CommonEventHandler {
* @param tabs the tabs
*/
@SafeVarargs
public static void addItemToTabs(RegistryObject<Block> item, ResourceKey<CreativeModeTab>... tabs) {
public static void addItemToTabs(DeferredHolder<Block, Block> item, ResourceKey<CreativeModeTab>... tabs) {
itemAddMap.put(item, tabs);
// 更新反向映射
@ -212,9 +217,9 @@ public class CommonEventHandler {
*/
@SubscribeEvent
public static void onBuildCreativeTabContents(BuildCreativeModeTabContentsEvent event) {
List<RegistryObject<Block>> itemsForTab = tabToItemsMap.get(event.getTabKey());
List<DeferredHolder<Block, Block>> itemsForTab = tabToItemsMap.get(event.getTabKey());
if (itemsForTab != null) {
itemsForTab.forEach(event::accept);
itemsForTab.forEach(i -> event.accept(i.get().asItem().getDefaultInstance(), CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS));
}
}
@ -223,7 +228,7 @@ public class CommonEventHandler {
*
* @return the item add map
*/
public static Map<RegistryObject<Block>, ResourceKey<CreativeModeTab>[]> getItemAddMap() {
public static Map<DeferredHolder<Block, Block>, ResourceKey<CreativeModeTab>[]> getItemAddMap() {
return itemAddMap;
}
}

View File

@ -1,73 +1,17 @@
package top.r3944realms.lib39.core.network;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.simple.SimpleChannel;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.network.toClient.SyncNBTCapDataEntityS2CPack;
/**
* The type Network handler.
*/
@SuppressWarnings("unused")
public class NetworkHandler {
private static int cid = 0;
/**
* The constant INSTANCE.
*/
public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
new ResourceLocation(Lib39.MOD_ID, "main"),
() -> Lib39.ModInfo.VERSION,
Lib39.ModInfo.VERSION::equals,
Lib39.ModInfo.VERSION::equals
);
/**
* Register.
*/
public static void register() {
INSTANCE.messageBuilder(SyncNBTCapDataEntityS2CPack.class, cid++, NetworkDirection.PLAY_TO_CLIENT)
.encoder(SyncNBTCapDataEntityS2CPack::encode)
.decoder(SyncNBTCapDataEntityS2CPack::decode)
.consumerNetworkThread(SyncNBTCapDataEntityS2CPack::handle)
.add();
}
/**
* Send to player.
*
* @param <MSG> the type parameter
* @param message the message
* @param player the player
*/
public static <MSG> void sendToPlayer(MSG message, ServerPlayer player){
INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), message);
}
/**
* Send to player.
*
* @param <MSG> the type parameter
* @param <T> the type parameter
* @param message the message
* @param entity the entity
* @param packetDistributor the packet distributor
*/
public static <MSG, T> void sendToPlayer(MSG message, T entity, @NotNull PacketDistributor<T> packetDistributor){
INSTANCE.send(packetDistributor.with(() -> entity), message);
}
/**
* Send to player.
*
* @param <MSG> the type parameter
* @param message the message
*/
public static <MSG> void sendToAllPlayer(MSG message){
INSTANCE.send(PacketDistributor.ALL.noArg(), message);
public static void registerPackets(@NotNull RegisterPayloadHandlersEvent event) {
PayloadRegistrar registrar = event.registrar(Lib39.MOD_ID + "-" + Lib39.ModInfo.VERSION);
}
}

View File

@ -1,94 +0,0 @@
package top.r3944realms.lib39.core.network.toClient;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.network.NetworkEvent;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.event.CommonEventHandler;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
import java.util.Optional;
import java.util.function.Supplier;
/**
* The type Sync nbt data s 2 c pack.
*/
@SuppressWarnings("unused")
public record SyncNBTCapDataEntityS2CPack(int entityId, ResourceLocation id, CompoundTag data) {
/**
* Instantiates a new Sync nbt data s 2 c pack.
*
* @param entityId the entity id
* @param id the id
* @param data the data
*/
public SyncNBTCapDataEntityS2CPack(int entityId, ResourceLocation id, @NotNull NBTEntitySyncData data) {
this(entityId, data.id(), data.serializeNBT());
}
/**
* Encode.
*
* @param msg the msg
* @param buffer the buffer
*/
public static void encode(@NotNull SyncNBTCapDataEntityS2CPack msg, @NotNull FriendlyByteBuf buffer) {
buffer.writeInt(msg.entityId);
buffer.writeResourceLocation(msg.id);
buffer.writeNbt(msg.data);
}
/**
* Decode sync nbt data s 2 c pack.
*
* @param buffer the buffer
* @return the sync nbt data s 2 c pack
*/
@Contract("_ -> new")
public static @NotNull SyncNBTCapDataEntityS2CPack decode(@NotNull FriendlyByteBuf buffer) {
return new SyncNBTCapDataEntityS2CPack(buffer.readInt(), buffer.readResourceLocation(), buffer.readNbt());
}
/**
* Handle.
*
* @param msg the msg
* @param ctx the ctx
*/
public static void handle(SyncNBTCapDataEntityS2CPack msg, @NotNull Supplier<NetworkEvent.Context> ctx) {
NetworkEvent.Context context = ctx.get();
context.enqueueWork(() -> {
ClientLevel level = Minecraft.getInstance().level;
if (level != null) {
Entity entity = level.getEntity(msg.entityId);
if (entity != null) {
Optional<SyncData2Manager.DataProvider<Entity, ISyncData<?>>> capability =
CommonEventHandler.Game
.getSyncData2Manager()
.getDataProvider(msg.id);
capability.flatMap(dataProvider -> dataProvider.getData(entity))
.ifPresent(cap -> {
if (cap instanceof NBTEntitySyncData nbtCap) {
CompoundTag current = nbtCap.serializeNBT();
if (!current.equals(msg.data)) {
nbtCap.deserializeNBT(msg.data);
}
} else Lib39.LOGGER.debug("Unhandled sync data: {}", msg.data);
}
);
}
}
});
context.setPacketHandled(true);
}
}

View File

@ -1,59 +0,0 @@
package top.r3944realms.lib39.core.sync;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.util.INBTSerializable;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.core.network.NetworkHandler;
import top.r3944realms.lib39.core.network.toClient.SyncNBTCapDataEntityS2CPack;
/**
* The type Nbt sync data.
*/
public abstract class NBTEntitySyncData implements IEntity, ISyncData<NBTEntitySyncData>, INBTSerializable<CompoundTag> {
/**
* The Dirty.
*/
protected boolean dirty;
/**
* The Id.
*/
protected final ResourceLocation id;
/**
* Instantiates a new Nbt sync data.
*
* @param id the id
*/
protected NBTEntitySyncData(ResourceLocation id) {
this.id = id;
}
@Override
public ResourceLocation id() {
return id;
}
@Override
public boolean isDirty() {
return dirty;
}
@Override
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
@Override
public void copyFrom(@NotNull NBTEntitySyncData src) {
this.dirty = src.isDirty();
}
@Override
public void checkIfDirtyThenUpdate() {
if (isDirty()) {
NetworkHandler.sendToAllPlayer(new SyncNBTCapDataEntityS2CPack(entityId(), id(), serializeNBT()));
}
dirty = false;
}
}

View File

@ -1,390 +0,0 @@
package top.r3944realms.lib39.core.sync;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.capabilities.Capability;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
/**
* The type Sync data 2 manager.
*/
@SuppressWarnings("unused")
public class SyncData2CapManager {
private final Map<ResourceLocation, TypedSyncEntry<?, ?>> typedEntries = Maps.newConcurrentMap();
private static class TypedSyncEntry<K, T extends ISyncData<?>> {
/**
* The Manager.
*/
final ISyncManager<K, T> manager;
/**
* The Capability.
*/
@Nullable
Capability<T> capability;
/**
* The Allowed classes.
*/
final Set<Class<?>> allowedClasses;
/**
* Instantiates a new Typed sync entry.
*
* @param manager the manager
* @param capability the capability
*/
TypedSyncEntry(ISyncManager<K, T> manager, @Nullable Capability<T> capability) {
this.manager = manager;
this.capability = capability;
this.allowedClasses = Sets.newConcurrentHashSet();
}
}
/**
* Register manager.
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param key the key
* @param manager the manager
* @param capability the capability
*/
public <K, T extends ISyncData<?>> void registerManager(
ResourceLocation key,
ISyncManager<K, T> manager,
Capability<T> capability
) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(manager, "Sync manager cannot be null");
Objects.requireNonNull(capability, "Capability cannot be null");
typedEntries.put(key, new TypedSyncEntry<>(manager, capability));
}
/**
* 向后兼容的注册方法只注册管理器不注册能力
*
* @param key the key
* @param manager the manager
*/
@SuppressWarnings("unchecked")
public void registerManager(ResourceLocation key, ISyncManager<?, ? extends ISyncData<?>> manager) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(manager, "Sync manager cannot be null");
// 创建一个虚拟的 TypedSyncEntry capability null
// 注意这种方法会限制类型安全的功能
typedEntries.put(key, new TypedSyncEntry<>(
(ISyncManager<?, ISyncData<?>>) manager,
null
));
}
/**
* Gets manager.
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param key the key
* @return the manager
*/
@SuppressWarnings("unchecked")
public <K, T extends ISyncData<?>> Optional<ISyncManager<K, T>> getManager(ResourceLocation key) {
TypedSyncEntry<?,?> entry = typedEntries.get(key);
return entry != null ? Optional.of((ISyncManager<K,T>) entry.manager) : Optional.empty();
}
/**
* Gets capability.
*
* @param <T> the type parameter
* @param key the key
* @return the capability
*/
@SuppressWarnings("unchecked")
public <T extends ISyncData<?>> Optional<Capability<T>> getCapability(ResourceLocation key) {
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null && entry.capability != null) {
return Optional.of((Capability<T>) entry.capability);
}
return Optional.empty();
}
/**
* Allow entity class.
*
* @param key the key
* @param classes the classes
*/
public final void allowEntityClass(ResourceLocation key, Class<?>... classes) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(classes, "Classes array cannot be null");
if (classes.length == 0) {
return;
}
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null) {
entry.allowedClasses.addAll(Arrays.asList(classes));
}
}
/**
* 移除允许的实体类
*
* @param key the key
* @param classes the classes
*/
public final void disallowEntityClass(ResourceLocation key, Class<?>... classes) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(classes, "Classes array cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null && classes.length > 0) {
Arrays.asList(classes).forEach(entry.allowedClasses::remove);
}
}
/**
* 绑定能力用于分离注册的情况
*
* @param <T> the type parameter
* @param key the key
* @param capability the capability
*/
public <T extends ISyncData<?>> void bindCapability(ResourceLocation key, Capability<T> capability) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(capability, "Capability cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null) {
// 更新现有条目的能力
updateCapabilityInEntry(key, entry, capability);
} else throw new IllegalArgumentException("No manager found for " + key);
}
/**
* 解绑能力
*
* @param key the key
*/
public void unbindCapability(ResourceLocation key) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null) {
// 将能力设置为null但保留管理器和其他配置
updateCapabilityInEntry(key, entry, null);
}
}
/**
* 清除允许的实体类
*
* @param key the key
*/
public void clearAllowedEntityClasses(ResourceLocation key) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null) {
entry.allowedClasses.clear();
}
}
/**
* Is entity class allowed boolean.
*
* @param key the key
* @param entityClass the entity class
* @return the boolean
*/
public boolean isEntityClassAllowed(ResourceLocation key, Class<?> entityClass) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(entityClass, "Entity class cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
boolean isAllowed = false;
if (entry != null) {
for (Class<?> allowedClass : entry.allowedClasses) {
if (entityClass.isAssignableFrom(allowedClass)) {
isAllowed = true;
break;
}
}
}
return entry != null && isAllowed ;
}
/**
* Track entity for manager.
*
* @param entity the entity
* @param managerId the manager id
*/
// 类型安全的事件处理
@SuppressWarnings("unchecked")
public void trackEntityForManager(Entity entity, ResourceLocation managerId) {
TypedSyncEntry<UUID, ?> entry = (TypedSyncEntry<UUID, ?>) typedEntries.get(managerId);
if (entry != null) {
trackEntityWithTypedEntry(entity, entry);
}
}
private <T extends ISyncData<?>> void trackEntityWithTypedEntry(Entity entity, @NotNull TypedSyncEntry<UUID, T> entry) {
if (entry.capability != null) {
entity.getCapability(entry.capability)
.ifPresent(cap -> entry.manager.track(entity.getUUID(), cap));
}
}
/**
* Untrack entity for manager.
*
* @param entity the entity
* @param managerId the manager id
*/
// 类型安全的事件处理 - 取消跟踪实体
@SuppressWarnings("unchecked")
public void untrackEntityForManager(Entity entity, ResourceLocation managerId) {
TypedSyncEntry<UUID, ?> entry = (TypedSyncEntry<UUID, ?>) typedEntries.get(managerId);
if (entry != null) {
untrackEntityWithTypedEntry(entity, entry);
}
}
private <T extends ISyncData<?>> void untrackEntityWithTypedEntry(Entity entity, @NotNull TypedSyncEntry<UUID, T> entry) {
if (entry.capability != null) {
entity.getCapability(entry.capability)
.ifPresent(cap -> entry.manager.untrack(entity.getUUID(), cap));
}
}
/**
* 从所有管理器中移除实体跟踪
*
* @param entity the entity
*/
public void untrackEntityFromAllManagers(Entity entity) {
for (ResourceLocation id : getRegisteredKeys()) {
if (isEntityClassAllowed(id, entity.getClass())) {
untrackEntityForManager(entity, id);
}
}
}
/**
* 批量从管理器中移除实体跟踪
*
* @param entities the entities
* @param managerId the manager id
*/
public void untrackEntitiesForManager(@NotNull Iterable<Entity> entities, ResourceLocation managerId) {
for (Entity entity : entities) {
untrackEntityForManager(entity, managerId);
}
}
/**
* 从所有管理器中批量移除实体跟踪
*
* @param entities the entities
*/
public void untrackEntitiesFromAllManagers(@NotNull Iterable<Entity> entities) {
for (Entity entity : entities) {
untrackEntityFromAllManagers(entity);
}
}
/**
* 强制清理管理器中的所有跟踪数据
*
* @param managerId the manager id
*/
public void clearAllTrackedData(ResourceLocation managerId) {
TypedSyncEntry<?, ?> entry = typedEntries.get(managerId);
if (entry != null) {
clearTrackedDataForEntry(entry);
}
}
private <K, T extends ISyncData<?>> void clearTrackedDataForEntry(@NotNull TypedSyncEntry<K, T> entry) {
// 获取当前跟踪的集合并清空
Set<T> syncSet = entry.manager.getSyncSet();
if (syncSet != null) {
syncSet.clear();
}
}
/**
* 清理所有管理器的跟踪数据
*/
public void clearAllTrackedData() {
for (ResourceLocation id : getRegisteredKeys()) {
clearAllTrackedData(id);
}
}
// 辅助方法更新条目的能力
@SuppressWarnings("unchecked")
private <K, T extends ISyncData<?>> void updateCapabilityInEntry(ResourceLocation id, TypedSyncEntry<?,?> entry, Capability<T> newCapability) {
TypedSyncEntry<K, T> typedEntry = (TypedSyncEntry<K, T>) entry;
//重构了 TypedSyncEntry 使 capability 可变
typedEntry.capability = newCapability;
typedEntries.computeIfPresent(id, (resourceLocation, typedSyncEntry) -> typedEntry);
}
/**
* Gets registered keys.
*
* @return the registered keys
*/
public Set<ResourceLocation> getRegisteredKeys() {
return Collections.unmodifiableSet(typedEntries.keySet());
}
/**
* For each.
*
* @param consumer the consumer
*/
public void forEach(BiConsumer<ResourceLocation, ISyncManager<?,?>> consumer) {
Objects.requireNonNull(consumer, "Consumer cannot be null");
typedEntries.forEach((key, entry) -> consumer.accept(key, entry.manager));
}
/**
* Gets manager count.
*
* @return the manager count
*/
public int getManagerCount() {
return typedEntries.size();
}
/**
* Clear all.
*/
public void clearAll() {
typedEntries.clear();
}
/**
* 移除管理器包括所有相关配置
*
* @param key the key
*/
public void removeManager(ResourceLocation key) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
typedEntries.remove(key);
}
}

View File

@ -1,7 +1,7 @@
package top.r3944realms.lib39.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.LanguageProvider;
import net.neoforged.neoforge.common.data.LanguageProvider;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.datagen.value.ILangKeyValue;
import top.r3944realms.lib39.datagen.value.ILangKeyValueCollection;

View File

@ -0,0 +1,8 @@
package top.r3944realms.lib39.datagen.value;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.Item;
public interface IResourceKeyValue {
ResourceKey<Item> getResourceKey();
}

View File

@ -21,7 +21,7 @@ public enum McLocale implements ILocaleEntry {
/**
* The Lzh.
*/
LZH("lzh", new Locale("lzh", "ZH")),
LZH("lzh", Locale.of("lzh", "ZH")),
/**
* Ja jp mc locale.
*/
@ -33,7 +33,7 @@ public enum McLocale implements ILocaleEntry {
/**
* The Ru ru.
*/
RU_RU("ru_ru", new Locale("ru", "RU")),
RU_RU("ru_ru", Locale.of("ru", "RU")),
/**
* Fr fr mc locale.
*/
@ -45,7 +45,7 @@ public enum McLocale implements ILocaleEntry {
/**
* The Es es.
*/
ES_ES("es_es", new Locale("es", "ES"));
ES_ES("es_es", Locale.of("es", "ES"));
private final String mcCode;
private final Locale javaLocale;

View File

@ -1,10 +1,9 @@
package top.r3944realms.lib39.example;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.common.NeoForge;
import top.r3944realms.lib39.example.core.event.ExCommonEventHandler;
import top.r3944realms.lib39.example.core.network.ExNetworkHandler;
import top.r3944realms.lib39.example.core.register.ExLib39Attachments;
import top.r3944realms.lib39.example.core.register.ExLib39Items;
/**
@ -16,22 +15,19 @@ public class Lib39Example {
/**
* Instantiates a new Lib 39 example.
*/
public Lib39Example() {
public Lib39Example(IEventBus modEventBus) {
if (!registered) {
init();
registerToEventBus();
init(modEventBus);
registerToEventBus(modEventBus);
registered = true;
}
}
private void init() {
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
private void init(IEventBus modEventBus) {
ExLib39Items.register(modEventBus);
ExNetworkHandler.register();
ExLib39Attachments.register(modEventBus);
}
private void registerToEventBus() {
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
IEventBus gameBus = MinecraftForge.EVENT_BUS;
private void registerToEventBus(IEventBus modBus) {
// DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> {
// modBus.register(ExClientEventHandler.Mod.class);
// gameBus.register(ExClientEventHandler.Game.class);
@ -43,7 +39,7 @@ public class Lib39Example {
// return null;
// });
modBus.register(ExCommonEventHandler.Mod.class);
gameBus.register(ExCommonEventHandler.Game.class);
NeoForge.EVENT_BUS.register(ExCommonEventHandler.Game.class);
}

View File

@ -1,20 +1,25 @@
package top.r3944realms.lib39.example.content.capability;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
import net.neoforged.neoforge.common.util.ValueIOSerializable;
import top.r3944realms.lib39.core.sync.IEntity;
/**
* The type Abstracted test sync data.
*/
@SuppressWarnings("unused")
public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
public abstract class AbstractedTestSyncData implements ValueIOSerializable, IEntity {
/**
* Instantiates a new Nbt sync data.
*
* @param id the id
*/
protected AbstractedTestSyncData(ResourceLocation id) {
super(id);
}
/**
@ -126,10 +131,17 @@ public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
* 测试数据对象
*/
public static class TestData {
public static StreamCodec<FriendlyByteBuf, TestData> CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8, TestData::getName,
ByteBufCodecs.INT, TestData::getValue,
ByteBufCodecs.BOOL, TestData::isFlag,
TestData::new
);
private String name;
private int value;
private boolean flag;
/**
* Instantiates a new Test data.
*/

View File

@ -1,12 +1,15 @@
package top.r3944realms.lib39.example.content.capability;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.capabilities.CapabilityToken;
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import org.jetbrains.annotations.NotNull;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.entity.vehicle.Minecart;
import net.neoforged.neoforge.capabilities.EntityCapability;
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.core.register.ExLib39Attachments;
import top.r3944realms.lib39.util.capability.EntityCapabilityHelper;
/**
* The type Ex capability handler.
@ -15,26 +18,29 @@ public class ExCapabilityHandler {
/**
* The constant TEST_CAP.
*/
public static final Capability<AbstractedTestSyncData> TEST_CAP = CapabilityManager.get(new CapabilityToken<>() {});
public static final EntityCapability<AbstractedTestSyncData, Void> TEST_CAP = EntityCapability.createVoid(
ResourceLocation.fromNamespaceAndPath(Lib39.MOD_ID, "test_data"),
AbstractedTestSyncData.class
);
/**
* Register capability.
*
* @param event the event
*/
public static void registerCapability(@NotNull RegisterCapabilitiesEvent event) {
event.register(AbstractedTestSyncData.class);
}
/**
* Attach capability.
*
* @param event the event
*/
public static void attachCapability(@NotNull AttachCapabilitiesEvent<?> event) {
Object object = event.getObject();
if(object instanceof Entity entity ) {
event.addCapability(TestSyncCapProvider.TEST_SYNC_REL, new TestSyncCapProvider(entity));
}
public static void registerCapabilities(RegisterCapabilitiesEvent event) {
EntityCapabilityHelper.registerForEntityClass(
event,
TEST_CAP,
(entity, context) -> entity.getData(ExLib39Attachments.TEST_DATA_ATTACHMENT),
LivingEntity.class
);
EntityCapabilityHelper.registerForEntityClass(
event,
TEST_CAP,
(entity, context) -> entity.getData(ExLib39Attachments.TEST_DATA_ATTACHMENT),
Boat.class
);
EntityCapabilityHelper.registerForEntityClass(
event,
TEST_CAP,
(entity, context) -> entity.getData(ExLib39Attachments.TEST_DATA_ATTACHMENT),
Minecart.class
);
}
}

View File

@ -1,49 +0,0 @@
package top.r3944realms.lib39.example.content.capability;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.common.util.LazyOptional;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
/**
* The type Test sync cap provider.
*/
public class TestSyncCapProvider implements ICapabilitySerializable<CompoundTag> {
/**
* The constant TEST_SYNC_REL.
*/
public static final ResourceLocation TEST_SYNC_REL = new ResourceLocation(Lib39.MOD_ID, "test_sync_data");
private final AbstractedTestSyncData instance;
private final LazyOptional<AbstractedTestSyncData> optional;
/**
* Instantiates a new Test sync cap provider.
*
* @param entity the entity
*/
public TestSyncCapProvider(Entity entity) {
this.instance = new TestSyncData(entity);
this.optional = LazyOptional.of(() -> instance);
}
@Override
public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
return ExCapabilityHandler.TEST_CAP.orEmpty(cap, optional);
}
@Override
public CompoundTag serializeNBT() {
return instance.serializeNBT();
}
@Override
public void deserializeNBT(CompoundTag nbt) {
instance.deserializeNBT(nbt);
}
}

View File

@ -1,14 +1,21 @@
package top.r3944realms.lib39.example.content.capability;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.neoforged.neoforge.attachment.IAttachmentHolder;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.util.nbt.NBTReader;
import top.r3944realms.lib39.util.nbt.NBTWriter;
import top.r3944realms.lib39.example.core.register.ExLib39Attachments;
import top.r3944realms.lib39.example.core.register.ExLib39ItemResourceKeys;
import top.r3944realms.lib39.util.storage.valueio.ValueInputReader;
import top.r3944realms.lib39.util.storage.valueio.ValueOutputWriter;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@ -16,25 +23,59 @@ import java.util.concurrent.atomic.AtomicReference;
/**
* 测试同步数据实现
*/
@SuppressWarnings("unused")
public class TestSyncData extends AbstractedTestSyncData {
/**
* The constant ID.
*/
public static final ResourceLocation ID = new ResourceLocation(Lib39.MOD_ID, "test_sync_data");
private static final Random RANDOM = new Random();
// NBT 键常量
private static final String NBT_KEY_STRING = "test_string";
private static final String NBT_KEY_INT = "test_int";
private static final String NBT_KEY_BOOLEAN = "test_boolean";
private static final String NBT_KEY_DOUBLE = "test_double";
private static final String NBT_KEY_COUNTER = "counter";
private static final String NBT_KEY_SYNC_TIME = "last_sync_time";
private static final String NBT_KEY_CUSTOM_DATA = "custom_data";
private static final String NBT_KEY_CUSTOM_NAME = "name";
private static final String NBT_KEY_CUSTOM_VALUE = "value";
private static final String NBT_KEY_CUSTOM_FLAG = "flag";
public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath(Lib39.MOD_ID, "test_sync_data");
// 网络同步编解码器
public static final StreamCodec<FriendlyByteBuf, TestSyncData> CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8, TestSyncData::getTestString,
ByteBufCodecs.INT, TestSyncData::getTestInt,
ByteBufCodecs.BOOL, TestSyncData::isTestBoolean,
ByteBufCodecs.DOUBLE, TestSyncData::getTestDouble,
TestData.CODEC, TestSyncData::getCustomData,
ByteBufCodecs.INT, TestSyncData::getCounter,
ByteBufCodecs.LONG, TestSyncData::getLastSyncTime,
(testString, testInt, testBoolean, testDouble, customData, counter, lastSyncTime) -> {
TestSyncData data = new TestSyncData();
data.testString = testString;
data.testInt = testInt;
data.testBoolean = testBoolean;
data.testDouble = testDouble;
data.customData = customData;
data.counter = counter;
data.lastSyncTime = lastSyncTime;
return data;
}
);
/** 重置为默认值 */
public void resetToDefaults() {
this.testString = "default_string";
this.testInt = 0;
this.testDouble = 0.0;
this.testBoolean = false;
this.counter = 0;
this.lastSyncTime = System.currentTimeMillis();
this.customData = new TestData("init", 0, false);
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
/** 随机生成一组数据 */
public void generateRandomData() {
this.testString = "rand_" + RANDOM.nextInt(10000);
this.testInt = RANDOM.nextInt(1000);
this.testDouble = RANDOM.nextDouble() * 100.0;
this.testBoolean = RANDOM.nextBoolean();
this.counter = RANDOM.nextInt(20);
this.lastSyncTime = System.currentTimeMillis();
this.customData = new TestData(
"custom_" + RANDOM.nextInt(1000),
RANDOM.nextInt(500),
RANDOM.nextBoolean()
);
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
// 数据字段
private String testString = "default_value";
private int testInt = 42;
@ -45,332 +86,134 @@ public class TestSyncData extends AbstractedTestSyncData {
private TestData customData = new TestData("default", 100, false);
private Entity self;
/**
* 构造函数
*
* @param entity 关联的实体
*/
// --- 构造函数区域 ---
/** 默认构造函数(反序列化/同步使用) */
public TestSyncData() {
super(ID);
}
/** 用于实体附加 */
public TestSyncData(Entity entity) {
super(ID);
this.self = entity;
}
/**
* 构造函数用于测试
*
* @param entityId 实体ID
* @param self the self
*/
public TestSyncData(int entityId, Entity self) {
super(ID);
this.self = self;
/** 用于 AttachmentType.serializable(holder -> ...) */
public TestSyncData(IAttachmentHolder holder) {
this(holder instanceof Entity e ? e : null);
if (self == null) throw new IllegalArgumentException("TestSyncData must be attached to Entity");
}
/**
* 构造函数用于数据包反序列化
*
* @param buf 字节缓冲区
*/
public TestSyncData(FriendlyByteBuf buf) {
super(ID);
this.self = null; // 实体在从数据包重建时可能为null需要在接收端设置
fromBytes(buf);
// --- 数据逻辑部分 ---
public String getTestString() { return testString; }
public void setTestString(String s) {
this.testString = s;
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
/**
* 将数据写入字节缓冲区用于网络传输
*
* @param buf 字节缓冲区
*/
public void toBytes(FriendlyByteBuf buf) {
// 写入基本类型字段
buf.writeUtf(testString != null ? testString : "");
buf.writeInt(testInt);
buf.writeBoolean(testBoolean);
buf.writeDouble(testDouble);
buf.writeInt(counter);
buf.writeLong(lastSyncTime);
// 写入自定义数据
if (customData != null) {
buf.writeUtf(customData.getName() != null ? customData.getName() : "");
buf.writeInt(customData.getValue());
buf.writeBoolean(customData.isFlag());
} else {
buf.writeUtf("");
buf.writeInt(0);
buf.writeBoolean(false);
}
// 写入实体ID如果实体存在
if (self != null) {
buf.writeInt(self.getId());
} else {
buf.writeInt(-1);
}
// 写入脏数据状态
buf.writeBoolean(isDirty());
public int getTestInt() { return testInt; }
public void setTestInt(int i) {
this.testInt = i;
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
/**
* 从字节缓冲区读取数据用于网络传输
*
* @param buf 字节缓冲区
*/
public void fromBytes(@NotNull FriendlyByteBuf buf) {
// 读取基本类型字段
this.testString = buf.readUtf(32767); // Minecraft字符串最大长度
this.testInt = buf.readInt();
this.testBoolean = buf.readBoolean();
this.testDouble = buf.readDouble();
this.counter = buf.readInt();
this.lastSyncTime = buf.readLong();
// 读取自定义数据
String customName = buf.readUtf();
int customValue = buf.readInt();
boolean customFlag = buf.readBoolean();
this.customData = new TestData(customName, customValue, customFlag);
// 读取实体ID在接收端可能需要额外处理
int entityId = buf.readInt();
// 读取脏数据状态
boolean wasDirty = buf.readBoolean();
if (wasDirty) {
markDirty();
}
public boolean isTestBoolean() { return testBoolean; }
public void setTestBoolean(boolean b) { this.testBoolean = b;
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
/**
* 静态方法从字节缓冲区创建 TestSyncData 实例
*
* @param buf 字节缓冲区
* @return 新的 TestSyncData 实例
*/
public static TestSyncData staticFromBytes(FriendlyByteBuf buf) {
return new TestSyncData(buf);
public double getTestDouble() { return testDouble; }
public void setTestDouble(double d) {
this.testDouble = d;
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
@Override
public String getTestString() {
return testString;
}
@Override
public void setTestString(String value) {
if (!java.util.Objects.equals(this.testString, value)) {
this.testString = value;
markDirty();
}
}
@Override
public int getTestInt() {
return testInt;
}
@Override
public void setTestInt(int value) {
if (this.testInt != value) {
this.testInt = value;
markDirty();
}
}
@Override
public boolean isTestBoolean() {
return testBoolean;
}
@Override
public void setTestBoolean(boolean value) {
if (this.testBoolean != value) {
this.testBoolean = value;
markDirty();
}
}
@Override
public double getTestDouble() {
return testDouble;
}
@Override
public void setTestDouble(double value) {
if (this.testDouble != value) {
this.testDouble = value;
markDirty();
}
}
@Override
public int getCounter() {
return counter;
}
@Override
public int getCounter() { return counter; }
public void incrementCounter() {
this.counter++;
markDirty();
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
@Override
public long getLastSyncTime() {
return lastSyncTime;
}
@Override
public long getLastSyncTime() { return lastSyncTime; }
public void updateSyncTime() {
this.lastSyncTime = System.currentTimeMillis();
markDirty();
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
@Override
public TestData getCustomData() {
return customData;
}
@Override
public TestData getCustomData() { return customData; }
public void setCustomData(TestData data) {
if (data == null) {
throw new IllegalArgumentException("Custom data cannot be null");
}
if (!java.util.Objects.equals(this.customData, data)) {
this.customData = data;
markDirty();
}
this.customData = data;
self.syncData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
}
@Override
public boolean validateData() {
return testString != null &&
!testString.isEmpty() &&
customData != null &&
customData.getName() != null &&
!customData.getName().isEmpty() &&
counter >= 0 &&
testInt >= 0;
return testString != null && !testString.isEmpty()
&& customData != null && customData.getName() != null
&& counter >= 0 && testInt >= 0;
}
// --- 实体管理 ---
public int entityId() { return self != null ? self.getId() : -1; }
public void setSelf(Entity entity) {
this.self = entity;
}
public TestSyncData createSyncCopy(Entity entity) {
TestSyncData copy = new TestSyncData(entity);
copy.testString = testString;
copy.testInt = testInt;
copy.testBoolean = testBoolean;
copy.testDouble = testDouble;
copy.counter = counter;
copy.lastSyncTime = lastSyncTime;
copy.customData = new TestData(customData.getName(), customData.getValue(), customData.isFlag());
return copy;
}
@Override
public CompoundTag serializeNBT() {
return NBTWriter.builder()
.string(NBT_KEY_STRING, testString)
.intValue(NBT_KEY_INT, testInt)
.booleanValue(NBT_KEY_BOOLEAN, testBoolean)
.doubleValue(NBT_KEY_DOUBLE, testDouble)
.intValue(NBT_KEY_COUNTER, counter)
.longValue(NBT_KEY_SYNC_TIME, lastSyncTime)
.compound(
NBT_KEY_CUSTOM_DATA,
NBTWriter.builder()
.string(NBT_KEY_CUSTOM_NAME, customData.getName())
.intValue(NBT_KEY_CUSTOM_VALUE, customData.getValue())
.booleanValue(NBT_KEY_CUSTOM_FLAG, customData.isFlag())
.build()
).build();
public void serialize(@NotNull ValueOutput out) {
ValueOutputWriter.of(out)
.string("test_string", testString)
.intValue("test_int", testInt)
.booleanValue("test_boolean", testBoolean)
.doubleValue("test_double", testDouble)
.intValue("counter", counter)
.longValue("last_sync_time", lastSyncTime)
.nested("custom_data", n ->
n.string("name", customData.getName())
.intValue("value", customData.getValue())
.booleanValue("flag", customData.isFlag()));
}
@Override
public void deserializeNBT(CompoundTag nbt) {
NBTReader.of(nbt)
.intValue(NBT_KEY_INT, integer -> testInt = integer)
.string(NBT_KEY_STRING, string -> testString = string)
.booleanValue(NBT_KEY_BOOLEAN, bool -> testBoolean = bool)
.intValue(NBT_KEY_COUNTER, integer -> counter = integer)
.doubleValue(NBT_KEY_DOUBLE, dou -> testDouble = dou)
.longValue(NBT_KEY_SYNC_TIME, sync -> lastSyncTime = sync)
.compound(NBT_KEY_CUSTOM_DATA, customDataTag -> {
AtomicReference<String> name = new AtomicReference<>("");
AtomicInteger value = new AtomicInteger(-1);
public void deserialize(@NotNull ValueInput in) {
ValueInputReader.of(in)
.string("test_string", s -> testString = s)
.intValue("test_int", i -> testInt = i)
.booleanValue("test_boolean", b -> testBoolean = b)
.doubleValue("test_double", d -> testDouble = d)
.intValue("counter", c -> counter = c)
.longValue("last_sync_time", t -> lastSyncTime = t)
.nested("custom_data", n -> {
AtomicReference<String> name = new AtomicReference<>("default");
AtomicInteger value = new AtomicInteger(100);
AtomicBoolean flag = new AtomicBoolean(false);
NBTReader.of(customDataTag)
.string(NBT_KEY_CUSTOM_NAME, name::set)
.intValue(NBT_KEY_CUSTOM_VALUE, value::set)
.booleanValue(NBT_KEY_CUSTOM_FLAG, flag::set);
this.customData = new TestData(name.get(), value.get(), flag.get());
n.string("name", name::set)
.intValue("value", value::set)
.booleanValue("flag", flag::set);
customData = new TestData(name.get(), value.get(), flag.get());
});
}
@Override
public int entityId() {
return self != null ? self.getId() : -1;
}
/**
* 设置关联的实体
*
* @param entity 关联的实体
*/
public void setEntity(Entity entity) {
this.self = entity;
}
/**
* 获取所有数据的字符串表示用于调试
*/
@Override
public String toString() {
return String.format(
"TestSyncData{id=%d, string='%s', int=%d, boolean=%s, double=%.2f, counter=%d, lastSync=%d, custom=%s}",
self.getId(), testString, testInt, testBoolean, testDouble, counter, lastSyncTime, customData
"TestSyncData[%s,id=%d]{str=%s,int=%d,bool=%s,double=%.2f,counter=%d,time=%d,custom=%s}",
self == null ? "null" : self.getClass().getSimpleName(),
entityId(), testString, testInt, testBoolean, testDouble, counter, lastSyncTime, customData
);
}
/**
* 重置为默认值
*/
public void resetToDefaults() {
testString = "default_value";
testInt = 42;
testBoolean = true;
testDouble = 3.14159;
counter = 0;
lastSyncTime = 0L;
customData = new TestData("default", 100, false);
markDirty();
}
/**
* 生成随机测试数据
*/
public void generateRandomData() {
testString = "random_" + System.currentTimeMillis();
testInt = (int) (Math.random() * 1000);
testBoolean = Math.random() > 0.5;
testDouble = Math.random() * 100.0;
counter++;
lastSyncTime = System.currentTimeMillis();
customData = new TestData(
"custom_" + counter,
(int) (Math.random() * 500),
Math.random() > 0.5
);
markDirty();
}
/**
* 创建一个不依赖实体的副本用于网络传输
*
* @return 不包含实体引用的副本 test sync data
*/
public TestSyncData createNetworkCopy() {
TestSyncData copy = new TestSyncData((Entity) null);
copy.testString = this.testString;
copy.testInt = this.testInt;
copy.testBoolean = this.testBoolean;
copy.testDouble = this.testDouble;
copy.counter = this.counter;
copy.lastSyncTime = this.lastSyncTime;
copy.customData = new TestData(
this.customData.getName(),
this.customData.getValue(),
this.customData.isFlag()
);
return copy;
}
}
}

View File

@ -1,9 +1,10 @@
package top.r3944realms.lib39.example.content.item;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
@ -11,20 +12,22 @@ import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.TooltipDisplay;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.client.network.ClientPacketDistributor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.content.capability.AbstractedTestSyncData;
import top.r3944realms.lib39.example.content.capability.ExCapabilityHandler;
import top.r3944realms.lib39.example.content.capability.TestSyncData;
import top.r3944realms.lib39.example.core.network.ClientDataPacket;
import top.r3944realms.lib39.example.core.network.ExNetworkHandler;
import top.r3944realms.lib39.example.core.network.ClientDataPayload;
import top.r3944realms.lib39.util.chat.MessageDisplayClientHelper;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
/**
* 用于执行数据查询并检查同步状态的物品
@ -43,8 +46,7 @@ public class FabricItem extends Item {
}
@Override
public @NotNull InteractionResultHolder<ItemStack> use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) {
ItemStack itemStack = player.getItemInHand(hand);
public @NotNull InteractionResult use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) {
if (level.isClientSide()) {
// 客户端逻辑
@ -61,17 +63,17 @@ public class FabricItem extends Item {
if (player.isShiftKeyDown()) {
// 服务器端已经通过数据包处理双端检查这里只发送开始消息
player.sendSystemMessage(Component.literal("§b开始双端同步检查请等待客户端数据..."));
((ServerPlayer) player).sendSystemMessage(Component.literal("§b开始双端同步检查请等待客户端数据..."));
} else {
// 服务器单端查询
handleServerSingleEndQuery(serverPlayer);
}
// 添加冷却时间
player.getCooldowns().addCooldown(this, 20); // 1秒冷却
player.getCooldowns().addCooldown(this.getDefaultInstance(), 20); // 1秒冷却
}
return InteractionResultHolder.sidedSuccess(itemStack, level.isClientSide());
return InteractionResult.SUCCESS;
}
/**
@ -89,15 +91,15 @@ public class FabricItem extends Item {
sendClientDataToServer(clientData, livingTarget.getId());
// 客户端提示
player.sendSystemMessage(Component.literal("§b已发送客户端数据到服务器等待对比结果..."));
Minecraft.getInstance().getChatListener().handleSystemMessage(Component.literal("§b已发送客户端数据到服务器等待对比结果..."), false);
} else {
player.sendSystemMessage(Component.literal("§c无法获取客户端本地数据"));
Minecraft.getInstance().getChatListener().handleSystemMessage(Component.literal("§c无法获取客户端本地数据"), false);
}
} else {
if (targetEntity == null && player.isShiftKeyDown()) {
handlePlayerSelfData(player);
} else {
player.sendSystemMessage(Component.literal("§c请对准一个生物进行同步检查"));
Minecraft.getInstance().getChatListener().handleSystemMessage(Component.literal("§c请对准一个生物进行同步检查"), false);
}
}
}
@ -114,9 +116,9 @@ public class FabricItem extends Item {
sendClientDataToServer(clientData, player.getId());
// 客户端提示
player.sendSystemMessage(Component.literal("§b已发送玩家自身客户端数据到服务器等待对比结果..."));
Minecraft.getInstance().getChatListener().handleSystemMessage(Component.literal("§b已发送玩家自身客户端数据到服务器等待对比结果..."), false);
} else {
player.sendSystemMessage(Component.literal("§c无法获取玩家自身客户端数据"));
Minecraft.getInstance().getChatListener().handleSystemMessage(Component.literal("§c无法获取玩家自身客户端数据"), false);
}
}
/**
@ -129,12 +131,12 @@ public class FabricItem extends Item {
TestSyncData clientData = getLocalClientData(livingTarget);
if (clientData != null) {
displayClientSideResults(player, livingTarget, clientData);
displayClientSideResults(livingTarget, clientData);
} else {
player.sendSystemMessage(Component.literal("§c无法查询客户端本地数据"));
Minecraft.getInstance().getChatListener().handleSystemMessage(Component.literal("§c无法查询客户端本地数据"), false);
}
} else {
player.sendSystemMessage(Component.literal("§c请对准一个生物使用"));
Minecraft.getInstance().getChatListener().handleSystemMessage(Component.literal("§c请对准一个生物使用"), false);
}
}
@ -161,7 +163,7 @@ public class FabricItem extends Item {
*/
private TestSyncData getLocalClientData(LivingEntity target) {
try {
AbstractedTestSyncData abstractData = target.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null);
AbstractedTestSyncData abstractData = target.getCapability(ExCapabilityHandler.TEST_CAP);
if (abstractData instanceof TestSyncData) {
return (TestSyncData) abstractData;
}
@ -176,7 +178,7 @@ public class FabricItem extends Item {
*/
private void sendClientDataToServer(TestSyncData clientData, int targetEntityId) {
// 使用网络系统发送数据包
ExNetworkHandler.INSTANCE.sendToServer(new ClientDataPacket(clientData, targetEntityId));
ClientPacketDistributor.sendToServer(new ClientDataPayload(clientData, targetEntityId));
}
/**
@ -209,7 +211,7 @@ public class FabricItem extends Item {
*/
private static @Nullable TestSyncData getServerSideData(LivingEntity target) {
try {
AbstractedTestSyncData abstractData = target.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null);
AbstractedTestSyncData abstractData = target.getCapability(ExCapabilityHandler.TEST_CAP);
if (abstractData instanceof TestSyncData) {
return (TestSyncData) abstractData;
}
@ -229,9 +231,7 @@ public class FabricItem extends Item {
Thread.sleep(3000);
// 在服务器线程中执行结果处理
player.server.execute(() -> {
displayServerSingleEndResults(player, target);
});
player.server.execute(() -> displayServerSingleEndResults(player, target));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@ -268,24 +268,23 @@ public class FabricItem extends Item {
/**
* 显示客户端查询结果
*/
private void displayClientSideResults(Player player, LivingEntity target, TestSyncData clientData) {
player.sendSystemMessage(Component.literal("§6=== 客户端数据查询结果 ==="));
player.sendSystemMessage(Component.literal("§7目标生物: §e" + target.getName().getString()));
player.sendSystemMessage(Component.literal("§7数据来源: §9客户端本地"));
player.sendSystemMessage(Component.literal(""));
private void displayClientSideResults(LivingEntity target, TestSyncData clientData) {
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§6=== 客户端数据查询结果 ==="));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§7目标生物: §e" + target.getName().getString()));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§7数据来源: §9客户端本地"));
MessageDisplayClientHelper.sendSystemMessage(Component.literal(""));
player.sendSystemMessage(Component.literal("§a基础数据:"));
player.sendSystemMessage(Component.literal("§7字符串: §f" + clientData.getTestString()));
player.sendSystemMessage(Component.literal("§7整数值: §f" + clientData.getTestInt()));
player.sendSystemMessage(Component.literal("§7布尔值: §f" + clientData.isTestBoolean()));
player.sendSystemMessage(Component.literal("§7双精度值: §f" + String.format("%.2f", clientData.getTestDouble())));
player.sendSystemMessage(Component.literal("§7计数器: §f" + clientData.getCounter()));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§a基础数据:"));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§7字符串: §f" + clientData.getTestString()));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§7整数值: §f" + clientData.getTestInt()));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§7布尔值: §f" + clientData.isTestBoolean()));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§7双精度值: §f" + String.format("%.2f", clientData.getTestDouble())));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§7计数器: §f" + clientData.getCounter()));
// 显示客户端特定信息
player.sendSystemMessage(Component.literal(""));
player.sendSystemMessage(Component.literal("§e客户端状态:"));
player.sendSystemMessage(Component.literal("§7数据验证: " + (clientData.validateData() ? "§a通过" : "§c失败")));
player.sendSystemMessage(Component.literal("§7同步状态: " + (clientData.isDirty() ? "§6待同步" : "§a已同步")));
MessageDisplayClientHelper.sendSystemMessage(Component.literal(""));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§e客户端状态:"));
MessageDisplayClientHelper.sendSystemMessage(Component.literal("§7数据验证: " + (clientData.validateData() ? "§a通过" : "§c失败")));
}
/**
@ -342,9 +341,6 @@ public class FabricItem extends Item {
player.sendSystemMessage(Component.literal(
String.format("§7数据验证: %s", isValid ? "§a通过" : "§c失败")
));
player.sendSystemMessage(Component.literal(
String.format("§7数据状态: %s", testData.isDirty() ? "§6未同步" : "§a已同步")
));
}
/**
@ -360,7 +356,7 @@ public class FabricItem extends Item {
// 显示双端数据来源
player.sendSystemMessage(Component.literal("§a数据来源:"));
player.sendSystemMessage(Component.literal("§7- §c服务器端§7: 实体ID " + serverData.entityId()));
player.sendSystemMessage(Component.literal("§7- §9客户端§7: 实体ID " + clientData.entityId()));
player.sendSystemMessage(Component.literal("§7- §9客户端§7: 实体ID " + serverData.entityId()));
player.sendSystemMessage(Component.literal(""));
// 比较各个字段
@ -410,12 +406,6 @@ public class FabricItem extends Item {
// 显示数据状态差异
player.sendSystemMessage(Component.literal(""));
player.sendSystemMessage(Component.literal("§a数据状态差异:"));
player.sendSystemMessage(Component.literal(
String.format("§7服务器脏数据状态: %s", serverData.isDirty() ? "§6脏" : "§a干净")
));
player.sendSystemMessage(Component.literal(
String.format("§7客户端脏数据状态: %s", clientData.isDirty() ? "§6脏" : "§a干净")
));
player.sendSystemMessage(Component.literal(
String.format("§7服务器验证状态: %s", serverData.validateData() ? "§a通过" : "§c失败")
));
@ -552,38 +542,37 @@ public class FabricItem extends Item {
*/
private AbstractedTestSyncData getTestSyncData(Entity entity) {
try {
return entity.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null);
return entity.getCapability(ExCapabilityHandler.TEST_CAP);
} catch (Exception e) {
Lib39.LOGGER.debug("[FabricItem] 获取生物 {} 的 TestSyncData 失败: {}",
entity.getName().getString(), e.getMessage());
return null;
}
}
@Override
public void appendHoverText(@NotNull ItemStack stack, @Nullable Level level,
@NotNull List<Component> tooltip, @NotNull TooltipFlag flag) {
super.appendHoverText(stack, level, tooltip, flag);
tooltip.add(Component.literal("§7右键点击在 3 秒后执行"));
tooltip.add(Component.literal("§7§e准星瞄准生物§7的数据查询"));
tooltip.add(Component.literal("§7§oShift + 右键§7进行§e客户端-服务器双端同步检查§7"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§6查询延迟: §e3秒"));
tooltip.add(Component.literal("§6瞄准距离: §e20格"));
tooltip.add(Component.literal("§6冷却时间: §e1秒"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§a单端查询内容:"));
tooltip.add(Component.literal("§7- 基础数据字段"));
tooltip.add(Component.literal("§7- 自定义数据结构"));
tooltip.add(Component.literal("§7- 数据验证状态"));
tooltip.add(Component.literal("§7- 同步状态信息"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§e双端同步检查:"));
tooltip.add(Component.literal("§7- 客户端和服务器同时查询"));
tooltip.add(Component.literal("§7- 字段级同步状态对比"));
tooltip.add(Component.literal("§7- 总体同步率计算"));
tooltip.add(Component.literal("§7- 双端数据状态差异"));
tooltip.add(Component.literal("§7- 同步建议"));
@SuppressWarnings("deprecation")
public void appendHoverText(
@NotNull ItemStack stack, Item.@NotNull TooltipContext context, @NotNull TooltipDisplay tooltipDisplay, @NotNull Consumer<Component> tooltipAdder, @NotNull TooltipFlag flag
) {
tooltipAdder.accept(Component.literal("§7右键点击在 3 秒后执行"));
tooltipAdder.accept(Component.literal("§7§e准星瞄准生物§7的数据查询"));
tooltipAdder.accept(Component.literal("§7§oShift + 右键§7进行§e客户端-服务器双端同步检查§7"));
tooltipAdder.accept(Component.literal(""));
tooltipAdder.accept(Component.literal("§6查询延迟: §e3秒"));
tooltipAdder.accept(Component.literal("§6瞄准距离: §e20格"));
tooltipAdder.accept(Component.literal("§6冷却时间: §e1秒"));
tooltipAdder.accept(Component.literal(""));
tooltipAdder.accept(Component.literal("§a单端查询内容:"));
tooltipAdder.accept(Component.literal("§7- 基础数据字段"));
tooltipAdder.accept(Component.literal("§7- 自定义数据结构"));
tooltipAdder.accept(Component.literal("§7- 数据验证状态"));
tooltipAdder.accept(Component.literal("§7- 同步状态信息"));
tooltipAdder.accept(Component.literal(""));
tooltipAdder.accept(Component.literal("§e双端同步检查:"));
tooltipAdder.accept(Component.literal("§7- 客户端和服务器同时查询"));
tooltipAdder.accept(Component.literal("§7- 字段级同步状态对比"));
tooltipAdder.accept(Component.literal("§7- 总体同步率计算"));
tooltipAdder.accept(Component.literal("§7- 双端数据状态差异"));
tooltipAdder.accept(Component.literal("§7- 同步建议"));
}
}

View File

@ -3,7 +3,7 @@ package top.r3944realms.lib39.example.content.item;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
@ -11,18 +11,19 @@ import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.TooltipDisplay;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.content.capability.AbstractedTestSyncData;
import top.r3944realms.lib39.example.content.capability.ExCapabilityHandler;
import top.r3944realms.lib39.example.content.capability.TestSyncData;
import top.r3944realms.lib39.util.chat.MessageDisplayClientHelper;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
/**
* 用于对准星生物触发 TestSyncData 随机变换的物品
@ -42,9 +43,7 @@ public class NeoForgeItem extends Item {
}
@Override
public @NotNull InteractionResultHolder<ItemStack> use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) {
ItemStack itemStack = player.getItemInHand(hand);
public @NotNull InteractionResult use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) {
if (!level.isClientSide()) {
ServerPlayer serverPlayer = (ServerPlayer) player;
@ -57,10 +56,10 @@ public class NeoForgeItem extends Item {
}
// 添加冷却时间
player.getCooldowns().addCooldown(this, 20); // 1秒冷却
player.getCooldowns().addCooldown(this.getDefaultInstance(), 20); // 1秒冷却
}
return InteractionResultHolder.sidedSuccess(itemStack, level.isClientSide());
return InteractionResult.SUCCESS_SERVER;
}
/**
@ -207,8 +206,8 @@ public class NeoForgeItem extends Item {
}
// 显示数据预览仅对玩家自己操作时显示
if (entity instanceof Player) {
displayDataPreview((Player) entity, testData);
if (entity instanceof Player player) {
displayDataPreview(player, testData);
}
return true;
@ -224,16 +223,18 @@ public class NeoForgeItem extends Item {
* 显示数据预览给玩家
*/
private void displayDataPreview(Player player, TestSyncData testData) {
player.sendSystemMessage(Component.literal("§6数据预览:"));
player.sendSystemMessage(Component.literal(
String.format("§7字符串: §f%s", testData.getTestString())
));
player.sendSystemMessage(Component.literal(
String.format("§7计数器: §f%d", testData.getCounter())
));
player.sendSystemMessage(Component.literal(
String.format("§7验证状态: %s", testData.validateData() ? "§a通过" : "§c失败")
));
if(player instanceof ServerPlayer serverPlayer) {
serverPlayer.sendSystemMessage(Component.literal("§6数据预览:"));
serverPlayer.sendSystemMessage(Component.literal(
String.format("§7字符串: §f%s", testData.getTestString())
));
serverPlayer.sendSystemMessage(Component.literal(
String.format("§7计数器: §f%d", testData.getCounter())
));
serverPlayer.sendSystemMessage(Component.literal(
String.format("§7验证状态: %s", testData.validateData() ? "§a通过" : "§c失败")
));
}
}
/**
@ -249,7 +250,7 @@ public class NeoForgeItem extends Item {
private AbstractedTestSyncData getOrCreateTestSyncData(Entity entity) {
try {
return entity.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElseThrow();
return entity.getCapability(ExCapabilityHandler.TEST_CAP);
} catch (Exception e) {
Lib39.LOGGER.error("[NeoForgeItem] 获取 {} 的 TestSyncData 失败: {}",
getEntityName((LivingEntity) entity), e.getMessage());
@ -257,28 +258,28 @@ public class NeoForgeItem extends Item {
}
}
@Override
public void appendHoverText(@NotNull ItemStack stack, @Nullable Level level,
@NotNull List<Component> tooltip, @NotNull TooltipFlag flag) {
super.appendHoverText(stack, level, tooltip, flag);
@SuppressWarnings("deprecation")
public void appendHoverText(
@NotNull ItemStack stack, Item.@NotNull TooltipContext context, @NotNull TooltipDisplay tooltipDisplay, @NotNull Consumer<Component> tooltipAdder, @NotNull TooltipFlag flag
) {
tooltip.add(Component.literal("§7右键点击触发§e准星瞄准生物§7的"));
tooltip.add(Component.literal("§7测试数据随机变换"));
tooltip.add(Component.literal("§7§oShift + 右键§7操作§e自身§7数据"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§6冷却时间: §e1秒"));
tooltip.add(Component.literal("§6瞄准距离: §e20格"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§a变换类型:"));
tooltip.add(Component.literal("§7- 完全随机数据"));
tooltip.add(Component.literal("§7- 字符串+计数器"));
tooltip.add(Component.literal("§7- 数值数据"));
tooltip.add(Component.literal("§7- 自定义数据"));
tooltip.add(Component.literal("§7- 重置默认值"));
tooltip.add(Component.literal("§7- 玩家专属数据"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§e自身操作特性:"));
tooltip.add(Component.literal("§7- 显示数据预览"));
tooltip.add(Component.literal("§7- 玩家专属数据变换"));
tooltipAdder.accept(Component.literal("§7右键点击触发§e准星瞄准生物§7的"));
tooltipAdder.accept(Component.literal("§7测试数据随机变换"));
tooltipAdder.accept(Component.literal("§7§oShift + 右键§7操作§e自身§7数据"));
tooltipAdder.accept(Component.literal(""));
tooltipAdder.accept(Component.literal("§6冷却时间: §e1秒"));
tooltipAdder.accept(Component.literal("§6瞄准距离: §e20格"));
tooltipAdder.accept(Component.literal(""));
tooltipAdder.accept(Component.literal("§a变换类型:"));
tooltipAdder.accept(Component.literal("§7- 完全随机数据"));
tooltipAdder.accept(Component.literal("§7- 字符串+计数器"));
tooltipAdder.accept(Component.literal("§7- 数值数据"));
tooltipAdder.accept(Component.literal("§7- 自定义数据"));
tooltipAdder.accept(Component.literal("§7- 重置默认值"));
tooltipAdder.accept(Component.literal("§7- 玩家专属数据"));
tooltipAdder.accept(Component.literal(""));
tooltipAdder.accept(Component.literal("§e自身操作特性:"));
tooltipAdder.accept(Component.literal("§7- 显示数据预览"));
tooltipAdder.accept(Component.literal("§7- 玩家专属数据变换"));
}
}

View File

@ -1,20 +1,20 @@
package top.r3944realms.lib39.example.core.event;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.data.event.GatherDataEvent;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import top.r3944realms.lib39.api.event.SyncManagerRegisterEvent;
import top.r3944realms.lib39.core.sync.CachedSyncManager;
import top.r3944realms.lib39.example.content.capability.AbstractedTestSyncData;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.entity.vehicle.Minecart;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
import net.neoforged.neoforge.data.event.GatherDataEvent;
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import top.r3944realms.lib39.example.content.capability.ExCapabilityHandler;
import top.r3944realms.lib39.example.content.capability.TestSyncData;
import top.r3944realms.lib39.example.datagen.EXLib39DataGenEvent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import top.r3944realms.lib39.example.core.network.ExNetworkHandler;
import top.r3944realms.lib39.example.core.register.ExLib39Attachments;
import top.r3944realms.lib39.example.datagen.ExLib39DataGenEvent;
/**
* The type Common handler.
@ -26,40 +26,18 @@ public class ExCommonEventHandler {
@SuppressWarnings("unused")
public static class Game extends ExCommonEventHandler {
/**
* Attach capability.
*
* @param event the event
*/
@SubscribeEvent
public static void attachCapability(AttachCapabilitiesEvent<?> event) {
ExCapabilityHandler.attachCapability(event);
public static void onEntityJoinLevel(EntityJoinLevelEvent event) {
Entity entity = event.getEntity();
if (entity instanceof LivingEntity || entity instanceof Boat || entity instanceof Minecart) {
TestSyncData data = entity.getData(ExLib39Attachments.TEST_DATA_ATTACHMENT);
if (data.entityId() == -1) {
data.setSelf(entity);
}
}
}
/**
* On register sync.
*
* @param event the event
*/
@SubscribeEvent
public static void onRegisterSync (SyncManagerRegisterEvent event) {
event.registerSyncManager(
TestSyncData.ID,
new CachedSyncManager<Entity, AbstractedTestSyncData>() {
private final Map<Entity, AbstractedTestSyncData> syncDataMap = new ConcurrentHashMap<>();
@Override
public Map<Entity, AbstractedTestSyncData> getSyncMap() {
return syncDataMap;
}
},
ExCapabilityHandler.TEST_CAP
);
}
}
/**
* The type Mod.
*/
@ -84,7 +62,7 @@ public class ExCommonEventHandler {
*/
@SubscribeEvent
public static void registerCapability(RegisterCapabilitiesEvent event) {
ExCapabilityHandler.registerCapability(event);
ExCapabilityHandler.registerCapabilities(event);
}
/**
@ -93,8 +71,12 @@ public class ExCommonEventHandler {
* @param event the event
*/
@SubscribeEvent
public static void gatherData(GatherDataEvent event) {
EXLib39DataGenEvent.gatherData(event);
public static void gatherData(GatherDataEvent.Client event) {
ExLib39DataGenEvent.gatherData(event);
}
@SubscribeEvent
public static void registerPayloadPacket (RegisterPayloadHandlersEvent event) {
ExNetworkHandler.registerPackets(event);
}
}
}

View File

@ -1,69 +0,0 @@
package top.r3944realms.lib39.example.core.network;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent;
import top.r3944realms.lib39.example.content.capability.TestSyncData;
import top.r3944realms.lib39.example.content.item.FabricItem;
import java.util.function.Supplier;
/**
* The type Client data packet.
*/
public class ClientDataPacket {
private final TestSyncData clientData;
private final int targetEntityId;
/**
* Instantiates a new Client data packet.
*
* @param clientData the client data
* @param targetEntityId the target entity id
*/
public ClientDataPacket(TestSyncData clientData, int targetEntityId) {
this.clientData = clientData;
this.targetEntityId = targetEntityId;
}
/**
* Instantiates a new Client data packet.
*
* @param buf the buf
*/
public ClientDataPacket(FriendlyByteBuf buf) {
this.clientData = TestSyncData.staticFromBytes(buf);
this.targetEntityId = buf.readInt();
}
/**
* To bytes.
*
* @param buf the buf
*/
public void toBytes(FriendlyByteBuf buf) {
clientData.toBytes(buf);
buf.writeInt(targetEntityId);
}
/**
* Handle.
*
* @param supplier the supplier
*/
public void handle(Supplier<NetworkEvent.Context> supplier) {
NetworkEvent.Context context = supplier.get();
context.enqueueWork(() -> {
ServerPlayer player = context.getSender();
if (player != null) {
// 处理客户端发送的数据
handleClientData(player, clientData, targetEntityId);
}
});
context.setPacketHandled(true);
}
private void handleClientData(ServerPlayer player, TestSyncData clientData, int targetEntityId) {
FabricItem.handleClientDataFromPacket(player, clientData, targetEntityId);
}
}

View File

@ -0,0 +1,43 @@
package top.r3944realms.lib39.example.core.network;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.content.capability.TestSyncData;
import top.r3944realms.lib39.example.content.item.FabricItem;
/**
* The type Client data packet.
*/
public record ClientDataPayload(TestSyncData clientData, int targetEntityId) implements CustomPacketPayload {
public static final Type<ClientDataPayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Lib39.MOD_ID, "client_data"));
public static final StreamCodec<FriendlyByteBuf, ClientDataPayload> STREAM_CODEC =
StreamCodec.composite(
TestSyncData.CODEC, ClientDataPayload::clientData,
ByteBufCodecs.INT, ClientDataPayload::targetEntityId,
ClientDataPayload::new
);
public void handle(IPayloadContext context) {
context.enqueueWork(() -> {
ServerPlayer player = (ServerPlayer) context.player();
// 处理客户端发送的数据
handleClientData(player, clientData, targetEntityId);
});
}
private void handleClientData(ServerPlayer player, TestSyncData clientData, int targetEntityId) {
FabricItem.handleClientDataFromPacket(player, clientData, targetEntityId);
}
@Override
public @NotNull Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

View File

@ -1,37 +1,21 @@
package top.r3944realms.lib39.example.core.network;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
/**
* The type Ex network handler.
*/
public class ExNetworkHandler {
/**
* The constant INSTANCE.
*/
public static final SimpleChannel INSTANCE;
private static int ID = 0;
static {
INSTANCE = NetworkRegistry.newSimpleChannel(
new ResourceLocation(Lib39.MOD_ID, "test"),
() -> "1.0",
s -> true,
s -> true
public static void registerPackets(@NotNull RegisterPayloadHandlersEvent event) {
PayloadRegistrar registrar = event.registrar(Lib39.MOD_ID + "-ex-" + Lib39.ModInfo.VERSION);
registrar.playToServer(
ClientDataPayload.TYPE,
ClientDataPayload.STREAM_CODEC,
ClientDataPayload::handle
);
}
/**
* Register.
*/
public static void register() {
// 注册数据包
INSTANCE.registerMessage(ID++, ClientDataPacket.class,
ClientDataPacket::toBytes,
ClientDataPacket::new,
ClientDataPacket::handle);
}
}

View File

@ -0,0 +1,36 @@
package top.r3944realms.lib39.example.core.register;
import net.minecraft.world.entity.Entity;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.attachment.AttachmentType;
import net.neoforged.neoforge.registries.DeferredRegister;
import net.neoforged.neoforge.registries.NeoForgeRegistries;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.content.capability.TestSyncData;
import java.util.function.Supplier;
public class ExLib39Attachments {
private static final DeferredRegister<AttachmentType<?>> ATTACHMENTS =
DeferredRegister.create(NeoForgeRegistries.ATTACHMENT_TYPES, Lib39.MOD_ID);
public static final Supplier<AttachmentType<TestSyncData>> TEST_DATA_ATTACHMENT =
ATTACHMENTS.register(
"test_data",
() -> AttachmentType
.serializable(holder -> new TestSyncData(holder))
.copyOnDeath()
.copyHandler((attachment, holder, provider) -> {
if (!(holder instanceof Entity newEntity)) {
throw new IllegalArgumentException("TestSyncData can only be copied to entities");
}
return attachment.createSyncCopy(newEntity);
})
.sync(TestSyncData.CODEC)
.build()
);
public static void register(IEventBus iModBus) {
ATTACHMENTS.register(iModBus);
}
}

View File

@ -0,0 +1,30 @@
package top.r3944realms.lib39.example.core.register;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.neoforged.neoforge.registries.DeferredHolder;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.datagen.value.IResourceKeyValue;
public enum ExLib39ItemResourceKeys implements IResourceKeyValue {
FABRIC("fabric"),
NEOFORGE("neoforge"),
;
private final ResourceKey<Item> resourceKey;
ExLib39ItemResourceKeys(String name) {
resourceKey = ResourceKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(Lib39.MOD_ID, name));
}
ExLib39ItemResourceKeys(@NotNull DeferredHolder<Item, ? extends Item> item) {
resourceKey = ResourceKey.create(Registries.ITEM, item.getId());
}
@Contract(pure = true)
@Override
public @Nullable ResourceKey<Item> getResourceKey() {
return resourceKey;
}
}

View File

@ -1,14 +1,18 @@
package top.r3944realms.lib39.example.core.register;
import net.minecraft.core.registries.Registries;
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 net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.content.item.FabricItem;
import top.r3944realms.lib39.example.content.item.NeoForgeItem;
import java.util.Objects;
/**
* The type Ex lib 39 items.
*/
@ -16,27 +20,29 @@ public class ExLib39Items {
/**
* The constant ITEMS.
*/
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Lib39.MOD_ID);
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(Registries.ITEM, Lib39.MOD_ID);
/**
* The constant SUPER_LEAD_ROPE.
*/
public static final RegistryObject<Item> FABRIC = ITEMS.register(
public static final DeferredHolder<Item, Item> FABRIC = ITEMS.register(
"fabric",
() -> new FabricItem(
new Item.Properties()
.stacksTo(1)
.fireResistant()
.setId(Objects.requireNonNull(ExLib39ItemResourceKeys.FABRIC.getResourceKey()))
)
);
/**
* The constant ETERNAL_POTATO.
*/
public static final RegistryObject<Item> NEOFORGE =
public static final DeferredHolder<Item, Item> NEOFORGE =
ITEMS.register("neoforge",
() -> new NeoForgeItem(
new Item.Properties()
.stacksTo(1)
.fireResistant()
.setId(Objects.requireNonNull(ExLib39ItemResourceKeys.NEOFORGE.getResourceKey()))
));
/**

View File

@ -1,48 +1,48 @@
package top.r3944realms.lib39.example.datagen;
import net.minecraft.data.DataProvider;
import net.minecraftforge.data.event.GatherDataEvent;
import net.neoforged.neoforge.data.event.GatherDataEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.datagen.provider.SimpleLanguageProvider;
import top.r3944realms.lib39.datagen.value.McLocale;
import top.r3944realms.lib39.example.datagen.data.ExLib39LangKeys;
import top.r3944realms.lib39.example.datagen.provider.ExItemModelProvider;
import top.r3944realms.lib39.example.datagen.provider.ExModelDataProvider;
/**
* The type Ex lib 39 data gen event.
*/
public class EXLib39DataGenEvent {
public class ExLib39DataGenEvent {
/**
* The Logger.
*/
static Logger logger = LoggerFactory.getLogger(EXLib39DataGenEvent.class);
static Logger logger = LoggerFactory.getLogger(ExLib39DataGenEvent.class);
/**
* Gather data.
*
* @param event the event
*/
public static void gatherData(GatherDataEvent event) {
public static void gatherData(GatherDataEvent.Client event) {
logger.info("GatherDataEvent thread: {}", Thread.currentThread().getName());
LanguageGenerator(event, McLocale.EN_US);
LanguageGenerator(event, McLocale.ZH_CN);
LanguageGenerator(event, McLocale.ZH_TW);
LanguageGenerator(event, McLocale.LZH);
LanguageGenerate(event, McLocale.EN_US);
LanguageGenerate(event, McLocale.ZH_CN);
LanguageGenerate(event, McLocale.ZH_TW);
LanguageGenerate(event, McLocale.LZH);
ModelDataGenerate(event);
}
private static void LanguageGenerator(GatherDataEvent event, McLocale language) {
private static void LanguageGenerate(GatherDataEvent event, McLocale language) {
event.getGenerator().addProvider(
event.includeClient(),
true,
(DataProvider.Factory<SimpleLanguageProvider>) pOutput -> new SimpleLanguageProvider(pOutput, Lib39.MOD_ID ,language ,ExLib39LangKeys.INSTANCE)
);
}
private static void ModelDataGenerate(GatherDataEvent event) {
event.getGenerator().addProvider(
event.includeClient(),
(DataProvider.Factory<ExItemModelProvider>) pOutput -> new ExItemModelProvider(pOutput, event.getExistingFileHelper())
true,
(DataProvider.Factory<ExModelDataProvider>) pOutput -> new ExModelDataProvider(pOutput, Lib39.MOD_ID)
);
}
}

View File

@ -0,0 +1,21 @@
package top.r3944realms.lib39.example.datagen.generator;
import net.minecraft.client.data.models.BlockModelGenerators;
import net.minecraft.client.data.models.ItemModelOutput;
import net.minecraft.client.data.models.blockstates.BlockModelDefinitionGenerator;
import net.minecraft.client.data.models.model.ModelInstance;
import net.minecraft.resources.ResourceLocation;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ExBlockModelGenerator extends BlockModelGenerators {
public ExBlockModelGenerator(Consumer<BlockModelDefinitionGenerator> blockStateOutput, ItemModelOutput itemModelOutput, BiConsumer<ResourceLocation, ModelInstance> modelOutput) {
super(blockStateOutput, itemModelOutput, modelOutput);
}
@Override
public void run() {
}
}

View File

@ -0,0 +1,68 @@
/*
* Super Lead rope mod
* Copyright (C) 2025 R3944Realms
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package top.r3944realms.lib39.example.datagen.generator;
import net.minecraft.client.data.models.ItemModelGenerators;
import net.minecraft.client.data.models.ItemModelOutput;
import net.minecraft.client.data.models.model.ModelInstance;
import net.minecraft.client.data.models.model.ModelTemplates;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import top.r3944realms.lib39.datagen.value.LangKeyValue;
import top.r3944realms.lib39.datagen.value.ModPartEnum;
import top.r3944realms.lib39.example.datagen.data.ExLib39LangKeys;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
/**
* The type Slp item model provider.
*/
public class ExItemModelGenerators extends ItemModelGenerators {
private static List<Item> objectList;
/**
* Instantiates a new Slp item model provider.
*
* @param output the output
* @param existingFileHelper the existing file helper
*/
public ExItemModelGenerators(ItemModelOutput itemModelOutput, BiConsumer<ResourceLocation, ModelInstance> modelOutput) {
super(itemModelOutput, modelOutput);
objectList = new ArrayList<>();
init();
}
private void init() {
for(LangKeyValue obj : ExLib39LangKeys.INSTANCE.getValues()) {
if(!(obj.isDefault() && obj.getMPE().equals(ModPartEnum.ITEM))) continue;
objectList.add(obj.getItem());
}
}
@Override
public void run() {
DefaultModItemModelRegister();
}
/**
* @implNote <br/>&nbsp;先有纹理才会成功构建
*/
private void DefaultModItemModelRegister() {
objectList.forEach(i -> generateFlatItem(i, ModelTemplates.FLAT_ITEM));
}
}

View File

@ -1,115 +0,0 @@
/*
* Super Lead rope mod
* Copyright (C) 2025 R3944Realms
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package top.r3944realms.lib39.example.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.datagen.value.LangKeyValue;
import top.r3944realms.lib39.datagen.value.ModPartEnum;
import top.r3944realms.lib39.example.datagen.data.ExLib39LangKeys;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The type Slp item model provider.
*/
public class ExItemModelProvider extends ItemModelProvider {
private static List<Item> objectList;
/**
* The constant GENERATED.
*/
public static final String GENERATED = "item/generated";
/**
* The constant HANDHELD.
*/
public static final String HANDHELD = "item/handheld";
/**
* Instantiates a new Slp item model provider.
*
* @param output the output
* @param existingFileHelper the existing file helper
*/
public ExItemModelProvider(PackOutput output, ExistingFileHelper existingFileHelper) {
super(output, Lib39.MOD_ID, existingFileHelper);
objectList = new ArrayList<>();
init();
}
@Override
protected void registerModels() {
DefaultModItemModelRegister();
}
private void init() {
for(LangKeyValue obj : ExLib39LangKeys.INSTANCE.getValues()) {
if(!(obj.isDefault() && obj.getMPE().equals(ModPartEnum.ITEM))) continue;
objectList.add(obj.getItem());
}
}
/**
* @implNote <br/>&nbsp;先有纹理才会成功构建
*/
private void DefaultModItemModelRegister() {
objectList.forEach(this::basicItem);
}
/**
* Item generate model.
*
* @param item the item
* @param location the location
*/
public void itemGenerateModel(Item item, ResourceLocation location){
withExistingParent(itemName(item), GENERATED).texture("layer0", location);
}
/**
* Item hand held model.
*
* @param item the item
* @param location the location
*/
public void itemHandHeldModel(Item item, ResourceLocation location){
withExistingParent(itemName(item), HANDHELD).texture("layer0", location);
}
/**
* Item name string.
*
* @param item the item
* @return the string
*/
public String itemName(Item item){
return Objects.requireNonNull(ForgeRegistries.ITEMS.getKey(item)).getPath();
}
/**
* Resource item resource location.
*
* @param path the path
* @return the resource location
*/
public ResourceLocation resourceItem(String path){
return modLoc("item/" + path);
}
}

View File

@ -0,0 +1,179 @@
package top.r3944realms.lib39.example.datagen.provider;
import com.google.common.collect.Maps;
import com.google.gson.JsonElement;
import net.minecraft.client.data.models.ItemModelOutput;
import net.minecraft.client.data.models.ModelProvider;
import net.minecraft.client.data.models.blockstates.BlockModelDefinitionGenerator;
import net.minecraft.client.data.models.model.ItemModelUtils;
import net.minecraft.client.data.models.model.ModelInstance;
import net.minecraft.client.data.models.model.ModelLocationUtils;
import net.minecraft.client.renderer.block.model.BlockModelDefinition;
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.example.datagen.generator.ExBlockModelGenerator;
import top.r3944realms.lib39.example.datagen.generator.ExItemModelGenerators;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class ExModelDataProvider extends ModelProvider {
private final PackOutput.PathProvider blockStatePathProvider;
private final PackOutput.PathProvider itemInfoPathProvider;
private final PackOutput.PathProvider modelPathProvider;
public ExModelDataProvider(PackOutput packOutput, String modId) {
super(packOutput, modId);
this.blockStatePathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "blockstates");
this.itemInfoPathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "items");
this.modelPathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "models");
}
@Override
public @NotNull CompletableFuture<?> run(@NotNull CachedOutput cachedOutput) {
ItemInfoCollector itemModelOutput = new ItemInfoCollector(this::getKnownItems);
BlockStateGeneratorCollector blockModelOutput = new BlockStateGeneratorCollector(this::getKnownBlocks);
SimpleModelCollector modelOutput = new SimpleModelCollector();
itemModelOutput.finalizeAndValidate();
this.registerModels(new ExBlockModelGenerator(blockModelOutput, itemModelOutput, modelOutput), new ExItemModelGenerators(itemModelOutput, modelOutput));
return CompletableFuture.allOf(blockModelOutput.save(cachedOutput, this.blockStatePathProvider), modelOutput.save(cachedOutput, this.modelPathProvider), itemModelOutput.save(cachedOutput, this.itemInfoPathProvider));
}
static class ItemInfoCollector implements ItemModelOutput {
private final Map<Item, ClientItem> itemInfos;
private final Map<Item, Item> copies;
private final Supplier<Stream<? extends Holder<Item>>> knownItems;
public ItemInfoCollector(Supplier<Stream<? extends Holder<Item>>> knownItems) {
this.itemInfos = new HashMap<>();
this.copies = new HashMap<>();
this.knownItems = knownItems;
}
public void accept(@NotNull Item item, ItemModel.@NotNull Unbaked model) {
this.register(item, new ClientItem(model, ClientItem.Properties.DEFAULT));
}
public void register(@NotNull Item item, @NotNull ClientItem clientItem) {
ClientItem clientitem = this.itemInfos.put(item, clientItem);
if (clientitem != null) {
throw new IllegalStateException("Duplicate item model definition for " + item);
}
}
public void copy(@NotNull Item item, @NotNull Item copyItem) {
this.copies.put(copyItem, item);
}
public void finalizeAndValidate() {
(this.knownItems.get()).map(Holder::value).forEach((item) -> {
if (!this.copies.containsKey(item) && item instanceof BlockItem blockitem) {
if (!this.itemInfos.containsKey(blockitem)) {
ResourceLocation resourcelocation = ModelLocationUtils.getModelLocation(blockitem.getBlock());
this.accept(blockitem, ItemModelUtils.plainModel(resourcelocation));
}
}
});
this.copies.forEach((item, item1) -> {
ClientItem clientitem = this.itemInfos.get(item1);
if (clientitem == null) {
String item1Name = String.valueOf(item1);
throw new IllegalStateException("Missing donor: " + item1Name + " -> " + item);
} else {
this.register(item, clientitem);
}
});
List<ResourceLocation> list = (this.knownItems.get()).filter((holder) -> !this.itemInfos.containsKey(holder.value())).map((holder) -> holder.unwrapKey().orElseThrow().location()).toList();
if (!list.isEmpty()) {
LOGGER.warn("Missing item model definitions for: {}", list);
}
}
public CompletableFuture<?> save(CachedOutput cachedOutput, PackOutput.PathProvider pathProvider) {
return DataProvider.saveAll(cachedOutput, ClientItem.CODEC, (item) -> pathProvider.json(item.builtInRegistryHolder().key().location()), this.itemInfos);
}
}
static class SimpleModelCollector implements BiConsumer<ResourceLocation, ModelInstance> {
private final Map<ResourceLocation, ModelInstance> models = new HashMap<>();
SimpleModelCollector() {
}
public void accept(ResourceLocation resourceLocation, ModelInstance modelInstance) {
Supplier<JsonElement> supplier = this.models.put(resourceLocation, modelInstance);
if (supplier != null) {
throw new IllegalStateException("Duplicate model definition for " + resourceLocation);
}
}
public CompletableFuture<?> save(CachedOutput cachedOutput, PackOutput.PathProvider pathProvider) {
Objects.requireNonNull(pathProvider);
return saveAll(cachedOutput, pathProvider::json, this.models);
}
static <T> CompletableFuture<?> saveAll(CachedOutput cachedOutput, Function<T, Path> tPathFunction, Map<T, ? extends Supplier<JsonElement>> tMap) {
return DataProvider.saveAll(cachedOutput, Supplier::get, tPathFunction, tMap);
}
}
static class BlockStateGeneratorCollector implements Consumer<BlockModelDefinitionGenerator> {
private final Map<Block, BlockModelDefinitionGenerator> generators = new HashMap<>();
private final Supplier<Stream<? extends Holder<Block>>> knownBlocks;
public BlockStateGeneratorCollector(Supplier<Stream<? extends Holder<Block>>> knownBlocks) {
this.knownBlocks = knownBlocks;
}
@Deprecated // Neo: Provided for vanilla/multi-loader compatibility. Use constructor with Supplier parameter.
public BlockStateGeneratorCollector() {
this(BuiltInRegistries.BLOCK::listElements);
}
public void accept(BlockModelDefinitionGenerator p_405192_) {
Block block = p_405192_.block();
BlockModelDefinitionGenerator blockmodeldefinitiongenerator = this.generators.put(block, p_405192_);
if (blockmodeldefinitiongenerator != null) {
throw new IllegalStateException("Duplicate blockstate definition for " + block);
}
}
public void validate() {
Stream<? extends Holder<Block>> stream = knownBlocks.get();
List<ResourceLocation> list = stream.filter(p_386843_ -> !this.generators.containsKey(p_386843_.value()))
.map(p_386823_ -> p_386823_.unwrapKey().orElseThrow().location())
.toList();
if (!list.isEmpty()) {
throw new IllegalStateException("Missing blockstate definitions for: " + list);
}
}
public CompletableFuture<?> save(CachedOutput output, PackOutput.PathProvider pathProvider) {
Map<Block, BlockModelDefinition> map = Maps.transformValues(this.generators, BlockModelDefinitionGenerator::create);
Function<Block, Path> function = p_387598_ -> pathProvider.json(p_387598_.builtInRegistryHolder().key().location());
return DataProvider.saveAll(output, BlockModelDefinition.CODEC, function, map);
}
}
}

View File

@ -4,8 +4,9 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.RegistryObject;
import net.neoforged.neoforge.registries.DeferredBlock;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.core.event.CommonEventHandler;
@ -18,7 +19,7 @@ import java.util.function.Supplier;
@SuppressWarnings({"UnusedReturnValue", "unused"})
public class BlockRegistryBuilder {
private String registryName;
private RegistryObject<Block> blockObject;
private DeferredHolder<Block, Block> blockObject;
/**
* 创建新的构建器实例
@ -57,7 +58,7 @@ public class BlockRegistryBuilder {
* 内部方法注册对应的方块物品
*/
@SafeVarargs
private void registerBlockItem(RegistryObject<Block> blockObject, ResourceKey<CreativeModeTab>... creativeTabs) {
private void registerBlockItem(DeferredHolder<Block, Block> blockObject, ResourceKey<CreativeModeTab>... creativeTabs) {
CommonEventHandler.Mod.addItemToTabs(blockObject, creativeTabs);
}
@ -92,7 +93,7 @@ public class BlockRegistryBuilder {
*
* @return the registry object
*/
public RegistryObject<Block> build() {
public DeferredHolder<Block, Block> build() {
return this.blockObject;
}

View File

@ -0,0 +1,199 @@
package top.r3944realms.lib39.util.capability;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.monster.Monster;
import net.neoforged.neoforge.capabilities.EntityCapability;
import net.neoforged.neoforge.capabilities.ICapabilityProvider;
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Predicate;
/**
* 实体 Capability 注册工具类
* 简化批量注册实体能力的过程
*/
@SuppressWarnings("unused")
public class EntityCapabilityHelper {
/**
* 为多个实体类型注册相同的 Capability Provider
*/
public static <T, C extends @Nullable Object> void registerForEntityTypes(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider,
EntityType<?>... entityTypes) {
if (entityTypes.length == 0) {
throw new IllegalArgumentException("必须提供至少一个实体类型");
}
for (EntityType<?> entityType : entityTypes) {
event.registerEntity(capability, entityType, provider);
}
}
/**
* 为继承自某个基类的所有实体注册 Capability
*/
public static <T, C extends @Nullable Object> void registerForEntityClass(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider,
Class<? extends Entity> baseClass) {
BuiltInRegistries.ENTITY_TYPE.stream()
.filter(entityType -> entityType.getBaseClass().isAssignableFrom(baseClass))
.forEach(entityType -> event.registerEntity(capability, entityType, provider));
}
/**
* 为特定命名空间的所有实体注册 Capability
*/
public static <T, C extends @Nullable Object> void registerForModEntities(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider,
String namespace) {
BuiltInRegistries.ENTITY_TYPE.stream()
.filter(entityType -> {
ResourceLocation key = BuiltInRegistries.ENTITY_TYPE.getKey(entityType);
return namespace.equals(key.getNamespace());
})
.forEach(entityType -> event.registerEntity(capability, entityType, provider));
}
/**
* 为特定生物类别的所有实体注册 Capability
*/
public static <T, C extends @Nullable Object> void registerForMobCategory(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider,
MobCategory category) {
BuiltInRegistries.ENTITY_TYPE.stream()
.filter(entityType -> entityType.getCategory() == category)
.forEach(entityType -> event.registerEntity(capability, entityType, provider));
}
/**
* 基于条件筛选注册实体 Capability
*/
public static <T, C extends @Nullable Object> void registerEntitiesByCondition(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider,
Predicate<EntityType<?>> condition) {
BuiltInRegistries.ENTITY_TYPE.stream()
.filter(condition)
.forEach(entityType -> event.registerEntity(capability, entityType, provider));
}
/**
* 为所有生物实体注册 Capability包括玩家
*/
public static <T, C extends @Nullable Object> void registerForAllLivingEntities(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider) {
registerForEntityClass(event, capability, provider, LivingEntity.class);
}
/**
* 为所有怪物实体注册 Capability
*/
public static <T, C extends @Nullable Object> void registerForAllMonsters(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider) {
registerForEntityClass(event, capability, provider, Monster.class);
}
/**
* 为所有动物实体注册 Capability
*/
public static <T, C extends @Nullable Object> void registerForAllAnimals(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider) {
registerForEntityClass(event, capability, provider, Animal.class);
}
/**
* 为所有玩家注册 Capability
*/
public static <T, C extends @Nullable Object> void registerForPlayers(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider) {
registerForEntityTypes(event, capability, provider, EntityType.PLAYER);
}
/**
* 为所有BOSS实体注册 Capability
*/
public static <T, C extends @Nullable Object> void registerForBossEntities(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider) {
registerEntitiesByCondition(event, capability, provider, entityType ->
entityType == EntityType.ENDER_DRAGON ||
entityType == EntityType.WITHER ||
entityType == EntityType.WARDEN
);
}
/**
* 排除特定实体类型进行注册
*/
public static <T, C extends @Nullable Object> void registerWithExclusions(
RegisterCapabilitiesEvent event,
EntityCapability<T, C> capability,
ICapabilityProvider<Entity, C, T> provider,
Collection<EntityType<?>> includedEntities,
EntityType<?>... excludedTypes) {
Collection<EntityType<?>> excludedSet = Arrays.asList(excludedTypes);
includedEntities.stream()
.filter(entityType -> !excludedSet.contains(entityType))
.forEach(entityType -> event.registerEntity(capability, entityType, provider));
}
/**
* 检查某个实体类型是否已经注册了指定的 Capability
*/
public static boolean isEntityCapabilityRegistered(
RegisterCapabilitiesEvent event,
EntityCapability<?, ?> capability,
EntityType<?> entityType) {
return event.isEntityRegistered(capability, entityType);
}
/**
* 获取已注册指定 Capability 的实体类型数量
*/
public static long getRegisteredEntityCount(
RegisterCapabilitiesEvent event,
EntityCapability<?, ?> capability) {
return BuiltInRegistries.ENTITY_TYPE.stream()
.filter(entityType -> event.isEntityRegistered(capability, entityType))
.count();
}
}

View File

@ -0,0 +1,13 @@
package top.r3944realms.lib39.util.chat;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
public class MessageDisplayClientHelper {
public static void sendSystemMessage(Component component) {
sendSystemMessage(component, false);
}
public static void sendSystemMessage(Component component, boolean isOverlay) {
Minecraft.getInstance().execute(() -> Minecraft.getInstance().getChatListener().handleSystemMessage(component, isOverlay));
}
}

View File

@ -75,7 +75,7 @@ public class RidingApplier {
if (RidingValidator.wouldCreateCycle(entity, vehicle)) {
throw new RidingCycleException(entityId, vehicleId);
}
boolean success = entity.startRiding(vehicle, true);
boolean success = entity.startRiding(vehicle, true, true);
if (!success) {
Lib39.LOGGER.error("Failed to mount entity {} to vehicle {}", entityId, vehicleId);
}

View File

@ -1,19 +1,4 @@
/*
* Super Lead rope mod
* Copyright (C) 2025 R3944Realms
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package top.r3944realms.lib39.util.nbt;
package top.r3944realms.lib39.util.storage.nbt;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
@ -21,7 +6,6 @@ import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
import java.util.function.Consumer;
/**
@ -55,8 +39,8 @@ public class NBTReader {
*/
// 基本读取方法 - 直接赋值给成员变量
public NBTReader string(String key, Consumer<String> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getString(key));
if (nbt.contains(key) && nbt.getString(key).isPresent()) {
setter.accept(nbt.getString(key).get());
}
return this;
}
@ -70,7 +54,7 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader string(String key, @NotNull Consumer<String> setter, String defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getString(key) : defaultValue);
setter.accept(nbt.contains(key) && nbt.getString(key).isPresent() ? nbt.getString(key).get() : defaultValue);
return this;
}
@ -82,8 +66,8 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader byteValue(String key, Consumer<Byte> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getByte(key));
if (nbt.contains(key) && nbt.getByte(key).isPresent()) {
setter.accept(nbt.getByte(key).get());
}
return this;
}
@ -97,7 +81,7 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader byteValue(String key, @NotNull Consumer<Byte> setter, byte defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getByte(key) : defaultValue);
setter.accept(nbt.contains(key) ? nbt.getByteOr(key, defaultValue) : defaultValue);
return this;
}
@ -109,8 +93,8 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader shortValue(String key, Consumer<Short> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getShort(key));
if (nbt.contains(key) && nbt.getShort(key).isPresent()) {
setter.accept(nbt.getShort(key).get());
}
return this;
}
@ -124,7 +108,7 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader shortValue(String key, @NotNull Consumer<Short> setter, short defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getShort(key) : defaultValue);
setter.accept(nbt.contains(key) ? nbt.getShortOr(key, defaultValue) : defaultValue);
return this;
}
@ -136,8 +120,8 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader intValue(String key, Consumer<Integer> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getInt(key));
if (nbt.contains(key) && nbt.getInt(key).isPresent()) {
setter.accept(nbt.getInt(key).get());
}
return this;
}
@ -151,7 +135,7 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader intValue(String key, @NotNull Consumer<Integer> setter, int defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getInt(key) : defaultValue);
setter.accept(nbt.contains(key) ? nbt.getIntOr(key, defaultValue) : defaultValue);
return this;
}
@ -163,8 +147,8 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader longValue(String key, Consumer<Long> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getLong(key));
if (nbt.contains(key) && nbt.getLong(key).isPresent()) {
setter.accept(nbt.getLong(key).get());
}
return this;
}
@ -178,7 +162,7 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader longValue(String key, @NotNull Consumer<Long> setter, long defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getLong(key) : defaultValue);
setter.accept(nbt.contains(key) ? nbt.getLongOr(key, defaultValue) : defaultValue);
return this;
}
@ -190,8 +174,8 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader floatValue(String key, Consumer<Float> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getFloat(key));
if (nbt.contains(key) && nbt.getFloat(key).isPresent()) {
setter.accept(nbt.getFloat(key).get());
}
return this;
}
@ -205,10 +189,9 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader floatValue(String key, @NotNull Consumer<Float> setter, float defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getFloat(key) : defaultValue);
setter.accept(nbt.contains(key) ? nbt.getFloatOr(key, defaultValue) : defaultValue);
return this;
}
/**
* Double value nbt reader.
*
@ -217,8 +200,8 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader doubleValue(String key, Consumer<Double> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getDouble(key));
if (nbt.contains(key) && nbt.getDouble(key).isPresent()) {
setter.accept(nbt.getDouble(key).get());
}
return this;
}
@ -232,7 +215,7 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader doubleValue(String key, @NotNull Consumer<Double> setter, double defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getDouble(key) : defaultValue);
setter.accept(nbt.contains(key) ? nbt.getDoubleOr(key, defaultValue) : defaultValue);
return this;
}
@ -244,8 +227,8 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader booleanValue(String key, Consumer<Boolean> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getBoolean(key));
if (nbt.contains(key) && nbt.getBoolean(key).isPresent()) {
setter.accept(nbt.getBoolean(key).get());
}
return this;
}
@ -259,7 +242,7 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader booleanValue(String key, @NotNull Consumer<Boolean> setter, boolean defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getBoolean(key) : defaultValue);
setter.accept(nbt.contains(key) ? nbt.getBooleanOr(key, defaultValue) : defaultValue);
return this;
}
@ -270,14 +253,26 @@ public class NBTReader {
* @param setter the setter
* @return the nbt reader
*/
// 数组类型
public NBTReader byteArray(String key, Consumer<byte[]> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getByteArray(key));
if (nbt.contains(key) && nbt.getByteArray(key).isPresent()) {
setter.accept(nbt.getByteArray(key).get());
}
return this;
}
/**
* Byte array nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader byteArray(String key, @NotNull Consumer<byte[]> setter, byte[] defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getByteArray(key).orElse(defaultValue) : defaultValue);
return this;
}
/**
* Int array nbt reader.
*
@ -286,12 +281,25 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader intArray(String key, Consumer<int[]> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getIntArray(key));
if (nbt.contains(key) && nbt.getIntArray(key).isPresent()) {
setter.accept(nbt.getIntArray(key).get());
}
return this;
}
/**
* Int array nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader intArray(String key, @NotNull Consumer<int[]> setter, int[] defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getIntArray(key).orElse(defaultValue) : defaultValue);
return this;
}
/**
* Long array nbt reader.
*
@ -300,37 +308,22 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader longArray(String key, Consumer<long[]> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getLongArray(key));
if (nbt.contains(key) && nbt.getLongArray(key).isPresent()) {
setter.accept(nbt.getLongArray(key).get());
}
return this;
}
/**
* Uuid nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
// UUID
public NBTReader uuid(String key, Consumer<UUID> setter) {
if (nbt.hasUUID(key)) {
setter.accept(nbt.getUUID(key));
}
return this;
}
/**
* Uuid nbt reader.
* Long array nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader uuid(String key, @NotNull Consumer<UUID> setter, UUID defaultValue) {
setter.accept(nbt.hasUUID(key) ? nbt.getUUID(key) : defaultValue);
public NBTReader longArray(String key, @NotNull Consumer<long[]> setter, long[] defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getLongArray(key).orElse(defaultValue) : defaultValue);
return this;
}
@ -341,10 +334,10 @@ public class NBTReader {
* @param setter the setter
* @return the nbt reader
*/
// CompoundTag
@SuppressWarnings("UnusedReturnValue")
public NBTReader compound(String key, Consumer<CompoundTag> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getCompound(key));
if (nbt.contains(key) && nbt.getCompound(key).isPresent()) {
setter.accept(nbt.getCompound(key).get());
}
return this;
}
@ -358,7 +351,7 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader compound(String key, @NotNull Consumer<CompoundTag> setter, CompoundTag defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getCompound(key) : defaultValue);
setter.accept(nbt.contains(key) ? nbt.getCompound(key).orElse(defaultValue) : defaultValue);
return this;
}
@ -366,14 +359,33 @@ public class NBTReader {
* List nbt reader.
*
* @param key the key
* @param type the type
* @param setter the setter
* @return the nbt reader
*/
// ListTag
public NBTReader list(String key, int type, Consumer<ListTag> setter) {
public NBTReader list(String key, Consumer<ListTag> setter) {
if (nbt.contains(key) && nbt.getList(key).isPresent()) {
ListTag list = nbt.getList(key).get();
if (!list.isEmpty()) {
setter.accept(list);
}
}
return this;
}
/**
* List nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader list(String key, @NotNull Consumer<ListTag> setter, ListTag defaultValue) {
if (nbt.contains(key)) {
setter.accept(nbt.getList(key, type));
ListTag list = nbt.getListOrEmpty(key);
setter.accept(list.isEmpty() ? defaultValue : list);
} else {
setter.accept(defaultValue);
}
return this;
}
@ -385,15 +397,14 @@ public class NBTReader {
* @param setter the setter
* @return the nbt reader
*/
// Vec3支持
public NBTReader vec3(String key, Consumer<Vec3> setter) {
if (nbt.contains(key)) {
CompoundTag vecTag = nbt.getCompound(key);
if (nbt.contains(key) && nbt.getCompound(key).isPresent()) {
CompoundTag vecTag = nbt.getCompound(key).get();
if (vecTag.contains("X") && vecTag.contains("Y") && vecTag.contains("Z")) {
setter.accept(new Vec3(
vecTag.getDouble("X"),
vecTag.getDouble("Y"),
vecTag.getDouble("Z")
vecTag.getDouble("X").orElse(0.0),
vecTag.getDouble("Y").orElse(0.0),
vecTag.getDouble("Z").orElse(0.0)
));
}
}
@ -409,13 +420,13 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader vec3(String key, Consumer<Vec3> setter, Vec3 defaultValue) {
if (nbt.contains(key)) {
CompoundTag vecTag = nbt.getCompound(key);
if (nbt.contains(key) && nbt.getCompound(key).isPresent()) {
CompoundTag vecTag = nbt.getCompound(key).get();
if (vecTag.contains("X") && vecTag.contains("Y") && vecTag.contains("Z")) {
setter.accept(new Vec3(
vecTag.getDouble("X"),
vecTag.getDouble("Y"),
vecTag.getDouble("Z")
vecTag.getDouble("X").orElse(0.0),
vecTag.getDouble("Y").orElse(0.0),
vecTag.getDouble("Z").orElse(0.0)
));
return this;
}
@ -433,14 +444,13 @@ public class NBTReader {
* @param setter the setter
* @return the nbt reader
*/
// 枚举支持
public <T extends Enum<T>> NBTReader enumValue(String key, Class<T> enumClass, Consumer<T> setter) {
if (nbt.contains(key)) {
String value = nbt.getString(key);
if (nbt.contains(key) && nbt.getString(key).isPresent()) {
String value = nbt.getString(key).get();
try {
setter.accept(Enum.valueOf(enumClass, value.toUpperCase()));
} catch (IllegalArgumentException ignored) {
// 保持setter的当前值
}
}
return this;
@ -457,8 +467,8 @@ public class NBTReader {
* @return the nbt reader
*/
public <T extends Enum<T>> NBTReader enumValue(String key, Class<T> enumClass, Consumer<T> setter, T defaultValue) {
if (nbt.contains(key)) {
String value = nbt.getString(key);
if (nbt.contains(key) && nbt.getString(key).isPresent()) {
String value = nbt.getString(key).get();
try {
setter.accept(Enum.valueOf(enumClass, value.toUpperCase()));
return this;
@ -476,10 +486,9 @@ public class NBTReader {
* @param consumer the consumer
* @return the nbt reader
*/
// 嵌套读取支持
public NBTReader nested(String key, Consumer<NBTReader> consumer) {
if (nbt.contains(key)) {
consumer.accept(new NBTReader(nbt.getCompound(key)));
if (nbt.contains(key) && nbt.getCompound(key).isPresent()) {
consumer.accept(new NBTReader(nbt.getCompound(key).get()));
}
return this;
}
@ -493,8 +502,8 @@ public class NBTReader {
* @return the nbt reader
*/
public NBTReader nested(String key, Consumer<NBTReader> consumer, Runnable orElse) {
if (nbt.contains(key)) {
consumer.accept(new NBTReader(nbt.getCompound(key)));
if (nbt.contains(key) && nbt.getCompound(key).isPresent()) {
consumer.accept(new NBTReader(nbt.getCompound(key).get()));
} else {
orElse.run();
}
@ -508,7 +517,6 @@ public class NBTReader {
* @param action the action
* @return the nbt reader
*/
// 条件读取
public NBTReader ifPresent(String key, Runnable action) {
if (nbt.contains(key)) {
action.run();
@ -535,7 +543,6 @@ public class NBTReader {
*
* @return the raw
*/
// 获取原始NBT
@NotNull
public CompoundTag getRaw() {
return nbt;
@ -547,14 +554,13 @@ public class NBTReader {
* @param nbt the nbt
* @return the vec 3
*/
// 便捷的静态方法保持原有功能
@NotNull
public static Vec3 readVec3(@NotNull CompoundTag nbt) {
if (nbt.contains("X") && nbt.contains("Y") && nbt.contains("Z")) {
return new Vec3(
nbt.getDouble("X"),
nbt.getDouble("Y"),
nbt.getDouble("Z")
nbt.getDouble("X").orElse(0.0),
nbt.getDouble("Y").orElse(0.0),
nbt.getDouble("Z").orElse(0.0)
);
} else {
throw new IllegalArgumentException("NBT is missing X, Y, or Z value for Vec3");
@ -571,11 +577,11 @@ public class NBTReader {
public static Vec3 readVec3Safe(@NotNull CompoundTag nbt) {
if (nbt.contains("X") && nbt.contains("Y") && nbt.contains("Z")) {
return new Vec3(
nbt.getDouble("X"),
nbt.getDouble("Y"),
nbt.getDouble("Z")
nbt.getDouble("X").orElse(0.0),
nbt.getDouble("Y").orElse(0.0),
nbt.getDouble("Z").orElse(0.0)
);
}
return null;
}
}
}

View File

@ -1,4 +1,4 @@
package top.r3944realms.lib39.util.nbt;
package top.r3944realms.lib39.util.storage.nbt;
import net.minecraft.nbt.*;
import net.minecraft.world.phys.Vec3;
@ -6,7 +6,6 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
import java.util.function.Consumer;
/**
@ -394,38 +393,6 @@ public class NBTWriter {
return this;
}
/**
* Uuid nbt writer.
*
* @param key the key
* @param value the value
* @return the nbt writer
*/
// UUID支持
public NBTWriter uuid(String key, UUID value) {
if (value != null) {
root.putUUID(key, value);
}
return this;
}
/**
* Uuid nbt writer.
*
* @param key the key
* @param value the value
* @param defaultValue the default value
* @return the nbt writer
*/
public NBTWriter uuid(String key, UUID value, UUID defaultValue) {
if (value != null) {
root.putUUID(key, value);
} else if (defaultValue != null) {
root.putUUID(key, defaultValue);
}
return this;
}
/**
* Compound nbt writer.
*
@ -1061,7 +1028,7 @@ public class NBTWriter {
* @return the all keys
*/
public java.util.Set<String> getAllKeys() {
return root.getAllKeys();
return root.tags.keySet();
}
/**

View File

@ -0,0 +1,526 @@
package top.r3944realms.lib39.util.storage.valueio;
import com.mojang.serialization.Codec;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.function.Consumer;
/**
* ValueInput Helper类
* 提供链式操作和类型安全的ValueInput数据读取
*/
@SuppressWarnings("unused")
public class ValueInputReader {
private final net.minecraft.world.level.storage.ValueInput valueInput;
private ValueInputReader(net.minecraft.world.level.storage.ValueInput valueInput) {
this.valueInput = valueInput;
}
/**
* 从ValueInput创建读取器
*
* @param valueInput the value input
* @return the value input reader
*/
@NotNull
public static ValueInputReader of(@NotNull net.minecraft.world.level.storage.ValueInput valueInput) {
return new ValueInputReader(valueInput);
}
/**
* String value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
// 基本读取方法 - 直接赋值给成员变量
public ValueInputReader string(String key, Consumer<String> setter) {
valueInput.getString(key).ifPresent(setter);
return this;
}
/**
* String value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader string(String key, @NotNull Consumer<String> setter, String defaultValue) {
setter.accept(valueInput.getStringOr(key, defaultValue));
return this;
}
/**
* Boolean value value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader booleanValue(String key, Consumer<Boolean> setter) {
valueInput.read(key, Codec.BOOL).ifPresent(setter);
return this;
}
/**
* Boolean value value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader booleanValue(String key, @NotNull Consumer<Boolean> setter, boolean defaultValue) {
setter.accept(valueInput.getBooleanOr(key, defaultValue));
return this;
}
/**
* Byte value value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader byteValue(String key, Consumer<Byte> setter) {
valueInput.read(key, Codec.BYTE).ifPresent(setter);
return this;
}
/**
* Byte value value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader byteValue(String key, @NotNull Consumer<Byte> setter, byte defaultValue) {
setter.accept(valueInput.getByteOr(key, defaultValue));
return this;
}
/**
* Short value value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader shortValue(String key, Consumer<Short> setter) {
valueInput.read(key, Codec.SHORT).ifPresent(setter);
return this;
}
/**
* Short value value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader shortValue(String key, @NotNull Consumer<Short> setter, short defaultValue) {
setter.accept((short) valueInput.getShortOr(key, defaultValue));
return this;
}
/**
* Int value value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader intValue(String key, Consumer<Integer> setter) {
valueInput.getInt(key).ifPresent(setter);
return this;
}
/**
* Int value value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader intValue(String key, @NotNull Consumer<Integer> setter, int defaultValue) {
setter.accept(valueInput.getIntOr(key, defaultValue));
return this;
}
/**
* Long value value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader longValue(String key, Consumer<Long> setter) {
valueInput.getLong(key).ifPresent(setter);
return this;
}
/**
* Long value value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader longValue(String key, @NotNull Consumer<Long> setter, long defaultValue) {
setter.accept(valueInput.getLongOr(key, defaultValue));
return this;
}
/**
* Float value value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader floatValue(String key, Consumer<Float> setter) {
valueInput.read(key, Codec.FLOAT).ifPresent(setter);
return this;
}
/**
* Float value value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader floatValue(String key, @NotNull Consumer<Float> setter, float defaultValue) {
setter.accept(valueInput.getFloatOr(key, defaultValue));
return this;
}
/**
* Double value value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader doubleValue(String key, Consumer<Double> setter) {
valueInput.read(key, Codec.DOUBLE).ifPresent(setter);
return this;
}
/**
* Double value value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader doubleValue(String key, @NotNull Consumer<Double> setter, double defaultValue) {
setter.accept(valueInput.getDoubleOr(key, defaultValue));
return this;
}
/**
* Int array value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader intArray(String key, Consumer<int[]> setter) {
valueInput.getIntArray(key).ifPresent(setter);
return this;
}
/**
* Int array value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader intArray(String key, @NotNull Consumer<int[]> setter, int[] defaultValue) {
setter.accept(valueInput.getIntArray(key).orElse(defaultValue));
return this;
}
/**
* Codec value value input reader.
*
* @param <T> the type parameter
* @param key the key
* @param codec the codec
* @param setter the setter
* @return the value input reader
*/
public <T> ValueInputReader codecValue(String key, Codec<T> codec, Consumer<T> setter) {
valueInput.read(key, codec).ifPresent(setter);
return this;
}
/**
* Codec value value input reader.
*
* @param <T> the type parameter
* @param key the key
* @param codec the codec
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public <T> ValueInputReader codecValue(String key, Codec<T> codec, @NotNull Consumer<T> setter, T defaultValue) {
setter.accept(valueInput.read(key, codec).orElse(defaultValue));
return this;
}
/**
* Vec 3 value input reader.
*
* @param key the key
* @param setter the setter
* @return the value input reader
*/
public ValueInputReader vec3(String key, Consumer<Vec3> setter) {
valueInput.child(key).ifPresent(child -> {
try {
Vec3 vec = readVec3(child);
setter.accept(vec);
} catch (IllegalArgumentException ignored) {
// 忽略解析错误
}
});
return this;
}
/**
* Vec 3 value input reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public ValueInputReader vec3(String key, Consumer<Vec3> setter, Vec3 defaultValue) {
Optional<net.minecraft.world.level.storage.ValueInput> child = valueInput.child(key);
if (child.isPresent()) {
try {
Vec3 vec = readVec3(child.get());
setter.accept(vec);
return this;
} catch (IllegalArgumentException ignored) {
// 忽略解析错误使用默认值
}
}
setter.accept(defaultValue);
return this;
}
/**
* Enum value value input reader.
*
* @param <T> the type parameter
* @param key the key
* @param enumClass the enum class
* @param setter the setter
* @return the value input reader
*/
public <T extends Enum<T>> ValueInputReader enumValue(String key, Class<T> enumClass, Consumer<T> setter) {
valueInput.getString(key).ifPresent(value -> {
try {
setter.accept(Enum.valueOf(enumClass, value.toUpperCase()));
} catch (IllegalArgumentException ignored) {
// 忽略枚举解析错误
}
});
return this;
}
/**
* Enum value value input reader.
*
* @param <T> the type parameter
* @param key the key
* @param enumClass the enum class
* @param setter the setter
* @param defaultValue the default value
* @return the value input reader
*/
public <T extends Enum<T>> ValueInputReader enumValue(String key, Class<T> enumClass, Consumer<T> setter, T defaultValue) {
Optional<String> value = valueInput.getString(key);
if (value.isPresent()) {
try {
setter.accept(Enum.valueOf(enumClass, value.get().toUpperCase()));
return this;
} catch (IllegalArgumentException ignored) {
// 忽略枚举解析错误
}
}
setter.accept(defaultValue);
return this;
}
/**
* Nested value input reader.
*
* @param key the key
* @param consumer the consumer
* @return the value input reader
*/
public ValueInputReader nested(String key, Consumer<ValueInputReader> consumer) {
valueInput.child(key).ifPresent(child -> consumer.accept(new ValueInputReader(child)));
return this;
}
/**
* Nested value input reader.
*
* @param key the key
* @param consumer the consumer
* @param orElse the or else
* @return the value input reader
*/
public ValueInputReader nested(String key, Consumer<ValueInputReader> consumer, Runnable orElse) {
Optional<net.minecraft.world.level.storage.ValueInput> child = valueInput.child(key);
if (child.isPresent()) {
consumer.accept(new ValueInputReader(child.get()));
} else {
orElse.run();
}
return this;
}
/**
* List value input reader.
*
* @param <T> the type parameter
* @param key the key
* @param codec the codec
* @param consumer the consumer
* @return the value input reader
*/
public <T> ValueInputReader list(String key, Codec<T> codec, Consumer<java.util.stream.Stream<T>> consumer) {
valueInput.list(key, codec).ifPresent(list -> {
if (!list.isEmpty()) {
consumer.accept(list.stream());
}
});
return this;
}
/**
* List value input reader.
*
* @param <T> the type parameter
* @param key the key
* @param codec the codec
* @param consumer the consumer
* @param defaultValue the default value
* @return the value input reader
*/
public <T> ValueInputReader list(String key, Codec<T> codec, @NotNull Consumer<java.util.stream.Stream<T>> consumer, java.util.stream.Stream<T> defaultValue) {
Optional<net.minecraft.world.level.storage.ValueInput.TypedInputList<T>> list = valueInput.list(key, codec);
if (list.isPresent() && !list.get().isEmpty()) {
consumer.accept(list.get().stream());
} else {
consumer.accept(defaultValue);
}
return this;
}
/**
* Children list value input reader.
*
* @param key the key
* @param consumer the consumer
* @return the value input reader
*/
public ValueInputReader childrenList(String key, Consumer<java.util.stream.Stream<ValueInputReader>> consumer) {
valueInput.childrenList(key).ifPresent(list -> {
if (!list.isEmpty()) {
consumer.accept(list.stream().map(ValueInputReader::new));
}
});
return this;
}
/**
* If present value input reader.
*
* @param key the key
* @param action the action
* @return the value input reader
*/
public ValueInputReader ifPresent(String key, Runnable action) {
if (valueInput.child(key).isPresent()) {
action.run();
}
return this;
}
/**
* If absent value input reader.
*
* @param key the key
* @param action the action
* @return the value input reader
*/
public ValueInputReader ifAbsent(String key, Runnable action) {
if (valueInput.child(key).isEmpty()) {
action.run();
}
return this;
}
/**
* Gets raw.
*
* @return the raw
*/
@NotNull
public net.minecraft.world.level.storage.ValueInput getRaw() {
return valueInput;
}
/**
* Read vec 3 vec 3.
*
* @param valueInput the value input
* @return the vec 3
*/
@NotNull
public static Vec3 readVec3(@NotNull net.minecraft.world.level.storage.ValueInput valueInput) {
double x = valueInput.getDoubleOr("X", 0.0);
double y = valueInput.getDoubleOr("Y", 0.0);
double z = valueInput.getDoubleOr("Z", 0.0);
return new Vec3(x, y, z);
}
/**
* Read vec 3 safe vec 3.
*
* @param valueInput the value input
* @return the vec 3
*/
@Nullable
public static Vec3 readVec3Safe(@NotNull net.minecraft.world.level.storage.ValueInput valueInput) {
try {
return readVec3(valueInput);
} catch (Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,634 @@
package top.r3944realms.lib39.util.storage.valueio;
import com.mojang.serialization.Codec;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.level.storage.TagValueOutput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* ValueOutput Helper类
* 提供链式操作和类型安全的ValueOutput数据写入
*/
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType", "UnusedReturnValue"})
public class ValueOutputWriter {
private final net.minecraft.world.level.storage.ValueOutput valueOutput;
private ValueOutputWriter() {
this.valueOutput = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING);
}
private ValueOutputWriter(net.minecraft.world.level.storage.ValueOutput valueOutput) {
this.valueOutput = valueOutput;
}
/**
* 创建一个新的NBT构建器
*
* @return the nbt writer
*/
@Contract(value = " -> new", pure = true)
public static @NotNull ValueOutputWriter builder() {
return new ValueOutputWriter();
}
/**
* 从ValueOutput创建写入器
*
* @param valueOutput the value output
* @return the value output writer
*/
@NotNull
public static ValueOutputWriter of(@NotNull net.minecraft.world.level.storage.ValueOutput valueOutput) {
return new ValueOutputWriter(valueOutput);
}
/**
* String value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
// 基本写入方法
public ValueOutputWriter string(String key, String value) {
if (value != null) {
valueOutput.putString(key, value);
}
return this;
}
/**
* String value output writer.
*
* @param key the key
* @param value the value
* @param defaultValue the default value
* @return the value output writer
*/
public ValueOutputWriter string(String key, @Nullable String value, String defaultValue) {
valueOutput.putString(key, value != null ? value : defaultValue);
return this;
}
/**
* String value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter string(String key, Optional<String> value) {
value.ifPresent(v -> valueOutput.putString(key, v));
return this;
}
/**
* Boolean value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter booleanValue(String key, boolean value) {
valueOutput.putBoolean(key, value);
return this;
}
/**
* Boolean value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter booleanValue(String key, Optional<Boolean> value) {
value.ifPresent(v -> valueOutput.putBoolean(key, v));
return this;
}
/**
* Byte value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter byteValue(String key, byte value) {
valueOutput.putByte(key, value);
return this;
}
/**
* Byte value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter byteValue(String key, Optional<Byte> value) {
value.ifPresent(v -> valueOutput.putByte(key, v));
return this;
}
/**
* Short value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter shortValue(String key, short value) {
valueOutput.putShort(key, value);
return this;
}
/**
* Short value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter shortValue(String key, Optional<Short> value) {
value.ifPresent(v -> valueOutput.putShort(key, v));
return this;
}
/**
* Int value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter intValue(String key, int value) {
valueOutput.putInt(key, value);
return this;
}
/**
* Int value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter intValue(String key, Optional<Integer> value) {
value.ifPresent(v -> valueOutput.putInt(key, v));
return this;
}
/**
* Long value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter longValue(String key, long value) {
valueOutput.putLong(key, value);
return this;
}
/**
* Long value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter longValue(String key, Optional<Long> value) {
value.ifPresent(v -> valueOutput.putLong(key, v));
return this;
}
/**
* Float value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter floatValue(String key, float value) {
valueOutput.putFloat(key, value);
return this;
}
/**
* Float value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter floatValue(String key, Optional<Float> value) {
value.ifPresent(v -> valueOutput.putFloat(key, v));
return this;
}
/**
* Double value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter doubleValue(String key, double value) {
valueOutput.putDouble(key, value);
return this;
}
/**
* Double value value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter doubleValue(String key, Optional<Double> value) {
value.ifPresent(v -> valueOutput.putDouble(key, v));
return this;
}
/**
* Int array value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter intArray(String key, int[] value) {
if (value != null && value.length > 0) {
valueOutput.putIntArray(key, value);
}
return this;
}
/**
* Int array value output writer.
*
* @param key the key
* @param value the value
* @return the value output writer
*/
public ValueOutputWriter intArray(String key, Optional<int[]> value) {
value.ifPresent(v -> {
if (v.length > 0) {
valueOutput.putIntArray(key, v);
}
});
return this;
}
/**
* Codec value value output writer.
*
* @param <T> the type parameter
* @param key the key
* @param codec the codec
* @param value the value
* @return the value output writer
*/
public <T> ValueOutputWriter codecValue(String key, Codec<T> codec, T value) {
if (value != null) {
valueOutput.store(key, codec, value);
}
return this;
}
/**
* Codec value value output writer.
*
* @param <T> the type parameter
* @param key the key
* @param codec the codec
* @param value the value
* @return the value output writer
*/
public <T> ValueOutputWriter codecValue(String key, Codec<T> codec, Optional<T> value) {
value.ifPresent(v -> valueOutput.store(key, codec, v));
return this;
}
/**
* Codec nullable value value output writer.
*
* @param <T> the type parameter
* @param key the key
* @param codec the codec
* @param value the value
* @return the value output writer
*/
public <T> ValueOutputWriter codecNullable(String key, Codec<T> codec, @Nullable T value) {
valueOutput.storeNullable(key, codec, value);
return this;
}
/**
* Vec 3 value output writer.
*
* @param key the key
* @param vec the vec
* @return the value output writer
*/
public ValueOutputWriter vec3(String key, Vec3 vec) {
if (vec != null) {
ValueOutput child = valueOutput.child(key);
writeVec3(child, vec);
}
return this;
}
/**
* Vec 3 value output writer.
*
* @param key the key
* @param vec the vec
* @return the value output writer
*/
public ValueOutputWriter vec3(String key, Optional<Vec3> vec) {
vec.ifPresent(v -> vec3(key, v));
return this;
}
/**
* Enum value value output writer.
*
* @param <T> the type parameter
* @param key the key
* @param value the value
* @return the value output writer
*/
public <T extends Enum<T>> ValueOutputWriter enumValue(String key, T value) {
if (value != null) {
valueOutput.putString(key, value.name().toLowerCase());
}
return this;
}
/**
* Enum value value output writer.
*
* @param <T> the type parameter
* @param key the key
* @param value the value
* @return the value output writer
*/
public <T extends Enum<T>> ValueOutputWriter enumValue(String key, Optional<T> value) {
value.ifPresent(v -> valueOutput.putString(key, v.name().toLowerCase()));
return this;
}
/**
* Nested value output writer.
*
* @param key the key
* @param consumer the consumer
* @return the value output writer
*/
public ValueOutputWriter nested(String key, Consumer<ValueOutputWriter> consumer) {
ValueOutput child = valueOutput.child(key);
consumer.accept(new ValueOutputWriter(child));
return this;
}
/**
* Nested if present value output writer.
*
* @param key the key
* @param supplier the supplier
* @param consumer the consumer
* @return the value output writer
*/
public ValueOutputWriter nestedIfPresent(String key, Supplier<Boolean> supplier, Consumer<ValueOutputWriter> consumer) {
if (supplier.get()) {
ValueOutput child = valueOutput.child(key);
consumer.accept(new ValueOutputWriter(child));
}
return this;
}
/**
* List value output writer.
*
* @param <T> the type parameter
* @param key the key
* @param elementCodec the element codec
* @param elements the elements
* @param consumer the consumer
* @return the value output writer
*/
public <T> ValueOutputWriter list(String key, Codec<T> elementCodec, Iterable<T> elements, Consumer<ValueOutputWriter.TypedListWriter<T>> consumer) {
ValueOutput.TypedOutputList<T> list = valueOutput.list(key, elementCodec);
if (!list.isEmpty()) {
TypedListWriter<T> writer = new TypedListWriter<>(list);
consumer.accept(writer);
}
return this;
}
/**
* List value output writer.
*
* @param <T> the type parameter
* @param key the key
* @param elementCodec the element codec
* @param elements the elements
* @return the value output writer
*/
public <T> ValueOutputWriter list(String key, Codec<T> elementCodec, Iterable<T> elements) {
ValueOutput.TypedOutputList<T> list = valueOutput.list(key, elementCodec);
for (T element : elements) {
if (element != null) {
list.add(element);
}
}
return this;
}
/**
* Children list value output writer.
*
* @param key the key
* @param consumer the consumer
* @return the value output writer
*/
public ValueOutputWriter childrenList(String key, Consumer<ValueOutputWriter.ChildrenListWriter> consumer) {
ValueOutput.ValueOutputList list = valueOutput.childrenList(key);
if (!list.isEmpty()) {
ChildrenListWriter writer = new ChildrenListWriter(list);
consumer.accept(writer);
}
return this;
}
/**
* If present value output writer.
*
* @param condition the condition
* @param action the action
* @return the value output writer
*/
public ValueOutputWriter ifPresent(boolean condition, Consumer<ValueOutputWriter> action) {
if (condition) {
action.accept(this);
}
return this;
}
/**
* If present value output writer.
*
* @param condition the condition
* @param action the action
* @return the value output writer
*/
public ValueOutputWriter ifPresent(Supplier<Boolean> condition, Consumer<ValueOutputWriter> action) {
if (condition.get()) {
action.accept(this);
}
return this;
}
/**
* Discard value output writer.
*
* @param key the key
* @return the value output writer
*/
public ValueOutputWriter discard(String key) {
valueOutput.discard(key);
return this;
}
/**
* Gets raw.
*
* @return the raw
*/
@NotNull
public net.minecraft.world.level.storage.ValueOutput getRaw() {
return valueOutput;
}
/**
* Write vec 3.
*
* @param valueOutput the value output
* @param vec the vec
*/
public static void writeVec3(@NotNull net.minecraft.world.level.storage.ValueOutput valueOutput, @NotNull Vec3 vec) {
valueOutput.putDouble("X", vec.x);
valueOutput.putDouble("Y", vec.y);
valueOutput.putDouble("Z", vec.z);
}
/**
* Write vec 3 safe.
*
* @param valueOutput the value output
* @param vec the vec
*/
public static void writeVec3Safe(@NotNull net.minecraft.world.level.storage.ValueOutput valueOutput, @Nullable Vec3 vec) {
if (vec != null) {
writeVec3(valueOutput, vec);
}
}
/**
* The type Typed list writer.
*
* @param <T> the type parameter
*/
public static class TypedListWriter<T> {
private final ValueOutput.TypedOutputList<T> list;
private TypedListWriter(ValueOutput.TypedOutputList<T> list) {
this.list = list;
}
/**
* Add typed list writer.
*
* @param element the element
* @return the typed list writer
*/
public TypedListWriter<T> add(T element) {
if (element != null) {
list.add(element);
}
return this;
}
/**
* Add all typed list writer.
*
* @param elements the elements
* @return the typed list writer
*/
public TypedListWriter<T> addAll(Iterable<T> elements) {
for (T element : elements) {
if (element != null) {
list.add(element);
}
}
return this;
}
/**
* Is empty boolean.
*
* @return the boolean
*/
public boolean isEmpty() {
return list.isEmpty();
}
}
/**
* The type Children list writer.
*/
public static class ChildrenListWriter {
private final ValueOutput.ValueOutputList list;
private ChildrenListWriter(ValueOutput.ValueOutputList list) {
this.list = list;
}
/**
* Add child value output writer.
*
* @param consumer the consumer
* @return the children list writer
*/
public ChildrenListWriter addChild(Consumer<ValueOutputWriter> consumer) {
ValueOutput child = list.addChild();
consumer.accept(new ValueOutputWriter(child));
return this;
}
/**
* Discard last children list writer.
*
* @return the children list writer
*/
public ChildrenListWriter discardLast() {
list.discardLast();
return this;
}
/**
* Is empty boolean.
*
* @return the boolean
*/
public boolean isEmpty() {
return list.isEmpty();
}
}
}

View File

@ -0,0 +1,3 @@
# private -> public
public net.minecraft.server.level.ServerPlayer server
public net.minecraft.nbt.CompoundTag tags

View File

@ -1,14 +1,3 @@
# This is an example mods.toml file. It contains the data relating to the loading mods.
# There are several mandatory fields (#mandatory), and many more that are optional (#optional).
# The overall format is standard TOML format, v0.5.0.
# Note that there are a couple of TOML lists in this file.
# Find more information on toml format here: https://github.com/toml-lang/toml
# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
modLoader="javafml" #mandatory
# A version range to match for said mod loader - for regular FML @Mod it will be the Forge version
loaderVersion="${loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See download page for lists of versions.
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
license="${mod_license}"
@ -57,14 +46,18 @@ authors="${mod_authors}" #optional
# The description text for the mod (multi line!) (#mandatory)
description='''${mod_description}'''
[[accessTransformers]]
file="META-INF/accesstransformer.cfg"
# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
[[dependencies.${mod_id}]] #optional
# the modid of the dependency
modId="forge" #mandatory
modId="neoforge" #mandatory
# Does this dependency have to exist - if not, ordering below must be specified
mandatory=true #mandatory
type="required"
# The version range of the dependency
versionRange="${forge_version_range}" #mandatory
versionRange="[${neo_version},)" #mandatory
# An ordering relationship for the dependency - BEFORE or AFTER required if the dependency is not mandatory
# BEFORE - This mod is loaded BEFORE the dependency
# AFTER - This mod is loaded AFTER the dependency
@ -75,7 +68,7 @@ description='''${mod_description}'''
# Here's another dependency
[[dependencies.${mod_id}]]
modId="minecraft"
mandatory=true
type="required"
# This version range declares a minimum of the current minecraft version up to but not including the next major version
versionRange="${minecraft_version_range}"
ordering="NONE"

View File

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