命令别名工具,调整McLangDataValue

This commit is contained in:
叁玖领域 2025-10-12 14:22:00 +08:00
parent 63ba46abc0
commit c039072e7c
20 changed files with 481 additions and 244 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025-2026 R3944Realms
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,29 +1,4 @@
# Lib39
Installation information
=======
This template repository can be directly cloned to get you started with a new
mod. Simply create a new repository cloned from this one, by following the
instructions provided by [GitHub](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template).
Once you have your clone, simply open the repository in the IDE of your choice. The usual recommendation for an IDE is either IntelliJ IDEA or Eclipse.
If at any point you are missing libraries in your IDE, or you've run into problems you can
run `gradlew --refresh-dependencies` to refresh the local cache. `gradlew clean` to reset everything
{this does not affect your code} and then start the process again.
Mapping Names:
============
The MDK is configured to use the official mapping names from Mojang for methods and fields
in the Minecraft codebase. These names are covered by a specific license. All modders should be aware of this
license. For the latest license text, refer to the mapping file itself, or the reference copy here:
https://github.com/NeoForged/NeoForm/blob/main/Mojang.md
MDG Legacy:
==========
This template uses [ModDevGradle Legacy](https://github.com/neoforged/ModDevGradle). Documentation can be found [here](https://github.com/neoforged/ModDevGradle/blob/main/LEGACY.md).
Additional Resources:
==========
Community Documentation: https://docs.neoforged.net/
NeoForged Discord: https://discord.neoforged.net/
**Lib39** is a general-purpose dependency library for Minecraft mods.
It provides utility methods and core functionality that other mods can build upon.

View File

@ -1,8 +1,11 @@
//file:noinspection GroovyAssignabilityCheck
plugins {
id 'java'
id 'idea'
id 'java-library'
id 'maven-publish'
id 'net.neoforged.moddev.legacyforge' version '2.0.91'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'net.neoforged.moddev.legacyforge' version '2.0.103'
}
tasks.named('wrapper', Wrapper).configure {
@ -14,11 +17,17 @@ tasks.named('wrapper', Wrapper).configure {
distributionType = Wrapper.DistributionType.BIN
}
version = mod_version
version = "${minecraft_version}-${mod_version}"
group = mod_group_id
repositories {
mavenLocal()
maven { url = "https://libraries.minecraft.net/" }
maven { url = "https://neoforged.forgecdn.net/releases" }
maven { url = "https://neoforged.forgecdn.net/mojang-meta" }
flatDir {
dir "libs"
}
}
base {
@ -49,6 +58,11 @@ legacyForge {
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
}
clientAuth{
devLogin = true
client()
systemProperty 'forge.enabledGameTestNamespaces', project.mod_id
}
server {
server()
@ -174,7 +188,8 @@ var generateModMetadata = tasks.register("generateModMetadata", ProcessResources
mod_license : mod_license,
mod_version : mod_version,
mod_authors : mod_authors,
mod_description : mod_description
mod_description : mod_description,
mod_credits : mod_credits
]
inputs.properties replaceProperties
expand replaceProperties

View File

@ -1,9 +1,9 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
org.gradle.jvmargs=-Xmx1G
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
org.gradle.configuration-cache=false
#read more on this at https://github.com/neoforged/ModDevGradle?tab=readme-ov-file#better-minecraft-parameter-names--javadoc-parchment
# you can also find the latest versions at: https://parchmentmc.org/docs/getting-started
@ -25,21 +25,22 @@ forge_version_range=[47.1.3,)
loader_version_range=[47,)
## Mod Properties
# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63}
# Must match the String constant located in the main mod class annotated with @Mod.
mod_id=examplemod
mod_id=lib39
# The human-readable display name for the mod.
mod_name=Example Mod
mod_name=3944Realms 's Lib Mod
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=All Rights Reserved
mod_license=MIT
# The mod version. See https://semver.org/
mod_version=1.0.0
mod_version=0.0.1
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
mod_group_id=com.example.examplemod
mod_group_id=top.r3944realms.lib39
# The authors of the mod. This is a simple text string that is used for display purposes in the mod list.
mod_authors=YourNameHere, OtherNameHere
mod_authors=R3944Realms
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
mod_description=Example mod description.\nNewline characters can be used and will be replaced properly.
mod_description=Lib39 is a general-purpose dependency library that provides utility methods and core functionality for other mods.
mod_credits=Logo created by Shanyi43, edited by R3944Realms

View File

@ -1,3 +1,12 @@
//file:noinspection GroovyAssignabilityCheck
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
maven { url = 'https://maven.neoforged.net/releases' }
maven { url = 'https://maven.parchmentmc.org' } // Add this line
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}

View File

@ -1,63 +0,0 @@
package com.example.examplemod;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.config.ModConfigEvent;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
// An example config class. This is not required, but it's a good idea to have one to keep your config organized.
// Demonstrates how to use Forge's config APIs
@Mod.EventBusSubscriber(modid = ExampleMod.MODID, bus = Mod.EventBusSubscriber.Bus.MOD)
public class Config
{
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
private static final ForgeConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER
.comment("Whether to log the dirt block on common setup")
.define("logDirtBlock", true);
private static final ForgeConfigSpec.IntValue MAGIC_NUMBER = BUILDER
.comment("A magic number")
.defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE);
public static final ForgeConfigSpec.ConfigValue<String> MAGIC_NUMBER_INTRODUCTION = BUILDER
.comment("What you want the introduction message to be for the magic number")
.define("magicNumberIntroduction", "The magic number is... ");
// a list of strings that are treated as resource locations for items
private static final ForgeConfigSpec.ConfigValue<List<? extends String>> ITEM_STRINGS = BUILDER
.comment("A list of items to log on common setup.")
.defineListAllowEmpty("items", List.of("minecraft:iron_ingot"), Config::validateItemName);
static final ForgeConfigSpec SPEC = BUILDER.build();
public static boolean logDirtBlock;
public static int magicNumber;
public static String magicNumberIntroduction;
public static Set<Item> items;
private static boolean validateItemName(final Object obj)
{
return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(new ResourceLocation(itemName));
}
@SubscribeEvent
static void onLoad(final ModConfigEvent event)
{
logDirtBlock = LOG_DIRT_BLOCK.get();
magicNumber = MAGIC_NUMBER.get();
magicNumberIntroduction = MAGIC_NUMBER_INTRODUCTION.get();
// convert the list of strings into a set of items
items = ITEM_STRINGS.get().stream()
.map(itemName -> BuiltInRegistries.ITEM.get(new ResourceLocation(itemName)))
.collect(Collectors.toSet());
}
}

View File

@ -1,128 +0,0 @@
package com.example.examplemod;
import com.mojang.logging.LogUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.MapColor;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.BuildCreativeModeTabContentsEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import org.slf4j.Logger;
// The value here should match an entry in the META-INF/mods.toml file
@Mod(ExampleMod.MODID)
public class ExampleMod
{
// Define mod id in a common place for everything to reference
public static final String MODID = "examplemod";
// Directly reference a slf4j logger
private static final Logger LOGGER = LogUtils.getLogger();
// Create a Deferred Register to hold Blocks which will all be registered under the "examplemod" namespace
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID);
// Create a Deferred Register to hold Items which will all be registered under the "examplemod" namespace
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
// Create a Deferred Register to hold CreativeModeTabs which will all be registered under the "examplemod" namespace
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID);
// Creates a new Block with the id "examplemod:example_block", combining the namespace and path
public static final RegistryObject<Block> EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(BlockBehaviour.Properties.of().mapColor(MapColor.STONE)));
// Creates a new BlockItem with the id "examplemod:example_block", combining the namespace and path
public static final RegistryObject<Item> EXAMPLE_BLOCK_ITEM = ITEMS.register("example_block", () -> new BlockItem(EXAMPLE_BLOCK.get(), new Item.Properties()));
// Creates a new food item with the id "examplemod:example_id", nutrition 1 and saturation 2
public static final RegistryObject<Item> EXAMPLE_ITEM = ITEMS.register("example_item", () -> new Item(new Item.Properties().food(new FoodProperties.Builder()
.alwaysEat().nutrition(1).saturationMod(2f).build())));
// Creates a creative tab with the id "examplemod:example_tab" for the example item, that is placed after the combat tab
public static final RegistryObject<CreativeModeTab> EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example_tab", () -> CreativeModeTab.builder()
.withTabsBefore(CreativeModeTabs.COMBAT)
.icon(() -> EXAMPLE_ITEM.get().getDefaultInstance())
.displayItems((parameters, output) -> {
output.accept(EXAMPLE_ITEM.get()); // Add the example item to the tab. For your own tabs, this method is preferred over the event
}).build());
public ExampleMod()
{
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
// Register the commonSetup method for modloading
modEventBus.addListener(this::commonSetup);
// Register the Deferred Register to the mod event bus so blocks get registered
BLOCKS.register(modEventBus);
// Register the Deferred Register to the mod event bus so items get registered
ITEMS.register(modEventBus);
// Register the Deferred Register to the mod event bus so tabs get registered
CREATIVE_MODE_TABS.register(modEventBus);
// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);
// Register the item to a creative tab
modEventBus.addListener(this::addCreative);
// Register our mod's ForgeConfigSpec so that Forge can create and load the config file for us
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, Config.SPEC);
}
private void commonSetup(final FMLCommonSetupEvent event)
{
// Some common setup code
LOGGER.info("HELLO FROM COMMON SETUP");
if (Config.logDirtBlock)
LOGGER.info("DIRT BLOCK >> {}", ForgeRegistries.BLOCKS.getKey(Blocks.DIRT));
LOGGER.info(Config.magicNumberIntroduction + Config.magicNumber);
Config.items.forEach((item) -> LOGGER.info("ITEM >> {}", item.toString()));
}
// Add the example block item to the building blocks tab
private void addCreative(BuildCreativeModeTabContentsEvent event)
{
if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS)
event.accept(EXAMPLE_BLOCK_ITEM);
}
// You can use SubscribeEvent and let the Event Bus discover methods to call
@SubscribeEvent
public void onServerStarting(ServerStartingEvent event)
{
// Do something when the server starts
LOGGER.info("HELLO from server starting");
}
// You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent
@Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
public static class ClientModEvents
{
@SubscribeEvent
public static void onClientSetup(FMLClientSetupEvent event)
{
// Some client setup code
LOGGER.info("HELLO FROM CLIENT SETUP");
LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName());
}
}
}

View File

@ -0,0 +1,14 @@
package top.r3944realms.lib39;
import net.minecraftforge.fml.common.Mod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Mod(Lib39.MOD_ID)
public class Lib39 {
public static final String MOD_ID = "lib39";
public static final Logger LOGGER = LoggerFactory.getLogger(Lib39.class);
public Lib39() {
LOGGER.info("[Lib39] Initializing Lib39");
}
}

View File

@ -0,0 +1,51 @@
package top.r3944realms.lib39.core.registry;
import top.r3944realms.lib39.datagen.value.ILocaleEntry;
import top.r3944realms.lib39.datagen.value.McLocale;
import java.util.*;
@SuppressWarnings("unused")
public class LocaleRegistry {
private static final Map<String, ILocaleEntry> REGISTRY = new LinkedHashMap<>();
// 初始化注册所有枚举值
static {
for (McLocale loc : McLocale.values()) {
register(loc);
}
}
/** 注册(覆盖已有时直接返回旧值) */
@SuppressWarnings("UnusedReturnValue")
public static ILocaleEntry register(ILocaleEntry entry) {
return REGISTRY.putIfAbsent(entry.mcCode().toLowerCase(), entry);
}
/** 通过 Minecraft 代码查找 */
public static ILocaleEntry fromMcCode(String code) {
return REGISTRY.get(code.toLowerCase());
}
/** 列出所有 */
public static Collection<ILocaleEntry> allValues() {
return Collections.unmodifiableCollection(REGISTRY.values());
}
/** 动态注册一个扩展 Locale */
public static ILocaleEntry registerDynamic(String mcCode, Locale locale) {
return REGISTRY.computeIfAbsent(mcCode.toLowerCase(),
k -> new ExtendedLocale(mcCode.toLowerCase(), locale));
}
/**
* 扩展类型
*/
private record ExtendedLocale(String mcCode, Locale javaLocale) implements ILocaleEntry {
@Override
public String toString() {
return "ExtendedLocale[" + mcCode + "]";
}
}
}

View File

@ -0,0 +1,39 @@
package top.r3944realms.lib39.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.LanguageProvider;
import top.r3944realms.lib39.datagen.value.ILangKeyValue;
import top.r3944realms.lib39.datagen.value.McLocale;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SimpleLanguageProvider extends LanguageProvider {
private final McLocale language;
private final ILangKeyValue langKeyValue;
private final Map<String, String> lanKeyMap;
private static final List<String> objects = new ArrayList<>();
public SimpleLanguageProvider(PackOutput output, String modId, McLocale Lan, ILangKeyValue langKeyValue) {
super(output, modId, Lan.mcCode());
this.language = Lan;
this.langKeyValue = langKeyValue;
lanKeyMap = new HashMap<>();
init();
}
private void init() {
for (ILangKeyValue iLangKeyValue : langKeyValue.getValues()) {
lanKeyMap.put(language.mcCode(), iLangKeyValue.getLang(language));
}
}
private void addLang(String Key, String value) {
if(!objects.contains(Key)) objects.add(Key);
lanKeyMap.put(Key, value);
}
@Override
protected void addTranslations() {
objects.forEach(key -> add(key, lanKeyMap.get(key)));
}
}

View File

@ -0,0 +1,11 @@
package top.r3944realms.lib39.datagen.value;
import java.util.List;
public interface ILangKeyValue {
static String getLang(McLocale locale, ILangKeyValue key) {
return key.getLang(locale);
}
String getLang(McLocale locale);
List<ILangKeyValue> getValues();
}

View File

@ -0,0 +1,8 @@
package top.r3944realms.lib39.datagen.value;
import java.util.Locale;
public interface ILocaleEntry {
String mcCode();
Locale javaLocale();
}

View File

@ -0,0 +1,65 @@
package top.r3944realms.lib39.datagen.value;
import org.jetbrains.annotations.NotNull;
import java.util.function.Supplier;
public abstract class LangKeyValue implements ILangKeyValue {
private final Supplier<?> supplier;
private String key;
private final String US_EN;
private final String SIM_CN;
private final String TRA_CN;
private final String LZH;
private final Boolean Default;
private final ModPartEnum MPE;
LangKeyValue(Supplier<?> Supplier, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, String LZH, Boolean isDefault) {
this.supplier = Supplier;
this.MPE = MPE;
this.US_EN = US_EN;
this.SIM_CN = SIM_CN;
this.TRA_CN = TRA_CN;
this.LZH = LZH;
this.Default = isDefault;
}
LangKeyValue(@NotNull String ResourceKey, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, String LZH, Boolean isDefault) {
this.supplier = null;
this.key = ResourceKey;
this.MPE = MPE;
this.US_EN = US_EN;
this.SIM_CN = SIM_CN;
this.TRA_CN = TRA_CN;
this.LZH = LZH;
this.Default = isDefault;
}
LangKeyValue(Supplier<?> Supplier, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, String LZH) {
this(Supplier, MPE, US_EN, SIM_CN, TRA_CN, LZH, false);
}
LangKeyValue(Supplier<?> Supplier, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, Boolean isDefault) {
this(Supplier, MPE, US_EN, SIM_CN, TRA_CN, null, isDefault);
}
LangKeyValue(@NotNull String ResourceKey, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, Boolean isDefault) {
this(ResourceKey, MPE, US_EN, SIM_CN, TRA_CN, null, isDefault);
}
LangKeyValue(@NotNull String ResourceKey, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, String LZH) {
this(ResourceKey, MPE, US_EN, SIM_CN, TRA_CN, LZH, false);
}
LangKeyValue(Supplier<?> Supplier, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN) {
this(Supplier, MPE, US_EN, SIM_CN, TRA_CN, null, false);
}
LangKeyValue(@NotNull String ResourceKey, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN) {
this(ResourceKey, MPE, US_EN, SIM_CN, TRA_CN, null, false);
}
public String getKey() {
return key;
}
@Override
public String getLang(@NotNull McLocale locale) {
return switch (locale) {
case EN_US, JA_JP, KO_KR, RU_RU, DE_DE, ES_ES, FR_FR -> US_EN;
case ZH_CN -> SIM_CN;
case ZH_TW -> TRA_CN;
case LZH -> LZH;
};
}
}

View File

@ -0,0 +1,30 @@
package top.r3944realms.lib39.datagen.value;
import java.util.Locale;
public enum McLocale implements ILocaleEntry {
EN_US("en_us", Locale.US),
ZH_CN("zh_cn", Locale.SIMPLIFIED_CHINESE),
ZH_TW("zh_tw", Locale.TRADITIONAL_CHINESE),
LZH("lzh", new Locale("lzh", "ZH")),
JA_JP("ja_jp", Locale.JAPAN),
KO_KR("ko_kr", Locale.KOREA),
RU_RU("ru_ru", new Locale("ru", "RU")),
FR_FR("fr_fr", Locale.FRANCE),
DE_DE("de_de", Locale.GERMANY),
ES_ES("es_es", new Locale("es", "ES"));
private final String mcCode;
private final Locale javaLocale;
McLocale(String mcCode, Locale javaLocale) {
this.mcCode = mcCode;
this.javaLocale = javaLocale;
}
@Override
public String mcCode() { return mcCode; }
@Override
public Locale javaLocale() { return javaLocale; }
}

View File

@ -0,0 +1,94 @@
package top.r3944realms.lib39.datagen.value;
/**
* 模组各部分的类型枚举用于数据生成与分类
*/
public enum ModPartEnum {
/** 默认/未指定类型 */
DEFAULT,
/** 物品 */
ITEM,
/** 方块 */
BLOCK,
/** 附魔 */
ENCHANTMENT,
/** 成就 / 进度 */
ADVANCEMENT,
/** 创造模式物品栏 */
CREATIVE_TAB,
/** 配置项 */
CONFIG,
/** 实体(生物、载具等) */
ENTITY,
/** 图形界面 */
GUI,
/** 作者信息 */
AUTHOR,
/** 标题 */
TITLE,
/** 名称 */
NAME,
/** 游戏规则(/gamerule */
GAME_RULE,
/** 描述文本 */
DESCRIPTION,
/** 一般信息 */
INFO,
/** 消息(聊天、提示等) */
MESSAGE,
/** 命令 */
COMMAND,
/** 声音资源 */
SOUND;
/**
* 根据枚举类型生成标准化 key 前缀
* 例如 ITEM -> "item.", BLOCK -> "block."
*/
public String getKeyPrefix() {
return switch (this) {
case ITEM -> "item.";
case BLOCK -> "block.";
case ENCHANTMENT -> "enchantment.";
case ADVANCEMENT -> "advancement.";
case CREATIVE_TAB -> "creative_tab.";
case CONFIG -> "config.";
case ENTITY -> "entity.";
case GUI -> "gui.";
case AUTHOR -> "author.";
case TITLE -> "title.";
case NAME -> "name.";
case GAME_RULE -> "gamerule.";
case DESCRIPTION -> "description.";
case INFO -> "info.";
case MESSAGE -> "message.";
case COMMAND -> "command.";
case SOUND -> "sound.";
default -> "";
};
}
/**
* 根据枚举类型和具体名称生成完整 key
* 例如 ITEM + "example_item" -> "item.example_item"
*/
public String getFullKey(String name) {
return getKeyPrefix() + name;
}
}

View File

@ -0,0 +1,102 @@
package top.r3944realms.lib39.utils.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CommandAliasHelper {
/**
* 注册命令及其别名
*/
public static void registerWithAliases(@NotNull CommandDispatcher<CommandSourceStack> dispatcher,
LiteralArgumentBuilder<CommandSourceStack> mainCommand,
String @NotNull ... aliases) {
// 注册主命令
LiteralCommandNode<CommandSourceStack> mainNode = dispatcher.register(mainCommand);
// 注册所有别名
for (String alias : aliases) {
LiteralArgumentBuilder<CommandSourceStack> aliasCommand = Commands.literal(alias);
// 复制主命令的所有子命令到别名命令递归复制
copyChildren(mainNode, aliasCommand);
dispatcher.register(aliasCommand);
}
}
/**
* 递归复制命令节点的所有子节点
*/
private static void copyChildren(@NotNull CommandNode<CommandSourceStack> source, ArgumentBuilder<CommandSourceStack, ?> target) {
for (CommandNode<CommandSourceStack> child : source.getChildren()) {
ArgumentBuilder<CommandSourceStack, ?> childBuilder = createBuilderFromNode(child);
if (childBuilder != null) {
// 递归复制孙子节点
copyChildren(child, childBuilder);
// 将子命令添加到目标命令
target.then(childBuilder);
}
}
}
/**
* 根据命令节点类型创建对应的构建器
*/
private static @Nullable ArgumentBuilder<CommandSourceStack, ?> createBuilderFromNode(CommandNode<CommandSourceStack> node) {
if (node instanceof LiteralCommandNode<CommandSourceStack> literalNode) {
// 处理字面量节点
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(literalNode.getLiteral());
copyNodeProperties(node, builder);
return builder;
} else if (node instanceof ArgumentCommandNode<CommandSourceStack, ?> argumentNode) {
// 处理参数节点
RequiredArgumentBuilder<CommandSourceStack, ?> builder = Commands.argument(
argumentNode.getName(),
argumentNode.getType()
);
// 设置参数建议提供器
if (argumentNode.getCustomSuggestions() != null) {
builder.suggests(argumentNode.getCustomSuggestions());
}
copyNodeProperties(node, builder);
return builder;
}
return null;
}
/**
* 复制命令节点的通用属性
*/
private static void copyNodeProperties(@NotNull CommandNode<CommandSourceStack> source, ArgumentBuilder<CommandSourceStack, ?> target) {
// 复制重定向
if (source.getRedirect() != null) {
target.redirect(source.getRedirect());
}
// 复制权限要求
if (source.getRequirement() != null) {
target.requires(source.getRequirement());
}
// 复制执行逻辑
if (source.getCommand() != null) {
target.executes(source.getCommand());
}
}
}

View File

@ -1,5 +0,0 @@
{
"itemGroup.examplemod": "Example Mod Tab",
"block.examplemod.example_block": "Example Block",
"item.examplemod.example_item": "Example Item"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 KiB

View File

@ -38,10 +38,10 @@ displayName="${mod_name}" #mandatory
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional
# A file name (in the root of the mod JAR) containing a logo for display
#logoFile="examplemod.png" #optional
logoFile="lib39_logo.png" #optional
# A text field displayed in the mod UI
#credits="" #optional
credits="${mod_credits}" #optional
# A text field displayed in the mod UI
authors="${mod_authors}" #optional

View File

@ -1,8 +1,6 @@
{
"pack": {
"description": {
"text": "${mod_name} resources"
},
"description": "${mod_name} resources",
"pack_format": 15
}
}