diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2e0f5f2 --- /dev/null +++ b/LICENSE @@ -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. \ No newline at end of file diff --git a/README.md b/README.md index 00fbba4..535f3ac 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/build.gradle b/build.gradle index 3fce4be..c113580 100644 --- a/build.gradle +++ b/build.gradle @@ -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 diff --git a/gradle.properties b/gradle.properties index 0f61595..7f3c1c3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4ced2d9..bcbdfe2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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' -} +} \ No newline at end of file diff --git a/src/main/java/com/example/examplemod/Config.java b/src/main/java/com/example/examplemod/Config.java deleted file mode 100644 index ca8e088..0000000 --- a/src/main/java/com/example/examplemod/Config.java +++ /dev/null @@ -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 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> 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 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()); - } -} diff --git a/src/main/java/com/example/examplemod/ExampleMod.java b/src/main/java/com/example/examplemod/ExampleMod.java deleted file mode 100644 index 990fd63..0000000 --- a/src/main/java/com/example/examplemod/ExampleMod.java +++ /dev/null @@ -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 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 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 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 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 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 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 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()); - } - } -} diff --git a/src/main/java/top/r3944realms/lib39/Lib39.java b/src/main/java/top/r3944realms/lib39/Lib39.java new file mode 100644 index 0000000..96bb90b --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/Lib39.java @@ -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"); + } +} diff --git a/src/main/java/top/r3944realms/lib39/core/registry/LocaleRegistry.java b/src/main/java/top/r3944realms/lib39/core/registry/LocaleRegistry.java new file mode 100644 index 0000000..bf53324 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/core/registry/LocaleRegistry.java @@ -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 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 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 + "]"; + } + } +} diff --git a/src/main/java/top/r3944realms/lib39/datagen/provider/SimpleLanguageProvider.java b/src/main/java/top/r3944realms/lib39/datagen/provider/SimpleLanguageProvider.java new file mode 100644 index 0000000..08892af --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/datagen/provider/SimpleLanguageProvider.java @@ -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 lanKeyMap; + private static final List 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))); + } +} diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValue.java b/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValue.java new file mode 100644 index 0000000..cec70f5 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/datagen/value/ILangKeyValue.java @@ -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 getValues(); +} diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/ILocaleEntry.java b/src/main/java/top/r3944realms/lib39/datagen/value/ILocaleEntry.java new file mode 100644 index 0000000..9757f7c --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/datagen/value/ILocaleEntry.java @@ -0,0 +1,8 @@ +package top.r3944realms.lib39.datagen.value; + +import java.util.Locale; + +public interface ILocaleEntry { + String mcCode(); + Locale javaLocale(); +} diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java b/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java new file mode 100644 index 0000000..c84dd17 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/datagen/value/LangKeyValue.java @@ -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; + }; + } +} diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/McLocale.java b/src/main/java/top/r3944realms/lib39/datagen/value/McLocale.java new file mode 100644 index 0000000..ac2169d --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/datagen/value/McLocale.java @@ -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; } +} diff --git a/src/main/java/top/r3944realms/lib39/datagen/value/ModPartEnum.java b/src/main/java/top/r3944realms/lib39/datagen/value/ModPartEnum.java new file mode 100644 index 0000000..61aeaeb --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/datagen/value/ModPartEnum.java @@ -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; + } +} diff --git a/src/main/java/top/r3944realms/lib39/utils/command/CommandAliasHelper.java b/src/main/java/top/r3944realms/lib39/utils/command/CommandAliasHelper.java new file mode 100644 index 0000000..f3e7323 --- /dev/null +++ b/src/main/java/top/r3944realms/lib39/utils/command/CommandAliasHelper.java @@ -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 dispatcher, + LiteralArgumentBuilder mainCommand, + String @NotNull ... aliases) { + // 注册主命令 + LiteralCommandNode mainNode = dispatcher.register(mainCommand); + + // 注册所有别名 + for (String alias : aliases) { + LiteralArgumentBuilder aliasCommand = Commands.literal(alias); + + // 复制主命令的所有子命令到别名命令(递归复制) + copyChildren(mainNode, aliasCommand); + + dispatcher.register(aliasCommand); + } + } + + /** + * 递归复制命令节点的所有子节点 + */ + private static void copyChildren(@NotNull CommandNode source, ArgumentBuilder target) { + for (CommandNode child : source.getChildren()) { + ArgumentBuilder childBuilder = createBuilderFromNode(child); + + if (childBuilder != null) { + // 递归复制孙子节点 + copyChildren(child, childBuilder); + + // 将子命令添加到目标命令 + target.then(childBuilder); + } + } + } + + /** + * 根据命令节点类型创建对应的构建器 + */ + private static @Nullable ArgumentBuilder createBuilderFromNode(CommandNode node) { + if (node instanceof LiteralCommandNode literalNode) { + // 处理字面量节点 + LiteralArgumentBuilder builder = Commands.literal(literalNode.getLiteral()); + copyNodeProperties(node, builder); + return builder; + + } else if (node instanceof ArgumentCommandNode argumentNode) { + // 处理参数节点 + RequiredArgumentBuilder 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 source, ArgumentBuilder 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()); + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/examplemod/lang/en_us.json b/src/main/resources/assets/examplemod/lang/en_us.json deleted file mode 100644 index 3b34651..0000000 --- a/src/main/resources/assets/examplemod/lang/en_us.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "itemGroup.examplemod": "Example Mod Tab", - "block.examplemod.example_block": "Example Block", - "item.examplemod.example_item": "Example Item" -} diff --git a/src/main/resources/lib39_logo.png b/src/main/resources/lib39_logo.png new file mode 100644 index 0000000..bf1977e Binary files /dev/null and b/src/main/resources/lib39_logo.png differ diff --git a/src/main/templates/META-INF/mods.toml b/src/main/templates/META-INF/mods.toml index 7d3d6bc..07e87be 100644 --- a/src/main/templates/META-INF/mods.toml +++ b/src/main/templates/META-INF/mods.toml @@ -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 diff --git a/src/main/templates/pack.mcmeta b/src/main/templates/pack.mcmeta index 7f45022..ae03c9b 100644 --- a/src/main/templates/pack.mcmeta +++ b/src/main/templates/pack.mcmeta @@ -1,8 +1,6 @@ { "pack": { - "description": { - "text": "${mod_name} resources" - }, + "description": "${mod_name} resources", "pack_format": 15 } }