diff --git a/build.gradle b/build.gradle index 4d41eb6..99a2ea0 100644 --- a/build.gradle +++ b/build.gradle @@ -94,7 +94,7 @@ neoForge { // these are used to tell the game which sources are for which mod // mostly optional in a single mod project // but multi mod projects should define one per mod - "${mod_id}" { + "ae2extrachannelmodes" { sourceSet(sourceSets.main) } } @@ -111,6 +111,14 @@ configurations { runtimeClasspath.extendsFrom localRuntime } +repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://modmaven.dev/") + } +} + dependencies { // Example optional mod dependency with JEI // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime @@ -119,6 +127,8 @@ dependencies { // We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it // localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}" + implementation "appeng:appliedenergistics2:${ae2_version}" + // Example mod dependency using a mod jar from ./libs with a flat dir repository // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar // The group id is ignored when searching -- in this case, it is "blank" @@ -144,6 +154,7 @@ var generateModMetadata = tasks.register("generateModMetadata", ProcessResources neo_version : neo_version, neo_version_range : neo_version_range, loader_version_range : loader_version_range, + ae2_version : ae2_version, mod_id : mod_id, mod_name : mod_name, mod_license : mod_license, @@ -152,9 +163,9 @@ var generateModMetadata = tasks.register("generateModMetadata", ProcessResources mod_description : mod_description ] inputs.properties replaceProperties - expand replaceProperties - from "src/main/templates" - into "build/generated/sources/modMetadata" + filesMatching('META-INF/neoforge.mods.toml') { + expand replaceProperties + } } // Include the output of "generateModMetadata" as an input directory for the build // this works with both building through Gradle and the IDE. diff --git a/gradle.properties b/gradle.properties index ea9a1aa..51eb545 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,25 +12,28 @@ parchment_mappings_version=2024.07.28 # Environment Properties # You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge # The Minecraft version must agree with the Neo version to get a valid artifact -minecraft_version=1.21 +minecraft_version=1.21.1 # The Minecraft version range can use any release version of Minecraft as bounds. # Snapshots, pre-releases, and release candidates are not guaranteed to sort properly # as they do not follow standard versioning conventions. minecraft_version_range=[1.21,1.21.1) # The Neo version must agree with the Minecraft version to get a valid artifact -neo_version=21.0.167 +neo_version=21.1.13 # The Neo version range can use any version of Neo as bounds neo_version_range=[21.0.0-beta,) # The loader version range can only use the major version of FML as bounds loader_version_range=[4,) +ae2_version=19.0.20-beta + + ## 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=ae2extrachannelmodes # The human-readable display name for the mod. -mod_name=Example Mod +mod_name=AE2 Extra Channel Modes # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=All Rights Reserved # The mod version. See https://semver.org/ @@ -38,8 +41,8 @@ mod_version=1.0.0 # 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=dev.aresiel.ae2extrachannelmodes # 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=Aresiel # 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=A useful Applied Energistics 2 addon that adds more channelmode options. 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 d70d1eb..0000000 --- a/src/main/java/com/example/examplemod/Config.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.example.examplemod; - -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.fml.event.config.ModConfigEvent; -import net.neoforged.neoforge.common.ModConfigSpec; - -// 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 Neo's config APIs -@EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.MOD) -public class Config -{ - private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); - - private static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER - .comment("Whether to log the dirt block on common setup") - .define("logDirtBlock", true); - - private static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER - .comment("A magic number") - .defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); - - public static final ModConfigSpec.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 ModConfigSpec.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 ModConfigSpec 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(ResourceLocation.parse(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(ResourceLocation.parse(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 835a48d..0000000 --- a/src/main/java/com/example/examplemod/ExampleMod.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.example.examplemod; - -import org.slf4j.Logger; - -import com.mojang.logging.LogUtils; - -import net.minecraft.client.Minecraft; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.chat.Component; -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.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.IEventBus; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.ModContainer; -import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.fml.common.Mod; -import net.neoforged.fml.config.ModConfig; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; -import net.neoforged.neoforge.event.server.ServerStartingEvent; -import net.neoforged.neoforge.registries.DeferredBlock; -import net.neoforged.neoforge.registries.DeferredHolder; -import net.neoforged.neoforge.registries.DeferredItem; -import net.neoforged.neoforge.registries.DeferredRegister; - -// The value here should match an entry in the META-INF/neoforge.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 BLOCKS = DeferredRegister.createBlocks(MODID); - // Create a Deferred Register to hold Items which will all be registered under the "examplemod" namespace - public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(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 DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock("example_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 DeferredItem EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("example_block", EXAMPLE_BLOCK); - - // Creates a new food item with the id "examplemod:example_id", nutrition 1 and saturation 2 - public static final DeferredItem EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", new Item.Properties().food(new FoodProperties.Builder() - .alwaysEdible().nutrition(1).saturationModifier(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 DeferredHolder EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example_tab", () -> CreativeModeTab.builder() - .title(Component.translatable("itemGroup.examplemod")) //The language key for the title of your CreativeModeTab - .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()); - - // The constructor for the mod class is the first code that is run when your mod is loaded. - // FML will recognize some parameter types like IEventBus or ModContainer and pass them in automatically. - public ExampleMod(IEventBus modEventBus, ModContainer modContainer) - { - // 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. - // Note that this is necessary if and only if we want *this* class (ExampleMod) to respond directly to events. - // Do not add this line if there are no @SubscribeEvent-annotated functions in this class, like onServerStarting() below. - NeoForge.EVENT_BUS.register(this); - - // Register the item to a creative tab - modEventBus.addListener(this::addCreative); - - // Register our mod's ModConfigSpec so that FML can create and load the config file for us - modContainer.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 >> {}", BuiltInRegistries.BLOCK.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 - @EventBusSubscriber(modid = MODID, bus = 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/dev/aresiel/ae2extrachannelmodes/AE2ExtraChannelModes.java b/src/main/java/dev/aresiel/ae2extrachannelmodes/AE2ExtraChannelModes.java new file mode 100644 index 0000000..34529f2 --- /dev/null +++ b/src/main/java/dev/aresiel/ae2extrachannelmodes/AE2ExtraChannelModes.java @@ -0,0 +1,7 @@ +package dev.aresiel.ae2extrachannelmodes; + +import net.neoforged.fml.common.Mod; + +@Mod("ae2extrachannelmodes") +public class AE2ExtraChannelModes { +} diff --git a/src/main/java/dev/aresiel/ae2extrachannelmodes/IntegerToWord.java b/src/main/java/dev/aresiel/ae2extrachannelmodes/IntegerToWord.java new file mode 100644 index 0000000..5ed49c1 --- /dev/null +++ b/src/main/java/dev/aresiel/ae2extrachannelmodes/IntegerToWord.java @@ -0,0 +1,102 @@ +package dev.aresiel.ae2extrachannelmodes; + +public class IntegerToWord { + public static String numberToWords(long n) + { + long limit = 1000000000000L, curr_hun, t = 0; + + // If zero return zero + if (n == 0) + return ("Zero"); + + // Array to store the powers of 10 + String multiplier[] = { "", "Trillion", "Billion", + "Million", "Thousand" }; + + // Array to store numbers till 20 + String first_twenty[] = { + "", "One", "Two", "Three", + "Four", "Five", "Six", "Seven", + "Eight", "Nine", "Ten", "Eleven", + "Twelve", "Thirteen", "Fourteen", "Fifteen", + "Sixteen", "Seventeen", "Eighteen", "Nineteen" + }; + + // Array to store multiples of ten + String tens[] = { "", "Twenty", "Thirty", + "Forty", "Fifty", "Sixty", + "Seventy", "Eighty", "Ninety" }; + + // If number is less than 20, return without any + // further computation + if (n < 20L) + return (first_twenty[(int)n]); + String answer = ""; + for (long i = n; i > 0; i %= limit, limit /= 1000) { + + // Store the value in multiplier[t], i.e n = + // 1000000, then r = 1, for multiplier(million), + // 0 for multipliers(trillion and billion) + // multiplier here refers to the current + // accessible limit + curr_hun = i / limit; + + // It might be possible that the current + // multiplier is bigger than your number + while (curr_hun == 0) { + + // Set i as the remainder obtained when n + // was divided by the limit + i %= limit; + + // Divide the limit by 1000, shifts the + // multiplier + limit /= 1000; + + // Get the current value in hundreds, as + // English system works in hundreds + curr_hun = i / limit; + + // Shift the multiplier + ++t; + } + + // If current hundred is greater than 99, Add + // the hundreds' place + if (curr_hun > 99) + answer += (first_twenty[(int)curr_hun / 100] + + "Hundred"); + + // Bring the current hundred to tens + curr_hun = curr_hun % 100; + + // If the value in tens belongs to [1,19], add + // using the first_twenty + if (curr_hun > 0 && curr_hun < 20) + answer + += (first_twenty[(int)curr_hun] + ""); + + // If curr_hun is now a multiple of 10, but not + // 0 Add the tens' value using the tens array + else if (curr_hun % 10 == 0 && curr_hun != 0) + answer + += (tens[(int)curr_hun / 10 - 1] + ""); + + // If the value belongs to [21,99], excluding + // the multiples of 10 Get the ten's place and + // one's place, and print using the first_twenty + // array + else if (curr_hun > 20 && curr_hun < 100) + answer + += (tens[(int)curr_hun / 10 - 1] + "" + + first_twenty[(int)curr_hun % 10] + + " "); + + // If Multiplier has not become less than 1000, + // shift it + if (t < 4) + answer += (multiplier[(int)++t] + ""); + } + return (answer); + } +} diff --git a/src/main/java/dev/aresiel/ae2extrachannelmodes/mixin/AddExtraChannelModes.java b/src/main/java/dev/aresiel/ae2extrachannelmodes/mixin/AddExtraChannelModes.java new file mode 100644 index 0000000..060cd26 --- /dev/null +++ b/src/main/java/dev/aresiel/ae2extrachannelmodes/mixin/AddExtraChannelModes.java @@ -0,0 +1,50 @@ +package dev.aresiel.ae2extrachannelmodes.mixin; + +import dev.aresiel.ae2extrachannelmodes.IntegerToWord; +import org.apache.commons.lang3.ArrayUtils; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(appeng.api.networking.pathing.ChannelMode.class) +public class AddExtraChannelModes { + @Mutable + @Shadow + @Final + private static appeng.api.networking.pathing.ChannelMode[] $VALUES; + + @Invoker(value = "") + private static appeng.api.networking.pathing.ChannelMode create(String name, int ordinal, int adHocNetworkChannels, int cableCapacityFactor){ + throw new IllegalStateException(); + } + + static { + //appeng.api.networking.pathing.ChannelMode NONE = create("NONE", $VALUES.length, 0, 0); + + int count = 10_000; + + appeng.api.networking.pathing.ChannelMode[] newValues = new appeng.api.networking.pathing.ChannelMode[count+1-5]; + appeng.api.networking.pathing.ChannelMode[] newWordValues = new appeng.api.networking.pathing.ChannelMode[count+1]; + + appeng.api.networking.pathing.ChannelMode x0 = create("x0", $VALUES.length, 0, 0); + appeng.api.networking.pathing.ChannelMode x1 = create("x1", $VALUES.length, 8, 1); + + for(int i = 5; i <= count && 16 * i < Integer.MAX_VALUE; i++){ + if(i == 2 || i == 3 || i == 4) + continue; + + newValues[i-5] = create("x" + i, $VALUES.length, 8 * i, 1 * i); + } + + for(int i = 0; i <= count && 16 * i < Integer.MAX_VALUE; i++){ + newWordValues[i] = create("times" + IntegerToWord.numberToWords(i), $VALUES.length, 8 * i, 1 * i); + } + + $VALUES = ArrayUtils.add($VALUES, x0); + $VALUES = ArrayUtils.add($VALUES, x1); + $VALUES = ArrayUtils.addAll($VALUES, newValues); + $VALUES = ArrayUtils.addAll($VALUES, newWordValues); + } +} \ No newline at end of file diff --git a/src/main/templates/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml similarity index 84% rename from src/main/templates/META-INF/neoforge.mods.toml rename to src/main/resources/META-INF/neoforge.mods.toml index 1b9f6e9..92c87d2 100644 --- a/src/main/templates/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -7,11 +7,11 @@ modLoader="javafml" #mandatory # A version range to match for said mod loader - for regular FML @Mod it will be the FML version. This is currently 2. -loaderVersion="${loader_version_range}" #mandatory +loaderVersion="[4,)" #mandatory # The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. # Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. -license="${mod_license}" +license="All Rights Reserved" # A URL to refer people to when problems occur with this mod #issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional @@ -20,13 +20,13 @@ license="${mod_license}" [[mods]] #mandatory # The modid of the mod -modId="${mod_id}" #mandatory +modId="ae2extrachannelmodes" #mandatory # The version number of the mod -version="${mod_version}" #mandatory +version="1.0.0" #mandatory # A display name for the mod -displayName="${mod_name}" #mandatory +displayName="AE2 Extra Channel Modes" #mandatory # A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ #updateJSONURL="https://change.me.example.invalid/updates.json" #optional @@ -41,14 +41,14 @@ displayName="${mod_name}" #mandatory #credits="" #optional # A text field displayed in the mod UI -authors="${mod_authors}" #optional +authors="Aresiel" #optional # The description text for the mod (multi line!) (#mandatory) -description='''${mod_description}''' +description='''A useful Applied Energistics 2 addon that adds more channelmode options.''' # The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. -#[[mixins]] -#config="${mod_id}.mixins.json" +[[mixins]] +config="ae2extrachannelmodes.mixins.json" # The [[accessTransformers]] block allows you to declare where your AT file is. # If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg @@ -58,7 +58,7 @@ description='''${mod_description}''' # The coremods config file path is not configurable and is always loaded from META-INF/coremods.json # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. -[[dependencies.${mod_id}]] #optional +[[dependencies.ae2extrachannelmodes]] #optional # the modid of the dependency modId="neoforge" #mandatory # The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). @@ -68,7 +68,7 @@ description='''${mod_description}''' # Optional field describing why the dependency is required or why it is incompatible # reason="..." # The version range of the dependency - versionRange="${neo_version_range}" #mandatory + versionRange="[21.0.0-beta,)" #mandatory # An ordering relationship for the dependency. # BEFORE - This mod is loaded BEFORE the dependency # AFTER - This mod is loaded AFTER the dependency @@ -77,16 +77,23 @@ description='''${mod_description}''' side="BOTH" # Here's another dependency -[[dependencies.${mod_id}]] +[[dependencies.ae2extrachannelmodes]] modId="minecraft" type="required" # This version range declares a minimum of the current minecraft version up to but not including the next major version - versionRange="${minecraft_version_range}" + versionRange="[1.21,1.21.1)" ordering="NONE" side="BOTH" +[[dependencies.ae2extrachannelmodes]] + modId = "ae2" + type="REQUIRED" + versionRange = "[19.0.20-beta,20.0.0)" + ordering = "AFTER" + side = "BOTH" + # Features are specific properties of the game environment, that you may want to declare you require. This example declares # that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't # stop your mod loading on the server for example. -#[features.${mod_id}] +#[features.ae2extrachannelmodes] #openGLVersion="[3.2,)" diff --git a/src/main/resources/ae2extrachannelmodes.mixins.json b/src/main/resources/ae2extrachannelmodes.mixins.json new file mode 100644 index 0000000..126c19f --- /dev/null +++ b/src/main/resources/ae2extrachannelmodes.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "dev.aresiel.ae2extrachannelmodes.mixin", + "compatibilityLevel": "JAVA_21", + "mixins": [ + "AddExtraChannelModes" + ], + "server": [], + "client": [], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file