diff --git a/src/generated/resources/.cache/1de3d2ee724999f84a11b20b51c37030049be277 b/src/generated/resources/.cache/1de3d2ee724999f84a11b20b51c37030049be277 new file mode 100644 index 0000000..6acc8d5 --- /dev/null +++ b/src/generated/resources/.cache/1de3d2ee724999f84a11b20b51c37030049be277 @@ -0,0 +1,2 @@ +// 1.20.1 2025-10-25T19:14:21.1829335 Languages: zh_tw +4cb94c651f6aa74538a2ab25cb183cffd75be688 assets/lib39/lang/zh_tw.json diff --git a/src/generated/resources/.cache/2a65ee2815744be1ef1ffdae1c9a37f2a9cbe2ac b/src/generated/resources/.cache/2a65ee2815744be1ef1ffdae1c9a37f2a9cbe2ac new file mode 100644 index 0000000..d291622 --- /dev/null +++ b/src/generated/resources/.cache/2a65ee2815744be1ef1ffdae1c9a37f2a9cbe2ac @@ -0,0 +1,2 @@ +// 1.20.1 2025-10-25T19:14:21.1764262 Languages: zh_cn +a1db601a0fca923c5434b8b0843773a3115d0b59 assets/lib39/lang/zh_cn.json diff --git a/src/generated/resources/.cache/2dbf84d84cf6f7b7a95fea9038e192dbf226e5f5 b/src/generated/resources/.cache/2dbf84d84cf6f7b7a95fea9038e192dbf226e5f5 new file mode 100644 index 0000000..c17c4ef --- /dev/null +++ b/src/generated/resources/.cache/2dbf84d84cf6f7b7a95fea9038e192dbf226e5f5 @@ -0,0 +1,3 @@ +// 1.20.1 2025-10-25T18:42:29.3405259 Item Models: lib39 +14f581c8f8e7f0de004c57a180f371e60e7b12ae assets/lib39/models/item/fabric.json +447b36747d0aa8748dcd86715f4cce2cff19aca7 assets/lib39/models/item/neoforge.json diff --git a/src/generated/resources/.cache/82018c5420b46ddbb7071e62df09fdecd98133e6 b/src/generated/resources/.cache/82018c5420b46ddbb7071e62df09fdecd98133e6 new file mode 100644 index 0000000..b518cbb --- /dev/null +++ b/src/generated/resources/.cache/82018c5420b46ddbb7071e62df09fdecd98133e6 @@ -0,0 +1,2 @@ +// 1.20.1 2025-10-25T19:14:21.1804258 Languages: lzh +098df025475d3f4d9d2abefa91a7d38c44644ba4 assets/lib39/lang/lzh.json diff --git a/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 b/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 new file mode 100644 index 0000000..99da09b --- /dev/null +++ b/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 @@ -0,0 +1,2 @@ +// 1.20.1 2025-10-25T19:14:21.1784269 Languages: en_us +c6b6aadca0a922823a8c949ebde93f8f999737f9 assets/lib39/lang/en_us.json diff --git a/src/generated/resources/assets/lib39/lang/en_us.json b/src/generated/resources/assets/lib39/lang/en_us.json new file mode 100644 index 0000000..1989dd6 --- /dev/null +++ b/src/generated/resources/assets/lib39/lang/en_us.json @@ -0,0 +1,4 @@ +{ + "item.lib39.fabric": "Fabric", + "item.lib39.neoforge": "NeoForge" +} \ No newline at end of file diff --git a/src/generated/resources/assets/lib39/lang/lzh.json b/src/generated/resources/assets/lib39/lang/lzh.json new file mode 100644 index 0000000..865d7b3 --- /dev/null +++ b/src/generated/resources/assets/lib39/lang/lzh.json @@ -0,0 +1,4 @@ +{ + "item.lib39.fabric": "織", + "item.lib39.neoforge": "狸" +} \ No newline at end of file diff --git a/src/generated/resources/assets/lib39/lang/zh_cn.json b/src/generated/resources/assets/lib39/lang/zh_cn.json new file mode 100644 index 0000000..ad3ca87 --- /dev/null +++ b/src/generated/resources/assets/lib39/lang/zh_cn.json @@ -0,0 +1,4 @@ +{ + "item.lib39.fabric": "织布", + "item.lib39.neoforge": "小狐狸" +} \ No newline at end of file diff --git a/src/generated/resources/assets/lib39/lang/zh_tw.json b/src/generated/resources/assets/lib39/lang/zh_tw.json new file mode 100644 index 0000000..e14a166 --- /dev/null +++ b/src/generated/resources/assets/lib39/lang/zh_tw.json @@ -0,0 +1,4 @@ +{ + "item.lib39.fabric": "織布", + "item.lib39.neoforge": "狐狸" +} \ No newline at end of file diff --git a/src/generated/resources/assets/lib39/models/item/fabric.json b/src/generated/resources/assets/lib39/models/item/fabric.json new file mode 100644 index 0000000..828b259 --- /dev/null +++ b/src/generated/resources/assets/lib39/models/item/fabric.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "lib39:item/fabric" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/lib39/models/item/neoforge.json b/src/generated/resources/assets/lib39/models/item/neoforge.json new file mode 100644 index 0000000..c0446b5 --- /dev/null +++ b/src/generated/resources/assets/lib39/models/item/neoforge.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "lib39:item/neoforge" + } +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/lib39/Lib39.java b/src/main/java/top/r3944realms/lib39/Lib39.java index 8658e95..e294953 100644 --- a/src/main/java/top/r3944realms/lib39/Lib39.java +++ b/src/main/java/top/r3944realms/lib39/Lib39.java @@ -2,9 +2,11 @@ package top.r3944realms.lib39; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.loading.FMLEnvironment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import top.r3944realms.lib39.core.network.NetworkHandler; +import top.r3944realms.lib39.example.Lib39Example; /** * The type Lib 39. @@ -33,6 +35,10 @@ public class Lib39 { public static void initialize() { LOGGER.info("[Lib39] Initializing Lib39"); NetworkHandler.register(); + if (shouldRegisterExamples()) { + LOGGER.info("[Lib39] Registering Examples"); + registerExamples(); + } LOGGER.info("[Lib39] Initialized Lib39"); } @@ -53,4 +59,29 @@ public class Lib39 { .orElse("UNKNOWN"); } } + + /** + * Should register examples boolean. + * + * @return the boolean + */ + static boolean shouldRegisterExamples() { + return !FMLEnvironment.production; + } + + /** + * Register examples. + */ + static void registerExamples() { + LOGGER.info("[Lib39] Starting example demonstrations"); + try { + // 创建示例实例并演示功能 + Lib39Example example = new Lib39Example(); + example.demonstrateFeature(); + + LOGGER.info("[Lib39] Example demonstrations completed successfully"); + } catch (Exception e) { + LOGGER.error("[Lib39] Failed to demonstrate examples", e); + } + } } diff --git a/src/main/java/top/r3944realms/lib39/core/compat/CompatManager.java b/src/main/java/top/r3944realms/lib39/core/compat/CompatManager.java index 952e9f8..6bede5d 100644 --- a/src/main/java/top/r3944realms/lib39/core/compat/CompatManager.java +++ b/src/main/java/top/r3944realms/lib39/core/compat/CompatManager.java @@ -210,16 +210,6 @@ public class CompatManager { } } - // ===================== 便捷方法 ===================== - - /** - * Add listener for all. - * - * @param bus the bus - */ - public void addListenerForAll(Mod.EventBusSubscriber.Bus bus) { - addListenerForAll(null, bus); - } /** * Add listener for compat. diff --git a/src/main/java/top/r3944realms/lib39/core/compat/ICompat.java b/src/main/java/top/r3944realms/lib39/core/compat/ICompat.java index 90fd8c8..467bfa6 100644 --- a/src/main/java/top/r3944realms/lib39/core/compat/ICompat.java +++ b/src/main/java/top/r3944realms/lib39/core/compat/ICompat.java @@ -4,7 +4,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraftforge.eventbus.api.IEventBus; import java.util.concurrent.Callable; -import java.util.function.Consumer; /** * The interface Compat. diff --git a/src/main/java/top/r3944realms/lib39/core/event/ClientEventHandler.java b/src/main/java/top/r3944realms/lib39/core/event/ClientEventHandler.java index 847720e..f148493 100644 --- a/src/main/java/top/r3944realms/lib39/core/event/ClientEventHandler.java +++ b/src/main/java/top/r3944realms/lib39/core/event/ClientEventHandler.java @@ -4,4 +4,13 @@ package top.r3944realms.lib39.core.event; * The type Client handler. */ public class ClientEventHandler { + /** + * The type Mod. + */ + public static class Mod extends ClientEventHandler {} + + /** + * The type Game. + */ + public static class Game extends ClientEventHandler {} } diff --git a/src/main/java/top/r3944realms/lib39/core/event/CommonEventHandler.java b/src/main/java/top/r3944realms/lib39/core/event/CommonEventHandler.java index 64748af..a4951fa 100644 --- a/src/main/java/top/r3944realms/lib39/core/event/CommonEventHandler.java +++ b/src/main/java/top/r3944realms/lib39/core/event/CommonEventHandler.java @@ -21,8 +21,8 @@ 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.ISyncData; import top.r3944realms.lib39.core.sync.SyncData2Manager; import java.util.ArrayList; @@ -111,7 +111,7 @@ public class CommonEventHandler { if (event.phase == TickEvent.Phase.END) { if (syncData2Manager == null) return; if (event.getServer().getTickCount() % 10 == 0) - syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::makeDirty))); + syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::markDirty))); syncData2Manager.forEach(((resourceLocation, iSyncManager) -> iSyncManager.foreach(ISyncData::checkIfDirtyThenUpdate))); } } diff --git a/src/main/java/top/r3944realms/lib39/core/event/ServerEventHandler.java b/src/main/java/top/r3944realms/lib39/core/event/ServerEventHandler.java index 1f36852..54ab70d 100644 --- a/src/main/java/top/r3944realms/lib39/core/event/ServerEventHandler.java +++ b/src/main/java/top/r3944realms/lib39/core/event/ServerEventHandler.java @@ -4,4 +4,13 @@ package top.r3944realms.lib39.core.event; * The type Server handler. */ public class ServerEventHandler { + /** + * The type Mod. + */ + public static class Mod extends ServerEventHandler {} + + /** + * The type Game. + */ + public static class Game extends ServerEventHandler {} } diff --git a/src/main/java/top/r3944realms/lib39/core/network/toClient/SyncNBTCapDataEntityS2CPack.java b/src/main/java/top/r3944realms/lib39/core/network/toClient/SyncNBTCapDataEntityS2CPack.java index aa02b52..d841474 100644 --- a/src/main/java/top/r3944realms/lib39/core/network/toClient/SyncNBTCapDataEntityS2CPack.java +++ b/src/main/java/top/r3944realms/lib39/core/network/toClient/SyncNBTCapDataEntityS2CPack.java @@ -6,7 +6,6 @@ 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; diff --git a/src/main/java/top/r3944realms/lib39/core/sync/ISyncData.java b/src/main/java/top/r3944realms/lib39/core/sync/ISyncData.java index 4960272..30f82b5 100644 --- a/src/main/java/top/r3944realms/lib39/core/sync/ISyncData.java +++ b/src/main/java/top/r3944realms/lib39/core/sync/ISyncData.java @@ -30,9 +30,9 @@ public interface ISyncData { void setDirty(boolean dirty); /** - * Make dirty. + * Mark dirty. */ - default void makeDirty() { + default void markDirty() { setDirty(true); } diff --git a/src/main/java/top/r3944realms/lib39/core/sync/NBTEntitySyncData.java b/src/main/java/top/r3944realms/lib39/core/sync/NBTEntitySyncData.java index 859a4a6..73f3470 100644 --- a/src/main/java/top/r3944realms/lib39/core/sync/NBTEntitySyncData.java +++ b/src/main/java/top/r3944realms/lib39/core/sync/NBTEntitySyncData.java @@ -54,5 +54,6 @@ public abstract class NBTEntitySyncData implements IEntity, ISyncData lanKeyMap; - private static final List objects = new ArrayList<>(); + private final ILangKeyValueCollection langKeyValueCollection; + private final Map translationMap; // Better naming + private final List orderedKeys; // Better naming than "objects" /** * 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 + * @param output the output + * @param modId the mod id + * @param language the language + * @param langKeyValueCollection the lang key value collection */ - public SimpleLanguageProvider(PackOutput output, String modId, @NotNull McLocale Lan, ILangKeyValue langKeyValue) { - super(output, modId, Lan.mcCode()); - this.language = Lan; - this.langKeyValue = langKeyValue; - lanKeyMap = new HashMap<>(); - init(); + public SimpleLanguageProvider(PackOutput output, String modId, + @NotNull McLocale language, + ILangKeyValueCollection langKeyValueCollection) { + super(output, modId, language.mcCode()); + this.language = language; + this.langKeyValueCollection = langKeyValueCollection; + this.translationMap = new HashMap<>(); + this.orderedKeys = new ArrayList<>(); + initializeTranslations(); } - private void init() { - for (ILangKeyValue iLangKeyValue : langKeyValue.getValues()) { - lanKeyMap.put(language.mcCode(), iLangKeyValue.getLang(language)); - } - } - private void addLang(String Key, String value) { - if (!objects.contains(Key)) objects.add(Key); - lanKeyMap.put(Key, value); + + private void initializeTranslations() { + for (ILangKeyValue langKeyValue : langKeyValueCollection.getValues()) { + String key = langKeyValue.getKey(); + String value = langKeyValue.getLang(language); + + if (!translationMap.containsKey(key)) { + orderedKeys.add(key); + } + translationMap.put(key, value); + } } @Override protected void addTranslations() { - objects.forEach(key -> add(key, lanKeyMap.get(key))); + orderedKeys.forEach(key -> add(key, translationMap.get(key))); + validateTranslations(); + } + + private void validateTranslations() { + long addedCount = orderedKeys.stream() + .filter(translationMap::containsKey) + .count(); + + LOGGER.info("Added {}/{} translations for {}", + addedCount, orderedKeys.size(), language.mcCode()); } } diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValue.java b/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValue.java index 93da8f9..241aaa1 100644 --- a/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValue.java +++ b/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValue.java @@ -1,23 +1,15 @@ package top.r3944realms.lib39.datagen.value; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - /** * The interface Lang key value. */ public interface ILangKeyValue { /** - * Gets lang. + * Gets key. * - * @param locale the locale - * @param key the key - * @return the lang + * @return the key */ - static String getLang(McLocale locale, @NotNull ILangKeyValue key) { - return key.getLang(locale); - } + String getKey(); /** * Gets lang. @@ -27,10 +19,4 @@ public interface ILangKeyValue { */ String getLang(McLocale locale); - /** - * Gets values. - * - * @return the values - */ - List getValues(); } diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValueCollection.java b/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValueCollection.java new file mode 100644 index 0000000..4785e4d --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValueCollection.java @@ -0,0 +1,30 @@ +package top.r3944realms.lib39.datagen.value; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * The interface Lang key value collection. + */ +public interface ILangKeyValueCollection { + + /** + * Gets values. + * + * @return the values + */ + List getValues(); + + /** + * Gets lang. + * + * @param locale the locale + * @param key the key + * @return the lang + */ + static String getLang(McLocale locale, @NotNull ILangKeyValue key) { + return key.getLang(locale); + } + +} diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java b/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java index aeb05b2..30e447d 100644 --- a/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java +++ b/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java @@ -1,9 +1,11 @@ package top.r3944realms.lib39.datagen.value; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import java.util.List; +import java.util.Objects; import java.util.function.Supplier; /** @@ -18,7 +20,7 @@ public class LangKeyValue implements ILangKeyValue { /** * The Key. */ - protected String key; + protected final String key; /** * The Us en. */ @@ -43,18 +45,197 @@ public class LangKeyValue implements ILangKeyValue { * 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) { - this.supplier = supplier; - this.key = key; - this.MPE = MPE; - this.US_EN = US_EN; - this.SIM_CN = SIM_CN; - this.TRA_CN = TRA_CN; - this.LZH = LZH; - this.Default = isDefault; + + /** + * Instantiates a new Lang key value. + * + * @param builder the builder + */ + protected LangKeyValue(Builder builder) { + this.supplier = builder.supplier; + this.key = builder.key; + this.MPE = builder.MPE; + this.US_EN = builder.US_EN; + this.SIM_CN = builder.SIM_CN; + this.TRA_CN = builder.TRA_CN; + this.LZH = builder.LZH; + this.Default = builder.Default; } + /** + * Builder for LangKeyValue + */ + public static class Builder { + private Supplier supplier; + private String key; + private ModPartEnum MPE; + private String US_EN; + private String SIM_CN; + private String TRA_CN; + private String LZH; + private Boolean Default = false; + + /** + * Set supplier + * + * @param supplier the supplier + * @return the builder + */ + @Contract("_ -> this") + public Builder supplier(Supplier supplier) { + this.supplier = supplier; + return this; + } + + /** + * Set key + * + * @param key the key + * @return the builder + */ + @Contract("_ -> this") + public Builder key(String key) { + this.key = key; + return this; + } + + /** + * Set mod part enum + * + * @param MPE the mpe + * @return the builder + */ + @Contract("_ -> this") + public Builder MPE(ModPartEnum MPE) { + this.MPE = MPE; + return this; + } + + /** + * Set US English translation + * + * @param US_EN the us en + * @return the builder + */ + @Contract("_ -> this") + public Builder US_EN(String US_EN) { + this.US_EN = US_EN; + return this; + } + + /** + * Set Simplified Chinese translation + * + * @param SIM_CN the sim cn + * @return the builder + */ + @Contract("_ -> this") + public Builder SIM_CN(String SIM_CN) { + this.SIM_CN = SIM_CN; + return this; + } + + /** + * Set Traditional Chinese translation + * + * @param TRA_CN the tra cn + * @return the builder + */ + @Contract("_ -> this") + public Builder TRA_CN(String TRA_CN) { + this.TRA_CN = TRA_CN; + return this; + } + + /** + * Set Literary Chinese translation + * + * @param LZH the lzh + * @return the builder + */ + @Contract("_ -> this") + public Builder LZH(String LZH) { + this.LZH = LZH; + return this; + } + + /** + * Set as default + * + * @param isDefault the is default + * @return the builder + */ + @Contract("_ -> this") + public Builder isDefault(Boolean isDefault) { + this.Default = isDefault; + return this; + } + + /** + * Build the LangKeyValue instance + * + * @return the lang key value + */ + @NotNull + public LangKeyValue build() { + // Validate required fields + if (MPE == null) { + throw new IllegalStateException("MPE (ModPartEnum) is required"); + } + if (US_EN == null) { + throw new IllegalStateException("US_EN translation is required"); + } + if (SIM_CN == null) { + throw new IllegalStateException("SIM_CN translation is required"); + } + if (TRA_CN == null) { + throw new IllegalStateException("TRA_CN translation is required"); + } + // Either supplier or key must be provided, but not both + if (supplier == null && key == null) { + throw new IllegalStateException("Either supplier or key must be provided"); + } + if (supplier != null && key != null) { + throw new IllegalStateException("Cannot provide both supplier and key"); + } + return new LangKeyValue(this); + } + } + + /** + * Create a new builder instance + * + * @return the builder + */ + @Contract(" -> new") + public static @NotNull Builder builder() { + return new Builder(); + } + + /** + * Create builder with supplier + * + * @param supplier the supplier + * @return the builder + */ + @Contract("_ -> new") + public static @NotNull Builder withSupplier(Supplier supplier) { + return new Builder().supplier(supplier); + } + + /** + * Create builder with key + * + * @param key the key + * @return the builder + */ + @Contract("_ -> new") + public static @NotNull Builder withKey(String key) { + return new Builder().key(key); + } + + // 保持原有的静态工厂方法作为便捷方法 + /** * Of supplier lang key value. * @@ -68,7 +249,13 @@ public class LangKeyValue implements ILangKeyValue { @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); + return builder() + .supplier(supplier) + .MPE(MPE) + .US_EN(US_EN) + .SIM_CN(SIM_CN) + .TRA_CN(TRA_CN) + .build(); } /** @@ -85,7 +272,40 @@ public class LangKeyValue implements ILangKeyValue { @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); + return builder() + .supplier(supplier) + .MPE(MPE) + .US_EN(US_EN) + .SIM_CN(SIM_CN) + .TRA_CN(TRA_CN) + .LZH(LZH) + .build(); + } + + /** + * Of supplier lang key value. + * + * @param supplier the supplier + * @param MPE the mpe + * @param US_EN the us en + * @param SIM_CN the sim cn + * @param TRA_CN the tra cn + * @param LZH the lzh + * @param isDefault the is default + * @return the lang key value + */ + @Contract(value = "_, _, _, _, _, _, _ -> new", pure = true) + public static @NotNull LangKeyValue ofSupplier(Supplier supplier, ModPartEnum MPE, + String US_EN, String SIM_CN, String TRA_CN, String LZH, boolean isDefault) { + return builder() + .supplier(supplier) + .MPE(MPE) + .US_EN(US_EN) + .SIM_CN(SIM_CN) + .TRA_CN(TRA_CN) + .LZH(LZH) + .isDefault(isDefault) + .build(); } /** @@ -102,7 +322,14 @@ public class LangKeyValue implements ILangKeyValue { @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); + return builder() + .supplier(supplier) + .MPE(MPE) + .US_EN(US_EN) + .SIM_CN(SIM_CN) + .TRA_CN(TRA_CN) + .isDefault(isDefault) + .build(); } /** @@ -118,7 +345,13 @@ public class LangKeyValue implements ILangKeyValue { @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); + return builder() + .key(key) + .MPE(MPE) + .US_EN(US_EN) + .SIM_CN(SIM_CN) + .TRA_CN(TRA_CN) + .build(); } /** @@ -135,7 +368,14 @@ public class LangKeyValue implements ILangKeyValue { @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); + return builder() + .key(key) + .MPE(MPE) + .US_EN(US_EN) + .SIM_CN(SIM_CN) + .TRA_CN(TRA_CN) + .LZH(LZH) + .build(); } /** @@ -152,29 +392,119 @@ public class LangKeyValue implements ILangKeyValue { @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); + return builder() + .key(key) + .MPE(MPE) + .US_EN(US_EN) + .SIM_CN(SIM_CN) + .TRA_CN(TRA_CN) + .isDefault(isDefault) + .build(); } - /** - * Gets key. - * - * @return the key - */ + @Override public String getKey() { - return key; + return Objects.requireNonNullElseGet(key, () -> switch (MPE) { + case ITEM -> getItem().getDescriptionId(); + case BLOCK -> getBlock().getDescriptionId(); + default -> + throw new UnsupportedOperationException("The Key value is NULL! Please use the correct constructor and write the parameters correctly"); + }); } + @Override public String getLang(@NotNull McLocale locale) { return switch (locale) { case EN_US, JA_JP, KO_KR, RU_RU, DE_DE, ES_ES, FR_FR -> US_EN; case ZH_CN -> SIM_CN; case ZH_TW -> TRA_CN; - case LZH -> LZH; + case LZH -> LZH != null ? LZH : TRA_CN; // Fallback to TRA_CN if LZH is null }; } - @Override - public List getValues() { - return List.of(); + + /** + * Gets supplier. + * + * @return the supplier + */ +// Getters for all fields + public Supplier getSupplier() { return supplier; } + + /** + * Gets us en. + * + * @return the us en + */ + public String getUS_EN() { return US_EN; } + + /** + * Gets sim cn. + * + * @return the sim cn + */ + public String getSIM_CN() { return SIM_CN; } + + /** + * Gets tra cn. + * + * @return the tra cn + */ + public String getTRA_CN() { return TRA_CN; } + + /** + * Gets lzh. + * + * @return the lzh + */ + public String getLZH() { return LZH; } + + /** + * Is default boolean. + * + * @return the boolean + */ + public Boolean isDefault() { return Default; } + + /** + * Gets mpe. + * + * @return the mpe + */ + public ModPartEnum getMPE() { return MPE; } + + /** + * Gets item. + * + * @return the item + * @throws IllegalArgumentException the illegal argument exception + */ + public Item getItem() throws IllegalArgumentException { + if(MPE == ModPartEnum.ITEM) { + return (Item) supplier.get(); + } + else throw new IllegalCallerException("Target's MPE is not ModPartEnum#ITEM."); } -} + + /** + * Gets block. + * + * @return the block + * @throws IllegalArgumentException the illegal argument exception + */ + public Block getBlock() throws IllegalArgumentException { + if(MPE == ModPartEnum.BLOCK) { + return (Block) supplier.get(); + } + else throw new IllegalCallerException("Target's MPE is not ModPartEnum#BLOCK."); + } + @Override + public String toString() { + return "LangKeyValue{" + + "key='" + key + '\'' + + ", US_EN='" + US_EN + '\'' + + ", SIM_CN='" + SIM_CN + '\'' + + ", MPE=" + MPE + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/ModPartEnum.java b/src/main/java/top/r3944realms/lib39/datagen/value/ModPartEnum.java index cefc189..bac61ce 100644 --- a/src/main/java/top/r3944realms/lib39/datagen/value/ModPartEnum.java +++ b/src/main/java/top/r3944realms/lib39/datagen/value/ModPartEnum.java @@ -1,7 +1,8 @@ package top.r3944realms.lib39.datagen.value; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import org.jetbrains.annotations.Nullable; /** * 模组各部分的类型枚举,用于数据生成与分类。 @@ -15,12 +16,12 @@ public enum ModPartEnum { /** * 物品 */ - ITEM, + ITEM(Item.class), /** * 方块 */ - BLOCK, + BLOCK(Block.class), /** * 附魔 @@ -28,9 +29,14 @@ public enum ModPartEnum { ENCHANTMENT, /** - * 成就 / 进度 + * 进度标题 */ - ADVANCEMENT, + ADVANCEMENT_TITLE, + + /** + * 成就描述 + */ + ADVANCEMENT_DESCRIPTION, /** * 创造模式物品栏 @@ -51,11 +57,14 @@ public enum ModPartEnum { * 图形界面 */ GUI, - /** - * 作者信息 + * 画作描述 */ - AUTHOR, + PAINTING_TITLE, + /** + * 画作作者 + */ + PAINTING_AUTHOR, /** * 标题 @@ -87,6 +96,11 @@ public enum ModPartEnum { */ MESSAGE, + /** + * 生物群系 + */ + BIOME, + /** * 命令 */ @@ -96,46 +110,56 @@ public enum ModPartEnum { * 声音资源 */ SOUND; + ; + @Nullable + private final Class clazz; + ModPartEnum() { + clazz = null; + } + ModPartEnum(@Nullable Class clazz) { + this.clazz = clazz; + } /** - * 根据枚举类型生成标准化 key 前缀 - * 例如 ITEM -> "item.", BLOCK -> "block." + * Gets full key. * - * @return the key prefix + * @param modId the mod id + * @param name the name + * @return the full key */ - @Contract(pure = true) - public @NotNull String getKeyPrefix() { + public String getFullKey(String modId, String name) { return switch (this) { - case ITEM -> "item."; - case BLOCK -> "block."; + case ITEM -> "item." + modId + "." + name; + case BLOCK -> "block." + modId + "." + name; case ENCHANTMENT -> "enchantment."; - case ADVANCEMENT -> "advancement."; - case CREATIVE_TAB -> "creative_tab."; - case CONFIG -> "config."; - case ENTITY -> "entity."; - case GUI -> "gui."; - case AUTHOR -> "author."; - case TITLE -> "title."; - case NAME -> "name."; + case ADVANCEMENT_TITLE -> "advancement." + modId + "." + name + ".title"; + case ADVANCEMENT_DESCRIPTION -> "advancement." + modId + "." + name + ".description"; + case CREATIVE_TAB -> "creativetab." + modId + "." + name; + case BIOME -> "biome." + modId + "." + name; + case CONFIG -> "config." + modId + "." + name; + case ENTITY -> "entity." + modId + "." + name; + case GUI -> "gui." + modId + "." + name; + case PAINTING_AUTHOR -> "painting." + modId + "." + name + ".author"; + case PAINTING_TITLE -> "painting." + modId + "." + name + ".title"; + case TITLE -> "title." + modId + "." + name; + case NAME -> "name." + modId + "." + name; case GAME_RULE -> "gamerule."; case DESCRIPTION -> "description."; - case INFO -> "info."; - case MESSAGE -> "message."; - case COMMAND -> "command."; - case SOUND -> "sound."; - default -> ""; + case INFO -> "info." + modId + "." + name; + case MESSAGE -> "message." + modId + "." + name; + case COMMAND -> "command." + modId + "." + name; + case SOUND -> "sound." + modId + "." + name; + default -> modId + name; }; } + /** - * 根据枚举类型和具体名称生成完整 key - * 例如 ITEM + "example_item" -> "item.example_item" + * Gets clazz. * - * @param name the name - * @return the full key + * @return the clazz */ - @Contract(pure = true) - public @NotNull String getFullKey(String name) { - return getKeyPrefix() + name; + public @Nullable Class getClazz() { + return clazz; } } diff --git a/src/main/java/top/r3944realms/lib39/example/Lib39Example.java b/src/main/java/top/r3944realms/lib39/example/Lib39Example.java new file mode 100644 index 0000000..78c6902 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/Lib39Example.java @@ -0,0 +1,56 @@ +package top.r3944realms.lib39.example; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import top.r3944realms.lib39.example.core.event.ExCommonEventHandler; +import top.r3944realms.lib39.example.core.network.ExNetworkHandler; +import top.r3944realms.lib39.example.core.register.ExLib39Items; + +/** + * The type Lib 39 example. + */ +public class Lib39Example { + private static boolean registered = false; + + /** + * Instantiates a new Lib 39 example. + */ + public Lib39Example() { + if (!registered) { + init(); + registerToEventBus(); + registered = true; + } + } + private void init() { + IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + ExLib39Items.register(modEventBus); + ExNetworkHandler.register(); + } + + private void registerToEventBus() { + IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus(); + IEventBus gameBus = MinecraftForge.EVENT_BUS; +// DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> { +// modBus.register(ExClientEventHandler.Mod.class); +// gameBus.register(ExClientEventHandler.Game.class); +// return null; +// }); +// DistExecutor.unsafeCallWhenOn(Dist.DEDICATED_SERVER, () -> { +// modBus.register(ExServerEventHandler.Mod.class); +// gameBus.register(ExServerEventHandler.Game.class); +// return null; +// }); + modBus.register(ExCommonEventHandler.Mod.class); + gameBus.register(ExCommonEventHandler.Game.class); + + } + + /** + * Demonstrate feature. + */ + public void demonstrateFeature() { + + } +} diff --git a/src/main/java/top/r3944realms/lib39/example/content/capability/AbstractedTestSyncData.java b/src/main/java/top/r3944realms/lib39/example/content/capability/AbstractedTestSyncData.java new file mode 100644 index 0000000..2dc49ad --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/content/capability/AbstractedTestSyncData.java @@ -0,0 +1,207 @@ +package top.r3944realms.lib39.example.content.capability; + +import net.minecraft.resources.ResourceLocation; +import top.r3944realms.lib39.core.sync.NBTEntitySyncData; + +/** + * The type Abstracted test sync data. + */ +@SuppressWarnings("unused") +public abstract class AbstractedTestSyncData extends NBTEntitySyncData { + /** + * Instantiates a new Nbt sync data. + * + * @param id the id + */ + protected AbstractedTestSyncData(ResourceLocation id) { + super(id); + } + + /** + * Gets test string. + * + * @return the test string + */ + abstract String getTestString(); + + /** + * Sets test string. + * + * @param value the value + */ + abstract void setTestString(String value); + + /** + * Gets test int. + * + * @return the test int + */ + abstract int getTestInt(); + + /** + * Sets test int. + * + * @param value the value + */ + abstract void setTestInt(int value); + + /** + * Is test boolean boolean. + * + * @return the boolean + */ + abstract boolean isTestBoolean(); + + /** + * Sets test boolean. + * + * @param value the value + */ + abstract void setTestBoolean(boolean value); + + /** + * Gets test double. + * + * @return the test double + */ + abstract double getTestDouble(); + + /** + * Sets test double. + * + * @param value the value + */ + abstract void setTestDouble(double value); + + /** + * Gets counter. + * + * @return the counter + */ +// 计数器,用于测试数据变化 + abstract int getCounter(); + + /** + * Increment counter. + */ + abstract void incrementCounter(); + + /** + * Gets last sync time. + * + * @return the last sync time + */ +// 时间戳,用于测试同步时机 + abstract long getLastSyncTime(); + + /** + * Update sync time. + */ + abstract void updateSyncTime(); + + /** + * Gets custom data. + * + * @return the custom data + */ +// 自定义对象测试 + abstract TestData getCustomData(); + + /** + * Sets custom data. + * + * @param data the data + */ + abstract void setCustomData(TestData data); + + /** + * Validate data boolean. + * + * @return the boolean + */ +// 验证数据完整性 + abstract boolean validateData(); + + /** + * 测试数据对象 + */ + public static class TestData { + private String name; + private int value; + private boolean flag; + + /** + * Instantiates a new Test data. + */ + public TestData() {} + + /** + * Instantiates a new Test data. + * + * @param name the name + * @param value the value + * @param flag the flag + */ + public TestData(String name, int value, boolean flag) { + this.name = name; + this.value = value; + this.flag = flag; + } + + /** + * Gets name. + * + * @return the name + */ +// getters and setters + public String getName() { return name; } + + /** + * Sets name. + * + * @param name the name + */ + public void setName(String name) { this.name = name; } + + /** + * Gets value. + * + * @return the value + */ + public int getValue() { return value; } + + /** + * Sets value. + * + * @param value the value + */ + public void setValue(int value) { this.value = value; } + + /** + * Is flag boolean. + * + * @return the boolean + */ + public boolean isFlag() { return flag; } + + /** + * Sets flag. + * + * @param flag the flag + */ + public void setFlag(boolean flag) { this.flag = flag; } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof TestData other)) return false; + return value == other.value && flag == other.flag && + java.util.Objects.equals(name, other.name); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(name, value, flag); + } + } +} diff --git a/src/main/java/top/r3944realms/lib39/example/content/capability/ExCapabilityHandler.java b/src/main/java/top/r3944realms/lib39/example/content/capability/ExCapabilityHandler.java new file mode 100644 index 0000000..dbdffe4 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/content/capability/ExCapabilityHandler.java @@ -0,0 +1,40 @@ +package top.r3944realms.lib39.example.content.capability; + +import net.minecraft.world.entity.Entity; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.common.capabilities.CapabilityToken; +import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import org.jetbrains.annotations.NotNull; + +/** + * The type Ex capability handler. + */ +public class ExCapabilityHandler { + /** + * The constant TEST_CAP. + */ + public static final Capability TEST_CAP = CapabilityManager.get(new CapabilityToken<>() {}); + + /** + * Register capability. + * + * @param event the event + */ + public static void registerCapability(@NotNull RegisterCapabilitiesEvent event) { + event.register(AbstractedTestSyncData.class); + } + + /** + * Attach capability. + * + * @param event the event + */ + public static void attachCapability(@NotNull AttachCapabilitiesEvent event) { + Object object = event.getObject(); + if(object instanceof Entity entity ) { + event.addCapability(TestSyncCapProvider.TEST_SYNC_REL, new TestSyncCapProvider(entity)); + } + } +} diff --git a/src/main/java/top/r3944realms/lib39/example/content/capability/TestSyncCapProvider.java b/src/main/java/top/r3944realms/lib39/example/content/capability/TestSyncCapProvider.java new file mode 100644 index 0000000..f500642 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/content/capability/TestSyncCapProvider.java @@ -0,0 +1,49 @@ +package top.r3944realms.lib39.example.content.capability; + +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import net.minecraftforge.common.util.LazyOptional; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.r3944realms.lib39.Lib39; + +/** + * The type Test sync cap provider. + */ +public class TestSyncCapProvider implements ICapabilitySerializable { + + /** + * The constant TEST_SYNC_REL. + */ + public static final ResourceLocation TEST_SYNC_REL = new ResourceLocation(Lib39.MOD_ID, "test_sync_data"); + private final AbstractedTestSyncData instance; + private final LazyOptional optional; + + /** + * Instantiates a new Test sync cap provider. + * + * @param entity the entity + */ + public TestSyncCapProvider(Entity entity) { + this.instance = new TestSyncData(entity); + this.optional = LazyOptional.of(() -> instance); + } + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { + return ExCapabilityHandler.TEST_CAP.orEmpty(cap, optional); + } + + @Override + public CompoundTag serializeNBT() { + return instance.serializeNBT(); + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + instance.deserializeNBT(nbt); + } +} diff --git a/src/main/java/top/r3944realms/lib39/example/content/capability/TestSyncData.java b/src/main/java/top/r3944realms/lib39/example/content/capability/TestSyncData.java new file mode 100644 index 0000000..bbba0d9 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/content/capability/TestSyncData.java @@ -0,0 +1,376 @@ +package top.r3944realms.lib39.example.content.capability; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.NotNull; +import top.r3944realms.lib39.Lib39; +import top.r3944realms.lib39.util.nbt.NBTReader; +import top.r3944realms.lib39.util.nbt.NBTWriter; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 测试同步数据实现 + */ +@SuppressWarnings("unused") +public class TestSyncData extends AbstractedTestSyncData { + /** + * The constant ID. + */ + public static final ResourceLocation ID = new ResourceLocation(Lib39.MOD_ID, "test_sync_data"); + + // NBT 键常量 + private static final String NBT_KEY_STRING = "test_string"; + private static final String NBT_KEY_INT = "test_int"; + private static final String NBT_KEY_BOOLEAN = "test_boolean"; + private static final String NBT_KEY_DOUBLE = "test_double"; + private static final String NBT_KEY_COUNTER = "counter"; + private static final String NBT_KEY_SYNC_TIME = "last_sync_time"; + private static final String NBT_KEY_CUSTOM_DATA = "custom_data"; + private static final String NBT_KEY_CUSTOM_NAME = "name"; + private static final String NBT_KEY_CUSTOM_VALUE = "value"; + private static final String NBT_KEY_CUSTOM_FLAG = "flag"; + + // 数据字段 + private String testString = "default_value"; + private int testInt = 42; + private boolean testBoolean = true; + private double testDouble = 3.14159; + private int counter = 0; + private long lastSyncTime = 0L; + private TestData customData = new TestData("default", 100, false); + private Entity self; + + /** + * 构造函数 + * + * @param entity 关联的实体 + */ + public TestSyncData(Entity entity) { + super(ID); + this.self = entity; + } + + /** + * 构造函数(用于测试) + * + * @param entityId 实体ID + * @param self the self + */ + public TestSyncData(int entityId, Entity self) { + super(ID); + this.self = self; + } + + /** + * 构造函数(用于数据包反序列化) + * + * @param buf 字节缓冲区 + */ + public TestSyncData(FriendlyByteBuf buf) { + super(ID); + this.self = null; // 实体在从数据包重建时可能为null,需要在接收端设置 + fromBytes(buf); + } + + /** + * 将数据写入字节缓冲区(用于网络传输) + * + * @param buf 字节缓冲区 + */ + public void toBytes(FriendlyByteBuf buf) { + // 写入基本类型字段 + buf.writeUtf(testString != null ? testString : ""); + buf.writeInt(testInt); + buf.writeBoolean(testBoolean); + buf.writeDouble(testDouble); + buf.writeInt(counter); + buf.writeLong(lastSyncTime); + + // 写入自定义数据 + if (customData != null) { + buf.writeUtf(customData.getName() != null ? customData.getName() : ""); + buf.writeInt(customData.getValue()); + buf.writeBoolean(customData.isFlag()); + } else { + buf.writeUtf(""); + buf.writeInt(0); + buf.writeBoolean(false); + } + + // 写入实体ID(如果实体存在) + if (self != null) { + buf.writeInt(self.getId()); + } else { + buf.writeInt(-1); + } + + // 写入脏数据状态 + buf.writeBoolean(isDirty()); + } + + /** + * 从字节缓冲区读取数据(用于网络传输) + * + * @param buf 字节缓冲区 + */ + public void fromBytes(@NotNull FriendlyByteBuf buf) { + // 读取基本类型字段 + this.testString = buf.readUtf(32767); // Minecraft字符串最大长度 + this.testInt = buf.readInt(); + this.testBoolean = buf.readBoolean(); + this.testDouble = buf.readDouble(); + this.counter = buf.readInt(); + this.lastSyncTime = buf.readLong(); + + // 读取自定义数据 + String customName = buf.readUtf(); + int customValue = buf.readInt(); + boolean customFlag = buf.readBoolean(); + this.customData = new TestData(customName, customValue, customFlag); + + // 读取实体ID(在接收端可能需要额外处理) + int entityId = buf.readInt(); + + // 读取脏数据状态 + boolean wasDirty = buf.readBoolean(); + if (wasDirty) { + markDirty(); + } + } + + /** + * 静态方法:从字节缓冲区创建 TestSyncData 实例 + * + * @param buf 字节缓冲区 + * @return 新的 TestSyncData 实例 + */ + public static TestSyncData staticFromBytes(FriendlyByteBuf buf) { + return new TestSyncData(buf); + } + + @Override + public String getTestString() { + return testString; + } + + @Override + public void setTestString(String value) { + if (!java.util.Objects.equals(this.testString, value)) { + this.testString = value; + markDirty(); + } + } + + @Override + public int getTestInt() { + return testInt; + } + + @Override + public void setTestInt(int value) { + if (this.testInt != value) { + this.testInt = value; + markDirty(); + } + } + + @Override + public boolean isTestBoolean() { + return testBoolean; + } + + @Override + public void setTestBoolean(boolean value) { + if (this.testBoolean != value) { + this.testBoolean = value; + markDirty(); + } + } + + @Override + public double getTestDouble() { + return testDouble; + } + + @Override + public void setTestDouble(double value) { + if (this.testDouble != value) { + this.testDouble = value; + markDirty(); + } + } + + @Override + public int getCounter() { + return counter; + } + + @Override + public void incrementCounter() { + this.counter++; + markDirty(); + } + + @Override + public long getLastSyncTime() { + return lastSyncTime; + } + + @Override + public void updateSyncTime() { + this.lastSyncTime = System.currentTimeMillis(); + markDirty(); + } + + @Override + public TestData getCustomData() { + return customData; + } + + @Override + public void setCustomData(TestData data) { + if (data == null) { + throw new IllegalArgumentException("Custom data cannot be null"); + } + if (!java.util.Objects.equals(this.customData, data)) { + this.customData = data; + markDirty(); + } + } + + @Override + public boolean validateData() { + return testString != null && + !testString.isEmpty() && + customData != null && + customData.getName() != null && + !customData.getName().isEmpty() && + counter >= 0 && + testInt >= 0; + } + + @Override + public CompoundTag serializeNBT() { + return NBTWriter.builder() + .string(NBT_KEY_STRING, testString) + .intValue(NBT_KEY_INT, testInt) + .booleanValue(NBT_KEY_BOOLEAN, testBoolean) + .doubleValue(NBT_KEY_DOUBLE, testDouble) + .intValue(NBT_KEY_COUNTER, counter) + .longValue(NBT_KEY_SYNC_TIME, lastSyncTime) + .compound( + NBT_KEY_CUSTOM_DATA, + NBTWriter.builder() + .string(NBT_KEY_CUSTOM_NAME, customData.getName()) + .intValue(NBT_KEY_CUSTOM_VALUE, customData.getValue()) + .booleanValue(NBT_KEY_CUSTOM_FLAG, customData.isFlag()) + .build() + ).build(); + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + NBTReader.of(nbt) + .intValue(NBT_KEY_INT, integer -> testInt = integer) + .string(NBT_KEY_STRING, string -> testString = string) + .booleanValue(NBT_KEY_BOOLEAN, bool -> testBoolean = bool) + .intValue(NBT_KEY_COUNTER, integer -> counter = integer) + .doubleValue(NBT_KEY_DOUBLE, dou -> testDouble = dou) + .longValue(NBT_KEY_SYNC_TIME, sync -> lastSyncTime = sync) + .compound(NBT_KEY_CUSTOM_DATA, customDataTag -> { + AtomicReference name = new AtomicReference<>(""); + AtomicInteger value = new AtomicInteger(-1); + AtomicBoolean flag = new AtomicBoolean(false); + NBTReader.of(customDataTag) + .string(NBT_KEY_CUSTOM_NAME, name::set) + .intValue(NBT_KEY_CUSTOM_VALUE, value::set) + .booleanValue(NBT_KEY_CUSTOM_FLAG, flag::set); + this.customData = new TestData(name.get(), value.get(), flag.get()); + }); + } + + @Override + public int entityId() { + return self != null ? self.getId() : -1; + } + + /** + * 设置关联的实体 + * + * @param entity 关联的实体 + */ + public void setEntity(Entity entity) { + this.self = entity; + } + + + /** + * 获取所有数据的字符串表示(用于调试) + */ + @Override + public String toString() { + return String.format( + "TestSyncData{id=%d, string='%s', int=%d, boolean=%s, double=%.2f, counter=%d, lastSync=%d, custom=%s}", + self.getId(), testString, testInt, testBoolean, testDouble, counter, lastSyncTime, customData + ); + } + + /** + * 重置为默认值 + */ + public void resetToDefaults() { + testString = "default_value"; + testInt = 42; + testBoolean = true; + testDouble = 3.14159; + counter = 0; + lastSyncTime = 0L; + customData = new TestData("default", 100, false); + markDirty(); + } + + /** + * 生成随机测试数据 + */ + public void generateRandomData() { + testString = "random_" + System.currentTimeMillis(); + testInt = (int) (Math.random() * 1000); + testBoolean = Math.random() > 0.5; + testDouble = Math.random() * 100.0; + counter++; + lastSyncTime = System.currentTimeMillis(); + customData = new TestData( + "custom_" + counter, + (int) (Math.random() * 500), + Math.random() > 0.5 + ); + markDirty(); + } + + /** + * 创建一个不依赖实体的副本(用于网络传输) + * + * @return 不包含实体引用的副本 test sync data + */ + public TestSyncData createNetworkCopy() { + TestSyncData copy = new TestSyncData((Entity) null); + copy.testString = this.testString; + copy.testInt = this.testInt; + copy.testBoolean = this.testBoolean; + copy.testDouble = this.testDouble; + copy.counter = this.counter; + copy.lastSyncTime = this.lastSyncTime; + copy.customData = new TestData( + this.customData.getName(), + this.customData.getValue(), + this.customData.isFlag() + ); + return copy; + } + +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/lib39/example/content/item/FabricItem.java b/src/main/java/top/r3944realms/lib39/example/content/item/FabricItem.java new file mode 100644 index 0000000..f3ba19a --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/content/item/FabricItem.java @@ -0,0 +1,589 @@ +package top.r3944realms.lib39.example.content.item; + +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ProjectileUtil; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.r3944realms.lib39.Lib39; +import top.r3944realms.lib39.example.content.capability.AbstractedTestSyncData; +import top.r3944realms.lib39.example.content.capability.ExCapabilityHandler; +import top.r3944realms.lib39.example.content.capability.TestSyncData; +import top.r3944realms.lib39.example.core.network.ClientDataPacket; +import top.r3944realms.lib39.example.core.network.ExNetworkHandler; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * 用于执行数据查询并检查同步状态的物品 + * Shift + 右键:客户端与服务器双端同时查询检查同步 + * 普通右键:单端查询目标生物数据 + */ +public class FabricItem extends Item { + + /** + * Instantiates a new Fabric item. + * + * @param properties the properties + */ + public FabricItem(Properties properties) { + super(properties); + } + + @Override + public @NotNull InteractionResultHolder use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); + + if (level.isClientSide()) { + // 客户端逻辑 + if (player.isShiftKeyDown()) { + // Shift + 右键:双端检查 - 先获取客户端数据,然后发送到服务器 + handleClientDualCheck(player); + } else { + // 普通右键:客户端单端查询 + handleClientSideQuery(player); + } + } else { + // 服务器逻辑 + ServerPlayer serverPlayer = (ServerPlayer) player; + + if (player.isShiftKeyDown()) { + // 服务器端已经通过数据包处理双端检查,这里只发送开始消息 + player.sendSystemMessage(Component.literal("§b开始双端同步检查,请等待客户端数据...")); + } else { + // 服务器单端查询 + handleServerSingleEndQuery(serverPlayer); + } + + // 添加冷却时间 + player.getCooldowns().addCooldown(this, 20); // 1秒冷却 + } + + return InteractionResultHolder.sidedSuccess(itemStack, level.isClientSide()); + } + + /** + * 客户端处理双端检查 + */ + private void handleClientDualCheck(Player player) { + Entity targetEntity = getClientTargetedEntity(player); + + if (targetEntity instanceof LivingEntity livingTarget) { + // 在客户端获取本地数据 + TestSyncData clientData = getLocalClientData(livingTarget); + + if (clientData != null) { + // 发送客户端数据到服务器 + sendClientDataToServer(clientData, livingTarget.getId()); + + // 客户端提示 + player.sendSystemMessage(Component.literal("§b已发送客户端数据到服务器,等待对比结果...")); + } else { + player.sendSystemMessage(Component.literal("§c无法获取客户端本地数据")); + } + } else { + if (targetEntity == null && player.isShiftKeyDown()) { + handlePlayerSelfData(player); + } else { + player.sendSystemMessage(Component.literal("§c请对准一个生物进行同步检查!")); + } + } + } + + /** + * 处理玩家自身数据的双端检查 + */ + private void handlePlayerSelfData(Player player) { + // 获取玩家自身的客户端数据 + TestSyncData clientData = getLocalClientData(player); + + if (clientData != null) { + // 发送玩家自身数据到服务器 + sendClientDataToServer(clientData, player.getId()); + + // 客户端提示 + player.sendSystemMessage(Component.literal("§b已发送玩家自身客户端数据到服务器,等待对比结果...")); + } else { + player.sendSystemMessage(Component.literal("§c无法获取玩家自身客户端数据")); + } + } + /** + * 客户端单端查询 + */ + private void handleClientSideQuery(Player player) { + Entity targetEntity = getClientTargetedEntity(player); + + if (targetEntity instanceof LivingEntity livingTarget) { + TestSyncData clientData = getLocalClientData(livingTarget); + + if (clientData != null) { + displayClientSideResults(player, livingTarget, clientData); + } else { + player.sendSystemMessage(Component.literal("§c无法查询客户端本地数据")); + } + } else { + player.sendSystemMessage(Component.literal("§c请对准一个生物使用!")); + } + } + + /** + * 服务器端处理单端查询 + */ + private void handleServerSingleEndQuery(ServerPlayer player) { + Entity targetEntity = getServerTargetedEntity(player); + + if (targetEntity instanceof LivingEntity livingTarget) { + player.sendSystemMessage(Component.literal( + String.format("§b开始查询 §e%s§b 的数据,3秒后显示结果...", livingTarget.getName().getString()) + )); + + // 启动异步数据查询 + startServerSingleEndQuery(player, livingTarget); + } else { + player.sendSystemMessage(Component.literal("§c请对准一个生物使用!")); + } + } + + /** + * 在客户端获取本地数据 + */ + private TestSyncData getLocalClientData(LivingEntity target) { + try { + AbstractedTestSyncData abstractData = target.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null); + if (abstractData instanceof TestSyncData) { + return (TestSyncData) abstractData; + } + } catch (Exception e) { + Lib39.LOGGER.error("[FabricItem] 获取客户端数据失败", e); + } + return null; + } + + /** + * 发送客户端数据到服务器 + */ + private void sendClientDataToServer(TestSyncData clientData, int targetEntityId) { + // 使用网络系统发送数据包 + ExNetworkHandler.INSTANCE.sendToServer(new ClientDataPacket(clientData, targetEntityId)); + } + + /** + * 服务器端处理客户端发送的数据(从数据包调用) + * + * @param player the player + * @param clientData the client data + * @param targetEntityId the target entity id + */ + public static void handleClientDataFromPacket(@NotNull ServerPlayer player, TestSyncData clientData, int targetEntityId) { + Entity target = player.level().getEntity(targetEntityId); + + if (target instanceof LivingEntity livingTarget) { + // 获取服务器端数据 + TestSyncData serverData = getServerSideData(livingTarget); + + if (serverData != null) { + // 显示双端对比结果 + displayDualEndComparison(player, livingTarget, serverData, clientData); + } else { + player.sendSystemMessage(Component.literal("§c无法获取服务器端数据")); + } + } else { + player.sendSystemMessage(Component.literal("§c目标生物不存在或已消失")); + } + } + + /** + * 获取服务器端数据 + */ + private static @Nullable TestSyncData getServerSideData(LivingEntity target) { + try { + AbstractedTestSyncData abstractData = target.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null); + if (abstractData instanceof TestSyncData) { + return (TestSyncData) abstractData; + } + } catch (Exception e) { + Lib39.LOGGER.error("[FabricItem] 获取服务器端数据失败", e); + } + return null; + } + + /** + * 启动服务器单端查询 + */ + private void startServerSingleEndQuery(ServerPlayer player, LivingEntity target) { + CompletableFuture.runAsync(() -> { + try { + // 等待 3 秒 + Thread.sleep(3000); + + // 在服务器线程中执行结果处理 + player.server.execute(() -> { + displayServerSingleEndResults(player, target); + }); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + Lib39.LOGGER.error("[FabricItem] 数据查询被中断", e); + player.sendSystemMessage(Component.literal("§c数据查询被中断")); + } catch (Exception e) { + Lib39.LOGGER.error("[FabricItem] 数据查询出错", e); + player.server.execute(() -> + player.sendSystemMessage(Component.literal("§c数据查询出错: " + e.getMessage())) + ); + } + }); + } + + /** + * 显示服务器单端查询结果 + */ + private void displayServerSingleEndResults(ServerPlayer player, @NotNull LivingEntity target) { + Lib39.LOGGER.info("[FabricItem] 查询生物 {} 的数据", target.getName().getString()); + + // 获取目标生物的数据 + AbstractedTestSyncData abstractData = getTestSyncData(target); + + if (abstractData instanceof TestSyncData testData) { + // 显示详细数据 + displayServerDetailedData(player, target, testData); + } else { + player.sendSystemMessage(Component.literal( + String.format("§c生物 §e%s§c 没有测试数据或数据无效", target.getName().getString()) + )); + } + } + + /** + * 显示客户端查询结果 + */ + private void displayClientSideResults(Player player, LivingEntity target, TestSyncData clientData) { + player.sendSystemMessage(Component.literal("§6=== 客户端数据查询结果 ===")); + player.sendSystemMessage(Component.literal("§7目标生物: §e" + target.getName().getString())); + player.sendSystemMessage(Component.literal("§7数据来源: §9客户端本地")); + player.sendSystemMessage(Component.literal("")); + + player.sendSystemMessage(Component.literal("§a基础数据:")); + player.sendSystemMessage(Component.literal("§7字符串: §f" + clientData.getTestString())); + player.sendSystemMessage(Component.literal("§7整数值: §f" + clientData.getTestInt())); + player.sendSystemMessage(Component.literal("§7布尔值: §f" + clientData.isTestBoolean())); + player.sendSystemMessage(Component.literal("§7双精度值: §f" + String.format("%.2f", clientData.getTestDouble()))); + player.sendSystemMessage(Component.literal("§7计数器: §f" + clientData.getCounter())); + + // 显示客户端特定信息 + player.sendSystemMessage(Component.literal("")); + player.sendSystemMessage(Component.literal("§e客户端状态:")); + player.sendSystemMessage(Component.literal("§7数据验证: " + (clientData.validateData() ? "§a通过" : "§c失败"))); + player.sendSystemMessage(Component.literal("§7同步状态: " + (clientData.isDirty() ? "§6待同步" : "§a已同步"))); + } + + /** + * 显示服务器详细数据(单端查询) + */ + private void displayServerDetailedData(ServerPlayer player, LivingEntity target, TestSyncData testData) { + player.sendSystemMessage(Component.literal("§6=== 数据查询结果 ===")); + player.sendSystemMessage(Component.literal( + String.format("§7目标生物: §e%s", target.getName().getString()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7实体ID: §e%d", target.getId()) + )); + player.sendSystemMessage(Component.literal("")); + + // 显示基础数据 + player.sendSystemMessage(Component.literal("§a基础数据:")); + player.sendSystemMessage(Component.literal( + String.format("§7字符串: §f%s", testData.getTestString()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7整数值: §f%d", testData.getTestInt()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7布尔值: §f%s", testData.isTestBoolean()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7双精度值: §f%.2f", testData.getTestDouble()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7计数器: §f%d", testData.getCounter()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7最后同步: §f%dms前", System.currentTimeMillis() - testData.getLastSyncTime()) + )); + player.sendSystemMessage(Component.literal("")); + + // 显示自定义数据 + TestSyncData.TestData customData = testData.getCustomData(); + player.sendSystemMessage(Component.literal("§a自定义数据:")); + player.sendSystemMessage(Component.literal( + String.format("§7名称: §f%s", customData.getName()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7数值: §f%d", customData.getValue()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7标志: §f%s", customData.isFlag()) + )); + player.sendSystemMessage(Component.literal("")); + + // 显示验证状态 + boolean isValid = testData.validateData(); + player.sendSystemMessage(Component.literal( + String.format("§7数据验证: %s", isValid ? "§a通过" : "§c失败") + )); + player.sendSystemMessage(Component.literal( + String.format("§7数据状态: %s", testData.isDirty() ? "§6未同步" : "§a已同步") + )); + } + + /** + * 显示双端比较结果 + */ + private static void displayDualEndComparison(ServerPlayer player, LivingEntity target, TestSyncData serverData, TestSyncData clientData) { + player.sendSystemMessage(Component.literal("§6=== 客户端-服务器双端同步检查结果 ===")); + player.sendSystemMessage(Component.literal( + String.format("§7目标生物: §e%s", target.getName().getString()) + )); + player.sendSystemMessage(Component.literal("")); + + // 显示双端数据来源 + player.sendSystemMessage(Component.literal("§a数据来源:")); + player.sendSystemMessage(Component.literal("§7- §c服务器端§7: 实体ID " + serverData.entityId())); + player.sendSystemMessage(Component.literal("§7- §9客户端§7: 实体ID " + clientData.entityId())); + player.sendSystemMessage(Component.literal("")); + + // 比较各个字段 + boolean stringSynced = serverData.getTestString().equals(clientData.getTestString()); + boolean intSynced = serverData.getTestInt() == clientData.getTestInt(); + boolean booleanSynced = serverData.isTestBoolean() == clientData.isTestBoolean(); + boolean doubleSynced = Math.abs(serverData.getTestDouble() - clientData.getTestDouble()) < 0.001; + boolean counterSynced = serverData.getCounter() == clientData.getCounter(); + boolean customDataSynced = compareCustomData(serverData.getCustomData(), clientData.getCustomData()); + + // 显示字段同步状态 + player.sendSystemMessage(Component.literal("§a字段同步状态:")); + displayDualEndSyncStatus(player, "字符串", stringSynced, + serverData.getTestString(), clientData.getTestString()); + displayDualEndSyncStatus(player, "整数值", intSynced, + serverData.getTestInt(), clientData.getTestInt()); + displayDualEndSyncStatus(player, "布尔值", booleanSynced, + serverData.isTestBoolean(), clientData.isTestBoolean()); + displayDualEndSyncStatus(player, "双精度值", doubleSynced, + serverData.getTestDouble(), clientData.getTestDouble()); + displayDualEndSyncStatus(player, "计数器", counterSynced, + serverData.getCounter(), clientData.getCounter()); + displayDualEndSyncStatus(player, "自定义数据", customDataSynced, + serverData.getCustomData().toString(), clientData.getCustomData().toString()); + + player.sendSystemMessage(Component.literal("")); + + // 计算总体同步率 + int totalFields = 6; + int syncedFields = (stringSynced ? 1 : 0) + (intSynced ? 1 : 0) + + (booleanSynced ? 1 : 0) + (doubleSynced ? 1 : 0) + + (counterSynced ? 1 : 0) + (customDataSynced ? 1 : 0); + double syncRate = (double) syncedFields / totalFields * 100; + + // 显示总体同步状态 + player.sendSystemMessage(Component.literal("§a总体同步状态:")); + player.sendSystemMessage(Component.literal( + String.format("§7同步字段: §e%d§7/§e%d", syncedFields, totalFields) + )); + player.sendSystemMessage(Component.literal( + String.format("§7同步率: %s", getSyncRateColor(syncRate) + String.format("%.1f%%", syncRate)) + )); + player.sendSystemMessage(Component.literal( + String.format("§7同步状态: %s", getOverallSyncStatus(syncRate)) + )); + + // 显示数据状态差异 + player.sendSystemMessage(Component.literal("")); + player.sendSystemMessage(Component.literal("§a数据状态差异:")); + player.sendSystemMessage(Component.literal( + String.format("§7服务器脏数据状态: %s", serverData.isDirty() ? "§6脏" : "§a干净") + )); + player.sendSystemMessage(Component.literal( + String.format("§7客户端脏数据状态: %s", clientData.isDirty() ? "§6脏" : "§a干净") + )); + player.sendSystemMessage(Component.literal( + String.format("§7服务器验证状态: %s", serverData.validateData() ? "§a通过" : "§c失败") + )); + player.sendSystemMessage(Component.literal( + String.format("§7客户端验证状态: %s", clientData.validateData() ? "§a通过" : "§c失败") + )); + + // 显示同步建议 + player.sendSystemMessage(Component.literal("")); + player.sendSystemMessage(Component.literal("§e同步建议:")); + if (syncRate == 100) { + player.sendSystemMessage(Component.literal("§a✓ 数据完全同步,无需操作")); + } else if (syncRate >= 80) { + player.sendSystemMessage(Component.literal("§e⚠ 数据基本同步,建议观察")); + } else if (syncRate >= 50) { + player.sendSystemMessage(Component.literal("§6⚠ 数据部分不同步,建议检查网络")); + } else { + player.sendSystemMessage(Component.literal("§c✗ 数据严重不同步,建议重新同步")); + } + } + + /** + * 显示双端同步状态 + */ + private static void displayDualEndSyncStatus(ServerPlayer player, String fieldName, boolean synced, Object serverValue, Object clientValue) { + String status = synced ? "§a✓ 同步" : "§c✗ 不同步"; + if (synced) { + player.sendSystemMessage(Component.literal( + String.format("§7%s: %s §8(值: §7%s§8)", fieldName, status, serverValue) + )); + } else { + player.sendSystemMessage(Component.literal( + String.format("§7%s: %s", fieldName, status) + )); + player.sendSystemMessage(Component.literal( + String.format("§8 §c服务器: §7%s", serverValue) + )); + player.sendSystemMessage(Component.literal( + String.format("§8 §9客户端: §7%s", clientValue) + )); + } + } + + /** + * 比较自定义数据 + */ + private static boolean compareCustomData(TestSyncData.TestData first, TestSyncData.TestData second) { + return first.getName().equals(second.getName()) && + first.getValue() == second.getValue() && + first.isFlag() == second.isFlag(); + } + + /** + * 获取同步率颜色 + */ + private static String getSyncRateColor(double syncRate) { + if (syncRate >= 90) return "§a"; + if (syncRate >= 70) return "§e"; + if (syncRate >= 50) return "§6"; + return "§c"; + } + + /** + * 获取总体同步状态 + */ + private static String getOverallSyncStatus(double syncRate) { + if (syncRate == 100) return "§a完全同步"; + if (syncRate >= 90) return "§a优秀同步"; + if (syncRate >= 70) return "§e良好同步"; + if (syncRate >= 50) return "§6部分同步"; + return "§c同步较差"; + } + + /** + * 客户端获取准星目标实体 + */ + private Entity getClientTargetedEntity(Player player) { + double reachDistance = 20.0; + float partialTicks = 1.0f; // 服务器端通常用1.0 + + // 获取玩家的视线向量和位置 + Vec3 eyePosition = player.getEyePosition(partialTicks); + Vec3 lookVector = player.getViewVector(partialTicks); + Vec3 endPosition = eyePosition.add(lookVector.x * reachDistance, lookVector.y * reachDistance, lookVector.z * reachDistance); + + // 先检测实体 + EntityHitResult entityHit = ProjectileUtil.getEntityHitResult( + player, + eyePosition, + endPosition, + player.getBoundingBox().expandTowards(lookVector.scale(reachDistance)).inflate(1.0), + entity -> !entity.isSpectator() && entity.isPickable(), + reachDistance * reachDistance // 平方距离 + ); + + if (entityHit != null) { + return entityHit.getEntity(); + } + + return null; + } + + /** + * 服务器获取准星目标实体 + */ + private Entity getServerTargetedEntity(ServerPlayer player) { + double reachDistance = 20.0; + float partialTicks = 1.0f; // 服务器端通常用1.0 + + // 获取玩家的视线向量和位置 + Vec3 eyePosition = player.getEyePosition(partialTicks); + Vec3 lookVector = player.getViewVector(partialTicks); + Vec3 endPosition = eyePosition.add(lookVector.x * reachDistance, lookVector.y * reachDistance, lookVector.z * reachDistance); + + // 先检测实体 + EntityHitResult entityHit = ProjectileUtil.getEntityHitResult( + player, + eyePosition, + endPosition, + player.getBoundingBox().expandTowards(lookVector.scale(reachDistance)).inflate(1.0), + entity -> !entity.isSpectator() && entity.isPickable(), + reachDistance * reachDistance // 平方距离 + ); + + if (entityHit != null) { + return entityHit.getEntity(); + } + + return null; + } + + /** + * 获取测试同步数据 + */ + private AbstractedTestSyncData getTestSyncData(Entity entity) { + try { + return entity.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElse(null); + } catch (Exception e) { + Lib39.LOGGER.debug("[FabricItem] 获取生物 {} 的 TestSyncData 失败: {}", + entity.getName().getString(), e.getMessage()); + return null; + } + } + + @Override + public void appendHoverText(@NotNull ItemStack stack, @Nullable Level level, + @NotNull List tooltip, @NotNull TooltipFlag flag) { + super.appendHoverText(stack, level, tooltip, flag); + + tooltip.add(Component.literal("§7右键点击在 3 秒后执行")); + tooltip.add(Component.literal("§7§e准星瞄准生物§7的数据查询")); + tooltip.add(Component.literal("§7§oShift + 右键§7进行§e客户端-服务器双端同步检查§7")); + tooltip.add(Component.literal("")); + tooltip.add(Component.literal("§6查询延迟: §e3秒")); + tooltip.add(Component.literal("§6瞄准距离: §e20格")); + tooltip.add(Component.literal("§6冷却时间: §e1秒")); + tooltip.add(Component.literal("")); + tooltip.add(Component.literal("§a单端查询内容:")); + tooltip.add(Component.literal("§7- 基础数据字段")); + tooltip.add(Component.literal("§7- 自定义数据结构")); + tooltip.add(Component.literal("§7- 数据验证状态")); + tooltip.add(Component.literal("§7- 同步状态信息")); + tooltip.add(Component.literal("")); + tooltip.add(Component.literal("§e双端同步检查:")); + tooltip.add(Component.literal("§7- 客户端和服务器同时查询")); + tooltip.add(Component.literal("§7- 字段级同步状态对比")); + tooltip.add(Component.literal("§7- 总体同步率计算")); + tooltip.add(Component.literal("§7- 双端数据状态差异")); + tooltip.add(Component.literal("§7- 同步建议")); + } +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/lib39/example/content/item/NeoForgeItem.java b/src/main/java/top/r3944realms/lib39/example/content/item/NeoForgeItem.java new file mode 100644 index 0000000..4d669f8 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/content/item/NeoForgeItem.java @@ -0,0 +1,284 @@ +package top.r3944realms.lib39.example.content.item; + +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.ProjectileUtil; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.r3944realms.lib39.Lib39; +import top.r3944realms.lib39.example.content.capability.AbstractedTestSyncData; +import top.r3944realms.lib39.example.content.capability.ExCapabilityHandler; +import top.r3944realms.lib39.example.content.capability.TestSyncData; + +import java.util.List; +import java.util.Random; + +/** + * 用于对准星生物触发 TestSyncData 随机变换的物品 + * Shift + 右键:操作自己的数据 + * 普通右键:操作瞄准生物的数据 + */ +public class NeoForgeItem extends Item { + private static final Random RANDOM = new Random(); + + /** + * Instantiates a new Neo forge item. + * + * @param properties the properties + */ + public NeoForgeItem(Properties properties) { + super(properties); + } + + @Override + public @NotNull InteractionResultHolder use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand hand) { + ItemStack itemStack = player.getItemInHand(hand); + + if (!level.isClientSide()) { + ServerPlayer serverPlayer = (ServerPlayer) player; + + if (player.isShiftKeyDown()) { + // Shift + 右键:操作自己的数据 + handleSelfDataOperation(serverPlayer); + } else { + // 普通右键:操作瞄准生物的数据 + handleTargetDataOperation(serverPlayer); + } + + // 添加冷却时间 + player.getCooldowns().addCooldown(this, 20); // 1秒冷却 + } + + return InteractionResultHolder.sidedSuccess(itemStack, level.isClientSide()); + } + + /** + * 处理玩家自身数据操作 + */ + private void handleSelfDataOperation(ServerPlayer player) { + boolean success = triggerRandomTransformation(player); + + if (success) { + player.sendSystemMessage(Component.literal("§a已触发§e自身§a测试数据的随机变换!")); + Lib39.LOGGER.info("[NeoForgeItem] 玩家 {} 触发了自身数据变换", player.getName().getString()); + } else { + player.sendSystemMessage(Component.literal("§c无法触发自身数据变换")); + } + } + + /** + * 处理目标生物数据操作 + */ + private void handleTargetDataOperation(ServerPlayer player) { + // 获取玩家准星瞄准的生物 + Entity targetEntity = getTargetedEntity(player); + + if (targetEntity instanceof LivingEntity livingTarget) { + // 触发对准星生物的数据变换 + boolean success = triggerRandomTransformation(livingTarget); + + if (success) { + player.sendSystemMessage(Component.literal( + String.format("§a已触发 §e%s§a 的测试数据随机变换!", livingTarget.getName().getString()) + )); + Lib39.LOGGER.info("[NeoForgeItem] 玩家 {} 触发生物 {} 的数据变换", + player.getName().getString(), livingTarget.getName().getString()); + } else { + player.sendSystemMessage(Component.literal( + String.format("§c无法触发 §e%s§c 的数据变换", livingTarget.getName().getString()) + )); + } + } else { + // 没有瞄准生物 + player.sendSystemMessage(Component.literal("§c请对准一个生物使用!")); + } + } + + /** + * 获取玩家准星瞄准的实体 + */ + private Entity getTargetedEntity(ServerPlayer player) { + double reachDistance = 20.0; + float partialTicks = 1.0f; // 服务器端通常用1.0 + + // 获取玩家的视线向量和位置 + Vec3 eyePosition = player.getEyePosition(partialTicks); + Vec3 lookVector = player.getViewVector(partialTicks); + Vec3 endPosition = eyePosition.add(lookVector.x * reachDistance, lookVector.y * reachDistance, lookVector.z * reachDistance); + + // 先检测实体 + EntityHitResult entityHit = ProjectileUtil.getEntityHitResult( + player, + eyePosition, + endPosition, + player.getBoundingBox().expandTowards(lookVector.scale(reachDistance)).inflate(1.0), + entity -> !entity.isSpectator() && entity.isPickable(), + reachDistance * reachDistance // 平方距离 + ); + + if (entityHit != null) { + return entityHit.getEntity(); + } + + return null; + } + + /** + * 为实体触发随机数据变换 + */ + private boolean triggerRandomTransformation(LivingEntity entity) { + try { + AbstractedTestSyncData abstractData = getOrCreateTestSyncData(entity); + if (!(abstractData instanceof TestSyncData testData)) { + return false; + } + + // 随机选择一种变换方式 + int transformationType = RANDOM.nextInt(6); // 增加更多变换类型 + + switch (transformationType) { + case 0 -> { + // 完全随机数据 + testData.generateRandomData(); + Lib39.LOGGER.debug("[NeoForgeItem] 为 {} 生成完全随机数据", getEntityName(entity)); + } + case 1 -> { + // 只修改字符串和计数器 + testData.setTestString("transformed_" + System.currentTimeMillis()); + testData.incrementCounter(); + testData.updateSyncTime(); + Lib39.LOGGER.debug("[NeoForgeItem] 为 {} 修改字符串和计数器", getEntityName(entity)); + } + case 2 -> { + // 修改数值数据 + testData.setTestInt(RANDOM.nextInt(1000)); + testData.setTestDouble(RANDOM.nextDouble() * 100.0); + testData.setTestBoolean(RANDOM.nextBoolean()); + testData.updateSyncTime(); + Lib39.LOGGER.debug("[NeoForgeItem] 为 {} 修改数值数据", getEntityName(entity)); + } + case 3 -> { + // 修改自定义数据 + TestSyncData.TestData newCustomData = new TestSyncData.TestData( + "custom_" + RANDOM.nextInt(100), + RANDOM.nextInt(500), + RANDOM.nextBoolean() + ); + testData.setCustomData(newCustomData); + testData.incrementCounter(); + Lib39.LOGGER.debug("[NeoForgeItem] 为 {} 修改自定义数据", getEntityName(entity)); + } + case 4 -> { + // 重置为默认值 + testData.resetToDefaults(); + Lib39.LOGGER.debug("[NeoForgeItem] 为 {} 重置数据", getEntityName(entity)); + } + case 5 -> { + // 特殊变换:玩家专属数据 + if (entity instanceof Player) { + testData.setTestString("player_special_" + entity.getUUID().toString().substring(0, 8)); + testData.setTestInt(entity.getId() * 10); + testData.setTestDouble(entity.getHealth()); + testData.incrementCounter(); + testData.updateSyncTime(); + Lib39.LOGGER.debug("[NeoForgeItem] 为玩家 {} 设置专属数据", getEntityName(entity)); + } else { + // 非玩家生物使用普通变换 + testData.generateRandomData(); + } + } + } + + // 验证数据有效性 + if (!testData.validateData()) { + Lib39.LOGGER.warn("[NeoForgeItem] {} 的数据验证失败,重置为默认值", getEntityName(entity)); + testData.resetToDefaults(); + } + + // 显示数据预览(仅对玩家自己操作时显示) + if (entity instanceof Player) { + displayDataPreview((Player) entity, testData); + } + + return true; + + } catch (Exception e) { + Lib39.LOGGER.error("[NeoForgeItem] 为 {} 触发数据变换时出错: {}", + getEntityName(entity), e.getMessage()); + return false; + } + } + + /** + * 显示数据预览给玩家 + */ + private void displayDataPreview(Player player, TestSyncData testData) { + player.sendSystemMessage(Component.literal("§6数据预览:")); + player.sendSystemMessage(Component.literal( + String.format("§7字符串: §f%s", testData.getTestString()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7计数器: §f%d", testData.getCounter()) + )); + player.sendSystemMessage(Component.literal( + String.format("§7验证状态: %s", testData.validateData() ? "§a通过" : "§c失败") + )); + } + + /** + * 获取实体名称(用于日志) + */ + private String getEntityName(LivingEntity entity) { + if (entity instanceof Player) { + return "玩家 " + entity.getName().getString(); + } else { + return "生物 " + entity.getName().getString(); + } + } + + private AbstractedTestSyncData getOrCreateTestSyncData(Entity entity) { + try { + return entity.getCapability(ExCapabilityHandler.TEST_CAP).resolve().orElseThrow(); + } catch (Exception e) { + Lib39.LOGGER.error("[NeoForgeItem] 获取 {} 的 TestSyncData 失败: {}", + getEntityName((LivingEntity) entity), e.getMessage()); + return null; + } + } + + @Override + public void appendHoverText(@NotNull ItemStack stack, @Nullable Level level, + @NotNull List tooltip, @NotNull TooltipFlag flag) { + super.appendHoverText(stack, level, tooltip, flag); + + tooltip.add(Component.literal("§7右键点击触发§e准星瞄准生物§7的")); + tooltip.add(Component.literal("§7测试数据随机变换")); + tooltip.add(Component.literal("§7§oShift + 右键§7操作§e自身§7数据")); + tooltip.add(Component.literal("")); + tooltip.add(Component.literal("§6冷却时间: §e1秒")); + tooltip.add(Component.literal("§6瞄准距离: §e20格")); + tooltip.add(Component.literal("")); + tooltip.add(Component.literal("§a变换类型:")); + tooltip.add(Component.literal("§7- 完全随机数据")); + tooltip.add(Component.literal("§7- 字符串+计数器")); + tooltip.add(Component.literal("§7- 数值数据")); + tooltip.add(Component.literal("§7- 自定义数据")); + tooltip.add(Component.literal("§7- 重置默认值")); + tooltip.add(Component.literal("§7- 玩家专属数据")); + tooltip.add(Component.literal("")); + tooltip.add(Component.literal("§e自身操作特性:")); + tooltip.add(Component.literal("§7- 显示数据预览")); + tooltip.add(Component.literal("§7- 玩家专属数据变换")); + } +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/lib39/example/core/event/ExClientEventHandler.java b/src/main/java/top/r3944realms/lib39/example/core/event/ExClientEventHandler.java new file mode 100644 index 0000000..f391b11 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/core/event/ExClientEventHandler.java @@ -0,0 +1,16 @@ +package top.r3944realms.lib39.example.core.event; + +/** + * The type Client handler. + */ +public class ExClientEventHandler { + /** + * The type Mod. + */ + public static class Mod extends ExClientEventHandler {} + + /** + * The type Game. + */ + public static class Game extends ExClientEventHandler {} +} diff --git a/src/main/java/top/r3944realms/lib39/example/core/event/ExCommonEventHandler.java b/src/main/java/top/r3944realms/lib39/example/core/event/ExCommonEventHandler.java new file mode 100644 index 0000000..d3c6a11 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/core/event/ExCommonEventHandler.java @@ -0,0 +1,100 @@ +package top.r3944realms.lib39.example.core.event; + +import net.minecraft.world.entity.Entity; +import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; +import net.minecraftforge.data.event.GatherDataEvent; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import top.r3944realms.lib39.api.event.SyncManagerRegisterEvent; +import top.r3944realms.lib39.core.sync.CachedSyncManager; +import top.r3944realms.lib39.example.content.capability.AbstractedTestSyncData; +import top.r3944realms.lib39.example.content.capability.ExCapabilityHandler; +import top.r3944realms.lib39.example.content.capability.TestSyncData; +import top.r3944realms.lib39.example.datagen.EXLib39DataGenEvent; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The type Common handler. + */ +public class ExCommonEventHandler { + /** + * The type Game. + */ + @SuppressWarnings("unused") + + public static class Game extends ExCommonEventHandler { + /** + * Attach capability. + * + * @param event the event + */ + @SubscribeEvent + public static void attachCapability(AttachCapabilitiesEvent event) { + ExCapabilityHandler.attachCapability(event); + } + + /** + * On register sync. + * + * @param event the event + */ + @SubscribeEvent + public static void onRegisterSync (SyncManagerRegisterEvent event) { + event.registerSyncManager( + TestSyncData.ID, + new CachedSyncManager() { + private final Map syncDataMap = new ConcurrentHashMap<>(); + @Override + public Map getSyncMap() { + return syncDataMap; + } + }, + ExCapabilityHandler.TEST_CAP + + ); + } + + } + + + /** + * The type Mod. + */ + public static class Mod extends ExCommonEventHandler { + + /** + * On fml common setup. + * + * @param event the event + */ + @SubscribeEvent + public static void onFMLCommonSetup(FMLCommonSetupEvent event) { + event.enqueueWork(() -> { + + }); + } + + /** + * Register capability. + * + * @param event the event + */ + @SubscribeEvent + public static void registerCapability(RegisterCapabilitiesEvent event) { + ExCapabilityHandler.registerCapability(event); + } + + /** + * Gather data. + * + * @param event the event + */ + @SubscribeEvent + public static void gatherData(GatherDataEvent event) { + EXLib39DataGenEvent.gatherData(event); + } + } +} diff --git a/src/main/java/top/r3944realms/lib39/example/core/event/ExServerEventHandler.java b/src/main/java/top/r3944realms/lib39/example/core/event/ExServerEventHandler.java new file mode 100644 index 0000000..9a46faf --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/core/event/ExServerEventHandler.java @@ -0,0 +1,16 @@ +package top.r3944realms.lib39.example.core.event; + +/** + * The type Server handler. + */ +public class ExServerEventHandler { + /** + * The type Mod. + */ + public static class Mod extends ExServerEventHandler {} + + /** + * The type Game. + */ + public static class Game extends ExServerEventHandler {} +} diff --git a/src/main/java/top/r3944realms/lib39/example/core/network/ClientDataPacket.java b/src/main/java/top/r3944realms/lib39/example/core/network/ClientDataPacket.java new file mode 100644 index 0000000..81e5a46 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/core/network/ClientDataPacket.java @@ -0,0 +1,69 @@ +package top.r3944realms.lib39.example.core.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.network.NetworkEvent; +import top.r3944realms.lib39.example.content.capability.TestSyncData; +import top.r3944realms.lib39.example.content.item.FabricItem; + +import java.util.function.Supplier; + +/** + * The type Client data packet. + */ +public class ClientDataPacket { + private final TestSyncData clientData; + private final int targetEntityId; + + /** + * Instantiates a new Client data packet. + * + * @param clientData the client data + * @param targetEntityId the target entity id + */ + public ClientDataPacket(TestSyncData clientData, int targetEntityId) { + this.clientData = clientData; + this.targetEntityId = targetEntityId; + } + + /** + * Instantiates a new Client data packet. + * + * @param buf the buf + */ + public ClientDataPacket(FriendlyByteBuf buf) { + this.clientData = TestSyncData.staticFromBytes(buf); + this.targetEntityId = buf.readInt(); + } + + /** + * To bytes. + * + * @param buf the buf + */ + public void toBytes(FriendlyByteBuf buf) { + clientData.toBytes(buf); + buf.writeInt(targetEntityId); + } + + /** + * Handle. + * + * @param supplier the supplier + */ + public void handle(Supplier supplier) { + NetworkEvent.Context context = supplier.get(); + context.enqueueWork(() -> { + ServerPlayer player = context.getSender(); + if (player != null) { + // 处理客户端发送的数据 + handleClientData(player, clientData, targetEntityId); + } + }); + context.setPacketHandled(true); + } + + private void handleClientData(ServerPlayer player, TestSyncData clientData, int targetEntityId) { + FabricItem.handleClientDataFromPacket(player, clientData, targetEntityId); + } +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/lib39/example/core/network/ExNetworkHandler.java b/src/main/java/top/r3944realms/lib39/example/core/network/ExNetworkHandler.java new file mode 100644 index 0000000..d85fdab --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/core/network/ExNetworkHandler.java @@ -0,0 +1,37 @@ +package top.r3944realms.lib39.example.core.network; + +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.network.NetworkRegistry; +import net.minecraftforge.network.simple.SimpleChannel; +import top.r3944realms.lib39.Lib39; + +/** + * The type Ex network handler. + */ +public class ExNetworkHandler { + /** + * The constant INSTANCE. + */ + public static final SimpleChannel INSTANCE; + private static int ID = 0; + + static { + INSTANCE = NetworkRegistry.newSimpleChannel( + new ResourceLocation(Lib39.MOD_ID, "test"), + () -> "1.0", + s -> true, + s -> true + ); + } + + /** + * Register. + */ + public static void register() { + // 注册数据包 + INSTANCE.registerMessage(ID++, ClientDataPacket.class, + ClientDataPacket::toBytes, + ClientDataPacket::new, + ClientDataPacket::handle); + } +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/lib39/example/core/register/ExLib39Items.java b/src/main/java/top/r3944realms/lib39/example/core/register/ExLib39Items.java new file mode 100644 index 0000000..de21c86 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/core/register/ExLib39Items.java @@ -0,0 +1,51 @@ +package top.r3944realms.lib39.example.core.register; + +import net.minecraft.world.item.Item; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; +import top.r3944realms.lib39.Lib39; +import top.r3944realms.lib39.example.content.item.FabricItem; +import top.r3944realms.lib39.example.content.item.NeoForgeItem; + +/** + * The type Ex lib 39 items. + */ +public class ExLib39Items { + /** + * The constant ITEMS. + */ + public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Lib39.MOD_ID); + /** + * The constant SUPER_LEAD_ROPE. + */ + public static final RegistryObject FABRIC = ITEMS.register( + "fabric", + () -> new FabricItem( + new Item.Properties() + .stacksTo(1) + .fireResistant() + ) + ); + /** + * The constant ETERNAL_POTATO. + */ + public static final RegistryObject NEOFORGE = + ITEMS.register("neoforge", + () -> new NeoForgeItem( + new Item.Properties() + .stacksTo(1) + .fireResistant() + )); + + /** + * Register. + * + * @param bus the bus + */ + public static void register(IEventBus bus) { + ITEMS.register(bus); + } + +} diff --git a/src/main/java/top/r3944realms/lib39/example/datagen/EXLib39DataGenEvent.java b/src/main/java/top/r3944realms/lib39/example/datagen/EXLib39DataGenEvent.java new file mode 100644 index 0000000..a1b4ca3 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/datagen/EXLib39DataGenEvent.java @@ -0,0 +1,48 @@ +package top.r3944realms.lib39.example.datagen; + +import net.minecraft.data.DataProvider; +import net.minecraftforge.data.event.GatherDataEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import top.r3944realms.lib39.Lib39; +import top.r3944realms.lib39.datagen.provider.SimpleLanguageProvider; +import top.r3944realms.lib39.datagen.value.McLocale; +import top.r3944realms.lib39.example.datagen.data.ExLib39LangKeys; +import top.r3944realms.lib39.example.datagen.provider.ExItemModelProvider; + +/** + * The type Ex lib 39 data gen event. + */ +public class EXLib39DataGenEvent { + /** + * The Logger. + */ + static Logger logger = LoggerFactory.getLogger(EXLib39DataGenEvent.class); + + /** + * Gather data. + * + * @param event the event + */ + public static void gatherData(GatherDataEvent event) { + logger.info("GatherDataEvent thread: {}", Thread.currentThread().getName()); + LanguageGenerator(event, McLocale.EN_US); + LanguageGenerator(event, McLocale.ZH_CN); + LanguageGenerator(event, McLocale.ZH_TW); + LanguageGenerator(event, McLocale.LZH); + ModelDataGenerate(event); + } + private static void LanguageGenerator(GatherDataEvent event, McLocale language) { + event.getGenerator().addProvider( + event.includeClient(), + (DataProvider.Factory) pOutput -> new SimpleLanguageProvider(pOutput, Lib39.MOD_ID ,language ,ExLib39LangKeys.INSTANCE) + ); + } + + private static void ModelDataGenerate(GatherDataEvent event) { + event.getGenerator().addProvider( + event.includeClient(), + (DataProvider.Factory) pOutput -> new ExItemModelProvider(pOutput, event.getExistingFileHelper()) + ); + } +} diff --git a/src/main/java/top/r3944realms/lib39/example/datagen/data/ExLib39LangKeys.java b/src/main/java/top/r3944realms/lib39/example/datagen/data/ExLib39LangKeys.java new file mode 100644 index 0000000..48c65a0 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/datagen/data/ExLib39LangKeys.java @@ -0,0 +1,69 @@ +package top.r3944realms.lib39.example.datagen.data; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Unmodifiable; +import top.r3944realms.lib39.datagen.value.ILangKeyValueCollection; +import top.r3944realms.lib39.datagen.value.LangKeyValue; +import top.r3944realms.lib39.datagen.value.ModPartEnum; +import top.r3944realms.lib39.example.core.register.ExLib39Items; + +import java.util.ArrayList; +import java.util.List; + +/** + * The enum Ex lib 39 lang keys. + */ +public enum ExLib39LangKeys implements ILangKeyValueCollection { + /** + * Instance ex lib 39 lang keys. + */ + INSTANCE; + ExLib39LangKeys() { + initLangKeyValues(); + } + + /** + * The Lang key values. + */ + final List langKeyValues = new ArrayList<>(); + + + /** + * Init lang key values. + */ + public void initLangKeyValues() { + addLang(LangKeyValue.ofSupplier( + ExLib39Items.FABRIC, ModPartEnum.ITEM, + "Fabric", "织布" , "織布" ,"織" , true + )); + addLang(LangKeyValue.ofSupplier( + ExLib39Items.NEOFORGE, ModPartEnum.ITEM, + "NeoForge", "小狐狸", "狐狸", "狸", true + )); + } + + + /** + * Add lang. + * + * @param keyValue the key value + */ + public void addLang(LangKeyValue keyValue) { + langKeyValues.add(keyValue); + } + + /** + * Clear. + */ + public void clear() { + langKeyValues.clear(); + } + + + @Contract(pure = true) + @Override + public @Unmodifiable List getValues() { + return List.copyOf(langKeyValues); + } + +} diff --git a/src/main/java/top/r3944realms/lib39/example/datagen/provider/ExItemModelProvider.java b/src/main/java/top/r3944realms/lib39/example/datagen/provider/ExItemModelProvider.java new file mode 100644 index 0000000..459ad8d --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/example/datagen/provider/ExItemModelProvider.java @@ -0,0 +1,115 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package top.r3944realms.lib39.example.datagen.provider; + +import net.minecraft.data.PackOutput; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraftforge.client.model.generators.ItemModelProvider; +import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.registries.ForgeRegistries; +import top.r3944realms.lib39.Lib39; +import top.r3944realms.lib39.datagen.value.LangKeyValue; +import top.r3944realms.lib39.datagen.value.ModPartEnum; +import top.r3944realms.lib39.example.datagen.data.ExLib39LangKeys; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * The type Slp item model provider. + */ +public class ExItemModelProvider extends ItemModelProvider { + private static List objectList; + /** + * The constant GENERATED. + */ + public static final String GENERATED = "item/generated"; + /** + * The constant HANDHELD. + */ + public static final String HANDHELD = "item/handheld"; + + /** + * Instantiates a new Slp item model provider. + * + * @param output the output + * @param existingFileHelper the existing file helper + */ + public ExItemModelProvider(PackOutput output, ExistingFileHelper existingFileHelper) { + super(output, Lib39.MOD_ID, existingFileHelper); + objectList = new ArrayList<>(); + init(); + } + + @Override + protected void registerModels() { + DefaultModItemModelRegister(); + } + private void init() { + for(LangKeyValue obj : ExLib39LangKeys.INSTANCE.getValues()) { + if(!(obj.isDefault() && obj.getMPE().equals(ModPartEnum.ITEM))) continue; + objectList.add(obj.getItem()); + } + } + /** + * @implNote
 先有纹理才会成功构建 + */ + private void DefaultModItemModelRegister() { + objectList.forEach(this::basicItem); + } + + /** + * Item generate model. + * + * @param item the item + * @param location the location + */ + public void itemGenerateModel(Item item, ResourceLocation location){ + withExistingParent(itemName(item), GENERATED).texture("layer0", location); + } + + /** + * Item hand held model. + * + * @param item the item + * @param location the location + */ + public void itemHandHeldModel(Item item, ResourceLocation location){ + withExistingParent(itemName(item), HANDHELD).texture("layer0", location); + } + + /** + * Item name string. + * + * @param item the item + * @return the string + */ + public String itemName(Item item){ + return Objects.requireNonNull(ForgeRegistries.ITEMS.getKey(item)).getPath(); + } + + /** + * Resource item resource location. + * + * @param path the path + * @return the resource location + */ + public ResourceLocation resourceItem(String path){ + return modLoc("item/" + path); + } +} diff --git a/src/main/java/top/r3944realms/lib39/util/riding/RidingApplier.java b/src/main/java/top/r3944realms/lib39/util/riding/RidingApplier.java index d17a77e..9611659 100644 --- a/src/main/java/top/r3944realms/lib39/util/riding/RidingApplier.java +++ b/src/main/java/top/r3944realms/lib39/util/riding/RidingApplier.java @@ -54,11 +54,7 @@ public class RidingApplier { // 获取实体和载具 Entity entity = entityProvider.apply(entityId); Entity vehicle = vehicleId != null ? entityProvider.apply(vehicleId) : null; - if (entity == null) continue; - - // ---------- 白名单保护 ---------- - if (vehicle != null) { // 将当前节点的乘客挂回上层载具 for (RidingRelationship child : current.getPassengers()) { diff --git a/src/main/resources/assets/lib39/textures/item/fabric.png b/src/main/resources/assets/lib39/textures/item/fabric.png new file mode 100644 index 0000000..4ab8370 Binary files /dev/null and b/src/main/resources/assets/lib39/textures/item/fabric.png differ diff --git a/src/main/resources/assets/lib39/textures/item/neoforge.png b/src/main/resources/assets/lib39/textures/item/neoforge.png new file mode 100644 index 0000000..9a58fc6 Binary files /dev/null and b/src/main/resources/assets/lib39/textures/item/neoforge.png differ