将SyncDataManager修改为更通用的同步管理

This commit is contained in:
叁玖领域 2025-10-25 16:00:39 +08:00
parent 4f7a030843
commit cf1b6dba5b
48 changed files with 3422 additions and 326 deletions

View File

@ -8,12 +8,11 @@ plugins {
id 'net.neoforged.moddev.legacyforge' version '2.0.103'
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(17)
}
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
}
@ -25,6 +24,11 @@ repositories {
maven { url = "https://libraries.minecraft.net/" }
maven { url = "https://neoforged.forgecdn.net/releases" }
maven { url = "https://neoforged.forgecdn.net/mojang-meta" }
maven { url = "https://maven.neoforged.net/releases" }
maven {
name = "LTD Maven"
url = "https://nexus.bot.leisuretimedock.top/repository/maven-public/"
}
flatDir {
dir "libs"
}
@ -46,16 +50,10 @@ legacyForge {
minecraftVersion = project.parchment_minecraft_version
}
// This line is optional. Access Transformers are automatically detected
// accessTransformers = project.files('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
client {
client()
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
}
clientAuth{
@ -70,9 +68,6 @@ legacyForge {
systemProperty 'forge.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
@ -80,35 +75,16 @@ legacyForge {
data {
data()
// 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
// mostly optional in a single mod project
// but multi mod projects should define one per mod
"${mod_id}" {
sourceSet(sourceSets.main)
}
@ -118,10 +94,6 @@ legacyForge {
// Include resources generated by data generators.
sourceSets.main.resources { srcDir 'src/generated/resources' }
// Sets up a dependency configuration called 'localRuntime' and a deobfuscating one called 'modLocalRuntime'
// These configurations 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
}
@ -130,52 +102,11 @@ obfuscation {
}
dependencies {
// If you wish to declare dependencies against mods, make sure to use the 'mod*' configurations so that they're remapped.
// See https://github.com/neoforged/ModDevGradle/blob/main/LEGACY.md#remapping-mod-dependencies for more information.
// Example optional mod dependency with JEI
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
// modCompileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}"
// modCompileOnly "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
// modLocalRuntime "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"
// modImplementation "blank:coolmod-${mc_version}:${coolmod_version}"
// Example mod dependency using a file as dependency
// modImplementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar")
// Example project dependency using a sister or child project:
// modImplementation 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
}
// Uncomment the lines below if you wish to configure mixin. The mixin file should be named modid.mixins.json.
/*
mixin {
add sourceSets.main, "${mod_id}.refmap.json"
config "${mod_id}.mixins.json"
}
dependencies {
annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
}
jar {
manifest.attributes([
"MixinConfigs": "${mod_id}.mixins.json"
])
}
*/
// 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,
@ -191,39 +122,239 @@ var generateModMetadata = tasks.register("generateModMetadata", ProcessResources
mod_description : mod_description,
mod_credits : mod_credits
]
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
inputs.properties replaceProperties
expand replaceProperties
from "src/main/templates"
into "build/generated/sources/modMetadata"
}
// 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
// To avoid having to run "generateModMetadata" manually, make it run on every project reload
legacyForge.ideSyncTask generateModMetadata
// Example configuration to allow publishing using the maven-publish plugin
// ==================== 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
}
// ==================== ====================
publishing {
publications {
register('mavenJava', MavenPublication) {
from components.java
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'
}
}
}
}
repositories {
//
maven {
url "file://${project.projectDir}/repo"
name = 'local'
url = layout.buildDirectory.dir("repo")
}
// Nexus
maven {
name = 'LTDNexus'
url = 'https://nexus.bot.leisuretimedock.top/repository/maven-releases/'
credentials {
username = System.getenv('LTDNexusUsername') ?: ''
password = System.getenv('LTDNexusPassword') ?: ''
}
}
}
}
// ==================== ====================
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
options.encoding = 'UTF-8'
options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation']
}
// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
// 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 {
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

@ -33,7 +33,7 @@ mod_name=3944Realms 's Lib Mod
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=MIT
# The mod version. See https://semver.org/
mod_version=0.0.14
mod_version=0.0.16
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html

View File

@ -6,20 +6,44 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.r3944realms.lib39.core.network.NetworkHandler;
/**
* The type Lib 39.
*/
@Mod(Lib39.MOD_ID)
public class Lib39 {
/**
* The constant MOD_ID.
*/
public static final String MOD_ID = "lib39";
/**
* The constant LOGGER.
*/
public static final Logger LOGGER = LoggerFactory.getLogger(Lib39.class);
/**
* Instantiates a new Lib 39.
*/
public Lib39() {
initialize();
}
/**
* Initialize.
*/
public static void initialize() {
LOGGER.info("[Lib39] Initializing Lib39");
NetworkHandler.register();
LOGGER.info("[Lib39] Initialized Lib39");
}
/**
* The type Mod info.
*/
public static class ModInfo {
/**
* The constant VERSION.
*/
public static final String VERSION;
static {
// ModList 获取当前 ModContainer 的元数据

View File

@ -0,0 +1,69 @@
package top.r3944realms.lib39.api.event;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import top.r3944realms.lib39.core.compat.CompatManager;
import top.r3944realms.lib39.core.compat.ICompat;
/**
* The type Register compat event.
*/
public class RegisterCompatEvent extends Event implements IModBusEvent {
/**
* The Compat manager.
*/
protected final CompatManager compatManager;
/**
* Instantiates a new Register compat event.
*
* @param compatManager the compat manager
*/
public RegisterCompatEvent(CompatManager compatManager) {
this.compatManager = compatManager;
}
/**
* Gets compat manager.
*
* @return the compat manager
*/
public CompatManager getCompatManager() {
return compatManager;
}
/**
* Register compat.
*
* @param id the id
* @param compat the compat
*/
// 注册兼容模块
public void registerCompat(ResourceLocation id, ICompat compat) {
compatManager.registerCompat(id, compat);
}
/**
* Register compat.
*
* @param namespace the namespace
* @param path the path
* @param compat the compat
*/
// 注册兼容模块简化版本
public void registerCompat(String namespace, String path, ICompat compat) {
compatManager.registerCompat(namespace, path, compat);
}
/**
* Unregister compat.
*
* @param id the id
*/
// 取消注册兼容模块
public void unregisterCompat(ResourceLocation id) {
compatManager.unregisterCompat(id);
}
}

View File

@ -1,43 +1,93 @@
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 top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.ISyncManager;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
import java.util.Optional;
import java.util.function.Function;
/**
* The type Sync manager register event.
*/
@SuppressWarnings("unused")
public class SyncManagerRegisterEvent extends Event {
/**
* The Syncs 2 manager.
*/
protected final SyncData2Manager syncs2Manager;
/**
* Instantiates a new Sync manager register event.
*
* @param syncsManager the syncs manager
*/
public SyncManagerRegisterEvent(SyncData2Manager syncsManager) {
this.syncs2Manager = syncsManager;
}
/**
* Gets syncs manager.
*
* @return the syncs manager
*/
public SyncData2Manager getSyncsManager() {
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, capability);
syncs2Manager.registerManager(id, syncManager, entity -> entity.getCapability(capability).resolve());
}
/**
* 类型安全的同步管理器注册
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param id the id
* @param syncManager the sync manager
* @param dataProvider the dataProvider
*/
public <K, T extends ISyncData<?>> void registerSyncManager(
ResourceLocation id,
ISyncManager<K, T> syncManager,
Function<Entity, Optional<T>> dataProvider
) {
syncs2Manager.registerManager(id, syncManager, dataProvider);
}
/**
* Unregister sync manager.
*
* @param id the id
*/
public void unregisterSyncManager(ResourceLocation id) {
syncs2Manager.removeManager(id);
}
/**
* 允许实体类
*
* @param id the id
* @param entityClasses the entity classes
*/
public final void addAllowEntityClass(ResourceLocation id, Class<?>... entityClasses) {
syncs2Manager.allowEntityClass(id, entityClasses);
@ -45,6 +95,9 @@ public class SyncManagerRegisterEvent extends Event {
/**
* 移除允许的实体类
*
* @param id the id
* @param entityClasses the entity classes
*/
public final void removeAllowEntityClass(ResourceLocation id, Class<?>... entityClasses) {
syncs2Manager.disallowEntityClass(id, entityClasses);
@ -52,21 +105,33 @@ public class SyncManagerRegisterEvent extends Event {
/**
* 绑定能力用于分离注册的情况
* @param id 必须先注册安全同步管理器再绑定Cap否则会抛出{@link IllegalStateException 未找到对应安全同步管理器}
*
* @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.bindCapability(id, capability);
syncs2Manager.bindDataGetter(id, entity -> entity.getCapability(capability).resolve());
}
/**
* 解绑能力
* 解绑数据提供者
*
* @param id the id
*/
public void unbindCapability(ResourceLocation id) {
syncs2Manager.unbindCapability(id);
public void unbindDataProvider(ResourceLocation id) {
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,
@ -79,4 +144,26 @@ public class SyncManagerRegisterEvent extends Event {
addAllowEntityClass(id, allowedEntityClasses);
}
}
/**
* 完整的类型安全注册
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param id the id
* @param syncManager the sync manager
* @param dataProvider the capability
* @param allowedEntityClasses the allowed entity classes
*/
public <K, T extends ISyncData<?>> void registerComplete(
ResourceLocation id,
ISyncManager<K, T> syncManager,
Function<Entity, Optional<T>> dataProvider,
Class<?>... allowedEntityClasses
) {
registerSyncManager(id, syncManager, dataProvider);
if (allowedEntityClasses.length > 0) {
addAllowEntityClass(id, allowedEntityClasses);
}
}
}

View File

@ -0,0 +1,332 @@
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* The type Compat manager.
*/
@SuppressWarnings("unused")
public class CompatManager {
private final Map<ResourceLocation, ICompat> compats = new HashMap<>();
private final IEventBus modEventBus, gameEventBus;
// 存储事件监听器配置
private final List<ListenerConfig> listenerConfigs = new ArrayList<>();
/**
* Instantiates a new Compat manager.
*
* @param modEventBus the mod event bus
* @param gameEventBus the game event bus
*/
public CompatManager(IEventBus modEventBus, IEventBus gameEventBus) {
this.modEventBus = modEventBus;
this.gameEventBus = gameEventBus;
}
/**
* Register compat.
*
* @param id the id
* @param compat the compat
*/
public void registerCompat(ResourceLocation id, ICompat compat) {
if (compats.containsKey(id)) {
Lib39.LOGGER.warn("Compat with id {} is already registered!", id);
return;
}
compats.put(id, compat);
Lib39.LOGGER.debug("Registered compat: {}", id);
}
/**
* Register compat.
*
* @param namespace the namespace
* @param path the path
* @param compat the compat
*/
public void registerCompat(String namespace, String path, ICompat compat) {
registerCompat(new ResourceLocation(namespace, path), compat);
}
/**
* 为所有兼容模块配置事件监听器
*
* @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));
}
/**
* 为特定兼容模块配置事件监听器
*
* @param compatId the compat id
* @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));
}
/**
* 为已加载的兼容模块配置事件监听器
*
* @param dists the dists
* @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) {
@Override
boolean shouldApply(@NotNull ICompat compat) {
return super.shouldApply(compat);
}
});
}
// ===================== 初始化和管理 =====================
/**
* 初始化所有兼容模块并应用事件监听器
*/
public void initializeAll() {
Lib39.LOGGER.info("Initializing {} compatibility modules", compats.size());
// 1. 先初始化所有兼容模块
for (Map.Entry<ResourceLocation, ICompat> entry : compats.entrySet()) {
try {
entry.getValue().initialize();
Lib39.LOGGER.info("Initialized compat: {}", entry.getKey());
} catch (Exception e) {
Lib39.LOGGER.error("Failed to initialize compat: {}", entry.getKey(), e);
}
}
// 2. 然后应用所有事件监听器
applyAllEventListeners();
}
/**
* 应用所有配置的事件监听器到对应的 ICompat 实例
*/
private void applyAllEventListeners() {
Lib39.LOGGER.info("Applying {} event listener configurations", listenerConfigs.size());
for (ListenerConfig config : listenerConfigs) {
if (config.compatId == null) {
// 应用到所有兼容模块
applyListenerToAllCompats(config);
} else {
// 应用到特定兼容模块
applyListenerToCompat(config.compatId, config);
}
}
}
/**
* 将监听器应用到所有兼容模块
*/
private void applyListenerToAllCompats(ListenerConfig config) {
for (ICompat compat : compats.values()) {
if (config.shouldApply(compat)) {
applyListenerToCompat(compat, config);
}
}
}
/**
* 将监听器应用到特定兼容模块
*/
private void applyListenerToCompat(ResourceLocation compatId, ListenerConfig config) {
ICompat compat = compats.get(compatId);
if (compat != null && config.shouldApply(compat)) {
applyListenerToCompat(compat, config);
}
}
/**
* 将监听器应用到具体的 ICompat 实例
*/
private void applyListenerToCompat(ICompat compat, ListenerConfig config) {
try {
// 根据配置调用对应的 ICompat 方法
if (config.dists != null) {
switch (config.dists) {
case CLIENT -> {
if (config.bus == Mod.EventBusSubscriber.Bus.FORGE) {
compat.addClientGameListener(gameEventBus);
} else {
compat.addClientModListener(modEventBus);
}
}
case DEDICATED_SERVER -> {
if (config.bus == Mod.EventBusSubscriber.Bus.FORGE) {
compat.addServerGameListener(gameEventBus);
} else {
compat.addServerModListener(modEventBus);
}
}
}
} else {
// 通用监听器
if (config.bus == Mod.EventBusSubscriber.Bus.FORGE) {
compat.addCommonGameListener(gameEventBus);
} else {
compat.addCommonModListener(modEventBus);
}
}
Lib39.LOGGER.debug("Applied {} listener to compat: {}",
getListenerTypeName(config), compat.id());
} catch (Exception e) {
Lib39.LOGGER.error("Failed to apply listener to compat: {}", compat.id(), e);
}
}
/**
* 获取监听器类型名称用于日志
*/
private @NotNull String getListenerTypeName(@NotNull ListenerConfig config) {
if (config.dists != null) {
return config.dists.name().toLowerCase() + " " +
(config.bus == Mod.EventBusSubscriber.Bus.FORGE ? "game" : "mod");
} else {
return "common " + (config.bus == Mod.EventBusSubscriber.Bus.FORGE ? "game" : "mod");
}
}
// ===================== 便捷方法 =====================
/**
* Add listener for all.
*
* @param bus the bus
*/
public void addListenerForAll(Mod.EventBusSubscriber.Bus bus) {
addListenerForAll(null, bus);
}
/**
* Add listener for compat.
*
* @param compatId the compat id
* @param bus the bus
*/
public void addListenerForCompat(ResourceLocation compatId, Mod.EventBusSubscriber.Bus bus) {
addListenerForCompat(compatId, null, bus);
}
private static class ListenerConfig {
/**
* The Compat id.
*/
final ResourceLocation compatId;
/**
* The Dists.
*/
final Dist dists;
/**
* The Bus.
*/
final Mod.EventBusSubscriber.Bus bus;
/**
* Instantiates a new Listener config.
*
* @param compatId the compat id
* @param dists the dists
* @param bus the bus
*/
ListenerConfig(ResourceLocation compatId, Dist dists, Mod.EventBusSubscriber.Bus bus) {
this.compatId = compatId;
this.dists = dists;
this.bus = bus;
}
/**
* Should apply boolean.
*
* @param compat the compat
* @return the boolean
*/
boolean shouldApply(@NotNull ICompat compat) {
return compat.isModLoaded();
}
}
// ===================== 其他方法 =====================
/**
* On load complete.
*/
public void onLoadComplete() {
Lib39.LOGGER.info("Calling onLoadComplete for {} compatibility modules", compats.size());
for (Map.Entry<ResourceLocation, ICompat> entry : compats.entrySet()) {
try {
entry.getValue().onLoadComplete();
} catch (Exception e) {
Lib39.LOGGER.error("Error in onLoadComplete for compat: {}", entry.getKey(), e);
}
}
}
/**
* Gets compat.
*
* @param id the id
* @return the compat
*/
public Optional<ICompat> getCompat(ResourceLocation id) {
return Optional.ofNullable(compats.get(id));
}
/**
* Has compat boolean.
*
* @param id the id
* @return the boolean
*/
public boolean hasCompat(ResourceLocation id) {
return compats.containsKey(id);
}
/**
* Unregister compat.
*
* @param id the id
*/
public void unregisterCompat(ResourceLocation id) {
ICompat removed = compats.remove(id);
if (removed != null) {
Lib39.LOGGER.debug("Unregistered compat: {}", id);
}
}
/**
* Gets loaded compats.
*
* @return the loaded compats
*/
public List<ICompat> getLoadedCompats() {
return compats.values().stream()
.filter(ICompat::isModLoaded)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,132 @@
package top.r3944realms.lib39.core.compat;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.eventbus.api.IEventBus;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
/**
* The interface Compat.
*/
public interface ICompat {
/**
* Id resource location.
*
* @return the resource location
*/
ResourceLocation id();
/**
* Initialize.
*/
void initialize();
/**
* On load complete.
*/
default void onLoadComplete() {}
/**
* Is mod loaded boolean.
*
* @return the boolean
*/
default boolean isModLoaded() {
return false;
}
/**
* Call if present t.
*
* @param <T> the type parameter
* @param callable the callable
* @return the t
* @throws Exception the exception
*/
default <T> T callIfPresent(Callable<T> callable) throws Exception {
if (isModLoaded()) return callable.call();
else return null;
}
/**
* Call if pesent t.
*
* @param <T> the type parameter
* @param callable the callable
* @param elseCall the else call
* @return the t
* @throws Exception the exception
*/
default <T> T callIfPresent(Callable<T> callable, Callable<T> elseCall) throws Exception {
if (isModLoaded()) return callable.call();
else return elseCall.call();
}
/**
* Run if present boolean.
*
* @param runnable the runnable
* @return the boolean
* @throws Exception the exception
*/
default boolean runIfPresent(Runnable runnable) throws Exception {
if (isModLoaded()) runnable.run(); else return false;
return true;
}
/**
* Add common game listener.
*
* @param gameBus the game bus
*/
default void addCommonGameListener(IEventBus gameBus) {
// 实现通用游戏事件监听器添加逻辑
}
/**
* Add common mod listener.
*
* @param modBus the mod bus
*/
default void addCommonModListener(IEventBus modBus) {
// 实现通用模组事件监听器添加逻辑
}
/**
* Add client game listener.
*
* @param gameBus the game bus
*/
default void addClientGameListener(IEventBus gameBus) {
// 实现客户端游戏事件监听器添加逻辑
}
/**
* Add client mod listener.
*
* @param modBus the mod bus
*/
default void addClientModListener(IEventBus modBus) {
// 实现客户端模组事件监听器添加逻辑
}
/**
* Add server game listener.
*
* @param gameBus the game bus
*/
default void addServerGameListener(IEventBus gameBus) {
// 实现服务端游戏事件监听器添加逻辑
}
/**
* Add server mod listener.
*
* @param modBus the mod bus
*/
default void addServerModListener(IEventBus modBus) {
// 实现服务端模组事件监听器添加逻辑
}
}

View File

@ -0,0 +1,7 @@
package top.r3944realms.lib39.core.event;
/**
* The type Client handler.
*/
public class ClientEventHandler {
}

View File

@ -1,4 +0,0 @@
package top.r3944realms.lib39.core.event;
public class ClientHandler {
}

View File

@ -13,11 +13,16 @@ 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 top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.api.event.RegisterCompatEvent;
import top.r3944realms.lib39.api.event.SyncManagerRegisterEvent;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.compat.CompatManager;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
import java.util.ArrayList;
@ -25,44 +30,82 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class CommonHandler {
/**
* The type Common handler.
*/
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)
public static class Game extends CommonHandler {
public static class Game extends CommonEventHandler {
private static ServerLevel sl;
/**
* Gets server level.
*
* @return the server level
*/
public static ServerLevel getServerLevel() {
return sl;
}
/**
* The Sync data 2 manager.
*/
static volatile SyncData2Manager syncData2Manager;
private static boolean isInitialized = false;
private static boolean isSync2MInitialized = false;
/**
* Gets sync data 2 manager.
*
* @return the sync data 2 manager
*/
public static SyncData2Manager getSyncData2Manager() {
return syncData2Manager;
}
/**
* On world load.
*
* @param event the event
*/
@SubscribeEvent
public static void onWorldLoad(LevelEvent.Load event) {
if (event.getLevel().isClientSide() || !(event.getLevel() instanceof ServerLevel serverLevel)) return;
// 只处理主世界避免多次初始化
if (!serverLevel.dimension().equals(Level.OVERWORLD)) return;
synchronized (Game.class) {
if (!isInitialized) {
if (!isSync2MInitialized) {
syncData2Manager = new SyncData2Manager();
MinecraftForge.EVENT_BUS.post(new SyncManagerRegisterEvent(syncData2Manager));
isInitialized = true;
isSync2MInitialized = true;
sl = serverLevel;
Lib39.LOGGER.info("SyncData2Manager initialized on world load");
}
}
}
/**
* On world unload.
*
* @param event the event
*/
@SubscribeEvent
public static void onWorldUnload(LevelEvent.Unload event) {
if (event.getLevel().isClientSide() || !(event.getLevel() instanceof ServerLevel serverLevel)) return;
if (!serverLevel.dimension().equals(Level.OVERWORLD)) return;
sl = null;
isInitialized = false;
isSync2MInitialized = false;
}
/**
* On server tick.
*
* @param event the event
*/
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.END) {
@ -72,6 +115,12 @@ public class CommonHandler {
syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::checkIfDirtyThenUpdate)));
}
}
/**
* On entity join world.
*
* @param event the event
*/
@SubscribeEvent
public static void onEntityJoinWorld(EntityJoinLevelEvent event) {
Entity entity = event.getEntity();
@ -83,6 +132,12 @@ public class CommonHandler {
}
}
}
/**
* On entity leave world.
*
* @param event the event
*/
@SubscribeEvent
public static void onEntityLeaveWorld(EntityLeaveLevelEvent event) {
Entity entity = event.getEntity();
@ -95,12 +150,51 @@ public class CommonHandler {
}
}
}
/**
* The type Mod.
*/
@SuppressWarnings("unused")
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = Lib39.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD)
public static class Mod extends CommonHandler {
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<>();
/**
* Gets compat manager.
*
* @return the compat manager
*/
public static CompatManager getCompatManager() {
return compatManager;
}
/**
* The Compat manager.
*/
static volatile CompatManager compatManager;
/**
* On fml common setup.
*
* @param event the event
*/
@SubscribeEvent
public static void onFMLCommonSetup (FMLCommonSetupEvent event) {
event.enqueueWork(() -> {
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
IEventBus gameBus = MinecraftForge.EVENT_BUS;
compatManager = new CompatManager(modBus, gameBus);
MinecraftForge.EVENT_BUS.post(new RegisterCompatEvent(compatManager));
});
}
/**
* Add item to tabs.
*
* @param item the item
* @param tabs the tabs
*/
@SafeVarargs
public static void addItemToTabs(RegistryObject<Block> item, ResourceKey<CreativeModeTab>... tabs) {
itemAddMap.put(item, tabs);
@ -111,6 +205,11 @@ public class CommonHandler {
}
}
/**
* On build creative tab contents.
*
* @param event the event
*/
@SubscribeEvent
public static void onBuildCreativeTabContents(BuildCreativeModeTabContentsEvent event) {
List<RegistryObject<Block>> itemsForTab = tabToItemsMap.get(event.getTabKey());
@ -119,6 +218,11 @@ public class CommonHandler {
}
}
/**
* Gets item add map.
*
* @return the item add map
*/
public static Map<RegistryObject<Block>, ResourceKey<CreativeModeTab>[]> getItemAddMap() {
return itemAddMap;
}

View File

@ -0,0 +1,7 @@
package top.r3944realms.lib39.core.event;
/**
* The type Server handler.
*/
public class ServerEventHandler {
}

View File

@ -1,4 +0,0 @@
package top.r3944realms.lib39.core.event;
public class ServerHandler {
}

View File

@ -8,28 +8,66 @@ import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.simple.SimpleChannel;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.network.toClient.SyncNBTDataS2CPack;
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(SyncNBTDataS2CPack.class, cid++, NetworkDirection.PLAY_TO_CLIENT)
.encoder(SyncNBTDataS2CPack::encode)
.decoder(SyncNBTDataS2CPack::decode)
.consumerNetworkThread(SyncNBTDataS2CPack::handle)
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);
}
}

View File

@ -0,0 +1,95 @@
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.common.capabilities.Capability;
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,60 +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.common.capabilities.Capability;
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.CommonHandler;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.NBTSyncData;
import java.util.Optional;
import java.util.function.Supplier;
public record SyncNBTDataS2CPack(int entityId, ResourceLocation id, CompoundTag data) {
public SyncNBTDataS2CPack(int entityId, ResourceLocation id, @NotNull NBTSyncData data) {
this(entityId, data.id(), data.serializeNBT());
}
public static void encode(@NotNull SyncNBTDataS2CPack msg, @NotNull FriendlyByteBuf buffer) {
buffer.writeInt(msg.entityId);
buffer.writeResourceLocation(msg.id);
buffer.writeNbt(msg.data);
}
@Contract("_ -> new")
public static @NotNull SyncNBTDataS2CPack decode(@NotNull FriendlyByteBuf buffer) {
return new SyncNBTDataS2CPack(buffer.readInt(), buffer.readResourceLocation(), buffer.readNbt());
}
public static void handle(SyncNBTDataS2CPack 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<Capability<ISyncData<?>>> capability = CommonHandler.Game.getSyncData2Manager().getCapability(msg.id);
capability.ifPresent(dataCapability -> entity.getCapability(dataCapability).ifPresent(cap -> {
if (cap instanceof NBTSyncData 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

@ -8,6 +8,9 @@ import top.r3944realms.lib39.datagen.value.McLocale;
import java.util.*;
/**
* The type Locale registry.
*/
@SuppressWarnings("unused")
public class LocaleRegistry {
private static final Map<String, ILocaleEntry> REGISTRY = new LinkedHashMap<>();
@ -19,23 +22,43 @@ public class LocaleRegistry {
}
}
/** 注册(覆盖已有时直接返回旧值) */
/**
* 注册覆盖已有时直接返回旧值 @param entry the entry
*
* @param entry the entry
* @return the locale entry
*/
@SuppressWarnings("UnusedReturnValue")
public static ILocaleEntry register(ILocaleEntry entry) {
return REGISTRY.putIfAbsent(entry.mcCode().toLowerCase(), entry);
}
/** 通过 Minecraft 代码查找 */
/**
* 通过 Minecraft 代码查找 @param code the code
*
* @param code the code
* @return the locale entry
*/
public static ILocaleEntry fromMcCode(@NotNull String code) {
return REGISTRY.get(code.toLowerCase());
}
/** 列出所有 */
/**
* 列出所有 @return the collection
*
* @return the collection
*/
public static @NotNull @UnmodifiableView Collection<ILocaleEntry> allValues() {
return Collections.unmodifiableCollection(REGISTRY.values());
}
/** 动态注册一个扩展 Locale */
/**
* 动态注册一个扩展 Locale @param mcCode the mc code
*
* @param mcCode the mc code
* @param locale the locale
* @return the locale entry
*/
public static ILocaleEntry registerDynamic(@NotNull String mcCode, Locale locale) {
return REGISTRY.computeIfAbsent(mcCode.toLowerCase(),
k -> new ExtendedLocale(mcCode.toLowerCase(), locale));

View File

@ -3,6 +3,12 @@ package top.r3944realms.lib39.core.sync;
import java.util.Map;
import java.util.Set;
/**
* The type Cached sync manager.
*
* @param <K> the type parameter
* @param <T> the type parameter
*/
@SuppressWarnings("unused")
public abstract class CachedSyncManager<K, T extends ISyncData<?>> implements ISyncManager<K, T> {

View File

@ -0,0 +1,13 @@
package top.r3944realms.lib39.core.sync;
/**
* The interface Entity.
*/
public interface IEntity {
/**
* Entity id int.
*
* @return the int
*/
int entityId();
}

View File

@ -2,13 +2,49 @@ package top.r3944realms.lib39.core.sync;
import net.minecraft.resources.ResourceLocation;
/**
* The interface Sync data.
*
* @param <T> the type parameter
*/
public interface ISyncData<T> {
/**
* Id resource location.
*
* @return the resource location
*/
ResourceLocation id();
/**
* Is dirty boolean.
*
* @return the boolean
*/
boolean isDirty();
/**
* Sets dirty.
*
* @param dirty the dirty
*/
void setDirty(boolean dirty);
/**
* Make dirty.
*/
default void makeDirty() {
setDirty(true);
}
/**
* Copy from.
*
* @param src the src
*/
void copyFrom(T src);
/**
* Check if dirty then update.
*/
void checkIfDirtyThenUpdate();
}

View File

@ -4,16 +4,26 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
/**
* The interface Sync manager.
*
* @param <K> the type parameter
* @param <T> the type parameter
*/
@SuppressWarnings("unused")
public interface ISyncManager<K, T extends ISyncData<?>> {
/**
* 获取同步映射
*
* @return the sync map
*/
Map<K, T> getSyncMap();
/**
* 获取同步集合
*
* @return the sync set
*/
default Set<T> getSyncSet() {
Map<K, T> syncMap = getSyncMap();
@ -22,6 +32,9 @@ public interface ISyncManager<K, T extends ISyncData<?>> {
/**
* 跟踪实例
*
* @param key the key
* @param instance the instance
*/
default void track(K key, T instance) {
Map<K, T> syncMap = getSyncMap();
@ -33,6 +46,9 @@ public interface ISyncManager<K, T extends ISyncData<?>> {
/**
* 取消跟踪
*
* @param key the key
* @param instance the instance
*/
default void untrack(K key, T instance) {
Map<K, T> syncMap = getSyncMap();
@ -45,6 +61,8 @@ public interface ISyncManager<K, T extends ISyncData<?>> {
/**
* 遍历操作
*
* @param consumer the consumer
*/
default void foreach(Consumer<T> consumer) {
Map<K, T> syncMap = getSyncMap();
@ -56,6 +74,8 @@ public interface ISyncManager<K, T extends ISyncData<?>> {
/**
* 批量操作
*
* @param instances the instances
*/
default void trackAll(Map<K, T> instances) {
Map<K, T> syncMap = getSyncMap();
@ -67,6 +87,8 @@ public interface ISyncManager<K, T extends ISyncData<?>> {
/**
* 获取大小
*
* @return the int
*/
default int size() {
Map<K, T> syncMap = getSyncMap();
@ -75,6 +97,9 @@ public interface ISyncManager<K, T extends ISyncData<?>> {
/**
* 检查是否包含key
*
* @param key the key
* @return the boolean
*/
default boolean containsKey(K key) {
Map<K, T> syncMap = getSyncMap();
@ -83,6 +108,9 @@ public interface ISyncManager<K, T extends ISyncData<?>> {
/**
* 检查是否包含value
*
* @param value the value
* @return the boolean
*/
default boolean containsValue(T value) {
Map<K, T> syncMap = getSyncMap();

View File

@ -0,0 +1,58 @@
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()));
}
}
}

View File

@ -1,36 +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;
public abstract class NBTSyncData implements ISyncData<NBTSyncData>, INBTSerializable<CompoundTag> {
protected boolean dirty;
protected final ResourceLocation id;
protected NBTSyncData(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 NBTSyncData src) {
this.dirty = src.isDirty();
}
}

View File

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

@ -4,73 +4,178 @@ 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;
import java.util.function.Function;
@SuppressWarnings("unused")
/**
* The type Sync data 2 manager.
*/
@SuppressWarnings({"unused", "DuplicatedCode"})
public class SyncData2Manager {
private final Map<ResourceLocation, TypedSyncEntry<?, ?>> typedEntries = Maps.newConcurrentMap();
/**
* 数据提供者接口 - 用于通过键获取数据
*
* @param <K> the type parameter
* @param <T> the type parameter
*/
@FunctionalInterface
public interface DataProvider<K, T> {
/**
* 通过键获取数据的 Optional
*
* @param key
* @return 数据的 Optional
*/
Optional<T> getData(K key);
}
private static class TypedSyncEntry<K, T extends ISyncData<?>> {
/**
* The Manager.
*/
final ISyncManager<K, T> manager;
/**
* The Data provider.
*/
@Nullable
Capability<T> capability;
final DataProvider<Entity, T> dataProvider;
/**
* The Allowed classes.
*/
final Set<Class<?>> allowedClasses;
TypedSyncEntry(ISyncManager<K, T> manager, @Nullable Capability<T> capability) {
/**
* Instantiates a new Typed sync entry.
*
* @param manager the manager
* @param dataProvider the data provider
*/
TypedSyncEntry(ISyncManager<K, T> manager, @Nullable DataProvider<Entity, T> dataProvider) {
this.manager = manager;
this.capability = capability;
this.dataProvider = dataProvider;
this.allowedClasses = Sets.newConcurrentHashSet();
}
}
public <K, T extends ISyncData<?>> void registerManager(
/**
* Register manager with data provider.
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param key the key
* @param manager the manager
* @param dataProvider the data provider
*/
public <K, T extends ISyncData<?>> void registerManagerWithProvider(
ResourceLocation key,
ISyncManager<K, T> manager,
Capability<T> capability
DataProvider<Entity, T> dataProvider
) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(manager, "Sync manager cannot be null");
Objects.requireNonNull(capability, "Capability cannot be null");
Objects.requireNonNull(dataProvider, "Data provider cannot be null");
typedEntries.put(key, new TypedSyncEntry<>(manager, capability));
typedEntries.put(key, new TypedSyncEntry<>(manager, dataProvider));
}
/**
* 向后兼容的注册方法只注册管理器不注册能力
* Register manager with function getter.
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param key the key
* @param manager the manager
* @param getter the data getter function
*/
public <K, T extends ISyncData<?>> void registerManager(
ResourceLocation key,
ISyncManager<K, T> manager,
Function<Entity, Optional<T>> getter
) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(manager, "Sync manager cannot be null");
Objects.requireNonNull(getter, "Data getter function cannot be null");
typedEntries.put(key, new TypedSyncEntry<>(manager, getter::apply));
}
/**
* 向后兼容的注册方法只注册管理器不注册数据提供者
*
* @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
// 注意这种方法会限制类型安全的功能
// 创建一个没有数据提供者的 TypedSyncEntry
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 data provider.
*
* @param <T> the type parameter
* @param key the key
* @return the data provider
*/
@SuppressWarnings("unchecked")
public <T extends ISyncData<?>> Optional<Capability<T>> getCapability(ResourceLocation key) {
public <T extends ISyncData<?>> Optional<DataProvider<Entity, T>> getDataProvider(ResourceLocation key) {
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null && entry.capability != null) {
return Optional.of((Capability<T>) entry.capability);
if (entry != null && entry.dataProvider != null) {
return Optional.of((DataProvider<Entity, T>) entry.dataProvider);
}
return Optional.empty();
}
/**
* 获取实体数据
*
* @param <T> the type parameter
* @param key the key
* @param entity the entity
* @return the entity data
*/
@SuppressWarnings("unchecked")
public <T extends ISyncData<?>> Optional<T> getEntityData(ResourceLocation key, Entity entity) {
return getDataProvider(key)
.flatMap(provider -> {
Optional<ISyncData<?>> result = provider.getData(entity);
return (Optional<T>) result;
});
}
/**
* 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");
@ -87,6 +192,9 @@ public class SyncData2Manager {
/**
* 移除允许的实体类
*
* @param key the key
* @param classes the classes
*/
public final void disallowEntityClass(ResourceLocation key, Class<?>... classes) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
@ -95,39 +203,59 @@ public class SyncData2Manager {
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 dataProvider the data provider
*/
public <T extends ISyncData<?>> void bindCapability(ResourceLocation key, Capability<T> capability) {
public <T extends ISyncData<?>> void bindDataProvider(ResourceLocation key, DataProvider<Entity, T> dataProvider) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
Objects.requireNonNull(capability, "Capability cannot be null");
Objects.requireNonNull(dataProvider, "Data provider cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null) {
// 更新现有条目的能力
updateCapabilityInEntry(key, entry, capability);
} else throw new IllegalArgumentException("No manager found for " + key);
// 更新现有条目的数据提供者
updateDataProviderInEntry(key, entry, dataProvider);
} else {
throw new IllegalArgumentException("No manager found for " + key);
}
}
/**
* 解绑能力
* 绑定简单的数据获取器
*
* @param <T> the type parameter
* @param key the key
* @param getter the data getter function
*/
public void unbindCapability(ResourceLocation key) {
public <T extends ISyncData<?>> void bindDataGetter(ResourceLocation key, Function<Entity, Optional<T>> getter) {
bindDataProvider(key, getter::apply);
}
/**
* 解绑数据提供者
*
* @param key the key
*/
public void unbindDataProvider(ResourceLocation key) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
TypedSyncEntry<?, ?> entry = typedEntries.get(key);
if (entry != null) {
// 将能力设置为null但保留管理器和其他配置
updateCapabilityInEntry(key, entry, null);
// 数据提供者设置为null但保留管理器和其他配置
updateDataProviderInEntry(key, entry, null);
}
}
/**
* 清除允许的实体类
*
* @param key the key
*/
public void clearAllowedEntityClasses(ResourceLocation key) {
Objects.requireNonNull(key, "ResourceLocation key cannot be null");
@ -138,24 +266,38 @@ public class SyncData2Manager {
}
}
/**
* 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;
}
}
if (entry == null) {
return false;
}
return entry != null && isAllowed ;
// 如果没有设置允许的类则默认允许所有类
if (entry.allowedClasses.isEmpty()) {
return true;
}
// 检查实体类是否在允许的类中
return entry.allowedClasses.stream()
.anyMatch(allowedClass -> allowedClass.isAssignableFrom(entityClass));
}
// 类型安全的事件处理
/**
* 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);
@ -165,12 +307,18 @@ public class SyncData2Manager {
}
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));
if (entry.dataProvider != null) {
entry.dataProvider.getData(entity)
.ifPresent(data -> entry.manager.track(entity.getUUID(), data));
}
}
// 类型安全的事件处理 - 取消跟踪实体
/**
* 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);
@ -180,14 +328,16 @@ public class SyncData2Manager {
}
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));
if (entry.dataProvider != null) {
entry.dataProvider.getData(entity)
.ifPresent(data -> entry.manager.untrack(entity.getUUID(), data));
}
}
/**
* 从所有管理器中移除实体跟踪
*
* @param entity the entity
*/
public void untrackEntityFromAllManagers(Entity entity) {
for (ResourceLocation id : getRegisteredKeys()) {
@ -199,6 +349,9 @@ public class SyncData2Manager {
/**
* 批量从管理器中移除实体跟踪
*
* @param entities the entities
* @param managerId the manager id
*/
public void untrackEntitiesForManager(@NotNull Iterable<Entity> entities, ResourceLocation managerId) {
for (Entity entity : entities) {
@ -208,6 +361,8 @@ public class SyncData2Manager {
/**
* 从所有管理器中批量移除实体跟踪
*
* @param entities the entities
*/
public void untrackEntitiesFromAllManagers(@NotNull Iterable<Entity> entities) {
for (Entity entity : entities) {
@ -217,6 +372,8 @@ public class SyncData2Manager {
/**
* 强制清理管理器中的所有跟踪数据
*
* @param managerId the manager id
*/
public void clearAllTrackedData(ResourceLocation managerId) {
TypedSyncEntry<?, ?> entry = typedEntries.get(managerId);
@ -226,7 +383,6 @@ public class SyncData2Manager {
}
private <K, T extends ISyncData<?>> void clearTrackedDataForEntry(@NotNull TypedSyncEntry<K, T> entry) {
// 获取当前跟踪的集合并清空
Set<T> syncSet = entry.manager.getSyncSet();
if (syncSet != null) {
syncSet.clear();
@ -242,36 +398,62 @@ public class SyncData2Manager {
}
}
// 辅助方法更新条目的能力
// 辅助方法更新条目的数据提供者
@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);
private <K, T extends ISyncData<?>> void updateDataProviderInEntry(
ResourceLocation id,
TypedSyncEntry<?,?> entry,
DataProvider<Entity, T> newDataProvider
) {
// 由于 DataProvider final我们需要创建一个新的 TypedSyncEntry
TypedSyncEntry<K, T> newEntry = new TypedSyncEntry<>(
(ISyncManager<K, T>) entry.manager,
newDataProvider
);
newEntry.allowedClasses.addAll(entry.allowedClasses);
typedEntries.put(id, newEntry);
}
/**
* 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");

View File

@ -11,12 +11,24 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The type Simple language provider.
*/
@SuppressWarnings("unused")
public class SimpleLanguageProvider extends LanguageProvider {
private final McLocale language;
private final ILangKeyValue langKeyValue;
private final Map<String, String> lanKeyMap;
private static final List<String> objects = new ArrayList<>();
/**
* Instantiates a new Simple language provider.
*
* @param output the output
* @param modId the mod id
* @param Lan the lan
* @param langKeyValue the lang key value
*/
public SimpleLanguageProvider(PackOutput output, String modId, @NotNull McLocale Lan, ILangKeyValue langKeyValue) {
super(output, modId, Lan.mcCode());
this.language = Lan;

View File

@ -4,10 +4,33 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* The interface Lang key value.
*/
public interface ILangKeyValue {
/**
* Gets lang.
*
* @param locale the locale
* @param key the key
* @return the lang
*/
static String getLang(McLocale locale, @NotNull ILangKeyValue key) {
return key.getLang(locale);
}
/**
* Gets lang.
*
* @param locale the locale
* @return the lang
*/
String getLang(McLocale locale);
/**
* Gets values.
*
* @return the values
*/
List<ILangKeyValue> getValues();
}

View File

@ -2,7 +2,21 @@ package top.r3944realms.lib39.datagen.value;
import java.util.Locale;
/**
* The interface Locale entry.
*/
public interface ILocaleEntry {
/**
* Mc code string.
*
* @return the string
*/
String mcCode();
/**
* Java locale locale.
*
* @return the locale
*/
Locale javaLocale();
}

View File

@ -6,15 +6,42 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.Supplier;
/**
* The type Lang key value.
*/
@SuppressWarnings("unused")
public class LangKeyValue implements ILangKeyValue {
/**
* The Supplier.
*/
protected final Supplier<?> supplier;
/**
* The Key.
*/
protected String key;
/**
* The Us en.
*/
protected final String US_EN;
/**
* The Sim cn.
*/
protected final String SIM_CN;
/**
* The Tra cn.
*/
protected final String TRA_CN;
/**
* The Lzh.
*/
protected final String LZH;
/**
* The Default.
*/
protected final Boolean Default;
/**
* The Mpe.
*/
protected final ModPartEnum MPE;
private LangKeyValue(Supplier<?> supplier, String key, ModPartEnum MPE,
String US_EN, String SIM_CN, String TRA_CN, String LZH, Boolean isDefault) {
@ -28,42 +55,111 @@ public class LangKeyValue implements ILangKeyValue {
this.Default = isDefault;
}
/**
* Of supplier lang key value.
*
* @param supplier the supplier
* @param MPE the mpe
* @param US_EN the us en
* @param SIM_CN the sim cn
* @param TRA_CN the tra cn
* @return the lang key value
*/
@Contract(value = "_, _, _, _, _ -> new", pure = true)
public static @NotNull LangKeyValue ofSupplier(Supplier<?> supplier, ModPartEnum MPE,
String US_EN, String SIM_CN, String TRA_CN) {
return new LangKeyValue(supplier, null, MPE, US_EN, SIM_CN, TRA_CN, null, false);
}
/**
* Of supplier lang key value.
*
* @param supplier the supplier
* @param MPE the mpe
* @param US_EN the us en
* @param SIM_CN the sim cn
* @param TRA_CN the tra cn
* @param LZH the lzh
* @return the lang key value
*/
@Contract(value = "_, _, _, _, _, _ -> new", pure = true)
public static @NotNull LangKeyValue ofSupplier(Supplier<?> supplier, ModPartEnum MPE,
String US_EN, String SIM_CN, String TRA_CN, String LZH) {
return new LangKeyValue(supplier, null, MPE, US_EN, SIM_CN, TRA_CN, LZH, false);
}
/**
* Of supplier lang key value.
*
* @param supplier the supplier
* @param MPE the mpe
* @param US_EN the us en
* @param SIM_CN the sim cn
* @param TRA_CN the tra cn
* @param isDefault the is default
* @return the lang key value
*/
@Contract(value = "_, _, _, _, _, _ -> new", pure = true)
public static @NotNull LangKeyValue ofSupplier(Supplier<?> supplier, ModPartEnum MPE,
String US_EN, String SIM_CN, String TRA_CN, Boolean isDefault) {
return new LangKeyValue(supplier, null, MPE, US_EN, SIM_CN, TRA_CN, null, isDefault);
}
/**
* Of key lang key value.
*
* @param key the key
* @param MPE the mpe
* @param US_EN the us en
* @param SIM_CN the sim cn
* @param TRA_CN the tra cn
* @return the lang key value
*/
@Contract(value = "_, _, _, _, _ -> new", pure = true)
public static @NotNull LangKeyValue ofKey(String key, ModPartEnum MPE,
String US_EN, String SIM_CN, String TRA_CN) {
return new LangKeyValue(null, key, MPE, US_EN, SIM_CN, TRA_CN, null, false);
}
/**
* Of key lang key value.
*
* @param key the key
* @param MPE the mpe
* @param US_EN the us en
* @param SIM_CN the sim cn
* @param TRA_CN the tra cn
* @param LZH the lzh
* @return the lang key value
*/
@Contract(value = "_, _, _, _, _, _ -> new", pure = true)
public static @NotNull LangKeyValue ofKey(String key, ModPartEnum MPE,
String US_EN, String SIM_CN, String TRA_CN, String LZH) {
return new LangKeyValue(null, key, MPE, US_EN, SIM_CN, TRA_CN, LZH, false);
}
/**
* Of key lang key value.
*
* @param key the key
* @param MPE the mpe
* @param US_EN the us en
* @param SIM_CN the sim cn
* @param TRA_CN the tra cn
* @param isDefault the is default
* @return the lang key value
*/
@Contract(value = "_, _, _, _, _, _ -> new", pure = true)
public static @NotNull LangKeyValue ofKey(String key, ModPartEnum MPE,
String US_EN, String SIM_CN, String TRA_CN, Boolean isDefault) {
return new LangKeyValue(null, key, MPE, US_EN, SIM_CN, TRA_CN, null, isDefault);
}
/**
* Gets key.
*
* @return the key
*/
public String getKey() {
return key;
}

View File

@ -2,16 +2,49 @@ package top.r3944realms.lib39.datagen.value;
import java.util.Locale;
/**
* The enum Mc locale.
*/
public enum McLocale implements ILocaleEntry {
/**
* En us mc locale.
*/
EN_US("en_us", Locale.US),
/**
* Zh cn mc locale.
*/
ZH_CN("zh_cn", Locale.SIMPLIFIED_CHINESE),
/**
* Zh tw mc locale.
*/
ZH_TW("zh_tw", Locale.TRADITIONAL_CHINESE),
/**
* The Lzh.
*/
LZH("lzh", new Locale("lzh", "ZH")),
/**
* Ja jp mc locale.
*/
JA_JP("ja_jp", Locale.JAPAN),
/**
* Ko kr mc locale.
*/
KO_KR("ko_kr", Locale.KOREA),
/**
* The Ru ru.
*/
RU_RU("ru_ru", new Locale("ru", "RU")),
/**
* Fr fr mc locale.
*/
FR_FR("fr_fr", Locale.FRANCE),
/**
* De de mc locale.
*/
DE_DE("de_de", Locale.GERMANY),
/**
* The Es es.
*/
ES_ES("es_es", new Locale("es", "ES"));
private final String mcCode;

View File

@ -7,62 +7,101 @@ import org.jetbrains.annotations.NotNull;
* 模组各部分的类型枚举用于数据生成与分类
*/
public enum ModPartEnum {
/** 默认/未指定类型 */
/**
* 默认/未指定类型
*/
DEFAULT,
/** 物品 */
/**
* 物品
*/
ITEM,
/** 方块 */
/**
* 方块
*/
BLOCK,
/** 附魔 */
/**
* 附魔
*/
ENCHANTMENT,
/** 成就 / 进度 */
/**
* 成就 / 进度
*/
ADVANCEMENT,
/** 创造模式物品栏 */
/**
* 创造模式物品栏
*/
CREATIVE_TAB,
/** 配置项 */
/**
* 配置项
*/
CONFIG,
/** 实体(生物、载具等) */
/**
* 实体生物载具等
*/
ENTITY,
/** 图形界面 */
/**
* 图形界面
*/
GUI,
/** 作者信息 */
/**
* 作者信息
*/
AUTHOR,
/** 标题 */
/**
* 标题
*/
TITLE,
/** 名称 */
/**
* 名称
*/
NAME,
/** 游戏规则(/gamerule */
/**
* 游戏规则/gamerule
*/
GAME_RULE,
/** 描述文本 */
/**
* 描述文本
*/
DESCRIPTION,
/** 一般信息 */
/**
* 一般信息
*/
INFO,
/** 消息(聊天、提示等) */
/**
* 消息聊天提示等
*/
MESSAGE,
/** 命令 */
/**
* 命令
*/
COMMAND,
/** 声音资源 */
/**
* 声音资源
*/
SOUND;
/**
* 根据枚举类型生成标准化 key 前缀
* 例如 ITEM -> "item.", BLOCK -> "block."
*
* @return the key prefix
*/
@Contract(pure = true)
public @NotNull String getKeyPrefix() {
@ -91,6 +130,9 @@ public enum ModPartEnum {
/**
* 根据枚举类型和具体名称生成完整 key
* 例如 ITEM + "example_item" -> "item.example_item"
*
* @param name the name
* @return the full key
*/
@Contract(pure = true)
public @NotNull String getFullKey(String name) {

View File

@ -6,10 +6,15 @@ import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.RegistryObject;
import top.r3944realms.lib39.core.event.CommonHandler;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.core.event.CommonEventHandler;
import java.util.function.Supplier;
/**
* The type Block registry builder.
*/
@SuppressWarnings({"UnusedReturnValue", "unused"})
public class BlockRegistryBuilder {
private String registryName;
@ -17,22 +22,33 @@ public class BlockRegistryBuilder {
/**
* 创建新的构建器实例
*
* @return the block registry builder
*/
public static BlockRegistryBuilder create() {
@Contract(value = " -> new", pure = true)
public static @NotNull BlockRegistryBuilder create() {
return new BlockRegistryBuilder();
}
/**
* 设置注册名称
*
* @param name the name
* @return the block registry builder
*/
public BlockRegistryBuilder withName(String name) {
this.registryName = name;
return this;
}
/**
* 注册方块不自动注册物品
*
* @param blockRegister the block register
* @param blockSupplier the block supplier
* @return the block registry builder
*/
public BlockRegistryBuilder registerBlock(DeferredRegister<Block> blockRegister, Supplier<? extends Block> blockSupplier) {
public BlockRegistryBuilder registerBlock(@NotNull DeferredRegister<Block> blockRegister, Supplier<? extends Block> blockSupplier) {
this.blockObject = blockRegister.register(this.registryName, blockSupplier);
return this;
}
@ -42,11 +58,15 @@ public class BlockRegistryBuilder {
*/
@SafeVarargs
private void registerBlockItem(RegistryObject<Block> blockObject, ResourceKey<CreativeModeTab>... creativeTabs) {
CommonHandler.Mod.addItemToTabs(blockObject, creativeTabs);
CommonEventHandler.Mod.addItemToTabs(blockObject, creativeTabs);
}
/**
* 注册方块和物品到建筑标签页
*
* @param blockRegister the block register
* @param blockSupplier the block supplier
* @return the block registry builder
*/
public BlockRegistryBuilder registerWithBuildingTab(DeferredRegister<Block> blockRegister, Supplier<? extends Block> blockSupplier) {
registerBlock(blockRegister, blockSupplier);
@ -56,6 +76,10 @@ public class BlockRegistryBuilder {
/**
* 注册方块和物品到功能标签页
*
* @param blockRegister the block register
* @param blockSupplier the block supplier
* @return the block registry builder
*/
public BlockRegistryBuilder registerWithFunctionalTab(DeferredRegister<Block> blockRegister, Supplier<? extends Block> blockSupplier) {
registerBlock(blockRegister, blockSupplier);
@ -65,6 +89,8 @@ public class BlockRegistryBuilder {
/**
* 获取注册的方块对象
*
* @return the registry object
*/
public RegistryObject<Block> build() {
return this.blockObject;

View File

@ -12,11 +12,18 @@ import net.minecraft.commands.Commands;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The type Command alias helper.
*/
@SuppressWarnings("unused")
public class CommandAliasHelper {
/**
* 注册命令及其别名
*
* @param dispatcher the dispatcher
* @param mainCommand the main command
* @param aliases the aliases
*/
public static void registerWithAliases(@NotNull CommandDispatcher<CommandSourceStack> dispatcher,
LiteralArgumentBuilder<CommandSourceStack> mainCommand,

View File

@ -3,8 +3,20 @@ package top.r3944realms.lib39.util.lang;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
/**
* The type Pair.
*
* @param <F> the type parameter
* @param <S> the type parameter
*/
public final class Pair<F, S> {
/**
* The First.
*/
public F first;
/**
* The Second.
*/
public S second;
private Pair(F first, S second) {
@ -12,6 +24,15 @@ public final class Pair<F, S> {
this.second = second;
}
/**
* Of @ not null pair.
*
* @param <F> the type parameter
* @param <S> the type parameter
* @param first the first
* @param second the second
* @return the @ not null pair
*/
@Contract("null, _ -> fail; !null, null -> fail; !null, !null -> new")
public static <F, S> @NotNull Pair<F, S> of(F first, S second) {
if (first == null || second == null) {

View File

@ -5,10 +5,26 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* The type Triple.
*
* @param <A> the type parameter
* @param <B> the type parameter
* @param <C> the type parameter
*/
@SuppressWarnings("unused")
public final class Triple<A, B, C> {
/**
* The First.
*/
public A first;
/**
* The Second.
*/
public B second;
/**
* The Third.
*/
public C third;
private Triple(A first, B second, C third) {
@ -17,6 +33,17 @@ public final class Triple<A, B, C> {
this.third = third;
}
/**
* Of @ not null triple.
*
* @param <A> the type parameter
* @param <B> the type parameter
* @param <C> the type parameter
* @param first the first
* @param second the second
* @param third the third
* @return the @ not null triple
*/
@Contract(value = "_, _, _ -> new", pure = true)
public static <A, B, C> @NotNull Triple<A, B, C> of(A first, B second, C third) {
return new Triple<>(first, second, third);
@ -37,8 +64,9 @@ public final class Triple<A, B, C> {
return Objects.hash(first, second, third);
}
@Contract(pure = true)
@Override
public String toString() {
public @NotNull String toString() {
return "Triple{" +
"first=" + first +
", second=" + second +

View File

@ -8,6 +8,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
/**
* The type Tuple.
*/
@SuppressWarnings("unused")
public final class Tuple {
private final List<Object> elements;
@ -16,15 +19,33 @@ public final class Tuple {
this.elements = List.of(elements);
}
/**
* Of tuple.
*
* @param elements the elements
* @return the tuple
*/
@Contract(value = "_ -> new", pure = true)
public static @NotNull Tuple of(Object... elements) {
return new Tuple(elements);
}
/**
* Size int.
*
* @return the int
*/
public int size() {
return elements.size();
}
/**
* Get t.
*
* @param <T> the type parameter
* @param index the index
* @return the t
*/
@SuppressWarnings("unchecked")
public <T> T get(int index) {
if (index < 0 || index >= elements.size()) {
@ -33,27 +54,63 @@ public final class Tuple {
return (T) elements.get(index);
}
/**
* First t.
*
* @param <T> the type parameter
* @return the t
*/
public <T> T first() {
return get(0);
}
/**
* Second t.
*
* @param <T> the type parameter
* @return the t
*/
public <T> T second() {
return get(1);
}
/**
* Third t.
*
* @param <T> the type parameter
* @return the t
*/
public <T> T third() {
return get(2);
}
/**
* Last t.
*
* @param <T> the type parameter
* @return the t
*/
public <T> T last() {
return get(elements.size() - 1);
}
public List<Object> toList() {
/**
* To list list.
*
* @return the list
*/
@Contract(value = " -> new", pure = true)
public @NotNull List<Object> toList() {
return new ArrayList<>(elements);
}
public Object[] toArray() {
/**
* To array object [ ].
*
* @return the object [ ]
*/
@Contract(pure = true)
public Object @NotNull [] toArray() {
return elements.toArray();
}
@ -70,15 +127,26 @@ public final class Tuple {
return Objects.hash(elements);
}
@Contract(pure = true)
@Override
public String toString() {
public @NotNull String toString() {
return "Tuple" + elements;
}
public Iterator<Object> iterator() {
/**
* Iterator iterator.
*
* @return the iterator
*/
public @NotNull Iterator<Object> iterator() {
return elements.iterator();
}
/**
* Stream java . util . stream . stream.
*
* @return the java . util . stream . stream
*/
public java.util.stream.Stream<Object> stream() {
return elements.stream();
}

View File

@ -24,6 +24,9 @@ import org.jetbrains.annotations.Nullable;
import java.util.UUID;
import java.util.function.Consumer;
/**
* The type Nbt reader.
*/
@SuppressWarnings("unused")
public class NBTReader {
private final CompoundTag nbt;
@ -34,13 +37,23 @@ public class NBTReader {
/**
* 从CompoundTag创建读取器
*
* @param nbt the nbt
* @return the nbt reader
*/
@NotNull
public static NBTReader of(@NotNull CompoundTag nbt) {
return new NBTReader(nbt);
}
// 基本读取方法 - 直接赋值给成员变量
/**
* String nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
// 基本读取方法 - 直接赋值给成员变量
public NBTReader string(String key, Consumer<String> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getString(key));
@ -48,11 +61,26 @@ public class NBTReader {
return this;
}
/**
* String nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader string(String key, @NotNull Consumer<String> setter, String defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getString(key) : defaultValue);
return this;
}
/**
* Byte value nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader byteValue(String key, Consumer<Byte> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getByte(key));
@ -60,11 +88,26 @@ public class NBTReader {
return this;
}
/**
* Byte value nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader byteValue(String key, @NotNull Consumer<Byte> setter, byte defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getByte(key) : defaultValue);
return this;
}
/**
* Short value nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader shortValue(String key, Consumer<Short> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getShort(key));
@ -72,11 +115,26 @@ public class NBTReader {
return this;
}
/**
* Short value nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader shortValue(String key, @NotNull Consumer<Short> setter, short defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getShort(key) : defaultValue);
return this;
}
/**
* Int value nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader intValue(String key, Consumer<Integer> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getInt(key));
@ -84,11 +142,26 @@ public class NBTReader {
return this;
}
/**
* Int value nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader intValue(String key, @NotNull Consumer<Integer> setter, int defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getInt(key) : defaultValue);
return this;
}
/**
* Long value nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader longValue(String key, Consumer<Long> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getLong(key));
@ -96,11 +169,26 @@ public class NBTReader {
return this;
}
/**
* Long value nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader longValue(String key, @NotNull Consumer<Long> setter, long defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getLong(key) : defaultValue);
return this;
}
/**
* Float value nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader floatValue(String key, Consumer<Float> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getFloat(key));
@ -108,11 +196,26 @@ public class NBTReader {
return this;
}
/**
* Float value nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader floatValue(String key, @NotNull Consumer<Float> setter, float defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getFloat(key) : defaultValue);
return this;
}
/**
* Double value nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader doubleValue(String key, Consumer<Double> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getDouble(key));
@ -120,11 +223,26 @@ public class NBTReader {
return this;
}
/**
* Double value nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader doubleValue(String key, @NotNull Consumer<Double> setter, double defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getDouble(key) : defaultValue);
return this;
}
/**
* Boolean value nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader booleanValue(String key, Consumer<Boolean> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getBoolean(key));
@ -132,12 +250,27 @@ public class NBTReader {
return this;
}
/**
* Boolean value nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader booleanValue(String key, @NotNull Consumer<Boolean> setter, boolean defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getBoolean(key) : defaultValue);
return this;
}
// 数组类型
/**
* Byte array nbt reader.
*
* @param key the key
* @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));
@ -145,6 +278,13 @@ public class NBTReader {
return this;
}
/**
* Int array nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader intArray(String key, Consumer<int[]> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getIntArray(key));
@ -152,6 +292,13 @@ public class NBTReader {
return this;
}
/**
* Long array nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
public NBTReader longArray(String key, Consumer<long[]> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getLongArray(key));
@ -159,7 +306,14 @@ public class NBTReader {
return this;
}
// UUID
/**
* 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));
@ -167,12 +321,27 @@ public class NBTReader {
return this;
}
/**
* Uuid 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);
return this;
}
// CompoundTag
/**
* Compound nbt reader.
*
* @param key the key
* @param setter the setter
* @return the nbt reader
*/
// CompoundTag
public NBTReader compound(String key, Consumer<CompoundTag> setter) {
if (nbt.contains(key)) {
setter.accept(nbt.getCompound(key));
@ -180,12 +349,28 @@ public class NBTReader {
return this;
}
/**
* Compound nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader compound(String key, @NotNull Consumer<CompoundTag> setter, CompoundTag defaultValue) {
setter.accept(nbt.contains(key) ? nbt.getCompound(key) : defaultValue);
return this;
}
// ListTag
/**
* 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) {
if (nbt.contains(key)) {
setter.accept(nbt.getList(key, type));
@ -193,7 +378,14 @@ public class NBTReader {
return this;
}
// Vec3支持
/**
* Vec 3 nbt reader.
*
* @param key the key
* @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);
@ -208,6 +400,14 @@ public class NBTReader {
return this;
}
/**
* Vec 3 nbt reader.
*
* @param key the key
* @param setter the setter
* @param defaultValue the default value
* @return the nbt reader
*/
public NBTReader vec3(String key, Consumer<Vec3> setter, Vec3 defaultValue) {
if (nbt.contains(key)) {
CompoundTag vecTag = nbt.getCompound(key);
@ -224,7 +424,16 @@ public class NBTReader {
return this;
}
// 枚举支持
/**
* Enum value nbt reader.
*
* @param <T> the type parameter
* @param key the key
* @param enumClass the enum class
* @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);
@ -237,6 +446,16 @@ public class NBTReader {
return this;
}
/**
* Enum value nbt 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 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);
@ -250,7 +469,14 @@ public class NBTReader {
return this;
}
// 嵌套读取支持
/**
* Nested nbt reader.
*
* @param key the key
* @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)));
@ -258,6 +484,14 @@ public class NBTReader {
return this;
}
/**
* Nested nbt reader.
*
* @param key the key
* @param consumer the consumer
* @param orElse the or else
* @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)));
@ -267,7 +501,14 @@ public class NBTReader {
return this;
}
// 条件读取
/**
* If present nbt reader.
*
* @param key the key
* @param action the action
* @return the nbt reader
*/
// 条件读取
public NBTReader ifPresent(String key, Runnable action) {
if (nbt.contains(key)) {
action.run();
@ -275,6 +516,13 @@ public class NBTReader {
return this;
}
/**
* If absent nbt reader.
*
* @param key the key
* @param action the action
* @return the nbt reader
*/
public NBTReader ifAbsent(String key, Runnable action) {
if (!nbt.contains(key)) {
action.run();
@ -282,13 +530,24 @@ public class NBTReader {
return this;
}
// 获取原始NBT
/**
* Gets raw.
*
* @return the raw
*/
// 获取原始NBT
@NotNull
public CompoundTag getRaw() {
return nbt;
}
// 便捷的静态方法保持原有功能
/**
* Read vec 3 vec 3.
*
* @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")) {
@ -302,6 +561,12 @@ public class NBTReader {
}
}
/**
* Read vec 3 safe vec 3.
*
* @param nbt the nbt
* @return the vec 3
*/
@Nullable
public static Vec3 readVec3Safe(@NotNull CompoundTag nbt) {
if (nbt.contains("X") && nbt.contains("Y") && nbt.contains("Z")) {

File diff suppressed because it is too large Load Diff

View File

@ -24,13 +24,17 @@ import java.util.Queue;
import java.util.UUID;
import java.util.function.Function;
/**
* The type Riding applier.
*/
@SuppressWarnings("unused")
public class RidingApplier {
/**
* 应用骑乘关系在服务器端调用
* @param relationship 骑乘关系
*
* @param relationship 骑乘关系
* @param entityProvider 实体提供器根据UUID获取实体
* @return 应用成功的实体数量
* @return 应用成功的实体数量 int
*/
public static int applyRidingRelationship(RidingRelationship relationship,
Function<UUID, Entity> entityProvider) {
@ -87,8 +91,12 @@ public class RidingApplier {
return appliedCount;
}
/**
* 批量应用骑乘关系适用于世界加载时
*
* @param relationships the relationships
* @param entityProvider the entity provider
*/
public static void applyRidingRelationships(Collection<RidingRelationship> relationships,
Function<UUID, Entity> entityProvider) {
@ -108,6 +116,10 @@ public class RidingApplier {
/**
* 从JSON字符串应用骑乘关系
*
* @param json the json
* @param entityProvider the entity provider
* @return the int
*/
public static int applyRidingRelationshipFromJson(String json,
Function<UUID, Entity> entityProvider) {

View File

@ -17,11 +17,20 @@ package top.r3944realms.lib39.util.riding;
import java.util.UUID;
/**
* The type Riding cycle exception.
*/
@SuppressWarnings("unused")
public class RidingCycleException extends IllegalStateException {
private final UUID entityId;
private final UUID vehicleId;
/**
* Instantiates a new Riding cycle exception.
*
* @param entityId the entity id
* @param vehicleId the vehicle id
*/
public RidingCycleException(UUID entityId, UUID vehicleId) {
super(String.format("Cyclic riding reference detected. " +
"Entity %s cannot be added as passenger to vehicle %s " +
@ -31,10 +40,20 @@ public class RidingCycleException extends IllegalStateException {
this.vehicleId = vehicleId;
}
/**
* Gets entity id.
*
* @return the entity id
*/
public UUID getEntityId() {
return entityId;
}
/**
* Gets vehicle id.
*
* @return the vehicle id
*/
public UUID getVehicleId() {
return vehicleId;
}

View File

@ -20,10 +20,15 @@ import net.minecraft.world.entity.Entity;
import java.util.*;
import java.util.function.Function;
/**
* The type Riding dismounts.
*/
@SuppressWarnings("unused")
public class RidingDismounts {
/**
* 解除单个实体的骑乘关系
*
* @param entity the entity
*/
public static void dismountEntity(Entity entity) {
if (entity == null) {
@ -41,6 +46,8 @@ public class RidingDismounts {
/**
* 解除实体及其所有乘客的骑乘关系非递归
*
* @param entity the entity
*/
public static void dismountAllPassengers(Entity entity) {
if (entity == null) {
@ -65,6 +72,8 @@ public class RidingDismounts {
/**
* 解除根实体的骑乘关系包括从载具下车
*
* @param entity the entity
*/
public static void dismountRootEntity(Entity entity) {
if (entity == null) {
@ -86,6 +95,10 @@ public class RidingDismounts {
/**
* 安全解除骑乘关系带超时保护
*
* @param entity the entity
* @param maxIterations the max iterations
* @return the boolean
*/
public static boolean safeDismountAll(Entity entity, int maxIterations) {
if (entity == null) {
@ -118,6 +131,8 @@ public class RidingDismounts {
/**
* 批量解除多个实体的骑乘关系
*
* @param entities the entities
*/
public static void dismountEntities(Collection<Entity> entities) {
if (entities == null || entities.isEmpty()) {
@ -151,6 +166,9 @@ public class RidingDismounts {
/**
* 根据骑乘关系数据结构解除骑乘
*
* @param relationship the relationship
* @param entityProvider the entity provider
*/
public static void dismountByRelationship(RidingRelationship relationship,
Function<UUID, Entity> entityProvider) {
@ -181,6 +199,8 @@ public class RidingDismounts {
/**
* 立即解除所有骑乘关系强制方式
*
* @param entity the entity
*/
public static void forceDismountAll(Entity entity) {
if (entity == null) {

View File

@ -22,10 +22,17 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
/**
* The type Riding finder.
*/
@SuppressWarnings("unused")
public class RidingFinder {
/**
* 从JSON字符串应用骑乘关系
*
* @param ship the ship
* @param entityProvider the entity provider
* @return the entity from riding ship
*/
public static @NotNull List<Entity> getEntityFromRidingShip(RidingRelationship ship,
Function<UUID, Entity> entityProvider) {
@ -42,8 +49,12 @@ public class RidingFinder {
}
return ret;
}
/**
* 查找根载具
*
* @param entity the entity
* @return the entity
*/
@Nullable
public static Entity findRootVehicle(@Nullable Entity entity) {
@ -64,6 +75,9 @@ public class RidingFinder {
/**
* 获取所有乘客包括嵌套乘客
*
* @param entity the entity
* @return the all passengers
*/
public static List<Entity> getAllPassengers(@Nullable Entity entity) {
return getAllPassengers(entity, true);
@ -71,6 +85,10 @@ public class RidingFinder {
/**
* 获取所有乘客包括嵌套乘客
*
* @param entity the entity
* @param findRoot the find root
* @return the all passengers
*/
public static List<Entity> getAllPassengers(@Nullable Entity entity, boolean findRoot) {
if (entity == null) {

View File

@ -26,46 +26,93 @@ public class RidingRelationship {
private UUID vehicleId;
private List<RidingRelationship> passengers;
/**
* Instantiates a new Riding relationship.
*/
public RidingRelationship() {
this.passengers = new ArrayList<>();
}
/**
* Instantiates a new Riding relationship.
*
* @param passengers the passengers
* @param vehicleId the vehicle id
* @param entityId the entity id
*/
public RidingRelationship(List<RidingRelationship> passengers, UUID vehicleId, UUID entityId) {
this.passengers = passengers != null ? passengers : new ArrayList<>();
this.vehicleId = vehicleId;
this.entityId = entityId;
}
/**
* Gets entity id.
*
* @return the entity id
*/
public UUID getEntityId() {
return entityId;
}
/**
* Sets entity id.
*
* @param entityId the entity id
*/
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
/**
* Gets passengers.
*
* @return the passengers
*/
public List<RidingRelationship> getPassengers() {
return Collections.unmodifiableList(passengers);
}
/**
* Sets passengers.
*
* @param passengers the passengers
*/
public void setPassengers(List<RidingRelationship> passengers) {
this.passengers = passengers != null ? passengers : new ArrayList<>();
}
/**
* Add passenger.
*
* @param passenger the passenger
*/
public void addPassenger(RidingRelationship passenger) {
this.passengers.add(passenger);
}
/**
* Gets vehicle id.
*
* @return the vehicle id
*/
public UUID getVehicleId() {
return vehicleId;
}
/**
* Sets vehicle id.
*
* @param vehicleId the vehicle id
*/
public void setVehicleId(UUID vehicleId) {
this.vehicleId = vehicleId;
}
/**
* 获取所有嵌套乘客的数量
*
* @return the total passenger count
*/
public int getTotalPassengerCount() {
int count = passengers.size();
@ -77,6 +124,9 @@ public class RidingRelationship {
/**
* 检查是否包含特定实体
*
* @param entityId the entity id
* @return the boolean
*/
public boolean containsEntity(UUID entityId) {
if (Objects.equals(this.entityId, entityId)) {

View File

@ -25,10 +25,16 @@ import top.r3944realms.lib39.util.lang.Pair;
import java.util.*;
import java.util.function.Function;
/**
* The type Riding saver.
*/
@SuppressWarnings("unused")
public class RidingSaver {
/**
* 保存骑乘关系
*
* @param entity the entity
* @return the riding relationship
*/
@Contract("null -> new")
public static @NotNull RidingRelationship save(@Nullable Entity entity) {
@ -37,6 +43,10 @@ public class RidingSaver {
/**
* 保存骑乘关系
*
* @param entity the entity
* @param findRoot the find root
* @return the riding relationship
*/
@Contract("null, _ -> new")
public static @NotNull RidingRelationship save(@Nullable Entity entity, boolean findRoot) {
@ -98,6 +108,11 @@ public class RidingSaver {
// 传入一个实体提供器 Function<UUID, Entity>通常在服务器侧就是 level::getEntity
private static Function<UUID, Entity> entityProvider;
/**
* Sets entity provider.
*
* @param provider the provider
*/
public static void setEntityProvider(Function<UUID, Entity> provider) {
entityProvider = provider;
}

View File

@ -17,11 +17,18 @@ package top.r3944realms.lib39.util.riding;
import com.google.gson.Gson;
/**
* The type Riding serializer.
*/
@SuppressWarnings("unused")
public class RidingSerializer {
private static final Gson GSON = new Gson();
/**
* 序列化骑乘关系
*
* @param relationship the relationship
* @return the string
*/
public static String serialize(RidingRelationship relationship) {
return GSON.toJson(relationship);
@ -29,6 +36,9 @@ public class RidingSerializer {
/**
* 反序列化骑乘关系
*
* @param json the json
* @return the riding relationship
*/
public static RidingRelationship deserialize(String json) {
return GSON.fromJson(json, RidingRelationship.class);

View File

@ -20,10 +20,17 @@ import net.minecraft.world.entity.Entity;
import java.util.LinkedList;
import java.util.Queue;
/**
* The type Riding validator.
*/
@SuppressWarnings("unused")
public class RidingValidator {
/**
* 检查骑乘是否会产生循环引用
*
* @param entity the entity
* @param vehicle the vehicle
* @return the boolean
*/
public static boolean wouldCreateCycle(Entity entity, Entity vehicle) {
// 如果实体就是载具本身直接产生循环
@ -37,6 +44,10 @@ public class RidingValidator {
/**
* 检查target是否是entity的间接乘客
*
* @param target the target
* @param entity the entity
* @return the boolean
*/
public static boolean isIndirectPassenger(Entity target, Entity entity) {
Queue<Entity> queue = new LinkedList<>();

View File

@ -4,16 +4,43 @@ package top.r3944realms.lib39.util.shape;
import com.mojang.math.Axis;
import org.joml.Quaternionf;
/**
* The type Quaternions.
*/
public final class Quaternions {
/**
* The constant XP_90.
*/
public static final Quaternionf XP_90 = Axis.XP.rotationDegrees(90);
/**
* The constant XP_180.
*/
public static final Quaternionf XP_180 = Axis.XP.rotationDegrees(180);
/**
* The constant XN_90.
*/
public static final Quaternionf XN_90 = Axis.XN.rotationDegrees(90);
/**
* The constant YP_90.
*/
public static final Quaternionf YP_90 = Axis.YP.rotationDegrees(90);
/**
* The constant YN_90.
*/
public static final Quaternionf YN_90 = Axis.YN.rotationDegrees(90);
/**
* The constant ZP_90.
*/
public static final Quaternionf ZP_90 = Axis.ZP.rotationDegrees(90);
/**
* The constant ZP_180.
*/
public static final Quaternionf ZP_180 = Axis.ZP.rotationDegrees(180);
/**
* The constant ZN_90.
*/
public static final Quaternionf ZN_90 = Axis.ZN.rotationDegrees(90);

View File

@ -4,10 +4,14 @@ import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* The type Shape util.
*/
@SuppressWarnings("unused")
public class ShapeUtil {
@ -15,6 +19,14 @@ public class ShapeUtil {
/**
* 创建基于像素的碰撞箱将像素坐标转换为方块坐标
*
* @param minX the min x
* @param minY the min y
* @param minZ the min z
* @param maxX the max x
* @param maxY the max y
* @param maxZ the max z
* @return the voxel shape
*/
public static @NotNull VoxelShape createPixelBasedShape(double minX, double minY, double minZ,
double maxX, double maxY, double maxZ) {
@ -24,6 +36,14 @@ public class ShapeUtil {
/**
* 便捷方法创建方块碰撞箱
*
* @param minX the min x
* @param minY the min y
* @param minZ the min z
* @param maxX the max x
* @param maxY the max y
* @param maxZ the max z
* @return the voxel shape
*/
public static @NotNull VoxelShape createBox(double minX, double minY, double minZ,
double maxX, double maxY, double maxZ) {
@ -34,15 +54,22 @@ public class ShapeUtil {
/**
* 创建单一形状的方向映射
*
* @param shape the shape
* @return the map
*/
public static Map<Direction, VoxelShape> createUniformDirectionMap(VoxelShape shape) {
public static @NotNull Map<Direction, VoxelShape> createUniformDirectionMap(VoxelShape shape) {
return createRotatedDirectionMap(shape);
}
/**
* 创建原版双方块的形状映射
*
* @param lowerShape the lower shape
* @param upperShape the upper shape
* @return the map
*/
public static Map<DoubleBlockHalf, Map<Direction, VoxelShape>> createDoubleBlockShapeMap(VoxelShape lowerShape, VoxelShape upperShape) {
public static @NotNull Map<DoubleBlockHalf, Map<Direction, VoxelShape>> createDoubleBlockShapeMap(VoxelShape lowerShape, VoxelShape upperShape) {
EnumMap<DoubleBlockHalf, Map<Direction, VoxelShape>> shapeMap = new EnumMap<>(DoubleBlockHalf.class);
shapeMap.put(DoubleBlockHalf.LOWER, createRotatedDirectionMap(lowerShape));
shapeMap.put(DoubleBlockHalf.UPPER, createRotatedDirectionMap(upperShape));
@ -53,6 +80,9 @@ public class ShapeUtil {
/**
* 顺时针旋转碰撞箱Y轴旋转
*
* @param shape the shape
* @return the voxel shape
*/
public static @NotNull VoxelShape rotateVoxelShapeClockwise(@NotNull VoxelShape shape) {
final List<VoxelShape> generatedShapes = new ArrayList<>();
@ -65,6 +95,9 @@ public class ShapeUtil {
/**
* 绕X轴旋转碰撞箱
*
* @param shape the shape
* @return the voxel shape
*/
public static @NotNull VoxelShape rotateVoxelShapeXAxis(@NotNull VoxelShape shape) {
final List<VoxelShape> generatedShapes = new ArrayList<>();
@ -77,6 +110,9 @@ public class ShapeUtil {
/**
* 绕Z轴旋转碰撞箱
*
* @param shape the shape
* @return the voxel shape
*/
@SuppressWarnings("SuspiciousNameCombination")
public static @NotNull VoxelShape rotateVoxelShapeZAxis(@NotNull VoxelShape shape) {
@ -90,6 +126,10 @@ public class ShapeUtil {
/**
* 按指定角度旋转碰撞箱
*
* @param shape the shape
* @param degrees the degrees
* @return the voxel shape
*/
public static @NotNull VoxelShape rotateShape(@NotNull VoxelShape shape, int degrees) {
int rotations = (degrees / 90) % 4;
@ -107,7 +147,7 @@ public class ShapeUtil {
* 组合多个形状列表
*/
@NotNull
private static VoxelShape combineShapes(List<VoxelShape> shapes) {
private static VoxelShape combineShapes(@NotNull List<VoxelShape> shapes) {
if (shapes.isEmpty()) {
return Shapes.block();
}
@ -122,6 +162,9 @@ public class ShapeUtil {
/**
* 组合多个形状
*
* @param shapes the shapes
* @return the voxel shape
*/
public static @NotNull VoxelShape combineShapes(VoxelShape... shapes) {
return combineShapes(Arrays.asList(shapes));
@ -132,7 +175,7 @@ public class ShapeUtil {
/**
* 创建旋转方向映射
*/
private static Map<Direction, VoxelShape> createRotatedDirectionMap(VoxelShape baseShape) {
private static @NotNull Map<Direction, VoxelShape> createRotatedDirectionMap(VoxelShape baseShape) {
EnumMap<Direction, VoxelShape> directionMap = new EnumMap<>(Direction.class);
directionMap.put(Direction.NORTH, baseShape);
directionMap.put(Direction.EAST, rotateShape(baseShape, 90));
@ -150,23 +193,56 @@ public class ShapeUtil {
public static class ShapeBuilder {
private final List<VoxelShape> shapes = new ArrayList<>();
/**
* Add pixel box shape builder.
*
* @param minX the min x
* @param minY the min y
* @param minZ the min z
* @param maxX the max x
* @param maxY the max y
* @param maxZ the max z
* @return the shape builder
*/
public ShapeBuilder addPixelBox(double minX, double minY, double minZ,
double maxX, double maxY, double maxZ) {
shapes.add(createPixelBasedShape(minX, minY, minZ, maxX, maxY, maxZ));
return this;
}
/**
* Add box shape builder.
*
* @param minX the min x
* @param minY the min y
* @param minZ the min z
* @param maxX the max x
* @param maxY the max y
* @param maxZ the max z
* @return the shape builder
*/
public ShapeBuilder addBox(double minX, double minY, double minZ,
double maxX, double maxY, double maxZ) {
shapes.add(createBox(minX, minY, minZ, maxX, maxY, maxZ));
return this;
}
/**
* Add shape shape builder.
*
* @param shape the shape
* @return the shape builder
*/
public ShapeBuilder addShape(VoxelShape shape) {
shapes.add(shape);
return this;
}
/**
* Build voxel shape.
*
* @return the voxel shape
*/
public VoxelShape build() {
return combineShapes(shapes);
}
@ -174,8 +250,11 @@ public class ShapeUtil {
/**
* 创建形状构建器
*
* @return the shape builder
*/
public static ShapeBuilder builder() {
@Contract(" -> new")
public static @NotNull ShapeBuilder builder() {
return new ShapeBuilder();
}
}

View File

@ -3,12 +3,22 @@ package top.r3944realms.lib39.util.sound;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.NotNull;
/**
* The type Sound util.
*/
public class SoundUtil {
/**
* 为实体播放声音
*
* @param entity the entity
* @param soundEvent the sound event
* @param soundCategory the sound category
* @param volume the volume
* @param pitch the pitch
*/
public static void playSoundForEntity(LivingEntity entity, SoundEvent soundEvent, SoundSource soundCategory, float volume, float pitch) {
public static void playSoundForEntity(@NotNull LivingEntity entity, SoundEvent soundEvent, SoundSource soundCategory, float volume, float pitch) {
entity.level().playSound(null, entity.getX(), entity.getY(), entity.getZ(), soundEvent, soundCategory, volume, pitch);
}
}