docs: 添加Javadoc

This commit is contained in:
叁玖领域 2026-03-14 22:21:18 +08:00
parent 418e85394c
commit 50856266d8
169 changed files with 573 additions and 14810 deletions

View File

@ -8,14 +8,30 @@ import org.slf4j.LoggerFactory;
import top.r3944realms.lib39.example.Lib39Example;
import top.r3944realms.lib39.platform.Services;
/**
* The type Lib 39.
*/
public class Lib39 {
/**
* The constant MOD_ID.
*/
public static final String MOD_ID = "lib39";
/**
* The constant MOD_NAME.
*/
public static final String MOD_NAME = "3944Realms 's Lib Mod";
/**
* The constant LOGGER.
*/
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME);
/**
* The constant ENABLE_EXAMPLES_PROPERTY_KEY.
*/
public static final String ENABLE_EXAMPLES_PROPERTY_KEY = "lib39.enable_examples";
/**
* Initialize.
*/
public static void initialize() {
Lib39.LOGGER.info("[Lib39-Common] Lib39-Common start initialization.");
if (shouldRegisterExamples()) {
@ -24,6 +40,7 @@ public class Lib39 {
}
Lib39.LOGGER.info("[Lib39-Common] Finished Lib39-Common!.");
}
/**
* Rl resource location.
*
@ -58,6 +75,11 @@ public class Lib39 {
return new ResourceLocation(path);
}
/**
* Is client environment boolean.
*
* @return the boolean
*/
public static boolean isClientEnvironment() {
return Services.PLATFORM.isClientEnvironment();
}
@ -87,6 +109,7 @@ public class Lib39 {
LOGGER.error("[Lib39-Common] Failed to demonstrate examples", e);
}
}
/**
* The type Mod info.
*/

View File

@ -28,6 +28,8 @@ public class Lib39HelpCommand extends SimpleHelpCommand {
/**
* Instantiates a new Lib 39 help command.
*
* @param dispatcher the dispatcher
* @param context the context
*/
public Lib39HelpCommand(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext context) {
super(dispatcher, context);

View File

@ -1,13 +1,6 @@
package top.r3944realms.lib39.client.shader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.server.packs.resources.ResourceProvider;
import top.r3944realms.lib39.Lib39;
import java.io.IOException;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* The type Lib 39 shaders.
@ -36,10 +29,20 @@ public class Lib39Shaders {
return selectionShader;
}
/**
* Sets ring shader.
*
* @param ringShader the ring shader
*/
public static void setRingShader(ShaderInstance ringShader) {
Lib39Shaders.ringShader = ringShader;
}
/**
* Sets selection shader.
*
* @param selectionShader the selection shader
*/
public static void setSelectionShader(ShaderInstance selectionShader) {
Lib39Shaders.selectionShader = selectionShader;
}

View File

@ -483,6 +483,7 @@ public interface ICommandHelpManager {
/**
* 获取命令树的字符串表示
*
* @param commandSourceStack the command source stack
* @return 命令树列表 command tree
*/
default List<MutableComponent> getCommandTree(CommandSourceStack commandSourceStack) {
@ -537,6 +538,7 @@ public interface ICommandHelpManager {
/**
* Build command tree help mutable component.
*
* @param commandSourceStack the command source stack
* @return the mutable component
*/
default MutableComponent buildCommandTreeHelp(CommandSourceStack commandSourceStack) {

View File

@ -35,9 +35,16 @@ public class CommandNode {
// 展开/闭合状态
private boolean expanded = true;
/**
* Test permission boolean.
*
* @param source the source
* @return the boolean
*/
public boolean testPermission(CommandSourceStack source) {
return testPermission.test(source);
}
/**
* Instantiates a new Command node.
*

View File

@ -14,20 +14,49 @@ import java.util.stream.Collectors;
*/
@SuppressWarnings("unused")
public abstract class CompatManager {
/**
* Gets id.
*
* @return the id
*/
public ResourceLocation getId() {
return id;
}
/**
* The Logger.
*/
protected final Logger logger;
/**
* The Id.
*/
protected final ResourceLocation id;
/**
* The Compats.
*/
protected final Map<ResourceLocation, ICompat> compats = new HashMap<>();
/**
* The Initialized.
*/
protected boolean initialized = false;
/**
* The Pending tasks.
*/
protected final List<Runnable> pendingTasks = new ArrayList<>();
/**
* Initialize.
*/
public void initialize() {
initializeAllCompat();
onLoadComplete();
}
/**
* Instantiates a new Compat manager.
*
* @param id the id
*/
public CompatManager(@NotNull ResourceLocation id) {
this.id = id;
this.logger = LoggerFactory.getLogger(id.toString());
@ -50,6 +79,12 @@ public abstract class CompatManager {
}
}
/**
* Do register compat.
*
* @param id the id
* @param compat the compat
*/
protected void doRegisterCompat(ResourceLocation id, ICompat compat) {
if (compats.containsKey(id)) {
logger.warn("Compat with id {} is already registered!", id);

View File

@ -8,8 +8,20 @@ import java.util.concurrent.Callable;
* The interface Compat.
*/
public interface ICompat {
/**
* Sets initialize.
*
* @param initialize the initialize
*/
void setInitialize(boolean initialize);
/**
* Is initialized boolean.
*
* @return the boolean
*/
boolean isInitialized();
/**
* Id resource location.
*

View File

@ -2,8 +2,23 @@ package top.r3944realms.lib39.core.sync;
import net.minecraft.nbt.Tag;
/**
* The interface Inbt serializable.
*
* @param <T> the type parameter
*/
public interface INBTSerializable <T extends Tag>{
/**
* Serialize nbt t.
*
* @return the t
*/
T serializeNBT();
/**
* Deserialize nbt.
*
* @param var1 the var 1
*/
void deserializeNBT(T var1);
}

View File

@ -1,6 +1,18 @@
package top.r3944realms.lib39.core.sync;
/**
* The interface Update.
*/
public interface IUpdate {
/**
* Update.
*/
void update();
/**
* Gets sync data.
*
* @return the sync data
*/
NBTEntitySyncData getSyncData();
}

View File

@ -4,6 +4,9 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
/**
* The type Nbt entity sync data.
*/
public abstract class NBTEntitySyncData implements IEntity, ISyncData<NBTEntitySyncData>, INBTSerializable<CompoundTag>, IUpdate {
/**
* The Dirty.

View File

@ -12,9 +12,16 @@ import java.util.function.Function;
/**
* The type Sync data 2 manager.
*
* @param <V> the type parameter
*/
@SuppressWarnings({"unused", "DuplicatedCode"})
public abstract class SyncData2Manager<V extends SyncData2Manager.TypedSyncEntry<?, ?>> {
/**
* Gets typed entries.
*
* @return the typed entries
*/
protected abstract Map<ResourceLocation, V> getTypedEntries();
/**
@ -34,6 +41,12 @@ public abstract class SyncData2Manager<V extends SyncData2Manager.TypedSyncEntry
Optional<T> getData(K key);
}
/**
* The type Typed sync entry.
*
* @param <K> the type parameter
* @param <T> the type parameter
*/
protected static class TypedSyncEntry<K, T extends ISyncData<?>> {
/**
* The Manager.
@ -397,7 +410,16 @@ public abstract class SyncData2Manager<V extends SyncData2Manager.TypedSyncEntry
}
}
// 辅助方法更新条目的数据提供者
/**
* Update data provider in entry.
*
* @param <K> the type parameter
* @param <T> the type parameter
* @param id the id
* @param entry the entry
* @param newDataProvider the new data provider
*/
// 辅助方法更新条目的数据提供者
@SuppressWarnings("unchecked")
protected <K, T extends ISyncData<?>> void updateDataProviderInEntry(
ResourceLocation id,

View File

@ -1,12 +1,6 @@
package top.r3944realms.lib39.datagen.provider;
import com.google.gson.JsonObject;
import java.nio.file.Path;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
@ -19,18 +13,38 @@ import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.level.block.Block;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Path;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
/**
* The type Language provider.
*/
public abstract class LanguageProvider implements DataProvider {
private final Map<String, String> data = new TreeMap<>();
private final PackOutput output;
private final String modid;
private final String locale;
/**
* Instantiates a new Language provider.
*
* @param output the output
* @param modid the modid
* @param locale the locale
*/
public LanguageProvider(PackOutput output, String modid, String locale) {
this.output = output;
this.modid = modid;
this.locale = locale;
}
/**
* Add translations.
*/
protected abstract void addTranslations();
public @NotNull CompletableFuture<?> run(@NotNull CachedOutput cache) {
@ -49,54 +63,132 @@ public abstract class LanguageProvider implements DataProvider {
return DataProvider.saveStable(cache, json, target);
}
/**
* Add block.
*
* @param key the key
* @param name the name
*/
public void addBlock(@NotNull Supplier<? extends Block> key, String name) {
this.add(key.get(), name);
}
/**
* Add.
*
* @param key the key
* @param name the name
*/
public void add(@NotNull Block key, String name) {
this.add(key.getDescriptionId(), name);
}
/**
* Add item.
*
* @param key the key
* @param name the name
*/
public void addItem(@NotNull Supplier<? extends Item> key, String name) {
this.add(key.get(), name);
}
/**
* Add.
*
* @param key the key
* @param name the name
*/
public void add(@NotNull Item key, String name) {
this.add(key.getDescriptionId(), name);
}
/**
* Add item stack.
*
* @param key the key
* @param name the name
*/
public void addItemStack(@NotNull Supplier<ItemStack> key, String name) {
this.add(key.get(), name);
}
/**
* Add.
*
* @param key the key
* @param name the name
*/
public void add(@NotNull ItemStack key, String name) {
this.add(key.getDescriptionId(), name);
}
/**
* Add enchantment.
*
* @param key the key
* @param name the name
*/
public void addEnchantment(@NotNull Supplier<? extends Enchantment> key, String name) {
this.add(key.get(), name);
}
/**
* Add.
*
* @param key the key
* @param name the name
*/
public void add(@NotNull Enchantment key, String name) {
this.add(key.getDescriptionId(), name);
}
/**
* Add effect.
*
* @param key the key
* @param name the name
*/
public void addEffect(@NotNull Supplier<? extends MobEffect> key, String name) {
this.add(key.get(), name);
}
/**
* Add.
*
* @param key the key
* @param name the name
*/
public void add(@NotNull MobEffect key, String name) {
this.add(key.getDescriptionId(), name);
}
/**
* Add entity type.
*
* @param key the key
* @param name the name
*/
public void addEntityType(@NotNull Supplier<? extends EntityType<?>> key, String name) {
this.add(key.get(), name);
}
/**
* Add.
*
* @param key the key
* @param name the name
*/
public void add(@NotNull EntityType<?> key, String name) {
this.add(key.getDescriptionId(), name);
}
/**
* Add.
*
* @param key the key
* @param value the value
*/
public void add(String key, String value) {
if (this.data.put(key, value) != null) {
throw new IllegalStateException("Duplicate translation key " + key);

View File

@ -15,7 +15,11 @@ public class Lib39Example {
registered = true;
}
}
public void init() {
/**
* Init.
*/
public void init() {
}

View File

@ -10,11 +10,27 @@ import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
*/
@SuppressWarnings("unused")
public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
/**
* The constant DEFAULT_TEST_STRING.
*/
public final static String DEFAULT_TEST_STRING = "default_value";
/**
* The constant DEFAULT_TEST_INT.
*/
public final static int DEFAULT_TEST_INT = 42;
/**
* The constant DEFAULT_TEST_BOOLEAN.
*/
public final static boolean DEFAULT_TEST_BOOLEAN = true;
/**
* The constant DEFAULT_TEST_DOUBLE.
*/
public final static double DEFAULT_TEST_DOUBLE = 3.14159;
/**
* The constant DEFAULT_TEST_DATA.
*/
public final static TestData DEFAULT_TEST_DATA = new TestData("default", 100, false);
/**
* Instantiates a new Nbt sync data.
*
@ -92,6 +108,9 @@ public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
*/
public abstract void incrementCounter();
/**
* Clear counter.
*/
public abstract void clearCounter();
/**
@ -106,7 +125,11 @@ public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
*/
public abstract void updateSyncTime();
/**
* Clear sync time.
*/
public abstract void clearSyncTime();
/**
* Gets custom data.
*
@ -128,6 +151,9 @@ public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
*/
public abstract boolean validateData();
/**
* Reset to defaults.
*/
public void resetToDefaults() {
setTestString(DEFAULT_TEST_STRING);
setTestInt(DEFAULT_TEST_INT);
@ -139,6 +165,9 @@ public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
markDirty();
}
/**
* Generate random data.
*/
public void generateRandomData() {
setTestString("random_" + System.currentTimeMillis());
setTestInt((int) (Math.random() * 1000));
@ -154,8 +183,20 @@ public abstract class AbstractedTestSyncData extends NBTEntitySyncData {
markDirty();
}
/**
* To bytes.
*
* @param buf the buf
*/
public abstract void toBytes(FriendlyByteBuf buf);
/**
* From bytes.
*
* @param buf the buf
*/
public abstract void fromBytes(@NotNull FriendlyByteBuf buf);
/**
* 测试数据对象
*/

View File

@ -151,9 +151,20 @@ public abstract class AbstractFabricItem extends Item {
player.sendSystemMessage(Component.literal("§c请对准一个生物使用"));
}
}
/**
* Gets data.
*
* @param target the target
* @return the data
*/
protected abstract AbstractedTestSyncData getData(Entity target);
/**
* 在客户端获取本地数据
*
* @param target the target
* @return the local client data
*/
protected AbstractedTestSyncData getLocalClientData(LivingEntity target) {
try {
@ -166,6 +177,9 @@ public abstract class AbstractFabricItem extends Item {
/**
* 发送客户端数据到服务器
*
* @param clientData the client data
* @param targetEntityId the target entity id
*/
protected abstract void sendClientDataToServer(AbstractedTestSyncData clientData, int targetEntityId);
@ -299,6 +313,11 @@ public abstract class AbstractFabricItem extends Item {
/**
* 显示双端比较结果
*
* @param player the player
* @param target the target
* @param serverData the server data
* @param clientData the client data
*/
protected static void displayDualEndComparison(ServerPlayer player, LivingEntity target, AbstractedTestSyncData serverData, AbstractedTestSyncData clientData) {
player.sendSystemMessage(Component.literal("§6=== 客户端-服务器双端同步检查结果 ==="));

View File

@ -243,8 +243,20 @@ public abstract class AbstractNeoForgeItem extends Item {
}
}
/**
* Gets data.
*
* @param entity the entity
* @return the data
*/
protected abstract AbstractedTestSyncData getData(Entity entity);
/**
* Gets or create test sync data.
*
* @param entity the entity
* @return the or create test sync data
*/
protected AbstractedTestSyncData getOrCreateTestSyncData(Entity entity) {
try {
return getData(entity);

View File

@ -32,7 +32,10 @@ public class ForgeItem extends Item {
return super.use(level, player, usedHand);
}
static class ClientOpt implements IClientOnly {
/**
* The type Client opt.
*/
static class ClientOpt implements IClientOnly {
private static void clientUse(@NotNull InteractionHand usedHand) {
IClientOnly.check(() -> Minecraft.getInstance().setScreen(new ForgeScreen(usedHand, 0)));
}

View File

@ -8,83 +8,166 @@ import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Comparator;
/**
* The interface Creative mode tabs accessor.
*/
@Mixin(CreativeModeTabs.class)
public interface CreativeModeTabsAccessor {
/**
* Gets building blocks.
*
* @return the building blocks
*/
@Accessor("BUILDING_BLOCKS")
static ResourceKey<CreativeModeTab> getBuildingBlocks() {
throw new AssertionError();
}
/**
* Gets colored blocks.
*
* @return the colored blocks
*/
@Accessor("COLORED_BLOCKS")
static ResourceKey<CreativeModeTab> getColoredBlocks() {
throw new AssertionError();
}
/**
* Gets natural blocks.
*
* @return the natural blocks
*/
@Accessor("NATURAL_BLOCKS")
static ResourceKey<CreativeModeTab> getNaturalBlocks() {
throw new AssertionError();
}
/**
* Gets functional blocks.
*
* @return the functional blocks
*/
@Accessor("FUNCTIONAL_BLOCKS")
static ResourceKey<CreativeModeTab> getFunctionalBlocks() {
throw new AssertionError();
}
/**
* Gets redstone blocks.
*
* @return the redstone blocks
*/
@Accessor("REDSTONE_BLOCKS")
static ResourceKey<CreativeModeTab> getRedstoneBlocks() {
throw new AssertionError();
}
/**
* Gets hotbar.
*
* @return the hotbar
*/
@Accessor("HOTBAR")
static ResourceKey<CreativeModeTab> getHotbar() {
throw new AssertionError();
}
/**
* Gets search.
*
* @return the search
*/
@Accessor("SEARCH")
static ResourceKey<CreativeModeTab> getSearch() {
throw new AssertionError();
}
/**
* Gets tools and utilities.
*
* @return the tools and utilities
*/
@Accessor("TOOLS_AND_UTILITIES")
static ResourceKey<CreativeModeTab> getToolsAndUtilities() {
throw new AssertionError();
}
/**
* Gets combat.
*
* @return the combat
*/
@Accessor("COMBAT")
static ResourceKey<CreativeModeTab> getCombat() {
throw new AssertionError();
}
/**
* Gets food and drinks.
*
* @return the food and drinks
*/
@Accessor("FOOD_AND_DRINKS")
static ResourceKey<CreativeModeTab> getFoodAndDrinks() {
throw new AssertionError();
}
/**
* Gets ingredients.
*
* @return the ingredients
*/
@Accessor("INGREDIENTS")
static ResourceKey<CreativeModeTab> getIngredients() {
throw new AssertionError();
}
/**
* Gets spawn eggs.
*
* @return the spawn eggs
*/
@Accessor("SPAWN_EGGS")
static ResourceKey<CreativeModeTab> getSpawnEggs() {
throw new AssertionError();
}
/**
* Gets op blocks.
*
* @return the op blocks
*/
@Accessor("OP_BLOCKS")
static ResourceKey<CreativeModeTab> getOpBlocks() {
throw new AssertionError();
}
/**
* Gets inventory.
*
* @return the inventory
*/
@Accessor("INVENTORY")
static ResourceKey<CreativeModeTab> getInventory() {
throw new AssertionError();
}
/**
* Gets cached parameters.
*
* @return the cached parameters
*/
@Accessor("CACHED_PARAMETERS")
static CreativeModeTab.ItemDisplayParameters getCachedParameters() {
throw new AssertionError();
}
/**
* Gets painting comparator.
*
* @return the painting comparator
*/
@Accessor("PAINTING_COMPARATOR")
static Comparator<net.minecraft.world.item.CreativeModeTab> getPaintingComparator() {
throw new AssertionError();

View File

@ -7,8 +7,16 @@ import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
/**
* The interface Screen accessor.
*/
@Mixin(Screen.class)
public interface ScreenAccessor {
/**
* Gets renderables.
*
* @return the renderables
*/
@Accessor("renderables")
List<Renderable> getrRenderables();
}

View File

@ -5,17 +5,30 @@ import top.r3944realms.lib39.platform.services.IPlatformHelper;
import java.util.ServiceLoader;
/**
* The type Services.
*/
// Service loaders are a built-in Java feature that allow us to locate implementations of an interface that vary from one
// environment to another. In the context of MultiLoader we use this feature to access a mock API in the common code that
// is swapped out for the platform specific implementation at runtime.
public class Services {
// In this example we provide a platform helper which provides information about what platform the mod is running on.
/**
* The constant PLATFORM.
*/
// In this example we provide a platform helper which provides information about what platform the mod is running on.
// For example this can be used to check if the code is running on Forge vs Fabric, or to ask the modloader if another
// mod is loaded.
public static final IPlatformHelper PLATFORM = load(IPlatformHelper.class);
// This code is used to load a service for the current environment. Your implementation of the service must be defined
/**
* Load t.
*
* @param <T> the type parameter
* @param clazz the clazz
* @return the t
*/
// This code is used to load a service for the current environment. Your implementation of the service must be defined
// manually by including a text file in META-INF/services named with the fully qualified class name of the service.
// Inside the file you should write the fully qualified class name of the implementation to load for the platform. For
// example our file on Forge points to ForgePlatformHelper while Fabric points to FabricPlatformHelper.

View File

@ -5,7 +5,17 @@ import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import top.r3944realms.lib39.core.command.ICommandHelpManager;
/**
* The interface Help command hook.
*/
@FunctionalInterface
public interface IHelpCommandHook {
/**
* On register.
*
* @param tree the tree
* @param manager the manager
* @param context the context
*/
void onRegister(LiteralArgumentBuilder<CommandSourceStack> tree, ICommandHelpManager manager, CommandBuildContext context);
}

View File

@ -36,6 +36,11 @@ public interface IPlatformHelper {
return isDevelopmentEnvironment() ? "development" : "production";
}
/**
* Is client environment boolean.
*
* @return the boolean
*/
boolean isClientEnvironment();
@ -46,7 +51,17 @@ public interface IPlatformHelper {
*/
String getModVersion();
/**
* Gets util helper.
*
* @return the util helper
*/
IUtilHelper getUtilHelper();
/**
* Gets help command hook.
*
* @return the help command hook
*/
IHelpCommandHook getHelpCommandHook();
}

View File

@ -2,6 +2,14 @@ package top.r3944realms.lib39.platform.services;
import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
/**
* The interface Util helper.
*/
public interface IUtilHelper {
/**
* Gets block registry builder.
*
* @return the block registry builder
*/
BlockRegistryBuilder getBlockRegistryBuilder();
}

View File

@ -32,12 +32,24 @@ public class GameProfileHelper {
* Client Only Class
*/
public static class ClientOpt implements IClientOnly {
/**
* Resolve skin texture resource location.
*
* @param gameProfile the game profile
* @return the resource location
*/
public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) {
return IClientOnly.check(() ->
Minecraft.getInstance().getSkinManager()
.getInsecureSkinLocation(gameProfile));
}
/**
* Gets skin texture.
*
* @param gameProfile the game profile
* @return the skin texture
*/
public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) {
return IClientOnly.check(() -> {
if (gameProfile == null) {
@ -47,6 +59,12 @@ public class GameProfileHelper {
});
}
/**
* Has slim arms client boolean.
*
* @param player the player
* @return the boolean
*/
public static boolean hasSlimArmsClient(Player player) {
return IClientOnly.check(() -> {
if (player instanceof AbstractClientPlayer clientPlayer) {
@ -59,6 +77,12 @@ public class GameProfileHelper {
});
}
/**
* Gets skin model name.
*
* @param player the player
* @return the skin model name
*/
public static @NotNull String getSkinModelName(@NotNull Player player) {
return IClientOnly.check(() -> {
if (player.level().isClientSide && player instanceof AbstractClientPlayer) {
@ -71,6 +95,7 @@ public class GameProfileHelper {
}
}
/**
* The constant TAG_BE.
*/
@ -80,11 +105,23 @@ public class GameProfileHelper {
*/
public static final String TAG_OWN_PROFILE = "OwnerProfile";
/**
* Gets skin texture.
*
* @param gameProfile the game profile
* @return the skin texture
*/
public static ResourceLocation getSkinTexture(@Nullable GameProfile gameProfile) {
return ClientOpt.getSkinTexture(gameProfile);
}
/**
* Resolve skin texture resource location.
*
* @param gameProfile the game profile
* @return the resource location
*/
public static @NotNull ResourceLocation resolveSkinTexture(@NotNull GameProfile gameProfile) {
return ClientOpt.resolveSkinTexture(gameProfile);
}

View File

@ -4,7 +4,15 @@ import top.r3944realms.lib39.Lib39;
import java.util.function.Supplier;
/**
* The interface Client only.
*/
public interface IClientOnly {
/**
* Check.
*
* @param runnable the runnable
*/
static void check(Runnable runnable) {
if (Lib39.isClientEnvironment()) {
runnable.run();
@ -13,6 +21,12 @@ public interface IClientOnly {
throw new RuntimeException("This method should be called in ClientEnvironment");
}
/**
* Check.
*
* @param runnable the runnable
* @param fallback the fallback
*/
static void check(Runnable runnable, Runnable fallback) {
if (Lib39.isClientEnvironment()) {
runnable.run();
@ -20,6 +34,14 @@ public interface IClientOnly {
}
fallback.run();
}
/**
* Check t.
*
* @param <T> the type parameter
* @param supplier the supplier
* @return the t
*/
static <T> T check(Supplier<T> supplier) {
if (Lib39.isClientEnvironment()) {
return supplier.get();
@ -27,6 +49,14 @@ public interface IClientOnly {
throw new RuntimeException("This method should be called in ClientEnvironment");
}
/**
* Check t.
*
* @param <T> the type parameter
* @param supplier the supplier
* @param fallback the fallback
* @return the t
*/
static <T> T check(Supplier<T> supplier, Supplier<T> fallback) {
if (Lib39.isClientEnvironment()) {
return supplier.get();

View File

@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
* <p><b>重要</b>由于使用了 weakValues()实现类必须被其他对象强引用
* 否则会被 GC 清理导致锁自动释放通常这意味着将实现类实例存储在
* 适当的管理器或容器中
*
* @author sch246
*/
public interface IUniPosOwner {
@ -67,8 +68,7 @@ public interface IUniPosOwner {
*
* @param level 维度 The level (dimension) of the position.
* @param pos 坐标 The position to lock.
* @return true if ownership was successfully claimed or was already held by this object,
* false if the position is owned by another object.
* @return true if ownership was successfully claimed or was already held by this object, false if the position is owned by another object.
*/
default boolean tryLock(Level level, BlockPos pos) {
return UniPosManager.INSTANCE.tryLock(level, pos, this);
@ -88,6 +88,7 @@ public interface IUniPosOwner {
/**
* 续租继续持有锁
*
* @param level 维度
* @param pos 位置
*/
@ -97,16 +98,19 @@ public interface IUniPosOwner {
}
/**
管理 IUniPosOwner 系统的单例存储
该类是后台实现大多数类不应直接使用
在同一维度键ResourceKey<Level范围内确保任意时刻一个 BlockPos 只能被一个对象拥有
注意锁的作用域是维度键级别因此在服务端同一维度的不同 Level 实例之间共享
缓存以 BlockPos long 值为键 IUniPosOwner 的弱值weak values为值
当所有者不再被强引用时条目会自动移除从而释放锁
该实现面向服务端使用
* 管理 IUniPosOwner 系统的单例存储
* 该类是后台实现大多数类不应直接使用
* 在同一维度键ResourceKey<Level范围内确保任意时刻一个 BlockPos 只能被一个对象拥有
* 注意锁的作用域是维度键级别因此在服务端同一维度的不同 Level 实例之间共享
* 缓存以 BlockPos long 值为键 IUniPosOwner 的弱值weak values为值
* 当所有者不再被强引用时条目会自动移除从而释放锁
* 该实现面向服务端使用
*/
final class UniPosManager {
/**
* The constant INSTANCE.
*/
public static final UniPosManager INSTANCE = new UniPosManager();
// 顶层映射维度键ResourceKey<Level> -> 每维度的坐标缓存
@ -139,7 +143,7 @@ final class UniPosManager {
*
* @param level 维度
* @param pos 坐标
* @return 如果存在所有者则返回所有者对象否则返回 null
* @return 如果存在所有者 则返回所有者对象否则返回 null
*/
@Nullable
public IUniPosOwner getOwner(Level level, BlockPos pos) {
@ -182,6 +186,7 @@ final class UniPosManager {
/**
* 续租继续持有锁
*
* @param level 维度
* @param pos 位置
* @param owner 对象

View File

@ -23,9 +23,15 @@ public abstract class BlockRegistryBuilder {
private boolean needBuildItem;
private Item.Properties properties;
/**
* Create block registry builder.
*
* @return the block registry builder
*/
public static BlockRegistryBuilder create() {
return Services.PLATFORM.getUtilHelper().getBlockRegistryBuilder();
}
/**
* 设置注册名称
*
@ -88,6 +94,9 @@ public abstract class BlockRegistryBuilder {
/**
* 内部方法注册对应的方块物品
*
* @param blockObject the block object
* @param creativeTabs the creative tabs
*/
protected abstract void registerBlockItem(Supplier<Block> blockObject, ResourceKey<CreativeModeTab>... creativeTabs);

View File

@ -28,8 +28,8 @@ import net.minecraft.world.level.saveddata.maps.MapDecoration;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;

View File

@ -2,7 +2,10 @@ package top.r3944realms.lib39;
import net.fabricmc.api.ModInitializer;
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
import top.r3944realms.lib39.core.register.*;
import top.r3944realms.lib39.core.register.FabricLib39BlockEntities;
import top.r3944realms.lib39.core.register.FabricLib39Blocks;
import top.r3944realms.lib39.core.register.FabricLib39Items;
import top.r3944realms.lib39.core.register.FabricLib39SoundEvents;
import top.r3944realms.lib39.example.FabricLib39Example;
public class Lib39Fabric implements ModInitializer {

View File

@ -2,13 +2,11 @@ package top.r3944realms.lib39.core.event;
import com.mojang.authlib.GameProfile;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
import net.fabricmc.loader.impl.launch.FabricLauncher;
import net.minecraft.Util;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.network.chat.Component;

View File

@ -2,7 +2,6 @@ package top.r3944realms.lib39.core.register;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

View File

@ -3,7 +3,6 @@ package top.r3944realms.lib39.core.register;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;

View File

@ -3,7 +3,6 @@ package top.r3944realms.lib39.core.register;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;

View File

@ -2,9 +2,7 @@ package top.r3944realms.lib39.core.register;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;

View File

@ -6,7 +6,9 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
public class SyncData2LookupManager extends SyncData2Manager<SyncData2LookupManager.TypedSyncEntry<? extends ISyncData<?>>> {
protected final Map<ResourceLocation, TypedSyncEntry<?>> typedEntries = Maps.newConcurrentMap();

View File

@ -2,24 +2,17 @@ package top.r3944realms.lib39.example.content.item;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.fabric.impl.screenhandler.client.ClientNetworking;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.PlayerList;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
import top.r3944realms.lib39.example.content.data.FabricTestSyncData;
import top.r3944realms.lib39.example.core.network.FabricClientDataPacket;
import java.util.List;
public class FabricFabricItem extends AbstractFabricItem {
public FabricFabricItem(Properties properties) {

View File

@ -3,8 +3,8 @@ package top.r3944realms.lib39.example.core.register;
import net.minecraft.world.item.Item;
import top.r3944realms.lib39.core.register.FabricLib39Items;
import top.r3944realms.lib39.example.content.item.FabricFabricItem;
import top.r3944realms.lib39.example.content.item.ForgeItem;
import top.r3944realms.lib39.example.content.item.FabricNeoForgeItem;
import top.r3944realms.lib39.example.content.item.ForgeItem;
/**
* The type Ex lib 39 items.

View File

@ -9,7 +9,9 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
import top.r3944realms.lib39.core.sync.*;
import top.r3944realms.lib39.core.sync.ILib39SyncDataHolder;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
import java.util.UUID;

View File

@ -1,12 +1,12 @@
package top.r3944realms.lib39.platform;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.ModMetadata;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.platform.services.IHelpCommandHook;
import top.r3944realms.lib39.platform.services.IPlatformHelper;
import net.fabricmc.loader.api.FabricLoader;
import top.r3944realms.lib39.platform.services.IUtilHelper;
import java.util.Objects;

View File

@ -8,7 +8,10 @@ import net.minecraftforge.fml.common.Mod;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**

View File

@ -4,7 +4,6 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
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.content.block.blockentity.DollBlockEntity;

View File

@ -4,7 +4,6 @@ import net.minecraft.world.level.block.Block;
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.content.block.DollBlock;
import top.r3944realms.lib39.util.block.BlockRegistryBuilder;

View File

@ -4,9 +4,7 @@ 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.content.item.DollItem;
import top.r3944realms.lib39.content.item.ForgeDollItem;
/**

View File

@ -1,11 +1,9 @@
package top.r3944realms.lib39.core.register;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
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;
/**

View File

@ -5,7 +5,9 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.capabilities.Capability;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
* The type Sync data 2 manager.

View File

@ -2,7 +2,6 @@ package top.r3944realms.lib39.example.compat;
import net.minecraftforge.eventbus.api.IEventBus;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.compat.CompatManager;
import top.r3944realms.lib39.core.compat.ForgeCompatManager;
public class Lib39CompatManager extends ForgeCompatManager {

View File

@ -1,6 +1,5 @@
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;

View File

@ -1,2 +0,0 @@
// 1.20.1 2026-01-16T18:15:18.0777323 Languages: zh_tw
bd0f71ef9ae49f69627d3122efa3b426a89ecbdf assets/lib39/lang/zh_tw.json

View File

@ -1,2 +0,0 @@
// 1.20.1 2026-01-16T18:15:18.0777323 Languages: zh_cn
5d897b4cc975c19532c77ec60fd1eb487820a69d assets/lib39/lang/zh_cn.json

View File

@ -1,5 +0,0 @@
// 1.20.1 2026-01-02T15:14:31.7762095 Item Models: lib39
663f22009a9420c3eeae3c829fc9f37d16f0901b assets/lib39/models/item/doll.json
14f581c8f8e7f0de004c57a180f371e60e7b12ae assets/lib39/models/item/fabric.json
70583055336790fc837836ea6b49d16cfc8b64b8 assets/lib39/models/item/forge.json
447b36747d0aa8748dcd86715f4cce2cff19aca7 assets/lib39/models/item/neoforge.json

View File

@ -1,2 +0,0 @@
// 1.20.1 2026-01-02T15:29:13.3418663 Block States: lib39
af9aae34357e6c630ed5037a6993ff211eba37ee assets/lib39/blockstates/doll.json

View File

@ -1,2 +0,0 @@
// 1.20.1 2026-01-02T15:11:02.2242532 Loot Tables
ac8ee9efcf316b2181fb0b56946ff6805b40c6f0 data/lib39/loot_tables/blocks/doll.json

View File

@ -1,2 +0,0 @@
// 1.20.1 2026-01-02T20:50:48.567559 Sound Definitions
474de39bdc277c91961609c6bddec279a755c846 assets/lib39/sounds.json

View File

@ -1,2 +0,0 @@
// 1.20.1 2026-01-16T18:15:18.0777323 Languages: lzh
4116ca2d84d4244b49a9ff8190c2faf0f2e57dc4 assets/lib39/lang/lzh.json

View File

@ -1,3 +0,0 @@
// 1.20.1 2026-01-02T20:34:46.0679751 Recipes
d65b7847071b919cdda2351bee53d2693de856c8 data/lib39/advancements/recipes/misc/doll.json
fbb6d29d5bfc1a2cef3888165671ac5e12ecc40f data/lib39/recipes/doll.json

View File

@ -1 +0,0 @@
// 1.20.1 2026-01-02T15:11:02.2272522 Block Models: lib39

View File

@ -1,2 +0,0 @@
// 1.20.1 2026-01-16T18:15:18.0777323 Languages: en_us
c2a9985bbad60c04fbd5e085bd25bfd00aa8a529 assets/lib39/lang/en_us.json

View File

@ -1,64 +0,0 @@
{
"variants": {
"facing=east,pose=default,waterlogged=false": {
"model": "lib39:block/base_doll",
"y": 90
},
"facing=east,pose=default,waterlogged=true": {
"model": "lib39:block/base_doll",
"y": 90
},
"facing=east,pose=further,waterlogged=false": {
"model": "lib39:block/base_doll",
"y": 90
},
"facing=east,pose=further,waterlogged=true": {
"model": "lib39:block/base_doll",
"y": 90
},
"facing=north,pose=default,waterlogged=false": {
"model": "lib39:block/base_doll"
},
"facing=north,pose=default,waterlogged=true": {
"model": "lib39:block/base_doll"
},
"facing=north,pose=further,waterlogged=false": {
"model": "lib39:block/base_doll"
},
"facing=north,pose=further,waterlogged=true": {
"model": "lib39:block/base_doll"
},
"facing=south,pose=default,waterlogged=false": {
"model": "lib39:block/base_doll",
"y": 180
},
"facing=south,pose=default,waterlogged=true": {
"model": "lib39:block/base_doll",
"y": 180
},
"facing=south,pose=further,waterlogged=false": {
"model": "lib39:block/base_doll",
"y": 180
},
"facing=south,pose=further,waterlogged=true": {
"model": "lib39:block/base_doll",
"y": 180
},
"facing=west,pose=default,waterlogged=false": {
"model": "lib39:block/base_doll",
"y": 270
},
"facing=west,pose=default,waterlogged=true": {
"model": "lib39:block/base_doll",
"y": 270
},
"facing=west,pose=further,waterlogged=false": {
"model": "lib39:block/base_doll",
"y": 270
},
"facing=west,pose=further,waterlogged=true": {
"model": "lib39:block/base_doll",
"y": 270
}
}
}

View File

@ -1,60 +0,0 @@
{
"block.lib39.doll": "Doll",
"commands.lib39.calculate": "Calculate sum of two numbers",
"commands.lib39.calculate.result": "%d + %d = %d",
"commands.lib39.config": "Show configuration",
"commands.lib39.debug": "Debug information",
"commands.lib39.demo": "Demo command",
"commands.lib39.demo.message": "This is a demo command showing Lib39 features!",
"commands.lib39.game": "Game control",
"commands.lib39.game.pause": "Pause current game",
"commands.lib39.game.pause.success": "Game paused!",
"commands.lib39.game.resume": "Resume paused game",
"commands.lib39.game.resume.success": "Game resumed!",
"commands.lib39.game.start": "Start a game",
"commands.lib39.game.start.success": "Game '%s' started!",
"commands.lib39.game.stop": "Stop current game",
"commands.lib39.game.stop.success": "Game stopped!",
"commands.lib39.greet.basic": "Greet everyone",
"commands.lib39.greet.default": "Hello everyone from Lib39!",
"commands.lib39.greet.player": "Greet specific player",
"commands.lib39.greet.received": "%s greeted you!",
"commands.lib39.help.basic.help": "Show Help Info",
"commands.lib39.help.click_expand": "Click to expand",
"commands.lib39.help.command_not_found": "Command not found: %s",
"commands.lib39.help.header": "===== %s =====",
"commands.lib39.help.hover.copy": "Copy to clipboard",
"commands.lib39.help.no_entries": "No help entries available",
"commands.lib39.help.node.expand": "%d subcommands collapsed",
"commands.lib39.help.node.toggle.collapse": "Collapse",
"commands.lib39.help.node.toggle.expand": "Expand",
"commands.lib39.help.page_info": "Page %d of %d",
"commands.lib39.help.subcommands_title": "Subcommands:",
"commands.lib39.help.toggle_failed": "Toggle Failed: No Hash Cached",
"commands.lib39.info": "Show player information",
"commands.lib39.info.dimension": "Dimension: %s",
"commands.lib39.info.message": "=== Player Information ===",
"commands.lib39.info.position": "Position: X=%.1f, Y=%.1f, Z=%.1f",
"commands.lib39.reload": "Reload configuration",
"commands.lib39.root": "Lib39 Command System",
"commands.lib39.settings": "Show settings",
"commands.lib39.team": "Team management",
"commands.lib39.team.create": "Create a new team",
"commands.lib39.team.create.success": "Team '%s' created successfully!",
"commands.lib39.team.join": "Join a team",
"commands.lib39.team.join.success": "Joined team '%s'",
"commands.lib39.team.leave": "Leave current team",
"commands.lib39.team.leave.success": "Left the team",
"commands.lib39.teleport": "Teleport to player (OP only)",
"commands.lib39.teleport.success": "Teleported to %s",
"commands.lib39.test": "Test command",
"commands.lib39.test.success": "Test command executed successfully!",
"commands.lib39.test.with_param": "Test command with parameter: %s",
"invalid.player_name.too_long": "§c§lPlayer 's Name is too long than 16 characters.",
"item.lib39.fabric": "Fabric",
"item.lib39.forge": "Forge",
"item.lib39.neoforge": "NeoForge",
"sound.lib39.subtitle.duck_toy": "Duck Doll Sound",
"tooltip.lib39.content.doll.hover.1": "§eSkinOwner §7:§a %s ",
"tooltip.lib39.content.doll.hover.2": "§7Rename with a player name in an anvil to change skin"
}

View File

@ -1,60 +0,0 @@
{
"block.lib39.doll": "偶",
"commands.lib39.calculate": "算二數和",
"commands.lib39.calculate.result": "%d 加 %d 等 %d",
"commands.lib39.config": "示配",
"commands.lib39.debug": "調訊",
"commands.lib39.demo": "演令",
"commands.lib39.demo.message": "此乃展 Lib39 能之演令!",
"commands.lib39.game": "戲控",
"commands.lib39.game.pause": "暫現戲",
"commands.lib39.game.pause.success": "戲已暫!",
"commands.lib39.game.resume": "復暫戲",
"commands.lib39.game.resume.success": "戲已復!",
"commands.lib39.game.start": "啟戲",
"commands.lib39.game.start.success": "戲 '%s' 已啟!",
"commands.lib39.game.stop": "止現戲",
"commands.lib39.game.stop.success": "戲已止!",
"commands.lib39.greet.basic": "問眾安",
"commands.lib39.greet.default": "自 Lib39 問安!",
"commands.lib39.greet.player": "問特者安",
"commands.lib39.greet.received": "%s 問汝安!",
"commands.lib39.help.basic.help": "示助之訊",
"commands.lib39.help.click_expand": "點展",
"commands.lib39.help.command_not_found": "令未見之: %s",
"commands.lib39.help.header": " %s ",
"commands.lib39.help.hover.copy": "點之複刻",
"commands.lib39.help.no_entries": "尚無助之目錄",
"commands.lib39.help.node.expand": "%d 子令已收",
"commands.lib39.help.node.toggle.collapse": "收",
"commands.lib39.help.node.toggle.expand": "展",
"commands.lib39.help.page_info": "第 %d 卷,凡 %d 卷",
"commands.lib39.help.subcommands_title": "子令:",
"commands.lib39.help.toggle_failed": "變更未果: 無貯存之哈希",
"commands.lib39.info": "示者訊",
"commands.lib39.info.dimension": "界:%s",
"commands.lib39.info.message": " 者訊 ",
"commands.lib39.info.position": "位X=%.1f, Y=%.1f, Z=%.1f",
"commands.lib39.reload": "重載配",
"commands.lib39.root": "Lib39 令系",
"commands.lib39.settings": "示置",
"commands.lib39.team": "隊管",
"commands.lib39.team.create": "創新隊",
"commands.lib39.team.create.success": "隊 '%s' 創新成!",
"commands.lib39.team.join": "入隊",
"commands.lib39.team.join.success": "已入隊 '%s'",
"commands.lib39.team.leave": "離現隊",
"commands.lib39.team.leave.success": "已離隊",
"commands.lib39.teleport": "送至者(唯管)",
"commands.lib39.teleport.success": "已送至 %s",
"commands.lib39.test": "試令",
"commands.lib39.test.success": "試令行成!",
"commands.lib39.test.with_param": "帶參試令:%s",
"invalid.player_name.too_long": "§c§l玩家名過長限十六字",
"item.lib39.fabric": "織",
"item.lib39.forge": "砧",
"item.lib39.neoforge": "狸",
"sound.lib39.subtitle.duck_toy": "偶音",
"tooltip.lib39.content.doll.hover.1": "§e膚主§7:§a%s",
"tooltip.lib39.content.doll.hover.2": "§7鐵砧之上更名以易膚"
}

View File

@ -1,60 +0,0 @@
{
"block.lib39.doll": "人偶",
"commands.lib39.calculate": "計算兩個數字的和",
"commands.lib39.calculate.result": "%d + %d = %d",
"commands.lib39.config": "顯示配置",
"commands.lib39.debug": "調試信息",
"commands.lib39.demo": "演示命令",
"commands.lib39.demo.message": "這是一個展示 Lib39 功能的演示命令!",
"commands.lib39.game": "遊戲控制",
"commands.lib39.game.pause": "暫停當前遊戲",
"commands.lib39.game.pause.success": "遊戲已暫停!",
"commands.lib39.game.resume": "恢復暫停的遊戲",
"commands.lib39.game.resume.success": "遊戲已恢復!",
"commands.lib39.game.start": "開始遊戲",
"commands.lib39.game.start.success": "遊戲 '%s' 已開始!",
"commands.lib39.game.stop": "停止當前遊戲",
"commands.lib39.game.stop.success": "遊戲已停止!",
"commands.lib39.greet.basic": "向大家問好",
"commands.lib39.greet.default": "來自 Lib39 的問候!",
"commands.lib39.greet.player": "問候特定玩家",
"commands.lib39.greet.received": "%s 向你問好!",
"commands.lib39.help.basic.help": "显示帮助信息",
"commands.lib39.help.click_expand": "點擊展開",
"commands.lib39.help.command_not_found": "命令不存在: %s",
"commands.lib39.help.header": "===== %s 命令帮助 =====",
"commands.lib39.help.hover.copy": "点击复制",
"commands.lib39.help.no_entries": "暂无帮助条目",
"commands.lib39.help.node.expand": "%d 个子命令已折叠",
"commands.lib39.help.node.toggle.collapse": "折叠",
"commands.lib39.help.node.toggle.expand": "展开",
"commands.lib39.help.page_info": "第 %d 页,共 %d 页",
"commands.lib39.help.subcommands_title": "子命令:",
"commands.lib39.help.toggle_failed": "切换失败: 无缓存Hash",
"commands.lib39.info": "顯示玩家信息",
"commands.lib39.info.dimension": "維度:%s",
"commands.lib39.info.message": "=== 玩家信息 ===",
"commands.lib39.info.position": "位置X=%.1f, Y=%.1f, Z=%.1f",
"commands.lib39.reload": "重新加載配置",
"commands.lib39.root": "Lib39 命令系統",
"commands.lib39.settings": "顯示設置",
"commands.lib39.team": "隊伍管理",
"commands.lib39.team.create": "創建新隊伍",
"commands.lib39.team.create.success": "隊伍 '%s' 創建成功!",
"commands.lib39.team.join": "加入隊伍",
"commands.lib39.team.join.success": "已加入隊伍 '%s'",
"commands.lib39.team.leave": "離開當前隊伍",
"commands.lib39.team.leave.success": "已離開隊伍",
"commands.lib39.teleport": "傳送到玩家僅OP",
"commands.lib39.teleport.success": "已傳送至 %s",
"commands.lib39.test": "測試命令",
"commands.lib39.test.success": "測試命令執行成功!",
"commands.lib39.test.with_param": "帶參數的測試命令:%s",
"invalid.player_name.too_long": "§c§l玩家名称过长最多16个字符",
"item.lib39.fabric": "织布",
"item.lib39.forge": "铁砧",
"item.lib39.neoforge": "小狐狸",
"sound.lib39.subtitle.duck_toy": "玩偶声音",
"tooltip.lib39.content.doll.hover.1": "§e皮肤所有者§7:§a%s",
"tooltip.lib39.content.doll.hover.2": "§7在铁砧上可通过重命名对应玩家名来改变皮肤"
}

View File

@ -1,60 +0,0 @@
{
"block.lib39.doll": "人偶",
"commands.lib39.calculate": "計算兩個數字的和",
"commands.lib39.calculate.result": "%d + %d = %d",
"commands.lib39.config": "顯示設定",
"commands.lib39.debug": "除錯資訊",
"commands.lib39.demo": "演示指令",
"commands.lib39.demo.message": "這是一個展示 Lib39 功能的演示指令!",
"commands.lib39.game": "遊戲控制",
"commands.lib39.game.pause": "暫停當前遊戲",
"commands.lib39.game.pause.success": "遊戲已暫停!",
"commands.lib39.game.resume": "恢復暫停的遊戲",
"commands.lib39.game.resume.success": "遊戲已恢復!",
"commands.lib39.game.start": "開始遊戲",
"commands.lib39.game.start.success": "遊戲 '%s' 已開始!",
"commands.lib39.game.stop": "停止當前遊戲",
"commands.lib39.game.stop.success": "遊戲已停止!",
"commands.lib39.greet.basic": "向大家問好",
"commands.lib39.greet.default": "來自 Lib39 的問候!",
"commands.lib39.greet.player": "問候特定玩家",
"commands.lib39.greet.received": "%s 向你問好!",
"commands.lib39.help.basic.help": "顯示幫助信息",
"commands.lib39.help.click_expand": "點擊展開",
"commands.lib39.help.command_not_found": "指令不存在: %s",
"commands.lib39.help.header": "===== %s 命令幫助 =====",
"commands.lib39.help.hover.copy": "點擊複製",
"commands.lib39.help.no_entries": "暫無幫助條目",
"commands.lib39.help.node.expand": "%d 個子指令已折疊",
"commands.lib39.help.node.toggle.collapse": "折疊",
"commands.lib39.help.node.toggle.expand": "展開",
"commands.lib39.help.page_info": "第 %d 頁,共 %d 頁",
"commands.lib39.help.subcommands_title": "子指令:",
"commands.lib39.help.toggle_failed": "切換失敗: 無緩存Hash",
"commands.lib39.info": "顯示玩家資訊",
"commands.lib39.info.dimension": "維度:%s",
"commands.lib39.info.message": "=== 玩家資訊 ===",
"commands.lib39.info.position": "位置X=%.1f, Y=%.1f, Z=%.1f",
"commands.lib39.reload": "重新載入設定",
"commands.lib39.root": "Lib39 指令系統",
"commands.lib39.settings": "顯示設定",
"commands.lib39.team": "隊伍管理",
"commands.lib39.team.create": "創建新隊伍",
"commands.lib39.team.create.success": "隊伍 '%s' 創建成功!",
"commands.lib39.team.join": "加入隊伍",
"commands.lib39.team.join.success": "已加入隊伍 '%s'",
"commands.lib39.team.leave": "離開當前隊伍",
"commands.lib39.team.leave.success": "已離開隊伍",
"commands.lib39.teleport": "傳送到玩家僅OP",
"commands.lib39.teleport.success": "已傳送至 %s",
"commands.lib39.test": "測試指令",
"commands.lib39.test.success": "測試指令執行成功!",
"commands.lib39.test.with_param": "帶參數的測試指令:%s",
"invalid.player_name.too_long": "§c§l玩家名稱過長最多16個字符",
"item.lib39.fabric": "織布",
"item.lib39.forge": "铁砧",
"item.lib39.neoforge": "狐狸",
"sound.lib39.subtitle.duck_toy": "玩偶聲音",
"tooltip.lib39.content.doll.hover.1": "§e皮膚所有者§7:§a%s",
"tooltip.lib39.content.doll.hover.2": "§7在鐵砧上可通過重命名對應玩家名來改變皮膚"
}

View File

@ -1,3 +0,0 @@
{
"parent": "lib39:block/base_doll"
}

View File

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

View File

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

View File

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

View File

@ -1,8 +0,0 @@
{
"duck_toy": {
"sounds": [
"lib39:duck_toy"
],
"subtitle": "sound.lib39.subtitle.duck_toy"
}
}

View File

@ -1,35 +0,0 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_armor_stand": {
"conditions": {
"items": [
{
"items": [
"minecraft:armor_stand"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "lib39:doll"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_armor_stand",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"lib39:doll"
]
},
"sends_telemetry_event": false
}

View File

@ -1,21 +0,0 @@
{
"type": "minecraft:block",
"pools": [
{
"bonus_rolls": 0.0,
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
],
"entries": [
{
"type": "minecraft:item",
"name": "lib39:doll"
}
],
"rolls": 1.0
}
],
"random_sequence": "lib39:blocks/doll"
}

View File

@ -1,15 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
{
"tag": "minecraft:wool"
},
{
"item": "minecraft:armor_stand"
}
],
"result": {
"item": "lib39:doll"
}
}

View File

@ -1,140 +0,0 @@
package top.r3944realms.lib39;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLEnvironment;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.r3944realms.lib39.content.register.Lib39BlockEntities;
import top.r3944realms.lib39.content.register.Lib39Blocks;
import top.r3944realms.lib39.content.register.Lib39Items;
import top.r3944realms.lib39.content.register.Lib39SoundEvents;
import top.r3944realms.lib39.core.network.NetworkHandler;
import top.r3944realms.lib39.example.Lib39Example;
/**
* The type Lib 39.
*/
@Mod(Lib39.MOD_ID)
public class Lib39 {
/**
* The constant MOD_ID.
*/
public static final String MOD_ID = "lib39";
/**
* The constant LOGGER.
*/
public static final Logger LOGGER = LoggerFactory.getLogger(Lib39.class);
/**
* Instantiates a new Lib 39.
*/
public Lib39() {
initialize();
}
/**
* The constant ENABLE_EXAMPLES_PROPERTY_KEY.
*/
public static final String ENABLE_EXAMPLES_PROPERTY_KEY = "lib39.enable_examples";
/**
* Initialize.
*/
public static void initialize() {
LOGGER.info("[Lib39] Initializing Lib39");
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
Lib39Blocks.register(modEventBus);
Lib39BlockEntities.register(modEventBus);
Lib39Items.register(modEventBus);
Lib39SoundEvents.register(modEventBus);
NetworkHandler.register();
if (shouldRegisterExamples()) {
LOGGER.info("[Lib39] Registering Examples");
registerExamples();
}
LOGGER.info("[Lib39] Initialized Lib39");
}
/**
* Rl resource location.
*
* @param path the path
* @return the resource location
*/
@Contract("_ -> new")
public static @NotNull ResourceLocation rl(String path) {
return new ResourceLocation(MOD_ID, path);
}
/**
* Rl resource location.
*
* @param modId the mod id
* @param path the path
* @return the resource location
*/
@Contract("_, _ -> new")
public static @NotNull ResourceLocation rl(String modId, String path) {
return new ResourceLocation(modId, path);
}
/**
* Mrl resource location.
*
* @param path the path
* @return the resource location
*/
@Contract("_ -> new")
public static @NotNull ResourceLocation mrl(String path) {
return new ResourceLocation(path);
}
/**
* The type Mod info.
*/
public static class ModInfo {
/**
* The constant VERSION.
*/
public static final String VERSION;
static {
// ModList 获取当前 ModContainer 的元数据
VERSION = ModList.get()
.getModContainerById(MOD_ID)
.map(c -> c.getModInfo().getVersion().toString())
.orElse("UNKNOWN");
}
}
/**
* Should register examples boolean.
*
* @return the boolean
*/
public static boolean shouldRegisterExamples() {
return !FMLEnvironment.production || Boolean.getBoolean(ENABLE_EXAMPLES_PROPERTY_KEY);
}
/**
* Register examples.
*/
static void registerExamples() {
LOGGER.info("[Lib39] Starting example demonstrations");
try {
// 创建示例实例并演示功能
Lib39Example example = new Lib39Example();
example.demonstrateFeature();
LOGGER.info("[Lib39] Example demonstrations completed successfully");
} catch (Exception e) {
LOGGER.error("[Lib39] Failed to demonstrate examples", e);
}
}
}

View File

@ -1,31 +0,0 @@
package top.r3944realms.lib39.api.event;
import net.minecraft.server.Services;
import net.minecraftforge.eventbus.api.Event;
import java.util.concurrent.Executor;
/**
* The type Minecraft set up service event.
*/
public class MinecraftSetUpServiceEvent extends Event {
/**
* The Services.
*/
public final Services services;
/**
* The Main thread executor.
*/
public final Executor mainThreadExecutor;
/**
* Instantiates a new Minecraft set up service event.
*
* @param services the services
* @param mainThreadExecutor the main thread executor
*/
public MinecraftSetUpServiceEvent(Services services, Executor mainThreadExecutor) {
this.services = services;
this.mainThreadExecutor = mainThreadExecutor;
}
}

View File

@ -1,100 +0,0 @@
package top.r3944realms.lib39.api.event;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.eventbus.api.Event;
import top.r3944realms.lib39.core.command.ICommandHelpManager;
import top.r3944realms.lib39.core.command.model.CommandNode;
import top.r3944realms.lib39.core.command.model.CommandPath;
import top.r3944realms.lib39.core.command.model.Parameter;
/**
* The type Register command help event.
*/
public class RegisterCommandHelpEvent extends Event {
private final LiteralArgumentBuilder<CommandSourceStack> builder;
private final ICommandHelpManager helpManager;
private final CommandBuildContext context;
/**
* Instantiates a new Register command help event.
*
* @param builder the builder
* @param helpManager the help manager
* @param source the source
*/
public RegisterCommandHelpEvent(LiteralArgumentBuilder<CommandSourceStack> builder, ICommandHelpManager helpManager, CommandBuildContext source) {
this.builder = builder;
this.helpManager = helpManager;
this.context = source;
}
/**
* Gets id.
*
* @return the id
*/
public ResourceLocation getID() {
return helpManager.getID();
}
/**
* Add child.
*
* @param child the child
*/
public void addChild(LiteralArgumentBuilder<CommandSourceStack> child) {
this.builder.then(child);
}
/**
* Get context command build context.
*
* @return the command build context
*/
public CommandBuildContext getContext(){
return this.context;
}
/**
* Get tree literal argument builder.
*
* @return the literal argument builder
*/
public LiteralArgumentBuilder<CommandSourceStack> getTree(){
return this.builder;
}
/**
* 注册命令帮助信息
*
* @param CommandNode 命令节点
* @param description 命令描述
*/
public void registerHelp(CommandNode CommandNode, MutableComponent description) {
this.helpManager.registerCommandHelp(CommandNode, description);
}
/**
* 注册命令帮助信息
*
* @param CommandNode 命令节点
* @param descriptionKey 命令描述的语言键
*/
public void registerHelp(CommandNode CommandNode, String descriptionKey) {
this.helpManager.registerCommandHelp(CommandNode, descriptionKey);
}
/**
* 注册命令参数
*
* @param commandPath 命令节点
* @param parametersBuilder 参数列表构造器
*/
public void registerParameters(CommandPath commandPath, Parameter.Builder parametersBuilder) {
this.helpManager.registerCommandParameters(commandPath, parametersBuilder);
}
}

View File

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

View File

@ -1,36 +0,0 @@
package top.r3944realms.lib39.base.command;
import net.minecraft.resources.ResourceLocation;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.command.SimpleCommandHelpManager;
/**
* The type Lib 39 command help manager.
*/
public class Lib39CommandHelpManager extends SimpleCommandHelpManager {
/**
* The constant INSTANCE.
*/
public static volatile Lib39CommandHelpManager INSTANCE = new Lib39CommandHelpManager();
/**
* The Id.
*/
ResourceLocation ID = Lib39.rl("command_helper");
/**
* Instantiates a new Lib 39 command help manager.
*/
public Lib39CommandHelpManager() {
initialize();
}
@Override
public ResourceLocation getID() {
return ID;
}
@Override
public String getHeadKey() {
return "lib39";
}
}

View File

@ -1,404 +0,0 @@
package top.r3944realms.lib39.base.command;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.event.RegisterCommandsEvent;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.command.ICommandHelpManager;
import top.r3944realms.lib39.core.command.SimpleHelpCommand;
import java.util.Map;
/**
* The type Lib 39 help command.
*/
public class Lib39HelpCommand extends SimpleHelpCommand {
/**
* Instantiates a new Lib 39 help command.
*
* @param event the event
*/
public Lib39HelpCommand(@NotNull RegisterCommandsEvent event) {
super(event);
if(Lib39.shouldRegisterExamples()) {
// 在這裡註冊測試命令
registerTestCommands(event.getDispatcher());
}
}
@Override
public ICommandHelpManager getCommandHelpManager() {
return Lib39CommandHelpManager.INSTANCE;
}
/**
* 註冊測試命令
*/
private void registerTestCommands(@NotNull CommandDispatcher<CommandSourceStack> dispatcher) {
// 註冊幫助系統本身
dispatcher.register(
getRoot()
.then(Commands.literal("test")
.executes(this::executeTest)
.then(Commands.argument("param", StringArgumentType.string())
.executes(this::executeTestWithParam))
)
.then(Commands.literal("demo")
.executes(this::executeDemo)
)
);
// 註冊其他測試命令
registerTestCommandTree(dispatcher);
// 在幫助系統中註冊這些命令
registerCommandsInHelpSystem();
}
/**
* 註冊測試命令樹
*/
private void registerTestCommandTree(@NotNull CommandDispatcher<CommandSourceStack> dispatcher) {
// 基本命令
dispatcher.register(
Commands.literal("lib39")
.then(Commands.literal("greet")
.executes(this::executeGreet)
.then(Commands.argument("player", EntityArgument.player())
.executes(this::executeGreetPlayer))
)
.then(Commands.literal("calculate")
.then(Commands.argument("a", IntegerArgumentType.integer())
.then(Commands.argument("b", IntegerArgumentType.integer())
.executes(this::executeCalculate)))
)
.then(Commands.literal("teleport")
.requires(source -> source.hasPermission(2)) // 需要OP權限
.then(Commands.argument("target", EntityArgument.player())
.executes(this::executeTeleport))
)
.then(Commands.literal("info")
.executes(this::executeInfo)
)
);
// 嵌套命令示例
dispatcher.register(
Commands.literal("lib39")
.then(Commands.literal("team")
.then(Commands.literal("create")
.then(Commands.argument("teamName", StringArgumentType.string())
.executes(this::executeTeamCreate))
)
.then(Commands.literal("join")
.then(Commands.argument("teamName", StringArgumentType.string())
.executes(this::executeTeamJoin))
)
.then(Commands.literal("leave")
.executes(this::executeTeamLeave))
)
.then(Commands.literal("game")
.then(Commands.literal("start")
.then(Commands.argument("map", StringArgumentType.string())
.executes(this::executeGameStart))
)
.then(Commands.literal("stop")
.executes(this::executeGameStop))
.then(Commands.literal("pause")
.executes(this::executeGamePause))
.then(Commands.literal("resume")
.executes(this::executeGameResume))
)
);
}
/**
* 在幫助系統中註冊命令
*/
private void registerCommandsInHelpSystem() {
ICommandHelpManager helpManager = getCommandHelpManager();
// 使用Builder模式註冊完整的命令樹
helpManager.registerCommands(builder -> {
builder.root("lib39", "commands.lib39.root")
.expanded(true) // 根節點默認展開
// 問候命令 - 添加多個子命令
.branch("greet", "commands.lib39.greet.basic", greetBuilder -> {
greetBuilder.expanded(false); // 默認摺疊
greetBuilder.leaf("hello", "commands.lib39.greet.hello");
greetBuilder.leaf("morning", "commands.lib39.greet.morning");
greetBuilder.leaf("evening", "commands.lib39.greet.evening");
greetBuilder.push("player", "commands.lib39.greet.player")
.required("player")
.pop();
})
// 計算命令
.push("calculate", "commands.lib39.calculate")
.required("a")
.required("b")
.pop()
// 傳送命令
.push("teleport", "commands.lib39.teleport")
.required("target")
.pop()
// 信息命令
.leaf("info", "commands.lib39.info")
// 隊伍系統 - 添加多個子命令
.branch("team", "commands.lib39.team", teamBuilder -> {
teamBuilder.expanded(false); // 默認摺疊
teamBuilder.leaf("create", "commands.lib39.team.create")
.required("teamName");
teamBuilder.leaf("join", "commands.lib39.team.join")
.required("teamName");
teamBuilder.leaf("leave", "commands.lib39.team.leave");
teamBuilder.leaf("list", "commands.lib39.team.list");
teamBuilder.leaf("info", "commands.lib39.team.info");
})
// 遊戲系統 - 添加多個子命令
.branch("game", "commands.lib39.game", gameBuilder -> {
gameBuilder.expanded(false); // 默認摺疊
gameBuilder.leaf("start", "commands.lib39.game.start")
.required("map");
gameBuilder.leaf("stop", "commands.lib39.game.stop");
gameBuilder.leaf("pause", "commands.lib39.game.pause");
gameBuilder.leaf("resume", "commands.lib39.game.resume");
gameBuilder.leaf("status", "commands.lib39.game.status");
})
// 設置命令
.leavesT(Map.of(
"settings", "commands.lib39.settings",
"config", "commands.lib39.config",
"reload", "commands.lib39.reload",
"debug", "commands.lib39.debug",
"demo", "commands.lib39.demo",
"test", "commands.lib39.test"
));
});
}
// ==================== 命令執行方法 ====================
private int executeTest(CommandContext<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.test.success")
.withStyle(net.minecraft.ChatFormatting.GREEN),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeTestWithParam(CommandContext<CommandSourceStack> context) {
String param = StringArgumentType.getString(context, "param");
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.test.with_param", param)
.withStyle(net.minecraft.ChatFormatting.AQUA),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeDemo(CommandContext<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.demo.message")
.withStyle(net.minecraft.ChatFormatting.GOLD),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeGreet(CommandContext<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.greet.default")
.withStyle(net.minecraft.ChatFormatting.YELLOW),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeGreetPlayer(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
ServerPlayer player = EntityArgument.getPlayer(context, "player");
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.greet.player", player.getDisplayName())
.withStyle(net.minecraft.ChatFormatting.GREEN),
false
);
player.sendSystemMessage(
Component.translatable("commands.lib39.greet.received", source.getDisplayName())
.withStyle(net.minecraft.ChatFormatting.AQUA)
);
return Command.SINGLE_SUCCESS;
}
private int executeCalculate(CommandContext<CommandSourceStack> context) {
int a = IntegerArgumentType.getInteger(context, "a");
int b = IntegerArgumentType.getInteger(context, "b");
int sum = a + b;
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.calculate.result", a, b, sum)
.withStyle(net.minecraft.ChatFormatting.LIGHT_PURPLE),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeTeleport(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
ServerPlayer target = EntityArgument.getPlayer(context, "target");
CommandSourceStack source = context.getSource();
if (source.getEntity() instanceof ServerPlayer player) {
player.teleportTo(
target.serverLevel(),
target.getX(),
target.getY(),
target.getZ(),
target.getYRot(),
target.getXRot()
);
source.sendSuccess(() ->
Component.translatable("commands.lib39.teleport.success", target.getDisplayName())
.withStyle(net.minecraft.ChatFormatting.GREEN),
false
);
}
return Command.SINGLE_SUCCESS;
}
private int executeInfo(CommandContext<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
ResourceLocation dimension = source.getLevel().dimension().location();
source.sendSuccess(() ->
Component.translatable("commands.lib39.info.message")
.append("\n")
.append(Component.translatable("commands.lib39.info.dimension", dimension))
.append("\n")
.append(Component.translatable("commands.lib39.info.position",
String.format("%.1f", source.getPosition().x()),
String.format("%.1f", source.getPosition().y()),
String.format("%.1f", source.getPosition().z())))
.withStyle(net.minecraft.ChatFormatting.AQUA),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeTeamCreate(CommandContext<CommandSourceStack> context) {
String teamName = StringArgumentType.getString(context, "teamName");
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.team.create.success", teamName)
.withStyle(net.minecraft.ChatFormatting.GREEN),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeTeamJoin(CommandContext<CommandSourceStack> context) {
String teamName = StringArgumentType.getString(context, "teamName");
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.team.join.success", teamName)
.withStyle(net.minecraft.ChatFormatting.GREEN),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeTeamLeave(CommandContext<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.team.leave.success")
.withStyle(net.minecraft.ChatFormatting.YELLOW),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeGameStart(CommandContext<CommandSourceStack> context) {
String map = StringArgumentType.getString(context, "map");
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.game.start.success", map)
.withStyle(net.minecraft.ChatFormatting.GREEN),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeGameStop(CommandContext<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.game.stop.success")
.withStyle(net.minecraft.ChatFormatting.RED),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeGamePause(CommandContext<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.game.pause.success")
.withStyle(net.minecraft.ChatFormatting.YELLOW),
false
);
return Command.SINGLE_SUCCESS;
}
private int executeGameResume(CommandContext<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
source.sendSuccess(() ->
Component.translatable("commands.lib39.game.resume.success")
.withStyle(net.minecraft.ChatFormatting.GREEN),
false
);
return Command.SINGLE_SUCCESS;
}
}

View File

@ -1,85 +0,0 @@
package top.r3944realms.lib39.base.datagen;
import net.minecraft.data.DataProvider;
import net.minecraftforge.data.event.GatherDataEvent;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.base.datagen.provider.*;
import top.r3944realms.lib39.base.datagen.value.Lib39LangKey;
import top.r3944realms.lib39.datagen.provider.SimpleLanguageProvider;
import top.r3944realms.lib39.datagen.provider.SimpleLootTableProvider;
import top.r3944realms.lib39.datagen.provider.SubProvidersWrapper;
import top.r3944realms.lib39.datagen.value.McLocale;
/**
* The type Lib 39 base data gen event.
*/
public class Lib39BaseDataGenEvent {
/**
* The Logger.
*/
static Logger logger = LoggerFactory.getLogger(Lib39BaseDataGenEvent.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);
BlockModelDataGenerate(event);
BlockStateDataGenerate(event);
ItemModelDataGenerate(event);
LootTableDataGenerate(event);
SoundDefinitionDataGenerate(event);
RecipeGenerator(event);
}
private static void LanguageGenerator(@NotNull GatherDataEvent event, McLocale language) {
event.getGenerator().addProvider(
event.includeClient(),
(DataProvider.Factory<SimpleLanguageProvider>) pOutput -> new SimpleLanguageProvider(pOutput, Lib39.MOD_ID ,language , Lib39LangKey.INSTANCE)
);
}
private static void ItemModelDataGenerate(@NotNull GatherDataEvent event) {
event.getGenerator().addProvider(
event.includeClient(),
(DataProvider.Factory<Lib39ItemModelProvider>) pOutput -> new Lib39ItemModelProvider(pOutput, event.getExistingFileHelper())
);
}
private static void BlockModelDataGenerate(@NotNull GatherDataEvent event) {
event.getGenerator().addProvider(
event.includeClient(),
(DataProvider.Factory<Lib39BlockModelProvider>) pOutput -> new Lib39BlockModelProvider(pOutput, event.getExistingFileHelper())
);
}
private static void BlockStateDataGenerate(@NotNull GatherDataEvent event) {
event.getGenerator().addProvider(
event.includeClient(),
(DataProvider.Factory<Lib39BlockStatesProvider>) pOutput -> new Lib39BlockStatesProvider(pOutput, event.getExistingFileHelper())
);
}
private static void SoundDefinitionDataGenerate(@NotNull GatherDataEvent event) {
event.getGenerator().addProvider(
event.includeClient(),
(DataProvider.Factory<Lib39SoundDefinitionsProvider>) pOutput -> new Lib39SoundDefinitionsProvider(pOutput, event.getExistingFileHelper())
);
}
private static void LootTableDataGenerate(@NotNull GatherDataEvent event) {
event.getGenerator().addProvider(
event.includeServer(),
(DataProvider.Factory<SimpleLootTableProvider>) pOutput -> new SimpleLootTableProvider(pOutput, new SubProvidersWrapper().addBlockEntry(new Lib39BlockLootTable()))
);
}
private static void RecipeGenerator(@NotNull GatherDataEvent event) {
event.getGenerator().addProvider(
event.includeServer(),
(DataProvider.Factory<Lib39RecipeProvider>) Lib39RecipeProvider::new
);
}
}

View File

@ -1,17 +0,0 @@
package top.r3944realms.lib39.base.datagen.provider;
import top.r3944realms.lib39.content.register.Lib39Blocks;
import top.r3944realms.lib39.datagen.provider.subprovider.BlockLootTables;
/**
* The type Lib 39 block loot table.
*/
public class Lib39BlockLootTable extends BlockLootTables {
/**
* Instantiates a new Lib 39 block loot table.
*/
public Lib39BlockLootTable() {
super(Lib39Blocks.BLOCKS);
dropSelf(Lib39Blocks.DOLL);
}
}

View File

@ -1,44 +0,0 @@
package top.r3944realms.lib39.base.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.client.model.generators.BlockModelProvider;
import net.minecraftforge.common.data.ExistingFileHelper;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.util.PlantHelper;
/**
* The type Lib 39 block model provider.
*/
public class Lib39BlockModelProvider extends BlockModelProvider {
/**
* Instantiates a new Lib 39 block model provider.
*
* @param output the output
* @param existingFileHelper the existing file helper
*/
public Lib39BlockModelProvider(PackOutput output, ExistingFileHelper existingFileHelper) {
super(output, Lib39.MOD_ID, existingFileHelper);
}
@Override
protected void registerModels() {
// registerPlants();
}
/**
* Register plants.
*/
protected void registerPlants() {
for (PlantHelper.Plant plant: PlantHelper.Plant.values()) {
createPlantsModel(plant);
}
}
private void createPlantsModel(PlantHelper.Plant plant) {
ResourceLocation rl = PlantHelper.getTextureRL(plant);
getBuilder("block/doll_item/" + plant)
.parent(getExistingFile(Lib39.rl("block/base_doll_item")))
.texture("item", rl)
.ao(false);
}
}

View File

@ -1,74 +0,0 @@
package top.r3944realms.lib39.base.datagen.provider;
import net.minecraft.core.Direction;
import net.minecraft.data.PackOutput;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraftforge.client.model.generators.BlockStateProvider;
import net.minecraftforge.client.model.generators.ConfiguredModel;
import net.minecraftforge.client.model.generators.ModelFile;
import net.minecraftforge.common.data.ExistingFileHelper;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.content.register.Lib39Blocks;
/**
* The type Lib 39 block states provider.
*/
public class Lib39BlockStatesProvider extends BlockStateProvider {
/**
* Instantiates a new Lib 39 block states provider.
*
* @param output the output
* @param exFileHelper the ex file helper
*/
public Lib39BlockStatesProvider(PackOutput output, ExistingFileHelper exFileHelper) {
super(output, Lib39.MOD_ID, exFileHelper);
}
@Override
protected void registerStatesAndModels() {
generateDollBlockStatesSimple();
}
private void generateDollBlockStatesSimple() {
Block doll = Lib39Blocks.DOLL.get();
// 创建GeckoLib模型引用
ModelFile modelFile = new ModelFile.ExistingModelFile(
Lib39.rl( "block/base_doll"),
models().existingFileHelper
);
getVariantBuilder(doll).forAllStates(state -> {
Direction direction = state.getValue(BlockStateProperties.HORIZONTAL_FACING);
int rotationY = getMainNorthRotationY(direction);
return ConfiguredModel.builder()
.modelFile(modelFile)
.rotationY(rotationY)
.build();
});
}
@Contract(pure = true)
private int getMainWestRotationY(@NotNull Direction direction) {
return switch (direction) {
case WEST -> 0; // 西 - 基准方向0度
case NORTH -> 90; // - 相对于西旋转90度
case EAST -> 180; // - 相对于西旋转180度
case SOUTH -> 270; // - 相对于西旋转270度
default -> 0;
};
}
@Contract(pure = true)
private int getMainNorthRotationY(@NotNull Direction direction) {
return switch (direction) {
case WEST -> 270; // 西 - 基准方向270度
case NORTH -> 0; // - 相对于西旋转0度
case EAST -> 90; // - 相对于西旋转90度
case SOUTH -> 180; // - 相对于西旋转180度
default -> 0;
};
}
}

View File

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

View File

@ -1,36 +0,0 @@
package top.r3944realms.lib39.base.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraft.data.recipes.FinishedRecipe;
import net.minecraft.data.recipes.RecipeCategory;
import net.minecraft.data.recipes.RecipeProvider;
import net.minecraft.data.recipes.ShapelessRecipeBuilder;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.item.Items;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.content.register.Lib39Items;
import java.util.function.Consumer;
/**
* The type Lib 39 recipe provider.
*/
public class Lib39RecipeProvider extends RecipeProvider {
/**
* Instantiates a new Lib 39 recipe provider.
*
* @param output the output
*/
public Lib39RecipeProvider(PackOutput output) {
super(output);
}
@Override
protected void buildRecipes(@NotNull Consumer<FinishedRecipe> consumer) {
ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, Lib39Items.DOLL.get())
.requires(ItemTags.WOOL)
.requires(Items.ARMOR_STAND)
.unlockedBy("has_armor_stand",has(Items.ARMOR_STAND))
.save(consumer);
}
}

View File

@ -1,45 +0,0 @@
package top.r3944realms.lib39.base.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.common.data.SoundDefinition;
import net.minecraftforge.common.data.SoundDefinitionsProvider;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.content.register.Lib39SoundEvents;
/**
* The type Lib 39 sound definitions provider.
*/
public class Lib39SoundDefinitionsProvider extends SoundDefinitionsProvider {
/**
* Instantiates a new Lib 39 sound definitions provider.
*
* @param output the output
* @param helper the helper
*/
public Lib39SoundDefinitionsProvider(PackOutput output, ExistingFileHelper helper) {
super(output, Lib39.MOD_ID, helper);
}
/**
* Gets sound definition.
*
* @param subTitle the sub title
* @param sounds the sounds
* @return the sound definition
*/
public SoundDefinition getSoundDefinition(String subTitle, SoundDefinition.Sound... sounds) {
return SoundDefinition.definition().subtitle(subTitle).with(sounds);
}
@Override
public void registerSounds() {
add(
Lib39SoundEvents.DUCK_TOY,
getSoundDefinition(
Lib39SoundEvents.getSubTitleTranslateKey("duck_toy"),
sound(Lib39SoundEvents.RL_DUCK_TOY, SoundDefinition.SoundType.SOUND)
)
);
}
}

View File

@ -1,874 +0,0 @@
package top.r3944realms.lib39.base.datagen.value;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Unmodifiable;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.content.register.Lib39Blocks;
import top.r3944realms.lib39.content.register.Lib39Items;
import top.r3944realms.lib39.content.register.Lib39SoundEvents;
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.HashSet;
import java.util.List;
import java.util.Set;
/**
* The enum Lib 39 lang key.
*/
public enum Lib39LangKey implements ILangKeyValueCollection {
/**
* Instance lib 39 lang key.
*/
INSTANCE;
Lib39LangKey() {
initLangKeyValues();
}
/**
* The type Message.
*/
public static final class Message {
private static final Set<LangKeyValue> items = new HashSet<>();
/**
* The constant HELP_HEADER.
*/
public static final LangKeyValue HELP_HEADER = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.header", ModPartEnum.MESSAGE,
"===== %s =====",
"===== %s 命令帮助 =====",
"===== %s 命令幫助 =====",
" %s ", // 文言文表示分隔線
true
)
);
/**
* The constant HELP_CLICK_EXPAND.
*/
public static final LangKeyValue HELP_CLICK_EXPAND = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.click_expand", ModPartEnum.MESSAGE,
"Click to expand",
"點擊展開",
"點擊展開",
"點展",
true
)
);
/**
* The constant HELP_PAGE_INFO.
*/
public static final LangKeyValue HELP_PAGE_INFO = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.page_info", ModPartEnum.MESSAGE,
"Page %d of %d",
"第 %d 页,共 %d 页",
"第 %d 頁,共 %d 頁",
"第 %d 卷,凡 %d 卷", // 文言文表示頁
true
)
);
/**
* The constant HELP_NO_ENTRIES.
*/
public static final LangKeyValue HELP_NO_ENTRIES = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.no_entries", ModPartEnum.MESSAGE,
"No help entries available",
"暂无帮助条目",
"暫無幫助條目",
"尚無助之目錄", // 文言文尚無幫助的目錄
true
)
);
/**
* The constant HELP_TOGGLE_FAILED.
*/
public static final LangKeyValue HELP_TOGGLE_FAILED = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.toggle_failed", ModPartEnum.MESSAGE,
"Toggle Failed: No Hash Cached",
"切换失败: 无缓存Hash",
"切換失敗: 無緩存Hash",
"變更未果: 無貯存之哈希", // 文言文變更沒有成功沒有貯存的哈希
true
)
);
/**
* The constant HELP_COMMAND_NOT_FOUND.
*/
public static final LangKeyValue HELP_COMMAND_NOT_FOUND = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.command_not_found", ModPartEnum.MESSAGE,
"Command not found: %s",
"命令不存在: %s",
"指令不存在: %s",
"令未見之: %s", // 文言文命令沒有見到
true
)
);
/**
* The constant DOLL_SOUND.
*/
public static final LangKeyValue DOLL_SOUND = addAndRet(
LangKeyValue.ofKey(
Lib39SoundEvents.getSubTitleTranslateKey("duck_toy"), ModPartEnum.SOUND,
"Duck Doll Sound",
"玩偶声音",
"玩偶聲音",
"偶音",
true
)
);
/**
* The constant HELP_SUBCOMMANDS_TITLE.
*/
public static final LangKeyValue HELP_SUBCOMMANDS_TITLE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.subcommands_title", ModPartEnum.MESSAGE,
"Subcommands:",
"子命令:",
"子指令:",
"子令:", // 文言文子命令
true
)
);
/**
* The constant HELP_NODE_EXPAND.
*/
public static final LangKeyValue HELP_NODE_EXPAND = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.node.expand", ModPartEnum.MESSAGE,
"%d subcommands collapsed",
"%d 个子命令已折叠",
"%d 個子指令已折疊",
"%d 子令已收", // 文言文子命令已經收起
true
)
);
/**
* The constant HELP_NODE_TOGGLE_EXPAND.
*/
public static final LangKeyValue HELP_NODE_TOGGLE_EXPAND = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.node.toggle.expand", ModPartEnum.MESSAGE,
"Expand",
"展开",
"展開",
"", // 文言文展開
true
)
);
/**
* The constant HELP_NODE_TOGGLE_COLLAPSE.
*/
public static final LangKeyValue HELP_NODE_TOGGLE_COLLAPSE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.node.toggle.collapse", ModPartEnum.MESSAGE,
"Collapse",
"折叠",
"折疊",
"", // 文言文收起
true
)
);
/**
* The constant BASIC_HELP.
*/
public static final LangKeyValue BASIC_HELP = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.basic.help", ModPartEnum.MESSAGE,
"Show Help Info",
"显示帮助信息",
"顯示幫助信息",
"示助之訊", // 文言文顯示幫助的訊息
true
)
);
/**
* The constant HELP_HOVER_COPY_TIP.
*/
public static final LangKeyValue HELP_HOVER_COPY_TIP = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.help.hover.copy", ModPartEnum.MESSAGE,
"Copy to clipboard",
"点击复制",
"點擊複製",
"點之複刻", // 文言文點之複刻
true
)
);
private static LangKeyValue addAndRet(LangKeyValue item) {
items.add(item);
return item;
}
/**
* Gets items.
*
* @return the items
*/
public static Set<LangKeyValue> getItems() {
return items;
}
}
/**
* The Lang key values.
*/
final List<LangKeyValue> langKeyValues = new ArrayList<>();
/**
* Init lang key values.
*/
public void initLangKeyValues() {
Message.getItems().forEach(this::addLang);
LangKeyValue dollName = LangKeyValue.ofSupplier(
Lib39Items.DOLL, ModPartEnum.ITEM,
"Doll", "人偶", "人偶", "", false
);
addLang(dollName);
addLang(LangKeyValue.copyOf(
Lib39Blocks.DOLL, ModPartEnum.BLOCK, dollName
));
addLang(LangKeyValue.ofKey(
"tooltip.lib39.content.doll.hover.1", ModPartEnum.DESCRIPTION,
"§eSkinOwner §7:§a %s ", "§e皮肤所有者§7:§a%s", "§e皮膚所有者§7:§a%s", "§e膚主§7:§a%s"
));
addLang(LangKeyValue.ofKey(
"tooltip.lib39.content.doll.hover.2", ModPartEnum.DESCRIPTION,
"§7Rename with a player name in an anvil to change skin",
"§7在铁砧上可通过重命名对应玩家名来改变皮肤", "§7在鐵砧上可通過重命名對應玩家名來改變皮膚", "§7鐵砧之上更名以易膚"
));
addLang(LangKeyValue.ofKey(
"invalid.player_name.too_long", ModPartEnum.DESCRIPTION,
"§c§lPlayer 's Name is too long than 16 characters.",
"§c§l玩家名称过长最多16个字符", "§c§l玩家名稱過長最多16個字符", "§c§l玩家名過長限十六字"
));
if (Lib39.shouldRegisterExamples()) {
addLang(LangKeyValue.ofSupplier(
ExLib39Items.FABRIC, ModPartEnum.ITEM,
"Fabric", "织布", "織布", "", true
));
addLang(LangKeyValue.ofSupplier(
ExLib39Items.NEOFORGE, ModPartEnum.ITEM,
"NeoForge", "小狐狸", "狐狸", "", true
));
addLang(LangKeyValue.ofSupplier(
ExLib39Items.FORGE, ModPartEnum.ITEM,
"Forge", "铁砧", "铁砧", "", true
));
TestMessage.getItems().forEach(this::addLang);
}
}
/**
* 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);
}
/**
* The type Test message.
*/
@SuppressWarnings("unused")
public static final class TestMessage {
private static final Set<LangKeyValue> items = new HashSet<>();
// ===== lib39 測試命令翻譯 =====
/**
* The constant LIB39_ROOT.
*/
// 根命令
public static final LangKeyValue LIB39_ROOT = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.root", ModPartEnum.MESSAGE,
"Lib39 Command System",
"Lib39 命令系統",
"Lib39 指令系統",
"Lib39 令系",
true
)
);
/**
* The constant LIB39_TEST.
*/
// 測試命令
public static final LangKeyValue LIB39_TEST = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.test", ModPartEnum.MESSAGE,
"Test command",
"測試命令",
"測試指令",
"試令",
true
)
);
/**
* The constant LIB39_TEST_SUCCESS.
*/
public static final LangKeyValue LIB39_TEST_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.test.success", ModPartEnum.MESSAGE,
"Test command executed successfully!",
"測試命令執行成功!",
"測試指令執行成功!",
"試令行成!",
true
)
);
/**
* The constant LIB39_TEST_WITH_PARAM.
*/
public static final LangKeyValue LIB39_TEST_WITH_PARAM = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.test.with_param", ModPartEnum.MESSAGE,
"Test command with parameter: %s",
"帶參數的測試命令:%s",
"帶參數的測試指令:%s",
"帶參試令:%s",
true
)
);
/**
* The constant LIB39_DEMO.
*/
public static final LangKeyValue LIB39_DEMO = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.demo", ModPartEnum.MESSAGE,
"Demo command",
"演示命令",
"演示指令",
"演令",
true
)
);
/**
* The constant LIB39_DEMO_MESSAGE.
*/
public static final LangKeyValue LIB39_DEMO_MESSAGE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.demo.message", ModPartEnum.MESSAGE,
"This is a demo command showing Lib39 features!",
"這是一個展示 Lib39 功能的演示命令!",
"這是一個展示 Lib39 功能的演示指令!",
"此乃展 Lib39 能之演令!",
true
)
);
/**
* The constant LIB39_GREET_BASIC.
*/
// 問候命令
public static final LangKeyValue LIB39_GREET_BASIC = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.greet.basic", ModPartEnum.MESSAGE,
"Greet everyone",
"向大家問好",
"向大家問好",
"問眾安",
true
)
);
/**
* The constant LIB39_GREET_DEFAULT.
*/
public static final LangKeyValue LIB39_GREET_DEFAULT = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.greet.default", ModPartEnum.MESSAGE,
"Hello everyone from Lib39!",
"來自 Lib39 的問候!",
"來自 Lib39 的問候!",
"自 Lib39 問安!",
true
)
);
/**
* The constant LIB39_GREET_PLAYER.
*/
public static final LangKeyValue LIB39_GREET_PLAYER = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.greet.player", ModPartEnum.MESSAGE,
"Greet specific player",
"問候特定玩家",
"問候特定玩家",
"問特者安",
true
)
);
/**
* The constant LIB39_GREET_RECEIVED.
*/
public static final LangKeyValue LIB39_GREET_RECEIVED = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.greet.received", ModPartEnum.MESSAGE,
"%s greeted you!",
"%s 向你問好!",
"%s 向你問好!",
"%s 問汝安!",
true
)
);
/**
* The constant LIB39_CALCULATE.
*/
// 計算命令
public static final LangKeyValue LIB39_CALCULATE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.calculate", ModPartEnum.MESSAGE,
"Calculate sum of two numbers",
"計算兩個數字的和",
"計算兩個數字的和",
"算二數和",
true
)
);
/**
* The constant LIB39_CALCULATE_RESULT.
*/
public static final LangKeyValue LIB39_CALCULATE_RESULT = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.calculate.result", ModPartEnum.MESSAGE,
"%d + %d = %d",
"%d + %d = %d",
"%d + %d = %d",
"%d 加 %d 等 %d",
true
)
);
/**
* The constant LIB39_TELEPORT.
*/
// 傳送命令
public static final LangKeyValue LIB39_TELEPORT = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.teleport", ModPartEnum.MESSAGE,
"Teleport to player (OP only)",
"傳送到玩家僅OP",
"傳送到玩家僅OP",
"送至者(唯管)",
true
)
);
/**
* The constant LIB39_TELEPORT_SUCCESS.
*/
public static final LangKeyValue LIB39_TELEPORT_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.teleport.success", ModPartEnum.MESSAGE,
"Teleported to %s",
"已傳送至 %s",
"已傳送至 %s",
"已送至 %s",
true
)
);
/**
* The constant LIB39_INFO.
*/
// 信息命令
public static final LangKeyValue LIB39_INFO = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.info", ModPartEnum.MESSAGE,
"Show player information",
"顯示玩家信息",
"顯示玩家資訊",
"示者訊",
true
)
);
/**
* The constant LIB39_INFO_MESSAGE.
*/
public static final LangKeyValue LIB39_INFO_MESSAGE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.info.message", ModPartEnum.MESSAGE,
"=== Player Information ===",
"=== 玩家信息 ===",
"=== 玩家資訊 ===",
" 者訊 ",
true
)
);
/**
* The constant LIB39_INFO_DIMENSION.
*/
public static final LangKeyValue LIB39_INFO_DIMENSION = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.info.dimension", ModPartEnum.MESSAGE,
"Dimension: %s",
"維度:%s",
"維度:%s",
"界:%s",
true
)
);
/**
* The constant LIB39_INFO_POSITION.
*/
public static final LangKeyValue LIB39_INFO_POSITION = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.info.position", ModPartEnum.MESSAGE,
"Position: X=%.1f, Y=%.1f, Z=%.1f",
"位置X=%.1f, Y=%.1f, Z=%.1f",
"位置X=%.1f, Y=%.1f, Z=%.1f",
"X=%.1f, Y=%.1f, Z=%.1f",
true
)
);
/**
* The constant LIB39_TEAM.
*/
// 隊伍系統
public static final LangKeyValue LIB39_TEAM = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.team", ModPartEnum.MESSAGE,
"Team management",
"隊伍管理",
"隊伍管理",
"隊管",
true
)
);
/**
* The constant LIB39_TEAM_CREATE.
*/
public static final LangKeyValue LIB39_TEAM_CREATE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.team.create", ModPartEnum.MESSAGE,
"Create a new team",
"創建新隊伍",
"創建新隊伍",
"創新隊",
true
)
);
/**
* The constant LIB39_TEAM_CREATE_SUCCESS.
*/
public static final LangKeyValue LIB39_TEAM_CREATE_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.team.create.success", ModPartEnum.MESSAGE,
"Team '%s' created successfully!",
"隊伍 '%s' 創建成功!",
"隊伍 '%s' 創建成功!",
"隊 '%s' 創新成!",
true
)
);
/**
* The constant LIB39_TEAM_JOIN.
*/
public static final LangKeyValue LIB39_TEAM_JOIN = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.team.join", ModPartEnum.MESSAGE,
"Join a team",
"加入隊伍",
"加入隊伍",
"入隊",
true
)
);
/**
* The constant LIB39_TEAM_JOIN_SUCCESS.
*/
public static final LangKeyValue LIB39_TEAM_JOIN_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.team.join.success", ModPartEnum.MESSAGE,
"Joined team '%s'",
"已加入隊伍 '%s'",
"已加入隊伍 '%s'",
"已入隊 '%s'",
true
)
);
/**
* The constant LIB39_TEAM_LEAVE.
*/
public static final LangKeyValue LIB39_TEAM_LEAVE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.team.leave", ModPartEnum.MESSAGE,
"Leave current team",
"離開當前隊伍",
"離開當前隊伍",
"離現隊",
true
)
);
/**
* The constant LIB39_TEAM_LEAVE_SUCCESS.
*/
public static final LangKeyValue LIB39_TEAM_LEAVE_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.team.leave.success", ModPartEnum.MESSAGE,
"Left the team",
"已離開隊伍",
"已離開隊伍",
"已離隊",
true
)
);
/**
* The constant LIB39_GAME.
*/
// 遊戲系統
public static final LangKeyValue LIB39_GAME = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game", ModPartEnum.MESSAGE,
"Game control",
"遊戲控制",
"遊戲控制",
"戲控",
true
)
);
/**
* The constant LIB39_GAME_START.
*/
public static final LangKeyValue LIB39_GAME_START = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game.start", ModPartEnum.MESSAGE,
"Start a game",
"開始遊戲",
"開始遊戲",
"啟戲",
true
)
);
/**
* The constant LIB39_GAME_START_SUCCESS.
*/
public static final LangKeyValue LIB39_GAME_START_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game.start.success", ModPartEnum.MESSAGE,
"Game '%s' started!",
"遊戲 '%s' 已開始!",
"遊戲 '%s' 已開始!",
"戲 '%s' 已啟!",
true
)
);
/**
* The constant LIB39_GAME_STOP.
*/
public static final LangKeyValue LIB39_GAME_STOP = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game.stop", ModPartEnum.MESSAGE,
"Stop current game",
"停止當前遊戲",
"停止當前遊戲",
"止現戲",
true
)
);
/**
* The constant LIB39_GAME_STOP_SUCCESS.
*/
public static final LangKeyValue LIB39_GAME_STOP_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game.stop.success", ModPartEnum.MESSAGE,
"Game stopped!",
"遊戲已停止!",
"遊戲已停止!",
"戲已止!",
true
)
);
/**
* The constant LIB39_GAME_PAUSE.
*/
public static final LangKeyValue LIB39_GAME_PAUSE = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game.pause", ModPartEnum.MESSAGE,
"Pause current game",
"暫停當前遊戲",
"暫停當前遊戲",
"暫現戲",
true
)
);
/**
* The constant LIB39_GAME_PAUSE_SUCCESS.
*/
public static final LangKeyValue LIB39_GAME_PAUSE_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game.pause.success", ModPartEnum.MESSAGE,
"Game paused!",
"遊戲已暫停!",
"遊戲已暫停!",
"戲已暫!",
true
)
);
/**
* The constant LIB39_GAME_RESUME.
*/
public static final LangKeyValue LIB39_GAME_RESUME = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game.resume", ModPartEnum.MESSAGE,
"Resume paused game",
"恢復暫停的遊戲",
"恢復暫停的遊戲",
"復暫戲",
true
)
);
/**
* The constant LIB39_GAME_RESUME_SUCCESS.
*/
public static final LangKeyValue LIB39_GAME_RESUME_SUCCESS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.game.resume.success", ModPartEnum.MESSAGE,
"Game resumed!",
"遊戲已恢復!",
"遊戲已恢復!",
"戲已復!",
true
)
);
/**
* The constant LIB39_SETTINGS.
*/
// 設置命令
public static final LangKeyValue LIB39_SETTINGS = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.settings", ModPartEnum.MESSAGE,
"Show settings",
"顯示設置",
"顯示設定",
"示置",
true
)
);
/**
* The constant LIB39_CONFIG.
*/
public static final LangKeyValue LIB39_CONFIG = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.config", ModPartEnum.MESSAGE,
"Show configuration",
"顯示配置",
"顯示設定",
"示配",
true
)
);
/**
* The constant LIB39_RELOAD.
*/
public static final LangKeyValue LIB39_RELOAD = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.reload", ModPartEnum.MESSAGE,
"Reload configuration",
"重新加載配置",
"重新載入設定",
"重載配",
true
)
);
/**
* The constant LIB39_DEBUG.
*/
public static final LangKeyValue LIB39_DEBUG = addAndRet(
LangKeyValue.ofKey(
"commands.lib39.debug", ModPartEnum.MESSAGE,
"Debug information",
"調試信息",
"除錯資訊",
"調訊",
true
)
);
// ===== 添加缺失的導入 =====
private static final java.util.function.Consumer<LangKeyValue> addConsumer = items::add;
private static LangKeyValue addAndRet(LangKeyValue item) {
items.add(item);
return item;
}
/**
* Gets items.
*
* @return the items
*/
public static Set<LangKeyValue> getItems() {
return items;
}
}
}

View File

@ -1,769 +0,0 @@
package top.r3944realms.lib39.client.gui.component;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.network.chat.Component;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import top.r3944realms.lib39.client.shader.Lib39Shaders;
import top.r3944realms.lib39.util.MathUtil;
import top.r3944realms.lib39.util.lang.FourConsumer;
import top.r3944realms.lib39.util.lang.Pair;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The type Wheel widget.
*
* @author QiuShui1012
*/
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class WheelWidget extends AbstractWidget {
/**
* The constant IGNORE_CURSOR_MOVE_LENGTH.
*/
public static final int IGNORE_CURSOR_MOVE_LENGTH = 15;
private static final Vector2f ROTATION_START = new Vector2f(0, 1);
private static final int SELECTION_EFFECT_COLOR = 0xddFFFF00;
private static final int SELECTION_EFFECT_RADIUS = 20;
private final Minecraft minecraft = Minecraft.getInstance();
private final Vector2f centerPos;
private final float ringInnerRadius;
private final float ringOuterRadius;
private final int delay;
private final int animationMs;
private final int closingAnimationMs; //ms
private final int ringColor;
private final int selectionEffectColor;
private final int selectionEffectRadius;
private final float selectionAnimationSpeedFactor;
private final int textColor;
private final float textScale;
private final List<WheelSection> sections = new ArrayList<>();
private long displayTime = System.currentTimeMillis();
private float currentAngle = 0;
/**
* Gets current section index.
*
* @return the current section index
*/
public int getCurrentSectionIndex() {
return currentSectionIndex;
}
/**
* Is closing animation started boolean.
*
* @return the boolean
*/
public boolean isClosingAnimationStarted() {
return closingAnimationStarted;
}
private int currentSectionIndex = -1;
private Vector2f selectionEffectPos;
private boolean animationStarted = false;
/**
* Sets closing animation started.
*
* @param closingAnimationStarted the closing animation started
*/
public void setClosingAnimationStarted(boolean closingAnimationStarted) {
this.closingAnimationStarted = closingAnimationStarted;
}
private boolean closingAnimationStarted = false;
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param textScale the text scale
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height,
float ringInnerRadius, float ringOuterRadius, float textScale,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, textScale, sections);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param textScale the text scale
* @param degreeOffsetAngle the degree offset angle
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height,
float ringInnerRadius, float ringOuterRadius, float textScale, float degreeOffsetAngle,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, textScale, degreeOffsetAngle, sections);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height,
float ringInnerRadius, float ringOuterRadius,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, sections);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param message the message
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param textScale the text scale
* @param degreeOffsetAngle the degree offset angle
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height, Component message,
float ringInnerRadius, float ringOuterRadius, float textScale, float degreeOffsetAngle,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(
x, y, width, height, message,
ringInnerRadius, ringOuterRadius,
150, 300, 150,
0x00000000,
0xddffff00, 20, 5f,
0xfdfdfd, textScale, degreeOffsetAngle,
sections
);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param message the message
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height, Component message,
float ringInnerRadius, float ringOuterRadius,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(
x, y, width, height, message,
ringInnerRadius, ringOuterRadius,
150, 300, 150,
0x00000000,
0xddffff00, 20, 5f,
0xfdfdfd, 1f, 0f,
sections
);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param message the message
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param degreeOffsetAngle the degree offset angle
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height, Component message,
float ringInnerRadius, float ringOuterRadius, float degreeOffsetAngle,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(
x, y, width, height, message,
ringInnerRadius, ringOuterRadius,
150, 300, 150,
0x00000000,
0xddffff00, 20, 5f,
0xfdfdfd, 1f, degreeOffsetAngle,
sections
);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param delay the delay
* @param animationMs the animation ms
* @param closingAnimationMs the closing animation ms
* @param ringColor the ring color
* @param selectionEffectColor the selection effect color
* @param selectionEffectRadius the selection effect radius
* @param selectionAnimationSpeedFactor the selection animation speed factor
* @param textColor the text color
* @param textScale the text scale
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height,
float ringInnerRadius, float ringOuterRadius,
int delay, int animationMs, int closingAnimationMs,
int ringColor,
int selectionEffectColor, int selectionEffectRadius, float selectionAnimationSpeedFactor,
int textColor, float textScale,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(
x, y, width, height, Component.empty(),
ringInnerRadius, ringOuterRadius,
delay, animationMs, closingAnimationMs,
ringColor,
selectionEffectColor, selectionEffectRadius, selectionAnimationSpeedFactor,
textColor, textScale, 0f,
sections
);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param message the message
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param delay the delay
* @param animationMs the animation ms
* @param closingAnimationMs the closing animation ms
* @param ringColor the ring color
* @param selectionEffectColor the selection effect color
* @param selectionEffectRadius the selection effect radius
* @param selectionAnimationSpeedFactor the selection animation speed factor
* @param textColor the text color
* @param textScale the text scale
* @param degreeOffsetAngle the degree offset angle
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height, Component message,
float ringInnerRadius, float ringOuterRadius,
int delay, int animationMs, int closingAnimationMs,
int ringColor,
int selectionEffectColor, int selectionEffectRadius, float selectionAnimationSpeedFactor,
int textColor, float textScale, float degreeOffsetAngle,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
super(x, y, width, height, message);
this.centerPos = new Vector2f(this.getX() + this.getWidth() / 2f, this.getY() + this.getHeight() / 2f);
this.ringInnerRadius = Math.max(ringInnerRadius, IGNORE_CURSOR_MOVE_LENGTH);
this.ringOuterRadius = ringOuterRadius;
this.delay = delay;
this.animationMs = animationMs;
this.closingAnimationMs = closingAnimationMs;
this.ringColor = ringColor;
this.selectionEffectColor = selectionEffectColor;
this.selectionEffectRadius = selectionEffectRadius;
this.selectionAnimationSpeedFactor = selectionAnimationSpeedFactor;
this.textColor = textColor;
this.textScale = textScale;
float degreeEachRotation = 360f / sections.size();
for (int i = 0; i < sections.size(); i++) {
Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>> section = sections.get(i);
float rotation = MathUtil.clampWithProportion((degreeEachRotation * i + degreeOffsetAngle) % 360, 0, 360);
Vector2f rotated = MathUtil.rotationDegrees(ROTATION_START, rotation)
.mul(1, -1)
.mul(this.getSectionCircleDiameter())
.add(this.centerPos);
float detectionStart = (float) (Math.toRadians(rotation - degreeEachRotation / 2f) + Math.PI * 2);
float detectionEnd = (float) (Math.toRadians(rotation + degreeEachRotation / 2f) + Math.PI * 2);
detectionStart = detectionStart % (float) (Math.PI * 2);
detectionEnd = detectionEnd % (float) (Math.PI * 2);
this.sections.add(new WheelSection(
rotated,
(float) (Math.toRadians(rotation) % (Math.PI * 2)),
detectionStart,
detectionEnd,
section.first,
section.second
));
}
this.selectionEffectPos = MathUtil.rotate(
MathUtil.copy(ROTATION_START)
.mul(this.getSectionCircleDiameter()),
this.currentAngle
);
}
/**
* Gets section circle diameter.
*
* @return the section circle diameter
*/
// 滚轮选择器中每个扇形的圆形直径
public float getSectionCircleDiameter() {
return this.ringOuterRadius + this.ringInnerRadius;
}
/**
* Sets current index.
*
* @param index the index
* @return the current index
*/
public WheelWidget setCurrentIndex(int index) {
this.currentSectionIndex = index;
this.currentAngle = this.sections.get(index).angle;
this.selectionEffectPos = MathUtil.rotate(
MathUtil.copy(ROTATION_START)
.mul(this.getSectionCircleDiameter()),
this.currentAngle
);
return this;
}
/**
* Gets section size.
*
* @return the section size
*/
public int getSectionSize() {
return this.sections.size();
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
if (delta > 0) {
if (this.currentSectionIndex == this.getSectionSize() - 1) {
this.currentSectionIndex = 0;
} else {
this.currentSectionIndex++;
}
} else if (delta < 0) {
if (this.currentSectionIndex == 0) {
this.currentSectionIndex = this.getSectionSize() - 1;
} else {
this.currentSectionIndex--;
}
}
for (WheelSection section : this.sections) {
if (this.sections.indexOf(section) == this.currentSectionIndex) {
this.currentAngle = section.angle;
return true;
}
}
return true;
}
/**
* Check mouse pos.
*
* @param mouseX the mouse x
* @param mouseY the mouse y
*/
public void checkMousePos(double mouseX, double mouseY) {
if (this.closingAnimationStarted) return;
float centerX = this.centerPos.x;
float centerY = this.centerPos.y;
// 鼠标距离屏幕中心的位置向量
Vector2f cursorPos = new Vector2f((float) mouseX - centerX, (float) mouseY - centerY);
if (cursorPos.length() < IGNORE_CURSOR_MOVE_LENGTH) return;
Vector2f rotationStart = new Vector2f(0, 1);
cursorPos.normalize();
// 计算夹角弧度
double rot = Math.acos(rotationStart.dot(cursorPos) / (rotationStart.length() * cursorPos.length()));
double rotation = cursorPos.x < 0 ? Math.PI - rot : Math.PI + rot;
for (WheelSection section : this.sections) {
if (section.angleStart > section.angleEnd && rotation >= section.angleStart
|| rotation >= section.angleStart && rotation <= section.angleEnd
) {
this.currentAngle = section.angle;
this.currentSectionIndex = this.sections.indexOf(section);
break;
}
}
}
/**
* Should render boolean.
*
* @return the boolean
*/
public boolean shouldRender() {
if (this.animationStarted) return true;
return (this.displayTime + this.delay) <= System.currentTimeMillis();
}
@Override
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
this.checkMousePos(mouseX, mouseY);
this.renderWidget(guiGraphics, mouseX, mouseY, partialTick);
}
@Override
protected void renderWidget(GuiGraphics guiGraphics, int i, int i1, float v) {
RenderSystem.enableDepthTest();
RenderSystem.enableBlend();
this.renderClosingAnimation(guiGraphics);
if (!this.shouldRender()) {
return;
}
if (this.closingAnimationStarted) return;
if (!this.animationStarted) {
this.animationStarted = true;
this.displayTime = System.currentTimeMillis();
}
PoseStack poseStack = guiGraphics.pose();
float delta = this.displayTime + this.animationMs - System.currentTimeMillis();
if (delta > 0) {
float progress = 1 - (delta / this.animationMs);
progress = (float) (-Math.pow(progress, 2) + 2 * progress);
if (progress == 0) return;
this.renderProgressAnimation(guiGraphics, progress);
return;
}
renderRing(
guiGraphics,
this.centerPos.x,
this.centerPos.y,
this.ringColor,
this.ringInnerRadius * 2,
this.ringOuterRadius * 2
);
this.renderSelection(guiGraphics);
for (WheelSection value : this.sections) {
float x = value.center.x;
float y = value.center.y;
poseStack.pushPose();
poseStack.translate(x - 10, y - 10, 100);
value.renderer.accept(guiGraphics, poseStack, 20, 20);
poseStack.popPose();
poseStack.pushPose();
float coordinateScale = 0.7f;
float offsetX = 0.1f * this.width;
float offsetY = 0.1f * this.height;
float adjustedX = (x - offsetX) / coordinateScale;
float adjustedY = (y - offsetY - 20 * this.textScale) / coordinateScale;
poseStack.translate(offsetX, offsetY, 0);
poseStack.scale(coordinateScale, coordinateScale, coordinateScale);
poseStack.translate(adjustedX, adjustedY, 0);
poseStack.scale(this.textScale / coordinateScale, this.textScale / coordinateScale, this.textScale / coordinateScale);
guiGraphics.drawCenteredString(
minecraft.font,
value.subTitle,
0,
0,
(0xff << 24) | this.textColor
);
poseStack.popPose();
}
RenderSystem.disableDepthTest();
RenderSystem.disableBlend();
}
/**
* Render closing animation.
*
* @param guiGraphics the gui graphics
*/
public void renderClosingAnimation(GuiGraphics guiGraphics) {
if (!this.closingAnimationStarted) return;
float delta = this.displayTime + this.closingAnimationMs - System.currentTimeMillis();
float progress = delta / this.closingAnimationMs;
if(progress >= 1 || progress <= 0) {
this.minecraft.setScreen(null);
}
this.renderProgressAnimation(guiGraphics, progress);
}
private void renderProgressAnimation(GuiGraphics guiGraphics, float progress) {
progress = (float) (-Math.pow(progress, 2) + 2 * progress);
if (progress == 0) return;
PoseStack poseStack = guiGraphics.pose();
poseStack.pushPose();
renderRing(
guiGraphics,
this.centerPos.x,
this.centerPos.y,
this.ringColor,
this.ringInnerRadius * 2 * progress,
this.ringOuterRadius * 2 * progress
);
poseStack.popPose();
if(this.currentSectionIndex != -1) {
WheelSection section = this.sections.get(this.currentSectionIndex);
Vector2f center = new Vector2f(
(section.center.x - this.centerPos.x) / this.getSectionCircleDiameter(),
(section.center.y - this.centerPos.y) / this.getSectionCircleDiameter()
).mul(this.getSectionCircleDiameter() * progress).add(this.centerPos.x, this.centerPos.y);
renderSelectionEffect(
guiGraphics,
center.x,
center.y,
this.selectionEffectColor,
this.selectionEffectRadius
);
}
for (WheelSection value : this.sections) {
if (sections.get(0) != value) continue;
Vector2f center = new Vector2f(
(value.center.x - this.centerPos.x) / this.getSectionCircleDiameter(),
(value.center.y - this.centerPos.y) / this.getSectionCircleDiameter()
).mul(this.getSectionCircleDiameter() * progress).add(this.centerPos.x, this.centerPos.y);
float x = center.x;
float y = center.y;
poseStack.pushPose();
poseStack.translate(x - 10, y - 10, 100);
value.renderer.accept(guiGraphics, poseStack, 20, 20);
poseStack.pushPose();
}
}
/**
* Render ring.
*
* @param guiGraphics the gui graphics
* @param centerX the center x
* @param centerY the center y
* @param color the color
* @param innerRadius the inner radius
* @param outerRadius the outer radius
*/
public static void renderRing(
GuiGraphics guiGraphics,
float centerX,
float centerY,
int color,
float innerRadius, // 改为半径
float outerRadius // 改为半径
) {
PoseStack poseStack = guiGraphics.pose();
poseStack.pushPose();
Tesselator tesselator = Tesselator.getInstance();
BufferBuilder buffer = tesselator.getBuilder();
// 计算足够大的绘制区域来覆盖整个环形基于外半径
float margin = outerRadius + 100f; // 使用半径计算边距
float x1 = centerX - margin;
float y1 = centerY - margin;
float x2 = centerX + margin;
float y2 = centerY + margin;
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
Matrix4f matrix = poseStack.last().pose();
buffer.vertex(matrix, x1, y1, -300).color(color).endVertex();
buffer.vertex(matrix, x1, y2, -300).color(color).endVertex();
buffer.vertex(matrix, x2, y2, -300).color(color).endVertex();
buffer.vertex(matrix, x2, y1, -300).color(color).endVertex();
setupRingShader(centerX, centerY, innerRadius, outerRadius);
BufferUploader.drawWithShader(buffer.end());
poseStack.popPose();
}
private static void setupRingShader(float centerX, float centerY, float innerRadius, float outerRadius) {
Window window = Minecraft.getInstance().getWindow();
float guiScale = (float) window.getGuiScale();
RenderSystem.setShader(Lib39Shaders::getRingShader);
// 转换到像素坐标考虑GUI缩放
float pixelCenterX = centerX * guiScale;
float pixelCenterY = window.getHeight() - (centerY * guiScale); // 翻转Y坐标
// 半径考虑GUI缩放
float pixelInnerRadius = innerRadius * guiScale;
float pixelOuterRadius = outerRadius * guiScale;
float pixelAntiAliasing = 2.0f * guiScale; // 抗锯齿范围
System.out.println("Shader Params - Center: (" + pixelCenterX + ", " + pixelCenterY +
"), InnerRadius: " + pixelInnerRadius + ", OuterRadius: " + pixelOuterRadius);
ShaderInstance shader = Lib39Shaders.getRingShader();
shader.safeGetUniform("Center").set(pixelCenterX, pixelCenterY);
shader.safeGetUniform("InnerRadius").set(pixelInnerRadius);
shader.safeGetUniform("OuterRadius").set(pixelOuterRadius);
shader.safeGetUniform("AntiAliasing").set(pixelAntiAliasing);
shader.safeGetUniform("ColorModulator").set(1.0f, 1.0f, 1.0f, .5f);
}
private void renderSelection(GuiGraphics guiGraphics) {
float selectionEffectAngle = MathUtil.angle(
MathUtil.copy(ROTATION_START),
this.selectionEffectPos
);
float diffAngle = this.currentAngle - selectionEffectAngle;
if (diffAngle > Math.PI) {
diffAngle -= (float) (Math.PI * 2);
} else if (diffAngle < -Math.PI) {
diffAngle += (float) (Math.PI * 2);
}
this.selectionEffectPos = MathUtil.rotate(
this.selectionEffectPos,
diffAngle / this.selectionAnimationSpeedFactor
);
Vector2f pos = MathUtil.copy(this.selectionEffectPos)
.mul(1, -1)
.add(this.centerPos);
// 调用时使用半径
renderSelectionEffect(
guiGraphics,
pos.x,
pos.y,
SELECTION_EFFECT_COLOR,
SELECTION_EFFECT_RADIUS // 确保这是半径值
);
}
/**
* Render selection effect.
*
* @param guiGraphics the gui graphics
* @param centerX the center x
* @param centerY the center y
* @param color the color
* @param radius the radius
*/
public static void renderSelectionEffect(
GuiGraphics guiGraphics,
float centerX,
float centerY,
int color,
float radius
) {
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.disableDepthTest();
PoseStack poseStack = guiGraphics.pose();
Matrix4f matrix4f = poseStack.last().pose();
Tesselator tesselator = Tesselator.getInstance();
BufferBuilder buffer = tesselator.getBuilder();
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
float x1 = centerX - radius - 5;
float y1 = centerY - radius - 5;
float x2 = centerX + radius + 5;
float y2 = centerY + radius + 5;
buffer.vertex(matrix4f, x1, y1, -200).color(color).endVertex();
buffer.vertex(matrix4f, x1, y2, -200).color(color).endVertex();
buffer.vertex(matrix4f, x2, y2, -200).color(color).endVertex();
buffer.vertex(matrix4f, x2, y1, -200).color(color).endVertex();
Window window = Minecraft.getInstance().getWindow();
float guiScale = (float) window.getGuiScale();
RenderSystem.setShader(Lib39Shaders::getSelectionShader);
System.out.println("Selection Effect Params:");
System.out.println(" Center: " + centerX + ", " + centerY);
System.out.println(" Radius: " + radius);
System.out.println(" GUI Scale: " + guiScale);
System.out.println(" Framebuffer: " + window.getWidth() + "x" + window.getHeight());
Lib39Shaders.getSelectionShader()
.safeGetUniform("Center")
.set(centerX * guiScale, centerY * guiScale);
Lib39Shaders.getSelectionShader()
.safeGetUniform("FramebufferSize")
.set((float) window.getWidth(), (float) window.getHeight());
Lib39Shaders.getSelectionShader()
.safeGetUniform("Radius")
.set(radius * guiScale);
Lib39Shaders.getSelectionShader()
.safeGetUniform("AntiAliasingRadius")
.set(guiScale); // 根据需要调整
RenderSystem.setShaderColor(1, 1, 1, 1);
BufferUploader.drawWithShader(Objects.requireNonNull(buffer.end()));
RenderSystem.enableDepthTest();
}
/**
* On closing.
*/
public void onClosing() {
if (this.shouldRender() && !this.closingAnimationStarted) {
this.displayTime = System.currentTimeMillis();
this.closingAnimationStarted = true;
} else {
this.minecraft.setScreen(null);
}
}
@Override
protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) {
}
/**
* The type Wheel section.
*/
public record WheelSection(
Vector2f center,
float angle,
float angleStart,
float angleEnd,
Component subTitle,
FourConsumer<GuiGraphics, PoseStack, Integer, Integer> renderer
) {
}
}

View File

@ -1,91 +0,0 @@
package top.r3944realms.lib39.client.model;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.geom.ModelLayerLocation;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.client.model.geom.builders.*;
import net.minecraft.client.renderer.RenderType;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
/**
* The type Doll model.
*/
public class DollModel extends Model {
/**
* The constant LAYER_LOCATION.
*/
public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(Lib39.rl("doll_model"), "main");
/**
* The Slim.
*/
public boolean slim = false;
private final ModelPart head;
private final ModelPart body;
private final ModelPart rightArm;
private final ModelPart leftArm;
private final ModelPart rightLeg;
private final ModelPart rightArmSlim;
private final ModelPart leftArmSlim;
private final ModelPart leftLeg;
/**
* Instantiates a new Doll model.
*
* @param root the root
*/
public DollModel(ModelPart root) {
super(RenderType::entityTranslucent);
this.head = root.getChild("head");
this.body = root.getChild("body");
this.rightArm = root.getChild("right_arm");
this.leftArm = root.getChild("left_arm");
this.rightArmSlim = root.getChild("right_arm_slim");
this.leftArmSlim = root.getChild("left_arm_slim");
this.rightLeg = root.getChild("right_leg");
this.leftLeg = root.getChild("left_leg");
}
/**
* Create body layer layer definition.
*
* @return the layer definition
*/
public static LayerDefinition createBodyLayer() {
MeshDefinition meshdefinition = new MeshDefinition();
PartDefinition partdefinition = meshdefinition.getRoot();
partdefinition.addOrReplaceChild("head", CubeListBuilder.create().texOffs(0, 0).addBox(-4.0F, -8.0F, -4.0F, 8.0F, 8.0F, 8.0F, new CubeDeformation(0.0F)).texOffs(32, 0).addBox(-4.0F, -8.0F, -4.0F, 8.0F, 8.0F, 8.0F, new CubeDeformation(0.5F)), PartPose.offset(0.0F, 9.0F, 0.0F));
partdefinition.addOrReplaceChild("body", CubeListBuilder.create().texOffs(16, 16).addBox(-4.0F, 0.0F, -2.0F, 8.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)).texOffs(16, 32).addBox(-4.0F, 0.0F, -2.0F, 8.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offset(0.0F, 9.0F, 0.0F));
partdefinition.addOrReplaceChild("right_arm", CubeListBuilder.create().texOffs(40, 16).addBox(-3.0F, -2.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)).texOffs(40, 32).addBox(-3.0F, -2.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offsetAndRotation(-5.0F, 11.0F, 0.0F, 0.0F, 0.0F, 0.3927F));
partdefinition.addOrReplaceChild("right_arm_slim", CubeListBuilder.create().texOffs(40, 16).addBox(-2.0F, -2.0F, -2.0F, 3.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)).texOffs(40, 32).addBox(-2.0F, -2.0F, -2.0F, 3.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offsetAndRotation(-5.0F, 11.0F, 0.0F, 0.0F, 0.0F, 0.3927F));
partdefinition.addOrReplaceChild("left_arm_slim", CubeListBuilder.create().texOffs(32, 48).addBox(-1.0F, -2.0F, -2.0F, 3.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)).texOffs(48, 48).addBox(-1.0F, -2.0F, -2.0F, 3.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offsetAndRotation(5.0F, 11.0F, 0.0F, 0.0F, 0.0F, -0.3927F));
partdefinition.addOrReplaceChild("left_arm", CubeListBuilder.create().texOffs(32, 48).addBox(-1.0F, -2.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)).texOffs(48, 48).addBox(-1.0F, -2.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offsetAndRotation(5.0F, 11.0F, 0.0F, 0.0F, 0.0F, -0.3927F));
partdefinition.addOrReplaceChild("right_leg", CubeListBuilder.create().texOffs(0, 16).addBox(-2.0F, 0.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)).texOffs(0, 32).addBox(-2.0F, 0.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offsetAndRotation(-2.0F, 19.0F, -2.0F, -1.5708F, 0.3927F, 0.0F));
partdefinition.addOrReplaceChild("left_leg", CubeListBuilder.create().texOffs(16, 48).addBox(-2.0F, 0.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.0F)).texOffs(0, 48).addBox(-2.0F, 0.0F, -2.0F, 4.0F, 12.0F, 4.0F, new CubeDeformation(0.25F)), PartPose.offsetAndRotation(2.0F, 19.0F, -2.0F, -1.5708F, -0.3927F, 0.0F));
return LayerDefinition.create(meshdefinition, 64, 64);
}
@Override
public void renderToBuffer(PoseStack poseStack, @NotNull VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) {
poseStack.pushPose();
poseStack.scale(0.5F, 0.5F, 0.5F);
poseStack.translate(0.0, 1.5010000467300415, 0.0);
this.head.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
this.body.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
if (this.slim) {
this.rightArmSlim.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
this.leftArmSlim.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
} else {
this.rightArm.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
this.leftArm.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
}
this.rightLeg.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
this.leftLeg.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha);
poseStack.popPose();
}
}

View File

@ -1,381 +0,0 @@
package top.r3944realms.lib39.client.renderer;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemStack;
import org.joml.Matrix4f;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
/**
* 圆形径向菜单渲染器
* 用于创建美观的圆形选择菜单
*
* @param <T> the type parameter
*/
public class RadialMenuRenderer<T> {
/**
* The constant DEFAULT_INNER_RADIUS.
*/
// 默认配置常量
public static final float DEFAULT_INNER_RADIUS = 30f;
/**
* The constant DEFAULT_OUTER_RADIUS.
*/
public static final float DEFAULT_OUTER_RADIUS = 80f;
/**
* The constant DEFAULT_MIDDLE_RADIUS.
*/
public static final float DEFAULT_MIDDLE_RADIUS = 55f;
/**
* The constant DEFAULT_SEGMENTS.
*/
public static final int DEFAULT_SEGMENTS = 64;
// 配置选项
private final float innerRadius;
private final float outerRadius;
private final float middleRadius;
private final int segments;
private final boolean enableHoverAnimation;
private final ColorScheme colorScheme;
// 状态
private int hoveredIndex = -1;
private final float[] hoverAnimations;
private long lastAnimationTime = 0;
/**
* The type Color scheme.
*/
public static class ColorScheme {
/**
* The Normal color.
*/
public final float[] normalColor;
/**
* The Hovered color.
*/
public final float[] hoveredColor;
/**
* The Selected color.
*/
public final float[] selectedColor;
/**
* The Background color.
*/
public final float[] backgroundColor;
/**
* Instantiates a new Color scheme.
*
* @param normalColor the normal color
* @param hoveredColor the hovered color
* @param selectedColor the selected color
* @param backgroundColor the background color
*/
public ColorScheme(float[] normalColor, float[] hoveredColor, float[] selectedColor, float[] backgroundColor) {
this.normalColor = normalColor;
this.hoveredColor = hoveredColor;
this.selectedColor = selectedColor;
this.backgroundColor = backgroundColor;
}
/**
* The constant DEFAULT.
*/
// 预定义颜色方案
public static final ColorScheme DEFAULT = new ColorScheme(
new float[]{0.3f, 0.3f, 0.8f, 0.6f}, // 正常 - 蓝色
new float[]{0.9f, 0.7f, 0.1f, 0.8f}, // 悬停 - 金色
new float[]{0.2f, 0.8f, 0.2f, 0.9f}, // 选中 - 绿色
new float[]{0.1f, 0.1f, 0.1f, 0.7f} // 背景
);
/**
* The constant FIRE.
*/
public static final ColorScheme FIRE = new ColorScheme(
new float[]{0.8f, 0.3f, 0.1f, 0.6f}, // 正常 - 红色
new float[]{1.0f, 0.5f, 0.0f, 0.8f}, // 悬停 - 橙色
new float[]{1.0f, 0.9f, 0.0f, 0.9f}, // 选中 - 黄色
new float[]{0.2f, 0.1f, 0.0f, 0.7f} // 背景
);
/**
* The constant NATURE.
*/
public static final ColorScheme NATURE = new ColorScheme(
new float[]{0.2f, 0.6f, 0.3f, 0.6f}, // 正常 - 绿色
new float[]{0.4f, 0.8f, 0.4f, 0.8f}, // 悬停 - 亮绿
new float[]{0.1f, 0.9f, 0.7f, 0.9f}, // 选中 - 青绿
new float[]{0.1f, 0.2f, 0.1f, 0.7f} // 背景
);
}
/**
* Instantiates a new Radial menu renderer.
*/
public RadialMenuRenderer() {
this(DEFAULT_INNER_RADIUS, DEFAULT_OUTER_RADIUS, ColorScheme.DEFAULT);
}
/**
* Instantiates a new Radial menu renderer.
*
* @param innerRadius the inner radius
* @param outerRadius the outer radius
*/
public RadialMenuRenderer(float innerRadius, float outerRadius) {
this(innerRadius, outerRadius, ColorScheme.DEFAULT);
}
/**
* Instantiates a new Radial menu renderer.
*
* @param innerRadius the inner radius
* @param outerRadius the outer radius
* @param colorScheme the color scheme
*/
public RadialMenuRenderer(float innerRadius, float outerRadius, ColorScheme colorScheme) {
this(innerRadius, outerRadius, (innerRadius + outerRadius) / 2f, DEFAULT_SEGMENTS, true, colorScheme);
}
/**
* Instantiates a new Radial menu renderer.
*
* @param innerRadius the inner radius
* @param outerRadius the outer radius
* @param middleRadius the middle radius
* @param segments the segments
* @param enableHoverAnimation the enable hover animation
* @param colorScheme the color scheme
*/
public RadialMenuRenderer(float innerRadius, float outerRadius, float middleRadius,
int segments, boolean enableHoverAnimation, ColorScheme colorScheme) {
this.innerRadius = innerRadius;
this.outerRadius = outerRadius;
this.middleRadius = middleRadius;
this.segments = segments;
this.enableHoverAnimation = enableHoverAnimation;
this.colorScheme = colorScheme;
this.hoverAnimations = new float[0];
}
/**
* 渲染圆形菜单
*
* @param guiGraphics the gui graphics
* @param entries the entries
* @param titleProvider the title provider
* @param iconProvider the icon provider
* @param selectedIndex the selected index
* @param trackMouse the track mouse
*/
public void render(GuiGraphics guiGraphics, List<T> entries,
Function<T, Component> titleProvider,
Function<T, ItemStack> iconProvider,
int selectedIndex, boolean trackMouse) {
if (entries.isEmpty()) return;
// 更新动画状态
updateHoverAnimations(entries.size());
// 设置渲染状态
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
float centerX = guiGraphics.guiWidth() / 2f;
float centerY = guiGraphics.guiHeight() / 2f;
guiGraphics.pose().pushPose();
guiGraphics.pose().translate(centerX, centerY, 0f);
// 渲染所有扇形区域
renderSectors(guiGraphics, entries, selectedIndex);
// 渲染图标和文本
renderIconsAndText(guiGraphics, entries, titleProvider, iconProvider);
guiGraphics.pose().popPose();
RenderSystem.disableBlend();
}
/**
* 渲染扇形区域
*/
private void renderSectors(GuiGraphics guiGraphics, List<T> entries, int selectedIndex) {
int count = entries.size();
float angleSize = 360f / count;
for (int i = 0; i < count; i++) {
float startAngle = -90f + i * angleSize;
float currentOuterRadius = outerRadius;
// 悬停动画效果
if (enableHoverAnimation && i < hoverAnimations.length) {
currentOuterRadius += hoverAnimations[i] * 5f;
}
// 颜色设置
float[] color = getSectorColor(i, selectedIndex, entries.get(i));
// 绘制扇形
drawSector(guiGraphics, startAngle, angleSize, innerRadius, currentOuterRadius, color);
}
}
/**
* 获取扇形颜色
*/
private float[] getSectorColor(int index, int selectedIndex, T entry) {
if (index == selectedIndex) {
return colorScheme.selectedColor; // 选中状态
} else if (index == hoveredIndex) {
return colorScheme.hoveredColor; // 悬停状态
} else {
return colorScheme.normalColor; // 普通状态
}
}
/**
* 绘制单个扇形
*/
private void drawSector(GuiGraphics guiGraphics, float startAngle, float angleSize,
float innerRadius, float outerRadius, float[] color) {
BufferBuilder buffer = Tesselator.getInstance().getBuilder();
buffer.begin(VertexFormat.Mode.TRIANGLE_STRIP, DefaultVertexFormat.POSITION_COLOR);
Matrix4f matrix = guiGraphics.pose().last().pose();
float segments = Math.max(8, this.segments * (angleSize / 360f));
for (int i = 0; i <= segments; i++) {
float progress = i / segments;
float angle = startAngle + progress * angleSize;
float rad = angle * Mth.DEG_TO_RAD;
float cos = Mth.cos(rad);
float sin = Mth.sin(rad);
// 外圈顶点
buffer.vertex(matrix, outerRadius * cos, outerRadius * sin, 0)
.color(color[0], color[1], color[2], color[3]).endVertex();
// 内圈顶点
buffer.vertex(matrix, innerRadius * cos, innerRadius * sin, 0)
.color(color[0], color[1], color[2], color[3] * 0.6f).endVertex();
}
BufferUploader.drawWithShader(buffer.end());
}
/**
* 渲染图标和文本
*/
private void renderIconsAndText(GuiGraphics guiGraphics, List<T> entries,
Function<T, Component> titleProvider,
Function<T, ItemStack> iconProvider) {
int count = entries.size();
var font = Minecraft.getInstance().font;
for (int i = 0; i < count; i++) {
T entry = entries.get(i);
float angle = (-90f + 360f * (i + 0.5f) / count) * Mth.DEG_TO_RAD;
// 计算位置
float x = Mth.cos(angle) * middleRadius;
float y = Mth.sin(angle) * middleRadius;
// 渲染图标
ItemStack icon = iconProvider.apply(entry);
if (!icon.isEmpty()) {
guiGraphics.renderItem(icon, (int)(x - 8), (int)(y - 8));
}
// 渲染文本
Component title = titleProvider.apply(entry);
guiGraphics.pose().pushPose();
guiGraphics.pose().translate(x, y + 12, 0);
guiGraphics.pose().scale(0.7f, 0.7f, 0.7f);
guiGraphics.drawString(font, title, -font.width(title) / 2, 0, 0xFFFFFF, true);
guiGraphics.pose().popPose();
}
}
/**
* 更新悬停动画
*/
private void updateHoverAnimations(int entryCount) {
if (!enableHoverAnimation) return;
long currentTime = System.currentTimeMillis();
float deltaTime = Math.min((currentTime - lastAnimationTime) / 1000f, 0.1f);
lastAnimationTime = currentTime;
// 确保数组大小正确
if (hoverAnimations.length != entryCount) {
// 这里需要重新初始化数组实际使用时应该处理数组大小变化
}
// 更新动画值
for (int i = 0; i < hoverAnimations.length && i < entryCount; i++) {
if (i == hoveredIndex) {
hoverAnimations[i] = Mth.clamp(hoverAnimations[i] + deltaTime * 2f, 0f, 1f);
} else {
hoverAnimations[i] = Mth.clamp(hoverAnimations[i] - deltaTime * 3f, 0f, 1f);
}
}
}
/**
* 获取鼠标下的条目索引
*
* @param entries the entries
* @param mouseX the mouse x
* @param mouseY the mouse y
* @return the hovered entry
*/
public int getHoveredEntry(List<T> entries, double mouseX, double mouseY) {
float centerX = Minecraft.getInstance().getWindow().getGuiScaledWidth() / 2f;
float centerY = Minecraft.getInstance().getWindow().getGuiScaledHeight() / 2f;
double relX = mouseX - centerX;
double relY = mouseY - centerY;
double distance = Math.sqrt(relX * relX + relY * relY);
// 检查是否在有效范围内
if (distance < innerRadius || distance > outerRadius) {
hoveredIndex = -1;
return -1;
}
// 计算角度
double angle = Math.atan2(relY, relX) * Mth.RAD_TO_DEG;
angle = (angle + 450) % 360; // 标准化到 0-360
int count = entries.size();
int index = (int) (angle / (360f / count)) % count;
hoveredIndex = index;
return index;
}
/**
* 清除状态
*/
public void clearState() {
hoveredIndex = -1;
// 重置动画数组
Arrays.fill(hoverAnimations, 0f);
}
}

View File

@ -1,55 +0,0 @@
package top.r3944realms.lib39.client.renderer.block;
import com.mojang.authlib.GameProfile;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.client.model.DollModel;
import top.r3944realms.lib39.client.renderer.item.DollItemRenderer;
import top.r3944realms.lib39.content.block.DollBlock;
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
import top.r3944realms.lib39.util.lang.Pair;
/**
* The type Doll block entity renderer.
*/
public class DollBlockEntityRenderer implements BlockEntityRenderer<DollBlockEntity> {
private final DollModel dollModel;
/**
* Instantiates a new Doll block entity renderer.
*
* @param context the context
*/
public DollBlockEntityRenderer(BlockEntityRendererProvider.@NotNull Context context) {
this.dollModel = new DollModel(context.bakeLayer(DollModel.LAYER_LOCATION));
}
@Override
public void render(@NotNull DollBlockEntity dollBlockEntity, float partialTick, @NotNull PoseStack poseStack, @NotNull MultiBufferSource buffer, int packedLight, int packedOverlay) {
BlockState blockState = dollBlockEntity.getBlockState();
if (blockState.getBlock() instanceof DollBlock) {
Direction facing = blockState.getValue(DollBlock.FACING);
GameProfile profile = dollBlockEntity.getOwnerProfile();
Pair<ResourceLocation, Boolean> resourceLocationBooleanPair = DollItemRenderer.loadSkin(profile);
poseStack.pushPose();
poseStack.translate(0.5, 1.5, 0.5);
poseStack.scale(1.0F, -1.0F, -1.0F);
float rotation = facing.toYRot();
poseStack.mulPose(Axis.YP.rotationDegrees(rotation));
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityTranslucent(resourceLocationBooleanPair.first));
this.dollModel.slim = resourceLocationBooleanPair.second;
this.dollModel.renderToBuffer(poseStack, vertexConsumer, packedLight, OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F, 1.0F);
poseStack.popPose();
}
}
}

View File

@ -1,98 +0,0 @@
package top.r3944realms.lib39.client.renderer.item;
import com.mojang.authlib.GameProfile;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.DefaultPlayerSkin;
import net.minecraft.client.resources.SkinManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.client.model.DollModel;
import top.r3944realms.lib39.content.item.DollItem;
import top.r3944realms.lib39.util.GameProfileHelper;
import top.r3944realms.lib39.util.lang.Pair;
/**
* The type Doll item renderer.
*/
public class DollItemRenderer extends BlockEntityWithoutLevelRenderer {
private static DollItemRenderer instance;
private final DollModel dollModel;
private DollItemRenderer() {
super(
Minecraft.getInstance().getBlockEntityRenderDispatcher(),
Minecraft.getInstance().getEntityModels()
);
this.dollModel = new DollModel(
Minecraft.getInstance().getEntityModels().bakeLayer(DollModel.LAYER_LOCATION)
);
}
/**
* Gets instance.
*
* @return the instance
*/
public static DollItemRenderer getInstance() {
if (instance == null) {
instance = new DollItemRenderer();
}
return instance;
}
@Override
public void renderByItem(@NotNull ItemStack stack, @NotNull ItemDisplayContext displayContext, @NotNull PoseStack poseStack, @NotNull MultiBufferSource buffer, int packedLight, int packedOverlay) {
if (!(stack.getItem() instanceof DollItem)) {
return;
}
GameProfile profile = GameProfileHelper.getProfileFromItemStack(stack);
Pair<ResourceLocation, Boolean> resourceLocationBooleanPair = loadSkin(profile);
ResourceLocation playerSkin = resourceLocationBooleanPair.first;
boolean isSlim = resourceLocationBooleanPair.second;
poseStack.pushPose();
VertexConsumer vertexConsumer = buffer.getBuffer(
RenderType.entityTranslucent(playerSkin)
);
poseStack.translate(0.5, 2.6, 0.8);
poseStack.scale(1.8F, -1.8F, -1.8F);
poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.dollModel.slim = isSlim;
this.dollModel.renderToBuffer(
poseStack,
vertexConsumer,
packedLight,
packedOverlay,
1.0F, 1.0F, 1.0F, 1.0F
);
poseStack.popPose();
}
/**
* Load skin pair.
*
* @param profile the profile
* @return the pair
*/
public static @NotNull Pair<ResourceLocation,Boolean> loadSkin(GameProfile profile) {
SkinManager skinManager = Minecraft.getInstance().getSkinManager();
ResourceLocation playerSkin;
boolean isSlim;
if (profile != null) {
playerSkin = skinManager.getInsecureSkinLocation(profile);
isSlim = GameProfileHelper.isSlimArms(profile);
} else {
playerSkin = DefaultPlayerSkin.getDefaultSkin(); //6 new SkinType("textures/entity/player/slim/steve.png", DefaultPlayerSkin.ModelType.SLIM),
isSlim = true;
}
return Pair.of(playerSkin, isSlim);
}
}

View File

@ -1,73 +0,0 @@
package top.r3944realms.lib39.client.shader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraftforge.client.event.RegisterShadersEvent;
import top.r3944realms.lib39.Lib39;
/**
* The type Lib 39 shaders.
*/
public class Lib39Shaders {
/**
* The Minecraft.
*/
static final Minecraft MINECRAFT = Minecraft.getInstance();
/**
* Gets ring shader.
*
* @return the ring shader
*/
public static ShaderInstance getRingShader() {
return ringShader;
}
/**
* The Ring shader.
*/
static ShaderInstance ringShader;
/**
* Gets selection shader.
*
* @return the selection shader
*/
public static ShaderInstance getSelectionShader() {
return selectionShader;
}
/**
* The Selection shader.
*/
static ShaderInstance selectionShader;
/**
* Register.
*
* @param event the event
*/
public static void register(RegisterShadersEvent event) {
try {
event.registerShader(
new ShaderInstance(
event.getResourceProvider(),
Lib39.rl("ring"),
DefaultVertexFormat.POSITION_COLOR
),
it -> ringShader = it
);
event.registerShader(
new ShaderInstance(
event.getResourceProvider(),
Lib39.rl("selection"),
DefaultVertexFormat.POSITION_COLOR
),
it -> selectionShader = it
);
} catch (Exception e) {
Lib39.LOGGER.error("Failed to register shader", e);
}
}
}

View File

@ -1,241 +0,0 @@
package top.r3944realms.lib39.content.block;
import com.mojang.authlib.GameProfile;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
import top.r3944realms.lib39.content.block.property.DollPose;
import top.r3944realms.lib39.content.register.Lib39BlockEntities;
import top.r3944realms.lib39.content.register.Lib39Items;
import top.r3944realms.lib39.content.register.Lib39SoundEvents;
import top.r3944realms.lib39.util.GameProfileHelper;
import java.util.List;
/**
* The type Doll block.
*/
@SuppressWarnings("deprecation")
public class DollBlock extends HorizontalDirectionalBlock implements SimpleWaterloggedBlock, EntityBlock {
private static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
/**
* The constant POSE.
*/
public static final EnumProperty<DollPose> POSE = EnumProperty.create("pose", DollPose.class);
private static final VoxelShape DOLL_SHAPE = Block.box(2.0d, 0.0d, 2.0d, 14.0d, 12.0d, 14.0d);
private static final Properties properties = Properties.of()
.instrument(NoteBlockInstrument.BASEDRUM)
.sound(SoundType.WOOL)
.strength(0f, 10f)
.noOcclusion();
private static final double PARTICLE_OFFSET_RANGE = 0.25;
private static final double PARTICLE_HEIGHT_OFFSET = 1.0;
private static final double PARTICLE_HEIGHT_VARIANCE = 0.2;
private static final float NOTE_COLOR_DIVISOR = 24.0F;
private static final int MAX_NOTE_COLORS = 4;
private static final float BASE_VOLUME = 1.0f;
private static final float PITCH_VARIANCE = 0.5f;
private static final float BASE_PITCH = 0.75f;
/**
* Instantiates a new Doll block.
*/
public DollBlock() {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.SOUTH)
.setValue(POSE, DollPose.DEFAULT)
.setValue(WATERLOGGED, false));
}
@Override
public boolean canBeReplaced(@NotNull BlockState state, @NotNull BlockPlaceContext useContext) {
return false;
}
@Override
public @Nullable PushReaction getPistonPushReaction(BlockState state) {
return PushReaction.DESTROY;
}
@Override
public @NotNull BlockState updateShape(@NotNull BlockState currentState, @NotNull Direction direction, @NotNull BlockState neighborState,
@NotNull LevelAccessor level, @NotNull BlockPos currentPos, @NotNull BlockPos neighborPos) {
if (currentState.getValue(WATERLOGGED)) {
level.scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
}
return super.updateShape(currentState, direction, neighborState, level, currentPos, neighborPos);
}
@Override
public @NotNull FluidState getFluidState(@NotNull BlockState blockState) {
return blockState.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(blockState);
}
@Override
public @NotNull InteractionResult use(@NotNull BlockState blockState, @NotNull Level level, @NotNull BlockPos blockPos, @NotNull Player player,
@NotNull InteractionHand hand, @NotNull BlockHitResult hitResult) {
if (level instanceof ServerLevel serverLevel) {
// 播放粒子效果
spawnNoteParticles(serverLevel, blockPos);
// 播放音效
playDollSound(serverLevel, blockPos);
}
return InteractionResult.SUCCESS;
}
/**
* 在玩偶位置生成音符粒子效果
*/
private void spawnNoteParticles(ServerLevel serverLevel, BlockPos blockPos) {
Vec3 particlePosition = calculateParticlePosition(serverLevel, blockPos);
float noteColor = calculateNoteColor(serverLevel);
serverLevel.sendParticles(ParticleTypes.NOTE,
particlePosition.x(), particlePosition.y(), particlePosition.z(),
0, noteColor, 0, 0, 1);
}
/**
* 计算粒子生成位置添加随机偏移
*/
private @NotNull Vec3 calculateParticlePosition(@NotNull ServerLevel serverLevel, BlockPos blockPos) {
return Vec3.atBottomCenterOf(blockPos).add(
(serverLevel.getRandom().nextFloat() - 0.5) * PARTICLE_OFFSET_RANGE * 2,
PARTICLE_HEIGHT_OFFSET + serverLevel.getRandom().nextFloat() * PARTICLE_HEIGHT_VARIANCE,
(serverLevel.getRandom().nextFloat() - 0.5) * PARTICLE_OFFSET_RANGE * 2
);
}
/**
* 计算音符粒子的颜色
*/
private float calculateNoteColor(@NotNull ServerLevel serverLevel) {
return serverLevel.getRandom().nextInt(MAX_NOTE_COLORS) / NOTE_COLOR_DIVISOR;
}
/**
* 播放玩偶音效
*/
private void playDollSound(@NotNull ServerLevel serverLevel, BlockPos blockPos) {
float pitch = BASE_PITCH + serverLevel.random.nextFloat() * PITCH_VARIANCE;
serverLevel.playSound(null, blockPos, Lib39SoundEvents.DUCK_TOY.get(),
SoundSource.BLOCKS, BASE_VOLUME, pitch);
}
@Override
public @Nullable BlockState getStateForPlacement(@NotNull BlockPlaceContext context) {
FluidState fluidState = context.getLevel().getFluidState(context.getClickedPos());
boolean isWaterlogged = fluidState.getType() == Fluids.WATER;
return this.defaultBlockState()
.setValue(FACING, context.getHorizontalDirection().getOpposite())
.setValue(WATERLOGGED, isWaterlogged)
.setValue(POSE, DollPose.DEFAULT);
}
@Override
public @NotNull VoxelShape getShape(@NotNull BlockState blockState, @NotNull BlockGetter level, @NotNull BlockPos blockPos, @NotNull CollisionContext context) {
return DOLL_SHAPE;
}
@Override
protected void createBlockStateDefinition(StateDefinition.@NotNull Builder<Block, BlockState> builder) {
super.createBlockStateDefinition(builder);
builder.add(FACING, WATERLOGGED, POSE);
}
@Nullable
@Override
public BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) {
return Lib39BlockEntities.DOLL_BLOCK_ENTITY.get().create(blockPos, blockState);
}
@SuppressWarnings("deprecation")
@Override
public @NotNull RenderShape getRenderShape(@NotNull BlockState state) {
return RenderShape.ENTITYBLOCK_ANIMATED;
}
@Override
public ItemStack getCloneItemStack(BlockState state, HitResult target, BlockGetter level, BlockPos pos, Player player) {
ItemStack stack = super.getCloneItemStack(state, target, level, pos, player);
BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof DollBlockEntity doll) {
GameProfile profile = doll.getOwnerProfile();
if (profile != null) {
GameProfileHelper.saveProfileToItemStack(stack, profile);
}
}
return stack;
}
/**
* 最重要的方法重写掉落逻辑
*/
@Override
@NotNull
public List<ItemStack> getDrops(@NotNull BlockState state, @NotNull LootParams.Builder params) {
// 获取方块实体
BlockEntity blockEntity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY);
if (blockEntity instanceof DollBlockEntity dollEntity) {
List<ItemStack> customDrops = getCustomDrops(dollEntity, params);
if (customDrops != null) return customDrops;
}
return super.getDrops(state, params);
}
/**
* 生成自定义掉落物
*/
@Nullable
private List<ItemStack> getCustomDrops(DollBlockEntity dollEntity, LootParams.Builder params) {
if (params.getOptionalParameter(LootContextParams.THIS_ENTITY) instanceof Player player) {
if (player.isCreative()) {
return List.of();
}
}
GameProfile profile = dollEntity.getOwnerProfile();
if (profile != null) {
ItemStack instance = Lib39Items.DOLL.get().getDefaultInstance();
GameProfileHelper.saveProfileToItemStack(instance, profile);
return List.of(instance);
}
return null;
}
}

View File

@ -1,98 +0,0 @@
package top.r3944realms.lib39.content.block.blockentity;
import com.mojang.authlib.GameProfile;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.content.register.Lib39BlockEntities;
import top.r3944realms.lib39.util.GameProfileHelper;
import top.r3944realms.lib39.util.nbt.NBTReader;
import top.r3944realms.lib39.util.nbt.NBTWriter;
import javax.annotation.Nullable;
/**
* The type Doll block entity.
*/
public class DollBlockEntity extends BlockEntity {
@Nullable
private GameProfile owner;
/**
* Instantiates a new Doll block entity.
*
* @param pos the pos
* @param blockState the block state
*/
public DollBlockEntity(BlockPos pos, BlockState blockState) {
super(Lib39BlockEntities.DOLL_BLOCK_ENTITY.get(), pos, blockState);
}
protected void saveAdditional(@NotNull CompoundTag tag) {
super.saveAdditional(tag);
NBTWriter.of(tag)
.compoundIf(GameProfileHelper.TAG_OWN_PROFILE, owner != null, () -> NbtUtils.writeGameProfile(new CompoundTag(), this.owner));
}
public void load(@NotNull CompoundTag tag) {
super.load(tag);
NBTReader.of(tag)
.compound(GameProfileHelper.TAG_OWN_PROFILE, compoundTag -> setOwner(NbtUtils.readGameProfile(compoundTag)));
}
/**
* Gets owner profile.
*
* @return the owner profile
*/
@Nullable
public GameProfile getOwnerProfile() {
return this.owner;
}
public ClientboundBlockEntityDataPacket getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
public @NotNull CompoundTag getUpdateTag() {
return this.saveWithoutMetadata();
}
/**
* Sets owner.
*
* @param owner the owner
*/
public void setOwner(@Nullable GameProfile owner) {
synchronized (this) {
this.owner = owner;
}
this.updateOwnerProfile();
}
/**
* Sets owner.
*
* @param ownerName the owner name
*/
public void setOwner(@Nullable String ownerName) {
setOwner(new GameProfile(Util.NIL_UUID, ownerName));
}
private void updateOwnerProfile() {
SkullBlockEntity.updateGameprofile(this.owner, gameProfile -> {
this.owner = gameProfile;
this.setChanged();
});
}
}

View File

@ -1,28 +0,0 @@
package top.r3944realms.lib39.content.block.property;
import net.minecraft.util.StringRepresentable;
import org.jetbrains.annotations.NotNull;
/**
* The enum Doll pose.
*/
public enum DollPose implements StringRepresentable {
/**
* Default doll pose.
*/
DEFAULT("default"),
/**
* further support
*/
FURTHER("further"),
;
private final String name;
DollPose(String name) {
this.name = name;
}
@Override
public @NotNull String getSerializedName() {
return name;
}
}

View File

@ -1,58 +0,0 @@
package top.r3944realms.lib39.content.item;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraftforge.client.extensions.common.IClientItemExtensions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.client.renderer.item.DollItemRenderer;
import top.r3944realms.lib39.content.register.Lib39Blocks;
import top.r3944realms.lib39.util.GameProfileHelper;
import java.util.List;
import java.util.function.Consumer;
/**
* The type Doll item.
*/
public class DollItem extends BlockItem {
/**
* Instantiates a new Doll item.
*
* @param properties the properties
*/
public DollItem(Properties properties) {
super(Lib39Blocks.DOLL.get(), properties);
}
@Override
public void initializeClient(@NotNull Consumer<IClientItemExtensions> consumer) {
consumer.accept(new IClientItemExtensions() {
@Override
public BlockEntityWithoutLevelRenderer getCustomRenderer() {
return DollItemRenderer.getInstance();
}
});
}
@Override
public boolean canEquip(ItemStack stack, EquipmentSlot armorType, Entity entity) {
return armorType == EquipmentSlot.HEAD;
}
@Override
public void appendHoverText(@NotNull ItemStack stack, @Nullable Level level, @NotNull List<Component> tooltip, @NotNull TooltipFlag flag) {
GameProfile profileFromItemStack = GameProfileHelper.getProfileFromItemStack(stack);
if (profileFromItemStack != null && profileFromItemStack.getName() != null) {
tooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.1", profileFromItemStack.getName()));
}
tooltip.add(Component.translatable("tooltip.lib39.content.doll.hover.2"));
}
}

View File

@ -1,37 +0,0 @@
package top.r3944realms.lib39.content.register;
import net.minecraft.world.level.block.entity.BlockEntityType;
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.content.block.blockentity.DollBlockEntity;
/**
* The type Lib 39 block entities.
*/
public class Lib39BlockEntities {
/**
* The constant BLOCK_ENTITY_TYPES.
*/
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITY_TYPES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, Lib39.MOD_ID);
/**
* The constant DOLL_BLOCK_ENTITY.
*/
@SuppressWarnings("DataFlowIssue")
public static final RegistryObject<BlockEntityType<DollBlockEntity>> DOLL_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("doll",
() -> BlockEntityType.Builder
.of(DollBlockEntity::new, Lib39Blocks.DOLL.get())
.build(null)
);
/**
* Register.
*
* @param bus the bus
*/
public static void register(IEventBus bus) {
BLOCK_ENTITY_TYPES.register(bus);
}
}

View File

@ -1,39 +0,0 @@
package top.r3944realms.lib39.content.register;
import net.minecraft.world.level.block.Block;
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.content.block.DollBlock;
import top.r3944realms.lib39.util.block.BlockRegistryBuilder;
/**
* The type Lib 39 blocks.
*/
public class Lib39Blocks {
/**
* The constant BLOCKS.
*/
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Lib39.MOD_ID);
/**
* The constant DOLL.
*/
public static final RegistryObject<Block> DOLL = BlockRegistryBuilder
.create()
.withName("doll")
.registerBlock(BLOCKS, DollBlock::new)
.build();
/**
* Register.
*
* @param bus the bus
*/
public static void register(IEventBus bus) {
BLOCKS.register(bus);
}
}

View File

@ -1,34 +0,0 @@
package top.r3944realms.lib39.content.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.content.item.DollItem;
/**
* The type Ex lib 39 items.
*/
public class Lib39Items {
/**
* The constant ITEMS.
*/
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, Lib39.MOD_ID);
/**
* The constant DOLL.
*/
public static final RegistryObject<Item> DOLL = ITEMS.register("doll", () -> new DollItem(new Item.Properties()));
/**
* Register.
*
* @param bus the bus
*/
public static void register(IEventBus bus) {
ITEMS.register(bus);
}
}

View File

@ -1,48 +0,0 @@
package top.r3944realms.lib39.content.register;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
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;
/**
* The type Lib 39 sound events.
*/
public class Lib39SoundEvents {
/**
* The constant SOUND_EVENTS.
*/
public static final DeferredRegister<SoundEvent> SOUND_EVENTS = DeferredRegister.create(ForgeRegistries.SOUND_EVENTS, Lib39.MOD_ID);
/**
* The constant RL_DUCK_TOY.
*/
public static final ResourceLocation RL_DUCK_TOY = Lib39.rl("duck_toy");
/**
* The constant DUCK_TOY.
*/
public static final RegistryObject<SoundEvent> DUCK_TOY = SOUND_EVENTS.register("duck_toy",
() -> SoundEvent.createFixedRangeEvent(RL_DUCK_TOY, 32.0f));
/**
* Register.
*
* @param bus the bus
*/
public static void register(IEventBus bus) {
SOUND_EVENTS.register(bus);
}
/**
* Gets sub title translate key.
*
* @param name the name
* @return the sub title translate key
*/
public static String getSubTitleTranslateKey(String name) {
return "sound." + Lib39.MOD_ID + ".subtitle." + name;
}
}

View File

@ -1,547 +0,0 @@
package top.r3944realms.lib39.core.command;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.*;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.base.datagen.value.Lib39LangKey;
import top.r3944realms.lib39.core.command.model.CommandNode;
import top.r3944realms.lib39.core.command.model.CommandPath;
import top.r3944realms.lib39.core.command.model.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
/**
* The interface Command help manager.
*/
public interface ICommandHelpManager {
/**
* The constant NEWLINE.
*/
String NEWLINE = "\n";
/**
* Gets id.
*
* @return the id
*/
ResourceLocation getID();
/**
* Gets head key.
*
* @return the head key
*/
String getHeadKey();
/**
* Gets command head.
*
* @return the command head
*/
default String getCommandHead() {
return getID().getNamespace();
}
/**
* Init command node.
*
* @return the command node
*/
default CommandNode init() {
CommandNode root;
root = new CommandNode(this, getCommandHead(), Component.translatable(Lib39LangKey.Message.HELP_HEADER.getKey(), Component.translatable(getHeadKey())), true);
root.addChild(new CommandNode(this, "help", Component.translatable(Lib39LangKey.Message.BASIC_HELP.getKey())));
return root;
}
/**
* Gets cache.
*
* @return the cache
*/
Map<Integer, CommandNode> getCache();
/**
* Gets root node.
*
* @return the root node
*/
CommandNode getRootNode();
/**
* Register command help.
*
* @param commandNode the command node
* @param description the description
*/
default void registerCommandHelp(@NotNull CommandNode commandNode, MutableComponent description) {
registerCommandHelp(commandNode.getFullPath(), description);
}
/**
* Register command help.
*
* @param commandNode the command node
* @param descriptionKey the description key
*/
default void registerCommandHelp(@NotNull CommandNode commandNode, String descriptionKey) {
registerCommandHelp(commandNode.getFullPath(), Component.translatable(descriptionKey));
}
/**
* Register command help.
*
* @param commandPath the command path
*/
default void registerCommandHelp(@NotNull CommandPath commandPath) {
registerCommandHelp(commandPath.fullPath(), Component.literal(""));
}
/**
* Register command parameters.
*
* @param commandPath the command path
* @param parameters the parameters
*/
default void registerCommandParameters(@NotNull CommandPath commandPath, @NotNull Parameter.Builder parameters) {
registerCommandParameters(commandPath.fullPath(), parameters.build());
}
private void registerCommandHelp(@NotNull String commandPath, MutableComponent description) {
String[] pathParts = commandPath.split(" ");
CommandNode currentNode = getRootNode();
int startIndex = pathParts[0].equals(getRootNode().getName()) ? 1 : 0;
for (int i = startIndex; i < pathParts.length; i++) {
String part = pathParts[i];
CommandNode child = currentNode.getChild(part);
if (child == null) {
MutableComponent nodeDescription = (i == pathParts.length - 1) ? description : Component.literal("");
child = new CommandNode(this, part, nodeDescription);
currentNode.addChild(child);
}
currentNode = child;
}
}
/**
* 註冊命令幫助節點
*
* @param builder the builder
*/
default void registerCommand(@NotNull CommandNode.Builder builder) {
CommandNode newRoot = builder.build();
mergeTree(getRootNode(), newRoot);
}
/**
* Merge tree.
*
* @param target the target
* @param source the source
*/
void mergeTree(@NotNull CommandNode target, @NotNull CommandNode source);
/**
* 使用Builder模式註冊命令樹
*
* @param builder the builder
*/
default void registerCommandTree(@NotNull CommandNode.Builder builder) {
registerCommand(builder);
}
/**
* 使用Builder模式快速註冊命令
*
* @param configurator the configurator
*/
default void registerCommands(@NotNull Consumer<CommandNode.Builder> configurator) {
CommandNode.Builder builder = CommandNode.Builder.of(this);
configurator.accept(builder);
registerCommand(builder);
}
/**
* 根据路径查找节点
*
* @param path the path
* @return the optional
*/
default Optional<CommandNode> findNode(CommandPath path) {
CommandNode currentNode = getRootNode();
String[] segments = path.segments();
// 跳过根节点如果路径包含
int startIndex = segments[0].equals(getRootNode().getName()) ? 1 : 0;
for (int i = startIndex; i < segments.length; i++) {
currentNode = currentNode.getChild(segments[i]);
if (currentNode == null) {
return Optional.empty();
}
}
return Optional.of(currentNode);
}
/**
* 检查命令是否存在
*
* @param path the path
* @return the boolean
*/
default boolean hasCommand(CommandPath path) {
return findNode(path).isPresent();
}
private void registerCommandHelp(String commandPath, String descriptionKey) {
registerCommandHelp(commandPath, Component.translatable(descriptionKey));
}
private void registerCommandHelp(String commandPath) {
registerCommandHelp(commandPath, Component.literal(""));
}
/**
* 注册命令参数支持单个参数可选标记使用*前缀表示必选参数
*
* @param commandPath 命令路径 "fpsm tacz dummy"
* @param parameters 参数列表 "*requiredParam", "optionalParam"
*/
private void registerCommandParameters(@NotNull String commandPath, Parameter... parameters) {
String[] pathParts = commandPath.split(" ");
CommandNode currentNode = getRootNode();
// 遍历命令路径找到目标节点
int startIndex = pathParts[0].equals(getRootNode().getName()) ? 1 : 0;
for (int i = startIndex; i < pathParts.length; i++) {
String part = pathParts[i];
CommandNode child = currentNode.getChild(part);
if (child == null) {
// 如果节点不存在创建空描述节点
child = new CommandNode(this, part, Component.literal(""));
currentNode.addChild(child);
}
currentNode = child;
}
// 添加参数处理可选标记
for (Parameter param : parameters) {
currentNode.addParameter(param.name(), param.required());
}
}
/**
* 动态添加子指令到指定命令路径
*
* @param commandPath 命令路径 "fpsm map modify"
* @param childName 子指令名称
* @param description 子指令描述
* @return 是否添加成功 boolean
*/
default boolean addChildCommand(@NotNull String commandPath, String childName, MutableComponent description) {
String[] pathParts = commandPath.split(" ");
CommandNode currentNode = getRootNode();
// 遍历命令路径找到目标父节点
for (String part : pathParts) {
if (!part.equals(currentNode.getName())) {
CommandNode child = currentNode.getChild(part);
if (child == null) {
// 路径不存在创建中间节点
child = new CommandNode(this, part, Component.literal(""));
currentNode.addChild(child);
}
currentNode = child;
}
}
// 添加子指令
CommandNode childNode = new CommandNode(this, childName, description);
currentNode.addChild(childNode);
return true;
}
/**
* 构建单个命令节点的显示格式
*
* @param node 当前命令节点
* @param indent 当前缩进
* @param isRoot 是否为根节点
* @return 格式化后的命令节点组件
*/
private @NotNull MutableComponent buildCommandLine(CommandNode node, String indent, boolean isRoot, @Nullable String currentFullPath) {
if (isRoot) {
// 根节点特殊处理
String rootCommand = "/" + node.getName();
return Component.literal(rootCommand)
.withStyle(ChatFormatting.AQUA)
.withStyle(Style.EMPTY
.withClickEvent(new ClickEvent(
ClickEvent.Action.SUGGEST_COMMAND,
rootCommand + " "
))
.withHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
Component.translatable(Lib39LangKey.Message.HELP_HOVER_COPY_TIP.getKey())
))
);
} else {
// 构建完整命令路径
String fullCommand = (currentFullPath != null && !currentFullPath.isEmpty())
? currentFullPath + " " + node.getName()
: "/" + getRootNode().getName() + " " + node.getFullPath();
// 构建建议的命令带参数占位符
String suggestedCommand = buildSuggestedCommand(node, fullCommand);
// 非根节点显示命令和描述
MutableComponent prefix = Component.literal(indent + "└─ ").withStyle(ChatFormatting.GRAY);
// 命令名称可点击
MutableComponent commandName = Component.literal(node.getName())
.withStyle(ChatFormatting.DARK_AQUA)
.withStyle(Style.EMPTY
.withClickEvent(new ClickEvent(
ClickEvent.Action.SUGGEST_COMMAND,
suggestedCommand
))
.withHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
Component.translatable(Lib39LangKey.Message.HELP_HOVER_COPY_TIP.getKey(), suggestedCommand)
))
);
MutableComponent displayLine = prefix.append(commandName);
// 添加参数显示只显示不添加额外空格
if (!node.getParameters().isEmpty()) {
displayLine.append(Component.literal(" ")); // 命令名和参数之间的空格
for (int i = 0; i < node.getParameters().size(); i++) {
Parameter param = node.getParameters().get(i);
if (param.required()) {
displayLine.append(Component.literal("<").withStyle(ChatFormatting.GRAY))
.append(Component.literal(param.name()).withStyle(ChatFormatting.WHITE))
.append(Component.literal(">").withStyle(ChatFormatting.GRAY));
} else {
displayLine.append(Component.literal("[").withStyle(ChatFormatting.GRAY))
.append(Component.literal(param.name()).withStyle(ChatFormatting.WHITE))
.append(Component.literal("]").withStyle(ChatFormatting.GRAY));
}
// 参数之间添加空格除了最后一个
if (i < node.getParameters().size() - 1) {
displayLine.append(Component.literal(" "));
}
}
}
// 添加分隔符和描述
displayLine.append(Component.literal(" - ").withStyle(ChatFormatting.DARK_GRAY))
.append(node.getDescription().copy().withStyle(ChatFormatting.GRAY));
// 如果有子节点添加展开/折叠按钮
boolean shouldShowToggle = node.hasChildren() && !node.isLeaf();
if (shouldShowToggle) {
String toggleKey = node.isExpanded()
? Lib39LangKey.Message.HELP_NODE_TOGGLE_COLLAPSE.getKey()
: Lib39LangKey.Message.HELP_NODE_TOGGLE_EXPAND.getKey();
MutableComponent toggleButton = Component.literal(" [")
.withStyle(ChatFormatting.GRAY)
.append(Component.translatable(toggleKey).withStyle(ChatFormatting.YELLOW))
.append(Component.literal("]").withStyle(ChatFormatting.GRAY));
// 为按钮添加点击事件
toggleButton.withStyle(Style.EMPTY
.withClickEvent(new ClickEvent(
ClickEvent.Action.RUN_COMMAND,
"/" + getCommandHead() + " help toggle " + node.hashCode()
))
.withHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
Component.translatable(Lib39LangKey.Message.HELP_CLICK_EXPAND.getKey())
.withStyle(ChatFormatting.GRAY)
))
);
displayLine.append(toggleButton);
}
return displayLine;
}
}
/**
* 构建建议的命令包含参数占位符
*/
private @NotNull String buildSuggestedCommand(@NotNull CommandNode node, @NotNull String baseCommand) {
StringBuilder sb = new StringBuilder(baseCommand);
// 如果有参数添加参数占位符
if (!node.getParameters().isEmpty()) {
for (Parameter param : node.getParameters()) {
sb.append(" ");
if (param.required()) {
sb.append("<").append(param.name()).append(">");
} else {
sb.append("[").append(param.name()).append("]");
}
}
}
// 如果是叶子节点且没有参数添加空格以便继续输入
if (node.isLeaf() && node.getParameters().isEmpty()) {
sb.append(" ");
}
return sb.toString();
}
/**
* 檢查節點是否應該顯示摺疊信息
*/
private boolean shouldShowCollapsedInfo(@NotNull CommandNode node) {
return node.hasChildren() && !node.isExpanded() && !node.getChildren().isEmpty();
}
/**
* 獲取有效的子命令數量過濾掉空描述的命令
*/
private long getValidChildCount(@NotNull CommandNode node) {
return node.getChildren().stream()
.filter(child -> !child.getDescription().getString().isEmpty())
.count();
}
/**
* 遞歸構建命令樹
*/
private void buildCommandTreeString(@NotNull CommandNode node,
@NotNull String indent,
@Nullable String currentFullPath,
@NotNull List<MutableComponent> result,
CommandSourceStack commandSourceStack) {
boolean isRoot = indent.isEmpty();
if (node.testPermission(commandSourceStack)) {
MutableComponent commandLine = buildCommandLine(node, indent, isRoot, currentFullPath);
result.add(commandLine.append(Component.literal(NEWLINE)));
// 遞歸處理子節點
String childIndent = indent + "| ";
if (node.isExpanded()) {
String newFullPath = (currentFullPath != null && !currentFullPath.isEmpty())
? currentFullPath + " " + node.getName()
: "/" + node.getName();
for (CommandNode child : node.getChildren()) {
// 只顯示有描述的子命令
if (!child.getDescription().getString().isEmpty() && node.testPermission(commandSourceStack)) {
buildCommandTreeString(child, childIndent, newFullPath, result, commandSourceStack);
}
}
} else if (shouldShowCollapsedInfo(node)) {
long childCount = getValidChildCount(node);
if (childCount > 0) {
MutableComponent collapsedInfo = Component.literal(indent + "| " + "└─ ")
.withStyle(ChatFormatting.GRAY)
.append(Component.translatable(
Lib39LangKey.Message.HELP_NODE_EXPAND.getKey(),
childCount
).withStyle(ChatFormatting.GRAY));
collapsedInfo.withStyle(Style.EMPTY
.withClickEvent(new ClickEvent(
ClickEvent.Action.RUN_COMMAND,
"/" + getCommandHead() + " help toggle " + node.hashCode()
))
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable(Lib39LangKey.Message.HELP_CLICK_EXPAND.getKey())))
);
result.add(collapsedInfo.append(Component.literal(NEWLINE)));
}
}
}
}
/**
* 获取命令树的字符串表示
*
* @return 命令树列表 command tree
*/
default List<MutableComponent> getCommandTree(CommandSourceStack commandSourceStack) {
List<MutableComponent> result = new ArrayList<>();
buildCommandTreeString(getRootNode(), "", "", result, commandSourceStack);
return result;
}
/**
* 切换指定节点的展开/闭合状态
*
* @param hashCode 节点哈希值
* @return 是否成功切换 boolean
*/
default boolean toggleNodeExpanded(int hashCode) {
CommandNode currentNode = getCache().getOrDefault(hashCode, null);
if (currentNode == null || currentNode.getChildren().isEmpty()) {
return false;
}
currentNode.toggleExpanded();
return true;
}
/**
* 构建帮助消息
*
* @param header 帮助头部
* @param entries 帮助条目列表
* @return the mutable component
*/
default MutableComponent buildHelpMessage(@NotNull Component header, @NotNull List<MutableComponent> entries) {
MutableComponent helpMessage = Component.empty();
// 添加头部
helpMessage.append(header.copy()).append(NEWLINE);
// 添加分隔线
helpMessage.append(Component.literal("\n"));
// 添加当前页的帮助内容
if (entries.isEmpty()) {
helpMessage.append(Component.translatable(Lib39LangKey.Message.HELP_NO_ENTRIES.getKey())).append(NEWLINE);
} else {
for (MutableComponent entry : entries) {
helpMessage.append(entry);
}
}
return helpMessage;
}
/**
* Build command tree help mutable component.
*
* @return the mutable component
*/
default MutableComponent buildCommandTreeHelp(CommandSourceStack commandSourceStack) {
List<MutableComponent> commandTree = getCommandTree(commandSourceStack);
return buildHelpMessage(Component.translatable(Lib39LangKey.Message.HELP_HEADER.getKey(), Component.translatable(getHeadKey())), commandTree);
}
}

View File

@ -1,133 +0,0 @@
package top.r3944realms.lib39.core.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraftforge.common.MinecraftForge;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.api.event.RegisterCommandHelpEvent;
import top.r3944realms.lib39.base.datagen.value.Lib39LangKey;
import javax.annotation.Nullable;
/**
* The interface Help command.
*/
public interface IHelpCommand {
/**
* Should show toggle failed boolean.
*
* @return the boolean
*/
default boolean shouldShowToggleFailed() {
return false;
}
/**
* Gets help head.
*
* @return the help head
*/
@Nullable
default LiteralArgumentBuilder<CommandSourceStack> getHelpHead() {
return null;
}
/**
* Gets command help manager.
*
* @return the command help manager
*/
ICommandHelpManager getCommandHelpManager();
/**
* Build command literal argument builder.
*
* @param dispatcher the dispatcher
* @param context the context
* @return the literal argument builder
*/
default LiteralArgumentBuilder<CommandSourceStack> buildCommand(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext context) {
LiteralArgumentBuilder<CommandSourceStack> head = getHelpHead();
if (head == null) {
head = LiteralArgumentBuilder.literal(getCommandHelpManager().getID().getNamespace());
}
LiteralArgumentBuilder<CommandSourceStack> tree = head.requires(this::requestPermission)
.then(Commands.literal("help").executes(this::handleHelp)
.then(Commands.literal("toggle")
.then(Commands.argument("hash", IntegerArgumentType.integer()).executes(this::handleHelpToggle))
));
RegisterCommandHelpEvent registerHelpCommandEvent = new RegisterCommandHelpEvent(tree, getCommandHelpManager(), context);
MinecraftForge.EVENT_BUS.post(registerHelpCommandEvent);
dispatcher.register(head);
return head;
}
/**
* Request permission boolean.
*
* @param context the context
* @return the boolean
*/
default boolean requestPermission(CommandSourceStack context) {
return true;
}
/**
* Handle help int.
*
* @param context the context
* @return the int
*/
default int handleHelp(@NotNull CommandContext<CommandSourceStack> context) {
ICommandHelpManager commandHelpManager = getCommandHelpManager();
MutableComponent helpMessage = commandHelpManager.buildCommandTreeHelp(context.getSource());
sendSuccess(context.getSource(), helpMessage);
return 1;
}
/**
* Handle help toggle int.
*
* @param context the context
* @return the int
*/
default int handleHelpToggle(@NotNull CommandContext<CommandSourceStack> context) {
int hash = IntegerArgumentType.getInteger(context, "hash");
ICommandHelpManager commandHelpManager = getCommandHelpManager();
boolean success = commandHelpManager.toggleNodeExpanded(hash);
if (success) {
MutableComponent helpMessage = Component.literal("\n".repeat(2)).append(commandHelpManager.buildCommandTreeHelp(context.getSource()));
sendSuccess(context.getSource(), helpMessage);
} else if (shouldShowToggleFailed()) {
sendFailure(context.getSource(), Component.translatable(Lib39LangKey.Message.HELP_TOGGLE_FAILED.getKey()));
}
return 1;
}
/**
* Send success.
*
* @param source the source
* @param key the key
*/
static void sendSuccess(@NotNull CommandSourceStack source, Component key) {
source.sendSuccess(() -> key, true);
}
/**
* Send failure.
*
* @param source the source
* @param key the key
*/
static void sendFailure(@NotNull CommandSourceStack source, Component key) {
source.sendFailure(key);
}
}

Some files were not shown because too many files have changed in this diff Show More