添加了示例代码

This commit is contained in:
叁玖领域 2025-10-25 23:58:30 +08:00
parent cf1b6dba5b
commit 8919d7ddae
44 changed files with 2706 additions and 125 deletions

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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 {}
}

View File

@ -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)));
}
}

View File

@ -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 {}
}

View File

@ -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;

View File

@ -30,9 +30,9 @@ public interface ISyncData<T> {
void setDirty(boolean dirty);
/**
* Make dirty.
* Mark dirty.
*/
default void makeDirty() {
default void markDirty() {
setDirty(true);
}

View File

@ -54,5 +54,6 @@ public abstract class NBTEntitySyncData implements IEntity, ISyncData<NBTEntityS
if (isDirty()) {
NetworkHandler.sendToAllPlayer(new SyncNBTCapDataEntityS2CPack(entityId(), id(), serializeNBT()));
}
dirty = false;
}
}

View File

@ -4,6 +4,7 @@ import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.LanguageProvider;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.datagen.value.ILangKeyValue;
import top.r3944realms.lib39.datagen.value.ILangKeyValueCollection;
import top.r3944realms.lib39.datagen.value.McLocale;
import java.util.ArrayList;
@ -14,40 +15,55 @@ import java.util.Map;
/**
* The type Simple language provider.
*/
@SuppressWarnings("unused")
public class SimpleLanguageProvider extends LanguageProvider {
private final McLocale language;
private final ILangKeyValue langKeyValue;
private final Map<String, String> lanKeyMap;
private static final List<String> objects = new ArrayList<>();
private final ILangKeyValueCollection langKeyValueCollection;
private final Map<String, String> translationMap; // Better naming
private final List<String> orderedKeys; // Better naming than "objects"
/**
* Instantiates a new Simple language provider.
*
* @param output the output
* @param modId the mod id
* @param 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());
}
}

View File

@ -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<ILangKeyValue> getValues();
}

View File

@ -0,0 +1,30 @@
package top.r3944realms.lib39.datagen.value;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* The interface Lang key value collection.
*/
public interface ILangKeyValueCollection {
/**
* Gets values.
*
* @return the values
*/
List<? extends ILangKeyValue> getValues();
/**
* Gets lang.
*
* @param locale the locale
* @param key the key
* @return the lang
*/
static String getLang(McLocale locale, @NotNull ILangKeyValue key) {
return key.getLang(locale);
}
}

View File

@ -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<ILangKeyValue> 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 +
'}';
}
}

View File

@ -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;
}
}

View File

@ -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() {
}
}

View File

@ -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);
}
}
}

View File

@ -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<AbstractedTestSyncData> 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));
}
}
}

View File

@ -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<CompoundTag> {
/**
* The constant TEST_SYNC_REL.
*/
public static final ResourceLocation TEST_SYNC_REL = new ResourceLocation(Lib39.MOD_ID, "test_sync_data");
private final AbstractedTestSyncData instance;
private final LazyOptional<AbstractedTestSyncData> optional;
/**
* Instantiates a new Test sync cap provider.
*
* @param entity the entity
*/
public TestSyncCapProvider(Entity entity) {
this.instance = new TestSyncData(entity);
this.optional = LazyOptional.of(() -> instance);
}
@Override
public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
return ExCapabilityHandler.TEST_CAP.orEmpty(cap, optional);
}
@Override
public CompoundTag serializeNBT() {
return instance.serializeNBT();
}
@Override
public void deserializeNBT(CompoundTag nbt) {
instance.deserializeNBT(nbt);
}
}

View File

@ -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<String> 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;
}
}

View File

@ -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<ItemStack> 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<Component> tooltip, @NotNull TooltipFlag flag) {
super.appendHoverText(stack, level, tooltip, flag);
tooltip.add(Component.literal("§7右键点击在 3 秒后执行"));
tooltip.add(Component.literal("§7§e准星瞄准生物§7的数据查询"));
tooltip.add(Component.literal("§7§oShift + 右键§7进行§e客户端-服务器双端同步检查§7"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§6查询延迟: §e3秒"));
tooltip.add(Component.literal("§6瞄准距离: §e20格"));
tooltip.add(Component.literal("§6冷却时间: §e1秒"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§a单端查询内容:"));
tooltip.add(Component.literal("§7- 基础数据字段"));
tooltip.add(Component.literal("§7- 自定义数据结构"));
tooltip.add(Component.literal("§7- 数据验证状态"));
tooltip.add(Component.literal("§7- 同步状态信息"));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal("§e双端同步检查:"));
tooltip.add(Component.literal("§7- 客户端和服务器同时查询"));
tooltip.add(Component.literal("§7- 字段级同步状态对比"));
tooltip.add(Component.literal("§7- 总体同步率计算"));
tooltip.add(Component.literal("§7- 双端数据状态差异"));
tooltip.add(Component.literal("§7- 同步建议"));
}
}

View File

@ -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<ItemStack> 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<Component> 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- 玩家专属数据变换"));
}
}

View File

@ -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 {}
}

View File

@ -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<Entity, AbstractedTestSyncData>() {
private final Map<Entity, AbstractedTestSyncData> syncDataMap = new ConcurrentHashMap<>();
@Override
public Map<Entity, AbstractedTestSyncData> 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);
}
}
}

View File

@ -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 {}
}

View File

@ -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<NetworkEvent.Context> supplier) {
NetworkEvent.Context context = supplier.get();
context.enqueueWork(() -> {
ServerPlayer player = context.getSender();
if (player != null) {
// 处理客户端发送的数据
handleClientData(player, clientData, targetEntityId);
}
});
context.setPacketHandled(true);
}
private void handleClientData(ServerPlayer player, TestSyncData clientData, int targetEntityId) {
FabricItem.handleClientDataFromPacket(player, clientData, targetEntityId);
}
}

View File

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

View File

@ -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<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Lib39.MOD_ID);
/**
* The constant SUPER_LEAD_ROPE.
*/
public static final RegistryObject<Item> FABRIC = ITEMS.register(
"fabric",
() -> new FabricItem(
new Item.Properties()
.stacksTo(1)
.fireResistant()
)
);
/**
* The constant ETERNAL_POTATO.
*/
public static final RegistryObject<Item> 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);
}
}

View File

@ -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<SimpleLanguageProvider>) pOutput -> new SimpleLanguageProvider(pOutput, Lib39.MOD_ID ,language ,ExLib39LangKeys.INSTANCE)
);
}
private static void ModelDataGenerate(GatherDataEvent event) {
event.getGenerator().addProvider(
event.includeClient(),
(DataProvider.Factory<ExItemModelProvider>) pOutput -> new ExItemModelProvider(pOutput, event.getExistingFileHelper())
);
}
}

View File

@ -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<LangKeyValue> 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<LangKeyValue> getValues() {
return List.copyOf(langKeyValues);
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package top.r3944realms.lib39.example.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.datagen.value.LangKeyValue;
import top.r3944realms.lib39.datagen.value.ModPartEnum;
import top.r3944realms.lib39.example.datagen.data.ExLib39LangKeys;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The type Slp item model provider.
*/
public class ExItemModelProvider extends ItemModelProvider {
private static List<Item> objectList;
/**
* The constant GENERATED.
*/
public static final String GENERATED = "item/generated";
/**
* The constant HANDHELD.
*/
public static final String HANDHELD = "item/handheld";
/**
* Instantiates a new Slp item model provider.
*
* @param output the output
* @param existingFileHelper the existing file helper
*/
public ExItemModelProvider(PackOutput output, ExistingFileHelper existingFileHelper) {
super(output, Lib39.MOD_ID, existingFileHelper);
objectList = new ArrayList<>();
init();
}
@Override
protected void registerModels() {
DefaultModItemModelRegister();
}
private void init() {
for(LangKeyValue obj : ExLib39LangKeys.INSTANCE.getValues()) {
if(!(obj.isDefault() && obj.getMPE().equals(ModPartEnum.ITEM))) continue;
objectList.add(obj.getItem());
}
}
/**
* @implNote <br/>&nbsp;先有纹理才会成功构建
*/
private void DefaultModItemModelRegister() {
objectList.forEach(this::basicItem);
}
/**
* Item generate model.
*
* @param item the item
* @param location the location
*/
public void itemGenerateModel(Item item, ResourceLocation location){
withExistingParent(itemName(item), GENERATED).texture("layer0", location);
}
/**
* Item hand held model.
*
* @param item the item
* @param location the location
*/
public void itemHandHeldModel(Item item, ResourceLocation location){
withExistingParent(itemName(item), HANDHELD).texture("layer0", location);
}
/**
* Item name string.
*
* @param item the item
* @return the string
*/
public String itemName(Item item){
return Objects.requireNonNull(ForgeRegistries.ITEMS.getKey(item)).getPath();
}
/**
* Resource item resource location.
*
* @param path the path
* @return the resource location
*/
public ResourceLocation resourceItem(String path){
return modLoc("item/" + path);
}
}

View File

@ -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()) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B