添加了示例代码
This commit is contained in:
parent
cf1b6dba5b
commit
8919d7ddae
|
|
@ -0,0 +1,2 @@
|
|||
// 1.20.1 2025-10-25T19:14:21.1829335 Languages: zh_tw
|
||||
4cb94c651f6aa74538a2ab25cb183cffd75be688 assets/lib39/lang/zh_tw.json
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.20.1 2025-10-25T19:14:21.1764262 Languages: zh_cn
|
||||
a1db601a0fca923c5434b8b0843773a3115d0b59 assets/lib39/lang/zh_cn.json
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.20.1 2025-10-25T19:14:21.1804258 Languages: lzh
|
||||
098df025475d3f4d9d2abefa91a7d38c44644ba4 assets/lib39/lang/lzh.json
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.20.1 2025-10-25T19:14:21.1784269 Languages: en_us
|
||||
c6b6aadca0a922823a8c949ebde93f8f999737f9 assets/lib39/lang/en_us.json
|
||||
4
src/generated/resources/assets/lib39/lang/en_us.json
Normal file
4
src/generated/resources/assets/lib39/lang/en_us.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"item.lib39.fabric": "Fabric",
|
||||
"item.lib39.neoforge": "NeoForge"
|
||||
}
|
||||
4
src/generated/resources/assets/lib39/lang/lzh.json
Normal file
4
src/generated/resources/assets/lib39/lang/lzh.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"item.lib39.fabric": "織",
|
||||
"item.lib39.neoforge": "狸"
|
||||
}
|
||||
4
src/generated/resources/assets/lib39/lang/zh_cn.json
Normal file
4
src/generated/resources/assets/lib39/lang/zh_cn.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"item.lib39.fabric": "织布",
|
||||
"item.lib39.neoforge": "小狐狸"
|
||||
}
|
||||
4
src/generated/resources/assets/lib39/lang/zh_tw.json
Normal file
4
src/generated/resources/assets/lib39/lang/zh_tw.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"item.lib39.fabric": "織布",
|
||||
"item.lib39.neoforge": "狐狸"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "lib39:item/fabric"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "lib39:item/neoforge"
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ public interface ISyncData<T> {
|
|||
void setDirty(boolean dirty);
|
||||
|
||||
/**
|
||||
* Make dirty.
|
||||
* Mark dirty.
|
||||
*/
|
||||
default void makeDirty() {
|
||||
default void markDirty() {
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,5 +54,6 @@ public abstract class NBTEntitySyncData implements IEntity, ISyncData<NBTEntityS
|
|||
if (isDirty()) {
|
||||
NetworkHandler.sendToAllPlayer(new SyncNBTCapDataEntityS2CPack(entityId(), id(), serializeNBT()));
|
||||
}
|
||||
dirty = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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- 同步建议"));
|
||||
}
|
||||
}
|
||||
|
|
@ -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- 玩家专属数据变换"));
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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/> 先有纹理才会成功构建
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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()) {
|
||||
|
|
|
|||
BIN
src/main/resources/assets/lib39/textures/item/fabric.png
Normal file
BIN
src/main/resources/assets/lib39/textures/item/fabric.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/main/resources/assets/lib39/textures/item/neoforge.png
Normal file
BIN
src/main/resources/assets/lib39/textures/item/neoforge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 280 B |
Loading…
Reference in New Issue
Block a user