diff options
| -rw-r--r-- | build.gradle | 179 | ||||
| -rw-r--r-- | gradle.properties | 12 | ||||
| -rw-r--r-- | src/main/java/com/coasteen/armcontainers/ArmContainers.java | 29 | ||||
| -rw-r--r-- | src/main/java/com/coasteen/armcontainers/create/GenericContainerPoint.java | 143 | ||||
| -rw-r--r-- | src/main/java/com/coasteen/armcontainers/create/GenericContainerPointType.java | 35 | ||||
| -rw-r--r-- | src/main/java/com/example/examplemod/Config.java | 42 | ||||
| -rw-r--r-- | src/main/java/com/example/examplemod/ExampleMod.java | 117 | ||||
| -rw-r--r-- | src/main/java/com/example/examplemod/ExampleModClient.java | 31 | ||||
| -rw-r--r-- | src/main/resources/META-INF/neoforge.mods.toml | 32 | ||||
| -rw-r--r-- | src/main/resources/armconainers.mixins.json | 12 |
10 files changed, 283 insertions, 349 deletions
diff --git a/build.gradle b/build.gradle index 97ff178..f3e0e2f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,190 +1,61 @@ plugins { id 'java-library' - id 'maven-publish' - id 'net.neoforged.moddev' version '2.0.143' + id 'eclipse' id 'idea' + id 'net.neoforged.moddev' version '2.0.5-beta' } -tasks.named('wrapper', Wrapper).configure { - // Define wrapper values here so as to not have to always do so when updating gradlew.properties. - // Switching this to Wrapper.DistributionType.ALL will download the full gradle sources that comes with - // documentation attached on cursor hover of gradle classes and methods. However, this comes with increased - // file size for Gradle. If you do switch this to ALL, run the Gradle wrapper task twice afterwards. - // (Verify by checking gradle/wrapper/gradle-wrapper.properties to see if distributionUrl now points to `-all`) +tasks.named('wrapper', Wrapper) { distributionType = Wrapper.DistributionType.BIN } -version = mod_version -group = mod_group_id +version = project.findProperty('mod_version') ?: '1.0.0' +group = project.findProperty('mod_group_id') ?: 'com.coasteen' +base.archivesName = project.findProperty('mod_id') ?: 'armcontainers' -sourceSets.main.resources { - // Include resources generated by data generators. - srcDir('src/generated/resources') - - // Exclude common development only resources from finalized outputs - exclude("**/*.bbmodel") // BlockBench project files - - exclude("src/generated/**/.cache") // datagen cache files -} - -repositories { - // Add here additional repositories if required by some of the dependencies below. -} - -base { - archivesName = mod_id -} - -// Mojang ships Java 21 to end users in 1.21.1, so mods should target Java 21. java.toolchain.languageVersion = JavaLanguageVersion.of(21) neoForge { - // Specify the version of NeoForge to use. - version = project.neo_version + version = project.findProperty('neo_version') ?: '21.1.248' parchment { - mappingsVersion = project.parchment_mappings_version - minecraftVersion = project.parchment_minecraft_version + mappingsVersion = project.findProperty('parchment_mappings_version') ?: '2024.07.28' + minecraftVersion = project.findProperty('parchment_minecraft_version') ?: '1.21' } - // This line is optional. Access Transformers are automatically detected - // accessTransformers = project.files('src/main/resources/META-INF/accesstransformer.cfg') - - // Default run configurations. - // These can be tweaked, removed, or duplicated as needed. runs { client { client() - // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. - systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id - } - - server { - server() - programArgument '--nogui' - systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id - } - // This run config launches GameTestServer and runs all registered gametests, then exits. - // By default, the server will crash when no gametests are provided. - // The gametest system is also enabled by default for other run configs under the /test command. - gameTestServer { - type = "gameTestServer" - systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id - } - - data { - data() - // example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it - // gameDirectory = project.file('run-data') - - // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. - programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() - } - - // applies to all the run configs above - configureEach { - // Recommended logging data for a userdev environment - // The markers can be added/remove as needed separated by commas. - // "SCAN": For mods scan. - // "REGISTRIES": For firing of registry events. - // "REGISTRYDUMP": For getting the contents of all registries. - systemProperty 'forge.logging.markers', 'REGISTRIES' - - // Recommended logging level for the console - // You can set various levels here. - // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels - logLevel = org.slf4j.event.Level.DEBUG + systemProperty 'neoforge.enabledGameTestNamespaces', (project.findProperty('mod_id') ?: 'armcontainers') } } mods { - // define mod <-> source bindings - // these are used to tell the game which sources are for which mod - // multi mod projects should define one per mod - "${mod_id}" { + "${project.findProperty('mod_id') ?: 'armcontainers'}" { sourceSet(sourceSets.main) } } } -// Sets up a dependency configuration called 'localRuntime'. -// This configuration should be used instead of 'runtimeOnly' to declare -// a dependency that will be present for runtime testing but that is -// "optional", meaning it will not be pulled by dependents of this mod. -configurations { - runtimeClasspath.extendsFrom localRuntime +repositories { + mavenCentral() + maven { + name = "CurseMaven" + url = "https://www.cursemaven.com" + content { + includeGroup "curse.maven" + } + } } 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 - // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" - // compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}" - // 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}" - - // 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" - // implementation "blank:coolmod-${mc_version}:${coolmod_version}" - - // Example mod dependency using a file as dependency - // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") - - // Example project dependency using a sister or child project: - // implementation project(":myproject") - - // For more info: - // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html - // http://www.gradle.org/docs/current/userguide/dependency_management.html -} - -// This block of code expands all declared replace properties in the specified resource targets. -// A missing property will result in an error. Properties are expanded using ${} Groovy notation. -var generateModMetadata = tasks.register("generateModMetadata", ProcessResources) { - var replaceProperties = [ - minecraft_version : minecraft_version, - minecraft_version_range: minecraft_version_range, - neo_version : neo_version, - loader_version_range : loader_version_range, - mod_id : mod_id, - mod_name : mod_name, - mod_license : mod_license, - mod_version : mod_version, - ] - inputs.properties replaceProperties - expand replaceProperties - from "src/main/templates" - into "build/generated/sources/modMetadata" -} -// Include the output of "generateModMetadata" as an input directory for the build -// this works with both building through Gradle and the IDE. -sourceSets.main.resources.srcDir generateModMetadata -// To avoid having to run "generateModMetadata" manually, make it run on every project reload -neoForge.ideSyncTask generateModMetadata - -// Example configuration to allow publishing using the maven-publish plugin -publishing { - publications { - register('mavenJava', MavenPublication) { - from components.java - } - } - repositories { - maven { - url "file://${project.projectDir}/repo" - } + if (project.hasProperty('create_curseforge_file_id')) { + implementation "curse.maven:create-328085:${project.create_curseforge_file_id}" + } else { + implementation fileTree(dir: 'libs', include: ['*.jar']) } } tasks.withType(JavaCompile).configureEach { - options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation -} - -// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior. -idea { - module { - downloadSources = true - downloadJavadoc = true - } + options.encoding = 'UTF-8' } diff --git a/gradle.properties b/gradle.properties index a4314e4..f236774 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,14 +26,16 @@ loader_version_range=[1,) # 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=armcontainer # The human-readable display name for the mod. -mod_name=Example Mod +mod_name=Create: Containers-at-Arms # 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=GPLv3 # The mod version. See https://semver.org/ -mod_version=1.0.0 +mod_version=0.0.1-alp # 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=com.coasteen.armcontainer + +create_curseforge_file_id=7963363 diff --git a/src/main/java/com/coasteen/armcontainers/ArmContainers.java b/src/main/java/com/coasteen/armcontainers/ArmContainers.java new file mode 100644 index 0000000..aceada0 --- /dev/null +++ b/src/main/java/com/coasteen/armcontainers/ArmContainers.java @@ -0,0 +1,29 @@ +package com.coasteen.armcontainers; + +import com.coasteen.armcontainers.create.GenericContainerPointType; +import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPointType; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.registries.RegisterEvent; + +@Mod(ArmContainers.MOD_ID) +public class ArmContainers { + public static final String MOD_ID = "armcontainers"; + + public ArmContainers(IEventBus modEventBus) { + modEventBus.addListener(this::onRegister); + } + + private void onRegister(RegisterEvent event) { + event.register( + ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath("create", "arm_interaction_point_type")), + helper -> helper.register( + ResourceLocation.fromNamespaceAndPath(MOD_ID, "generic_container"), + new GenericContainerPointType() + ) + ); + } +} diff --git a/src/main/java/com/coasteen/armcontainers/create/GenericContainerPoint.java b/src/main/java/com/coasteen/armcontainers/create/GenericContainerPoint.java new file mode 100644 index 0000000..6a8d649 --- /dev/null +++ b/src/main/java/com/coasteen/armcontainers/create/GenericContainerPoint.java @@ -0,0 +1,143 @@ +package com.coasteen.armcontainers.create; + +import com.simibubi.create.content.kinetics.mechanicalArm.ArmBlockEntity; +import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPoint; +import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPointType; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.entity.BarrelBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.entity.EnderChestBlockEntity; +import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.ChestType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class GenericContainerPoint extends ArmInteractionPoint { + + // Track active interactions per container position to allow overlapping arm access + private static final Map<BlockPos, AtomicInteger> CONTAINER_REF_COUNTS = new ConcurrentHashMap<>(); + + public GenericContainerPoint(ArmInteractionPointType type, Level level, BlockPos pos, BlockState state) { + super(type, level, pos, state); + } + + @Override + public ItemStack extract(ArmBlockEntity arm, int slot, int amount, boolean simulate) { + ItemStack result = super.extract(arm, slot, amount, simulate); + if (!simulate && !result.isEmpty()) { + animateContainer(); + } + return result; + } + + @Override + public ItemStack extract(ArmBlockEntity arm, int amount, boolean simulate) { + ItemStack result = super.extract(arm, amount, simulate); + if (!simulate && !result.isEmpty()) { + animateContainer(); + } + return result; + } + + @Override + public ItemStack insert(ArmBlockEntity arm, ItemStack stack, boolean simulate) { + int originalCount = stack.getCount(); + ItemStack remainder = super.insert(arm, stack, simulate); + if (!simulate && remainder.getCount() < originalCount) { + animateContainer(); + } + return remainder; + } + + private void animateContainer() { + if (level == null || level.isClientSide()) return; + + BlockEntity be = level.getBlockEntity(pos); + BlockState state = level.getBlockState(pos); + + AtomicInteger refCount = CONTAINER_REF_COUNTS.computeIfAbsent(pos, k -> new AtomicInteger(0)); + + // If the container isn't currently held open by an arm, trigger the open animation + if (refCount.getAndIncrement() == 0) { + openContainer(be, state); + } + + if (level instanceof ServerLevel serverLevel) { + // Extended delay (~1.2 seconds) to keep the container open longer + CompletableFuture.delayedExecutor(1200, TimeUnit.MILLISECONDS).execute(() -> { + serverLevel.getServer().execute(() -> { + // Only close if all pending arm actions are complete + if (refCount.decrementAndGet() <= 0) { + CONTAINER_REF_COUNTS.remove(pos); + closeContainer(be); + } + }); + }); + } + } + + private void openContainer(BlockEntity be, BlockState state) { + if (be instanceof ChestBlockEntity) { + triggerChestEvent(state, 1); + playSound(SoundEvents.CHEST_OPEN); + } else if (be instanceof EnderChestBlockEntity) { + level.blockEvent(pos, state.getBlock(), 1, 1); + playSound(SoundEvents.ENDER_CHEST_OPEN); + } else if (be instanceof ShulkerBoxBlockEntity) { + level.blockEvent(pos, state.getBlock(), 1, 1); + playSound(SoundEvents.SHULKER_BOX_OPEN); + } else if (be instanceof BarrelBlockEntity) { + if (state.hasProperty(BarrelBlock.OPEN) && !state.getValue(BarrelBlock.OPEN)) { + level.setBlock(pos, state.setValue(BarrelBlock.OPEN, true), 3); + playSound(SoundEvents.BARREL_OPEN); + } + } + } + + private void closeContainer(BlockEntity be) { + if (level == null || level.isClientSide()) return; + BlockState currentState = level.getBlockState(pos); + + if (be instanceof ChestBlockEntity) { + triggerChestEvent(currentState, 0); + playSound(SoundEvents.CHEST_CLOSE); + } else if (be instanceof EnderChestBlockEntity) { + level.blockEvent(pos, currentState.getBlock(), 1, 0); + playSound(SoundEvents.ENDER_CHEST_CLOSE); + } else if (be instanceof ShulkerBoxBlockEntity) { + level.blockEvent(pos, currentState.getBlock(), 1, 0); + playSound(SoundEvents.SHULKER_BOX_CLOSE); + } else if (be instanceof BarrelBlockEntity) { + if (currentState.hasProperty(BarrelBlock.OPEN) && currentState.getValue(BarrelBlock.OPEN)) { + level.setBlock(pos, currentState.setValue(BarrelBlock.OPEN, false), 3); + playSound(SoundEvents.BARREL_CLOSE); + } + } + } + + private void triggerChestEvent(BlockState state, int param) { + level.blockEvent(pos, state.getBlock(), 1, param); + if (state.hasProperty(ChestBlock.TYPE) && state.getValue(ChestBlock.TYPE) != ChestType.SINGLE) { + BlockPos neighborPos = pos.relative(ChestBlock.getConnectedDirection(state)); + level.blockEvent(neighborPos, state.getBlock(), 1, param); + } + } + + private void playSound(SoundEvent sound) { + level.playSound(null, pos, sound, SoundSource.BLOCKS, 0.5F, level.random.nextFloat() * 0.1F + 0.9F); + } +} diff --git a/src/main/java/com/coasteen/armcontainers/create/GenericContainerPointType.java b/src/main/java/com/coasteen/armcontainers/create/GenericContainerPointType.java new file mode 100644 index 0000000..9020041 --- /dev/null +++ b/src/main/java/com/coasteen/armcontainers/create/GenericContainerPointType.java @@ -0,0 +1,35 @@ +package com.coasteen.armcontainers.create; + +import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPoint; +import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPointType; +import com.simibubi.create.content.logistics.vault.ItemVaultBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BarrelBlockEntity; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.capabilities.Capabilities; +import org.jetbrains.annotations.Nullable; + +public class GenericContainerPointType extends ArmInteractionPointType { + + @Override + public boolean canCreatePoint(Level level, BlockPos pos, BlockState state) { + var be = level.getBlockEntity(pos); + if (be == null) return false; + + boolean isSupportedType = be instanceof ChestBlockEntity + || be instanceof ShulkerBoxBlockEntity + || be instanceof BarrelBlockEntity + || be instanceof ItemVaultBlockEntity; + + return isSupportedType && level.getCapability(Capabilities.ItemHandler.BLOCK, pos, null) != null; + } + + @Nullable + @Override + public ArmInteractionPoint createPoint(Level level, BlockPos pos, BlockState state) { + return new GenericContainerPoint(this, level, pos, state); + } +} 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 358706c..0000000 --- a/src/main/java/com/example/examplemod/Config.java +++ /dev/null @@ -1,42 +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 -public class Config { - private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); - - public static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER - .comment("Whether to log the dirt block on common setup") - .define("logDirtBlock", true); - - public static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER - .comment("A magic number") - .defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); - - public static final ModConfigSpec.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 - public static final ModConfigSpec.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 ModConfigSpec SPEC = BUILDER.build(); - - private static boolean validateItemName(final Object obj) { - return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(ResourceLocation.parse(itemName)); - } -} 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 c35a97b..0000000 --- a/src/main/java/com/example/examplemod/ExampleMod.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.example.examplemod; - -import org.slf4j.Logger; - -import com.mojang.logging.LogUtils; - -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.common.Mod; -import net.neoforged.fml.config.ModConfig; -import net.neoforged.fml.ModContainer; -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 - public 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<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 DeferredBlock<Block> 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<BlockItem> 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<Item> 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<CreativeModeTab, CreativeModeTab> 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(FMLCommonSetupEvent event) { - // Some common setup code - LOGGER.info("HELLO FROM COMMON SETUP"); - - if (Config.LOG_DIRT_BLOCK.getAsBoolean()) { - LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); - } - - LOGGER.info("{}{}", Config.MAGIC_NUMBER_INTRODUCTION.get(), Config.MAGIC_NUMBER.getAsInt()); - - Config.ITEM_STRINGS.get().forEach((item) -> LOGGER.info("ITEM >> {}", item)); - } - - // 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"); - } -} diff --git a/src/main/java/com/example/examplemod/ExampleModClient.java b/src/main/java/com/example/examplemod/ExampleModClient.java deleted file mode 100644 index bf9b4c6..0000000 --- a/src/main/java/com/example/examplemod/ExampleModClient.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.example.examplemod; - -import net.minecraft.client.Minecraft; -import net.neoforged.api.distmarker.Dist; -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.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.neoforge.client.gui.ConfigurationScreen; -import net.neoforged.neoforge.client.gui.IConfigScreenFactory; - -// This class will not load on dedicated servers. Accessing client side code from here is safe. -@Mod(value = ExampleMod.MODID, dist = Dist.CLIENT) -// You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent -@EventBusSubscriber(modid = ExampleMod.MODID, value = Dist.CLIENT) -public class ExampleModClient { - public ExampleModClient(ModContainer container) { - // Allows NeoForge to create a config screen for this mod's configs. - // The config screen is accessed by going to the Mods screen > clicking on your mod > clicking on config. - // Do not forget to add translations for your config options to the en_us.json file. - container.registerExtensionPoint(IConfigScreenFactory.class, ConfigurationScreen::new); - } - - @SubscribeEvent - static void onClientSetup(FMLClientSetupEvent event) { - // Some client setup code - ExampleMod.LOGGER.info("HELLO FROM CLIENT SETUP"); - ExampleMod.LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName()); - } -} diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..3624ed4 --- /dev/null +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,32 @@ +modLoader="javafml" +loaderVersion="[4,)" +license="MIT" +issueTrackerURL="https://github.com/coasteen/armcontainers/issues" + +[[mods]] +modId="armcontainers" +version="1.0.0" +displayName="Arm Containers" +authors="coasteen" +description="Allows Mechanical Arms to target standard chests, barrels, and shulker boxes directly." + +[[dependencies.armcontainers]] + modId="neoforge" + type="required" + versionRange="[21.1.248,)" + ordering="NONE" + side="BOTH" + +[[dependencies.armcontainers]] + modId="minecraft" + type="required" + versionRange="[1.21.1,1.22)" + ordering="NONE" + side="BOTH" + +[[dependencies.armcontainers]] + modId="create" + type="required" + versionRange="[6.0,)" + ordering="NONE" + side="BOTH" diff --git a/src/main/resources/armconainers.mixins.json b/src/main/resources/armconainers.mixins.json new file mode 100644 index 0000000..d14d9f7 --- /dev/null +++ b/src/main/resources/armconainers.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.coasteen.armcontainers.mixin", + "compatibilityLevel": "JAVA_21", + "mixins": [ + "ArmInteractionPointTypeMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} |
