├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── fabric ├── src │ └── main │ │ ├── resources │ │ ├── META-INF │ │ │ └── services │ │ │ │ └── com.mrcrayfish.goldenhopper.platform.IPlatformHelper │ │ └── fabric.mod.json │ │ └── java │ │ └── com │ │ └── mrcrayfish │ │ └── goldenhopper │ │ ├── platform │ │ └── FabricPlatformHelper.java │ │ ├── data │ │ └── DataGeneration.java │ │ └── client │ │ └── ClientHandler.java └── build.gradle ├── neoforge ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ ├── services │ │ │ └── com.mrcrayfish.goldenhopper.platform.IPlatformHelper │ │ │ └── neoforge.mods.toml │ │ └── java │ │ └── com │ │ └── mrcrayfish │ │ └── goldenhopper │ │ ├── platform │ │ └── NeoForgePlatformHelper.java │ │ ├── GoldenHopper.java │ │ └── client │ │ └── ClientHandler.java └── build.gradle ├── common ├── src │ └── main │ │ ├── resources │ │ ├── assets │ │ │ ├── goldenhopper │ │ │ │ ├── icon.png │ │ │ │ ├── banner.png │ │ │ │ ├── background.png │ │ │ │ ├── models │ │ │ │ │ ├── item │ │ │ │ │ │ ├── golden_hopper.json │ │ │ │ │ │ └── golden_hopper_minecart.json │ │ │ │ │ └── block │ │ │ │ │ │ ├── golden_hopper.json │ │ │ │ │ │ └── golden_hopper_side.json │ │ │ │ ├── textures │ │ │ │ │ ├── item │ │ │ │ │ │ ├── golden_hopper.png │ │ │ │ │ │ └── golden_hopper_minecart.png │ │ │ │ │ ├── block │ │ │ │ │ │ ├── golden_hopper_top.png │ │ │ │ │ │ ├── golden_hopper_inside.png │ │ │ │ │ │ └── golden_hopper_outside.png │ │ │ │ │ └── gui │ │ │ │ │ │ ├── container │ │ │ │ │ │ └── golden_hopper.png │ │ │ │ │ │ └── sprites │ │ │ │ │ │ └── container │ │ │ │ │ │ └── slot │ │ │ │ │ │ └── filter.png │ │ │ │ ├── lang │ │ │ │ │ ├── ja_jp.json │ │ │ │ │ ├── no_no.json │ │ │ │ │ ├── zh_hk.json │ │ │ │ │ ├── zh_tw.json │ │ │ │ │ ├── es_mx.json │ │ │ │ │ ├── en_us.json │ │ │ │ │ └── de_de.json │ │ │ │ └── blockstates │ │ │ │ │ └── golden_hopper.json │ │ │ └── minecraft │ │ │ │ └── atlases │ │ │ │ └── blocks.json │ │ ├── pack.mcmeta │ │ └── goldenhopper.common.mixins.json │ │ └── java │ │ └── com │ │ └── mrcrayfish │ │ └── goldenhopper │ │ ├── platform │ │ ├── IPlatformHelper.java │ │ └── Services.java │ │ ├── blockentity │ │ ├── BlockEntityTypeSetter.java │ │ └── GoldenHopperBlockEntity.java │ │ ├── Constants.java │ │ ├── core │ │ ├── ModContainers.java │ │ ├── ModBlockEntities.java │ │ ├── ModBlocks.java │ │ ├── ModEntities.java │ │ └── ModItems.java │ │ ├── inventory │ │ ├── slot │ │ │ └── FilterSlot.java │ │ ├── GoldenHopperScreen.java │ │ └── GoldenHopperMenu.java │ │ ├── data │ │ ├── CommonItemTagsProvider.java │ │ ├── CommonBlockTagsProvider.java │ │ ├── CommonLootTableProvider.java │ │ ├── CommonModelProvider.java │ │ └── CommonRecipeProvider.java │ │ ├── mixin │ │ ├── HopperBlockEntityMixin.java │ │ ├── DetectorRailBlockMixin.java │ │ └── BlockEntityMixin.java │ │ ├── block │ │ └── GoldenHopperBlock.java │ │ └── entity │ │ └── vehicle │ │ └── GoldenHopperMinecart.java └── build.gradle ├── .gitattributes ├── .gitignore ├── .idea └── scopes │ ├── Forge_sources.xml │ └── Fabric_sources.xml ├── gradle.properties ├── LICENSE ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.yml └── workflows │ └── build.yml ├── settings.gradle ├── README.md ├── gradlew.bat └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /fabric/src/main/resources/META-INF/services/com.mrcrayfish.goldenhopper.platform.IPlatformHelper: -------------------------------------------------------------------------------- 1 | com.mrcrayfish.goldenhopper.platform.FabricPlatformHelper -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/services/com.mrcrayfish.goldenhopper.platform.IPlatformHelper: -------------------------------------------------------------------------------- 1 | com.mrcrayfish.goldenhopper.platform.NeoForgePlatformHelper -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/icon.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/banner.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/background.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/models/item/golden_hopper.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "goldenhopper:item/golden_hopper" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/textures/item/golden_hopper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/textures/item/golden_hopper.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/textures/block/golden_hopper_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/textures/block/golden_hopper_top.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/models/item/golden_hopper_minecart.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "goldenhopper:item/golden_hopper_minecart" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/textures/block/golden_hopper_inside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/textures/block/golden_hopper_inside.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/textures/block/golden_hopper_outside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/textures/block/golden_hopper_outside.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/textures/gui/container/golden_hopper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/textures/gui/container/golden_hopper.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/textures/item/golden_hopper_minecart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/textures/item/golden_hopper_minecart.png -------------------------------------------------------------------------------- /common/src/main/resources/assets/minecraft/atlases/blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { 4 | "type": "minecraft:single", 5 | "resource": "goldenhopper:item/empty_slot_filter" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/textures/gui/sprites/container/slot/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrCrayfish/GoldenHopper/HEAD/common/src/main/resources/assets/goldenhopper/textures/gui/sprites/container/slot/filter.png -------------------------------------------------------------------------------- /common/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "${mod_name} resources", 4 | "pack_format": 12, 5 | "forge:resource_pack_format": 12, 6 | "forge:data_pack_format": 10 7 | } 8 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/lang/ja_jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.goldenhopper.golden_hopper": "金のホッパー", 3 | "item.goldenhopper.golden_hopper_minecart": "金のホッパー付きのトロッコ", 4 | "container.goldenhopper.golden_hopper": "金のホッパー", 5 | "entity.goldenhopper.golden_hopper_minecart": "金のホッパー付きのトロッコ" 6 | } 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.bat text eol=crlf 3 | *.patch text eol=lf 4 | *.java text eol=lf 5 | *.gradle text eol=crlf 6 | *.png binary 7 | *.gif binary 8 | *.exe binary 9 | *.dll binary 10 | *.jar binary 11 | *.lzma binary 12 | *.zip binary 13 | *.pyd binary 14 | *.cfg text eol=lf 15 | *.jks binary 16 | *.ogg binary -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/lang/no_no.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.goldenhopper.golden_hopper": "Gulltrakt", 3 | "item.goldenhopper.golden_hopper_minecart": "Gruvevogn med gulltrakt", 4 | "container.goldenhopper.golden_hopper": "Gulltrakt", 5 | "entity.goldenhopper.golden_hopper_minecart": "Gruvevogn med gulltrakt" 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/lang/zh_hk.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.goldenhopper.golden_hopper": "黃金漏斗", 3 | "item.goldenhopper.golden_hopper": "黃金漏斗", 4 | "item.goldenhopper.golden_hopper_minecart": "黃金漏斗礦車", 5 | "container.goldenhopper.golden_hopper": "黃金漏斗", 6 | "entity.goldenhopper.golden_hopper_minecart": "黃金漏斗礦車" 7 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/lang/zh_tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.goldenhopper.golden_hopper": "黃金漏斗", 3 | "item.goldenhopper.golden_hopper": "黃金漏斗", 4 | "item.goldenhopper.golden_hopper_minecart": "黃金漏斗礦車", 5 | "container.goldenhopper.golden_hopper": "黃金漏斗", 6 | "entity.goldenhopper.golden_hopper_minecart": "黃金漏斗礦車" 7 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/lang/es_mx.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.goldenhopper.golden_hopper": "Tolva de oro", 3 | "item.goldenhopper.golden_hopper_minecart": "Vagón con tolva de oro", 4 | "container.goldenhopper.golden_hopper": "Tolva de oro", 5 | "entity.goldenhopper.golden_hopper_minecart": "Vagón con tolva de oro" 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/platform/IPlatformHelper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.platform; 2 | 3 | import net.minecraft.tags.TagKey; 4 | import net.minecraft.world.item.Item; 5 | 6 | /** 7 | * Author: MrCrayfish 8 | */ 9 | public interface IPlatformHelper 10 | { 11 | TagKey getGoldIngotsTag(); 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.goldenhopper.golden_hopper": "Golden Hopper", 3 | "item.goldenhopper.golden_hopper_minecart": "Minecart with Golden Hopper", 4 | "container.goldenhopper.golden_hopper": "Golden Hopper", 5 | "entity.goldenhopper.golden_hopper_minecart": "Minecart with Golden Hopper" 6 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea/* 15 | !.idea/scopes 16 | 17 | # gradle 18 | build 19 | .gradle 20 | 21 | # other 22 | eclipse 23 | run 24 | .cache 25 | neoforge/runs 26 | forge/runs 27 | fabric/runs 28 | */src/generated/* 29 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/lang/de_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.goldenhopper.golden_hopper": "Goldener Trichter", 3 | "item.goldenhopper.golden_hopper_minecart": "Lore mit goldenem Trichter", 4 | "container.goldenhopper.golden_hopper": "Goldener Trichter", 5 | "entity.goldenhopper.golden_hopper_minecart": "Lore mit goldenem Trichter" 6 | } 7 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/blockentity/BlockEntityTypeSetter.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.blockentity; 2 | 3 | import net.minecraft.world.level.block.entity.BlockEntityType; 4 | 5 | /** 6 | * Author: MrCrayfish 7 | */ 8 | public interface BlockEntityTypeSetter 9 | { 10 | void goldenHopper$SetType(BlockEntityType type); 11 | } 12 | -------------------------------------------------------------------------------- /.idea/scopes/Forge_sources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/models/block/golden_hopper.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/hopper", 3 | "textures": { 4 | "particle": "goldenhopper:block/golden_hopper_outside", 5 | "top": "goldenhopper:block/golden_hopper_top", 6 | "side": "goldenhopper:block/golden_hopper_outside", 7 | "inside": "goldenhopper:block/golden_hopper_inside" 8 | } 9 | } -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/models/block/golden_hopper_side.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/hopper_side", 3 | "textures": { 4 | "particle": "goldenhopper:block/golden_hopper_outside", 5 | "top": "goldenhopper:block/golden_hopper_top", 6 | "side": "goldenhopper:block/golden_hopper_outside", 7 | "inside": "goldenhopper:block/golden_hopper_inside" 8 | } 9 | } -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/Constants.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * Author: MrCrayfish 8 | */ 9 | public class Constants 10 | { 11 | public static final String MOD_ID = "goldenhopper"; 12 | public static final String MOD_NAME = "Golden Hopper"; 13 | public static final Logger LOG = LoggerFactory.getLogger(MOD_NAME); 14 | } 15 | -------------------------------------------------------------------------------- /.idea/scopes/Fabric_sources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /common/src/main/resources/goldenhopper.common.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.mrcrayfish.goldenhopper.mixin", 5 | "compatibilityLevel": "JAVA_17", 6 | "refmap": "goldenhopper.refmap.json", 7 | "mixins": [ 8 | "BlockEntityMixin", 9 | "DetectorRailBlockMixin", 10 | "HopperBlockEntityMixin" 11 | ], 12 | "injectors": { 13 | "defaultRequire": 1 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/goldenhopper/platform/NeoForgePlatformHelper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.platform; 2 | 3 | import net.minecraft.tags.TagKey; 4 | import net.minecraft.world.item.Item; 5 | import net.neoforged.neoforge.common.Tags; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public class NeoForgePlatformHelper implements IPlatformHelper 11 | { 12 | @Override 13 | public TagKey getGoldIngotsTag() 14 | { 15 | return Tags.Items.INGOTS_GOLD; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/goldenhopper/platform/FabricPlatformHelper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.platform; 2 | 3 | import net.fabricmc.fabric.api.tag.convention.v2.ConventionalItemTags; 4 | import net.minecraft.tags.TagKey; 5 | import net.minecraft.world.item.Item; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public class FabricPlatformHelper implements IPlatformHelper 11 | { 12 | @Override 13 | public TagKey getGoldIngotsTag() 14 | { 15 | return ConventionalItemTags.GOLD_INGOTS; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/resources/assets/goldenhopper/blockstates/golden_hopper.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "facing=down": { 4 | "model": "goldenhopper:block/golden_hopper" 5 | }, 6 | "facing=north": { 7 | "model": "goldenhopper:block/golden_hopper_side" 8 | }, 9 | "facing=south": { 10 | "model": "goldenhopper:block/golden_hopper_side", 11 | "y": 180 12 | }, 13 | "facing=west": { 14 | "model": "goldenhopper:block/golden_hopper_side", 15 | "y": 270 16 | }, 17 | "facing=east": { 18 | "model": "goldenhopper:block/golden_hopper_side", 19 | "y": 90 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/platform/Services.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.platform; 2 | 3 | import com.mrcrayfish.goldenhopper.Constants; 4 | 5 | import java.util.ServiceLoader; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public class Services 11 | { 12 | public static final IPlatformHelper PLATFORM = load(IPlatformHelper.class); 13 | 14 | public static T load(Class clazz) 15 | { 16 | final T loadedService = ServiceLoader.load(clazz).findFirst().orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName())); 17 | Constants.LOG.debug("Loaded {} for service {}", loadedService, clazz); 18 | return loadedService; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/core/ModContainers.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.core; 2 | 3 | import com.mrcrayfish.framework.api.registry.RegistryContainer; 4 | import com.mrcrayfish.framework.api.registry.RegistryEntry; 5 | import com.mrcrayfish.goldenhopper.Constants; 6 | import com.mrcrayfish.goldenhopper.inventory.GoldenHopperMenu; 7 | import net.minecraft.resources.Identifier; 8 | import net.minecraft.world.inventory.MenuType; 9 | 10 | /** 11 | * Author: MrCrayfish 12 | */ 13 | @RegistryContainer 14 | public class ModContainers 15 | { 16 | public static final RegistryEntry> GOLDEN_HOPPER = RegistryEntry.menuType(Identifier.fromNamespaceAndPath(Constants.MOD_ID, "golden_hopper"), GoldenHopperMenu::new); 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/core/ModBlockEntities.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.core; 2 | 3 | import com.mrcrayfish.framework.api.registry.RegistryContainer; 4 | import com.mrcrayfish.framework.api.registry.RegistryEntry; 5 | import com.mrcrayfish.goldenhopper.Constants; 6 | import com.mrcrayfish.goldenhopper.blockentity.GoldenHopperBlockEntity; 7 | import net.minecraft.resources.Identifier; 8 | import net.minecraft.world.level.block.Block; 9 | import net.minecraft.world.level.block.entity.BlockEntityType; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | @RegistryContainer 15 | public class ModBlockEntities 16 | { 17 | public static final RegistryEntry> GOLDEN_HOPPER = RegistryEntry.blockEntity(Identifier.fromNamespaceAndPath(Constants.MOD_ID, "golden_hopper"), GoldenHopperBlockEntity::new, () -> new Block[]{ModBlocks.GOLDEN_HOPPER.get()}); 18 | } 19 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/core/ModBlocks.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.core; 2 | 3 | import com.mrcrayfish.framework.api.registry.RegistryContainer; 4 | import com.mrcrayfish.framework.api.registry.RegistryEntry; 5 | import com.mrcrayfish.goldenhopper.Constants; 6 | import com.mrcrayfish.goldenhopper.block.GoldenHopperBlock; 7 | import net.minecraft.resources.Identifier; 8 | import net.minecraft.world.level.block.Block; 9 | import net.minecraft.world.level.block.SoundType; 10 | import net.minecraft.world.level.material.MapColor; 11 | 12 | /** 13 | * Author: MrCrayfish 14 | */ 15 | @RegistryContainer 16 | public class ModBlocks 17 | { 18 | public static final RegistryEntry GOLDEN_HOPPER = RegistryEntry.block(Identifier.fromNamespaceAndPath(Constants.MOD_ID, "golden_hopper"), GoldenHopperBlock::new, () -> Block.Properties.of().sound(SoundType.METAL).mapColor(MapColor.GOLD).strength(2.0F)); 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/core/ModEntities.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.core; 2 | 3 | import com.mrcrayfish.framework.api.registry.RegistryContainer; 4 | import com.mrcrayfish.framework.api.registry.RegistryEntry; 5 | import com.mrcrayfish.goldenhopper.Constants; 6 | import com.mrcrayfish.goldenhopper.entity.vehicle.GoldenHopperMinecart; 7 | import net.minecraft.resources.Identifier; 8 | import net.minecraft.world.entity.EntityType; 9 | import net.minecraft.world.entity.MobCategory; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | @RegistryContainer 15 | public class ModEntities 16 | { 17 | public static final RegistryEntry> GOLDEN_HOPPER_MINECART = RegistryEntry.entityType(Identifier.fromNamespaceAndPath(Constants.MOD_ID, "golden_hopper_minecart"), () -> EntityType.Builder.of(GoldenHopperMinecart::new, MobCategory.MISC).sized(0.98F, 0.7F)); 18 | } 19 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/goldenhopper/data/DataGeneration.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.data; 2 | 3 | import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; 4 | import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; 5 | import net.minecraft.client.data.models.ModelProvider; 6 | 7 | /** 8 | * Author: MrCrayfish 9 | */ 10 | public class DataGeneration implements DataGeneratorEntrypoint 11 | { 12 | @Override 13 | public void onInitializeDataGenerator(FabricDataGenerator generator) 14 | { 15 | FabricDataGenerator.Pack pack = generator.createPack(); 16 | pack.addProvider((FabricDataGenerator.Pack.Factory) CommonModelProvider::new); 17 | pack.addProvider(CommonLootTableProvider::new); 18 | pack.addProvider(CommonRecipeProvider.Runner::new); 19 | pack.addProvider(CommonBlockTagsProvider::new); 20 | pack.addProvider(CommonItemTagsProvider::new); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/inventory/slot/FilterSlot.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.inventory.slot; 2 | 3 | import com.mrcrayfish.goldenhopper.Constants; 4 | import net.minecraft.resources.Identifier; 5 | import net.minecraft.world.Container; 6 | import net.minecraft.world.inventory.Slot; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** 10 | * Author: MrCrayfish 11 | */ 12 | public class FilterSlot extends Slot 13 | { 14 | private static final Identifier EMPTY_SLOT_FILTER = Identifier.fromNamespaceAndPath(Constants.MOD_ID, "container/slot/filter"); 15 | 16 | public FilterSlot(Container container, int index, int x, int y) 17 | { 18 | super(container, index, x, y); 19 | } 20 | 21 | @Override 22 | public int getMaxStackSize() 23 | { 24 | return 1; 25 | } 26 | 27 | @Nullable 28 | @Override 29 | public Identifier getNoItemIcon() 30 | { 31 | return EMPTY_SLOT_FILTER; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project 2 | group=com.mrcrayfish 3 | 4 | # Java 5 | java_version=21 6 | 7 | # Common 8 | minecraft_version=1.21.11 9 | neo_form_version=1.21.11-20251209.172050 10 | 11 | # Fabric 12 | fabric_version=0.139.5+1.21.11 13 | fabric_loader_version=0.18.2 14 | 15 | # NeoForge 16 | neoforge_version=21.11.6-beta 17 | neoforge_version_range=[21.11.,) 18 | neoforge_loader_version_range=[2,) 19 | parchment_minecraft=1.21.10 20 | parchment_version=2025.10.12 21 | 22 | # Mod options 23 | mod_version=1.7.0 24 | mod_name=Golden Hopper 25 | mod_author=MrCrayfish 26 | mod_id=goldenhopper 27 | mod_desc=Simply adds a Golden Hopper that can filter items, nothing more! 28 | mod_homepage=https://mrcrayfish.com/mods/goldenhopper 29 | mod_source=https://github.com/MrCrayfish/GoldenHopper 30 | mod_issues=https://github.com/MrCrayfish/GoldenHopper/issues 31 | mod_license=MIT 32 | 33 | # Dependency options 34 | framework_version=0.13.6 35 | catalogue_version=1.12.3 36 | modmenu_version=17.0.0-alpha.1 37 | 38 | # Gradle 39 | org.gradle.jvmargs=-Xmx3G 40 | org.gradle.daemon=false -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/data/CommonItemTagsProvider.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.data; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModBlocks; 4 | import com.mrcrayfish.goldenhopper.platform.Services; 5 | import net.minecraft.core.HolderLookup; 6 | import net.minecraft.data.PackOutput; 7 | import net.minecraft.data.tags.VanillaItemTagsProvider; 8 | import net.minecraft.tags.ItemTags; 9 | import net.minecraft.world.item.Items; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | /** 14 | * Author: MrCrayfish 15 | */ 16 | public class CommonItemTagsProvider extends VanillaItemTagsProvider 17 | { 18 | public CommonItemTagsProvider(PackOutput output, CompletableFuture providerCompletableFuture) 19 | { 20 | super(output, providerCompletableFuture); 21 | } 22 | 23 | @Override 24 | protected void addTags(HolderLookup.Provider provider) 25 | { 26 | this.tag(Services.PLATFORM.getGoldIngotsTag()).add(Items.GOLD_INGOT); 27 | this.tag(ItemTags.PIGLIN_LOVED).add(ModBlocks.GOLDEN_HOPPER.get().asItem()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 MrCrayfish 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/core/ModItems.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.core; 2 | 3 | import com.mrcrayfish.framework.api.registry.RegistryContainer; 4 | import com.mrcrayfish.framework.api.registry.RegistryEntry; 5 | import com.mrcrayfish.goldenhopper.Constants; 6 | import net.minecraft.resources.Identifier; 7 | import net.minecraft.world.item.BlockItem; 8 | import net.minecraft.world.item.Item; 9 | import net.minecraft.world.item.MinecartItem; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | @RegistryContainer 15 | public class ModItems 16 | { 17 | public static final RegistryEntry GOLDEN_HOPPER = RegistryEntry.item(Identifier.fromNamespaceAndPath(Constants.MOD_ID, "golden_hopper"), properties -> new BlockItem(ModBlocks.GOLDEN_HOPPER.get(), properties), () -> new Item.Properties().useBlockDescriptionPrefix()); 18 | public static final RegistryEntry GOLDEN_HOPPER_MINECART = RegistryEntry.item(Identifier.fromNamespaceAndPath(Constants.MOD_ID, "golden_hopper_minecart"), properties -> new MinecartItem(ModEntities.GOLDEN_HOPPER_MINECART.get(), properties), () -> new Item.Properties().stacksTo(1)); 19 | } 20 | -------------------------------------------------------------------------------- /neoforge/src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "${neoforge_loader_version_range}" 3 | license = "${mod_license}" 4 | 5 | [[mods]] 6 | modId = "${mod_id}" 7 | version = "${mod_version}" 8 | displayName = "${mod_name}" 9 | updateJSONURL = "https://mrcrayfish.com/modupdatejson?id=${mod_id}" 10 | displayURL = "${mod_homepage}" 11 | logoFile = "assets/${mod_id}/banner.png" 12 | logoBlur = false 13 | authors = "${mod_author}" 14 | description = '''${mod_desc}''' 15 | 16 | [[mixins]] 17 | config = "${mod_id}.common.mixins.json" 18 | 19 | [[dependencies.${mod_id}]] 20 | modId = "neoforge" 21 | type = "required" 22 | versionRange = "${neoforge_version_range}" 23 | ordering = "NONE" 24 | side = "BOTH" 25 | 26 | [[dependencies.${mod_id}]] 27 | modId = "minecraft" 28 | type = "required" 29 | versionRange = "[${minecraft_version},)" 30 | ordering = "NONE" 31 | side = "BOTH" 32 | 33 | [[dependencies.${mod_id}]] 34 | modId = "framework" 35 | type = "required" 36 | versionRange = "[${framework_version},)" 37 | ordering = "NONE" 38 | side = "BOTH" 39 | 40 | [modproperties."${mod_id}"] 41 | catalogueImageIcon="assets/${mod_id}/icon.png" 42 | catalogueBackground="assets/${mod_id}/background.png" 43 | configuredBackground="minecraft:textures/block/stone.png" 44 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/data/CommonBlockTagsProvider.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.data; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModBlocks; 4 | import net.minecraft.core.HolderLookup; 5 | import net.minecraft.core.Registry; 6 | import net.minecraft.core.registries.Registries; 7 | import net.minecraft.data.PackOutput; 8 | import net.minecraft.data.tags.IntrinsicHolderTagsProvider; 9 | import net.minecraft.data.tags.TagsProvider; 10 | import net.minecraft.resources.ResourceKey; 11 | import net.minecraft.tags.BlockTags; 12 | import net.minecraft.tags.TagKey; 13 | import net.minecraft.world.level.block.Block; 14 | 15 | import java.util.concurrent.CompletableFuture; 16 | import java.util.function.Function; 17 | 18 | /** 19 | * Author: MrCrayfish 20 | */ 21 | public class CommonBlockTagsProvider extends IntrinsicHolderTagsProvider 22 | { 23 | public CommonBlockTagsProvider(PackOutput output, CompletableFuture completableFuture) 24 | { 25 | super(output, Registries.BLOCK, completableFuture, block -> block.builtInRegistryHolder().key()); 26 | } 27 | 28 | @Override 29 | protected void addTags(HolderLookup.Provider provider) 30 | { 31 | this.tag(BlockTags.MINEABLE_WITH_PICKAXE).add(ModBlocks.GOLDEN_HOPPER.get()); 32 | this.tag(BlockTags.NEEDS_IRON_TOOL).add(ModBlocks.GOLDEN_HOPPER.get()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/goldenhopper/GoldenHopper.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModItems; 4 | import com.mrcrayfish.goldenhopper.data.CommonBlockTagsProvider; 5 | import com.mrcrayfish.goldenhopper.data.CommonItemTagsProvider; 6 | import com.mrcrayfish.goldenhopper.data.CommonLootTableProvider; 7 | import com.mrcrayfish.goldenhopper.data.CommonRecipeProvider; 8 | import net.minecraft.core.HolderLookup; 9 | import net.minecraft.data.DataGenerator; 10 | import net.minecraft.data.PackOutput; 11 | import net.minecraft.world.item.CreativeModeTabs; 12 | import net.neoforged.bus.api.IEventBus; 13 | import net.neoforged.fml.common.Mod; 14 | import net.neoforged.neoforge.data.event.GatherDataEvent; 15 | import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; 16 | 17 | import java.util.concurrent.CompletableFuture; 18 | 19 | /** 20 | * Author: MrCrayfish 21 | */ 22 | @Mod(Constants.MOD_ID) 23 | public class GoldenHopper 24 | { 25 | public GoldenHopper(IEventBus bus) 26 | { 27 | bus.addListener(this::onCreativeTabBuilding); 28 | } 29 | 30 | private void onCreativeTabBuilding(BuildCreativeModeTabContentsEvent event) 31 | { 32 | if(event.getTabKey().equals(CreativeModeTabs.REDSTONE_BLOCKS)) 33 | { 34 | event.accept(ModItems.GOLDEN_HOPPER::get); 35 | event.accept(ModItems.GOLDEN_HOPPER_MINECART::get); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "${mod_id}", 4 | "version": "${mod_version}", 5 | "name": "${mod_name}", 6 | "description": "${mod_desc}", 7 | "authors": [ "${mod_author}" ], 8 | "contact": { 9 | "homepage": "${mod_homepage}", 10 | "sources": "${mod_source}", 11 | "issues": "${mod_issues}" 12 | }, 13 | "license": "${mod_license}", 14 | "icon": "assets/${mod_id}/icon.png", 15 | "environment": "*", 16 | "entrypoints": { 17 | "client": [ 18 | "com.mrcrayfish.goldenhopper.client.ClientHandler" 19 | ], 20 | "fabric-datagen": [ 21 | "com.mrcrayfish.goldenhopper.data.DataGeneration" 22 | ] 23 | }, 24 | "mixins": [ 25 | "${mod_id}.common.mixins.json" 26 | ], 27 | "depends": { 28 | "fabricloader": ">=${fabric_loader_version}", 29 | "fabric-api": ">=${fabric_version}", 30 | "minecraft": "${minecraft_version}", 31 | "java": ">=17" 32 | }, 33 | "custom": { 34 | "configured": { 35 | "configs": [ ], 36 | "background": "minecraft:textures/block/dirt.png" 37 | }, 38 | "catalogue": { 39 | "banner": "assets/${mod_id}/banner.png", 40 | "background": "assets/${mod_id}/background.png" 41 | }, 42 | "framework": { 43 | "containers": [ 44 | "com.mrcrayfish.goldenhopper.core" 45 | ] 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /fabric/src/main/java/com/mrcrayfish/goldenhopper/client/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.client; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModContainers; 4 | import com.mrcrayfish.goldenhopper.core.ModEntities; 5 | import com.mrcrayfish.goldenhopper.core.ModItems; 6 | import com.mrcrayfish.goldenhopper.inventory.GoldenHopperScreen; 7 | import net.fabricmc.api.ClientModInitializer; 8 | import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; 9 | import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; 10 | import net.minecraft.client.gui.screens.MenuScreens; 11 | import net.minecraft.client.model.geom.ModelLayers; 12 | import net.minecraft.client.renderer.entity.MinecartRenderer; 13 | import net.minecraft.core.registries.BuiltInRegistries; 14 | import net.minecraft.world.item.CreativeModeTabs; 15 | 16 | /** 17 | * Author: MrCrayfish 18 | */ 19 | public class ClientHandler implements ClientModInitializer 20 | { 21 | @Override 22 | public void onInitializeClient() 23 | { 24 | MenuScreens.register(ModContainers.GOLDEN_HOPPER.get(), GoldenHopperScreen::new); 25 | EntityRendererRegistry.register(ModEntities.GOLDEN_HOPPER_MINECART.get(), context -> 26 | new MinecartRenderer(context, ModelLayers.HOPPER_MINECART)); 27 | ItemGroupEvents.MODIFY_ENTRIES_ALL.register((group, entries) -> { 28 | if(group == BuiltInRegistries.CREATIVE_MODE_TAB.getValue(CreativeModeTabs.REDSTONE_BLOCKS)) { 29 | entries.accept(ModItems.GOLDEN_HOPPER::get); 30 | entries.accept(ModItems.GOLDEN_HOPPER_MINECART::get); 31 | } 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/inventory/GoldenHopperScreen.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.inventory; 2 | 3 | import com.mrcrayfish.goldenhopper.Constants; 4 | import net.minecraft.client.gui.GuiGraphics; 5 | import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; 6 | import net.minecraft.client.renderer.RenderPipelines; 7 | import net.minecraft.network.chat.Component; 8 | import net.minecraft.resources.Identifier; 9 | import net.minecraft.world.entity.player.Inventory; 10 | 11 | /** 12 | * Author: MrCrayfish 13 | */ 14 | public class GoldenHopperScreen extends AbstractContainerScreen 15 | { 16 | private static final Identifier GUI_TEXTURE = Identifier.fromNamespaceAndPath(Constants.MOD_ID, "textures/gui/container/golden_hopper.png"); 17 | 18 | public GoldenHopperScreen(GoldenHopperMenu container, Inventory playerInventory, Component titleIn) 19 | { 20 | super(container, playerInventory, titleIn); 21 | this.imageHeight = 133; 22 | this.inventoryLabelY = 40; 23 | } 24 | 25 | @Override 26 | public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) 27 | { 28 | super.render(graphics, mouseX, mouseY, partialTicks); 29 | this.renderTooltip(graphics, mouseX, mouseY); 30 | } 31 | 32 | @Override 33 | protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) 34 | { 35 | int startX = (this.width - this.imageWidth) / 2; 36 | int startY = (this.height - this.imageHeight) / 2; 37 | graphics.blit(RenderPipelines.GUI_TEXTURED, GUI_TEXTURE, startX, startY, 0, 0, this.imageWidth, this.imageHeight, 256, 256); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/mixin/HopperBlockEntityMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.mixin; 2 | 3 | import com.mrcrayfish.goldenhopper.blockentity.GoldenHopperBlockEntity; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.level.Level; 6 | import net.minecraft.world.level.block.entity.HopperBlockEntity; 7 | import net.minecraft.world.level.block.state.BlockState; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 | 13 | import java.util.function.BooleanSupplier; 14 | 15 | /** 16 | * Author: MrCrayfish 17 | */ 18 | @Mixin(HopperBlockEntity.class) 19 | public class HopperBlockEntityMixin 20 | { 21 | @Inject(method = "tryMoveItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;isEmpty()Z")) 22 | private static void goldenHopper$beforeEject(Level level, BlockPos pos, BlockState state, HopperBlockEntity hopper, BooleanSupplier $$4, CallbackInfoReturnable cir) 23 | { 24 | if(hopper instanceof GoldenHopperBlockEntity) 25 | { 26 | GoldenHopperBlockEntity.ejecting = true; 27 | } 28 | } 29 | 30 | @Inject(method = "tryMoveItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;inventoryFull()Z")) 31 | private static void goldenHopper$afterEject(Level level, BlockPos pos, BlockState state, HopperBlockEntity hopper, BooleanSupplier $$4, CallbackInfoReturnable cir) 32 | { 33 | GoldenHopperBlockEntity.ejecting = false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/data/CommonLootTableProvider.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.data; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModBlocks; 4 | import net.minecraft.core.HolderLookup; 5 | import net.minecraft.data.PackOutput; 6 | import net.minecraft.data.loot.BlockLootSubProvider; 7 | import net.minecraft.data.loot.LootTableProvider; 8 | import net.minecraft.resources.ResourceKey; 9 | import net.minecraft.world.flag.FeatureFlagSet; 10 | import net.minecraft.world.level.storage.loot.LootTable; 11 | import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; 12 | 13 | import java.util.Collections; 14 | import java.util.List; 15 | import java.util.Set; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.function.BiConsumer; 18 | 19 | /** 20 | * Author: MrCrayfish 21 | */ 22 | public class CommonLootTableProvider extends LootTableProvider 23 | { 24 | public CommonLootTableProvider(PackOutput output, CompletableFuture completableFuture) 25 | { 26 | super(output, Set.of(), List.of(new SubProviderEntry(Block::new, LootContextParamSets.BLOCK)), completableFuture); 27 | } 28 | 29 | public static class Block extends BlockLootSubProvider 30 | { 31 | protected Block(HolderLookup.Provider provider) 32 | { 33 | super(Collections.emptySet(), FeatureFlagSet.of(), provider); 34 | } 35 | 36 | @Override 37 | public void generate(BiConsumer, LootTable.Builder> consumer) 38 | { 39 | this.generate(); 40 | this.map.forEach(consumer); 41 | } 42 | 43 | @Override 44 | public void generate() 45 | { 46 | this.dropSelf(ModBlocks.GOLDEN_HOPPER.get()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Use this template to report bugs. 3 | labels: bug 4 | 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: 'Describe the bug' 9 | description: A clear and concise description of what the bug is. 10 | 11 | - type: textarea 12 | attributes: 13 | label: 'To Reproduce' 14 | description: 'Steps to reproduce the behavior.' 15 | placeholder: | 16 | 1. Go to '...' 17 | 2. Click on '...' 18 | 3. See error 19 | 20 | - type: textarea 21 | attributes: 22 | label: 'Expected behavior' 23 | description: 'A clear and concise description of what you expected to happen.' 24 | placeholder: 'Describe what you expected to happen.' 25 | 26 | - type: input 27 | attributes: 28 | label: 'Mod Version' 29 | description: 'The version of the mod you are using.' 30 | placeholder: 'e.g., 1.2.3' 31 | 32 | - type: input 33 | attributes: 34 | label: 'Minecraft Version' 35 | description: 'The Minecraft version you are using.' 36 | placeholder: 'e.g., 1.20.1' 37 | 38 | - type: input 39 | attributes: 40 | label: 'Modloader' 41 | description: 'The modloader you are using.' 42 | placeholder: 'e.g., Forge/Fabric/NeoForge' 43 | 44 | - type: input 45 | attributes: 46 | label: 'Crash Log (if applicable)' 47 | description: 'If the game crashed, please provide the crash report. Please upload the log to [mclo.gs](https://mclo.gs/) or [Gist](https://gist.github.com/) and provide the link here' 48 | 49 | - type: textarea 50 | attributes: 51 | label: 'Additional context' 52 | description: 'Add any other context about the problem here.' 53 | placeholder: 'Provide any additional details or observations that might help.' 54 | -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'multiloader-common' 3 | id 'net.neoforged.moddev' 4 | } 5 | 6 | neoForge { 7 | enable { 8 | neoFormVersion = neo_form_version 9 | disableRecompilation = System.getenv("CI") == "true" 10 | } 11 | 12 | def at = file('src/main/resources/META-INF/accesstransformer.cfg') 13 | if (at.exists()) { 14 | accessTransformers.add(at.absolutePath) 15 | } 16 | } 17 | 18 | dependencies { 19 | compileOnly "com.mrcrayfish:framework-common:${minecraft_version}-${framework_version}" 20 | compileOnly "org.spongepowered:mixin:0.8.7" 21 | compileOnly "io.github.llamalad7:mixinextras-common:0.5.0" 22 | annotationProcessor "io.github.llamalad7:mixinextras-common:0.5.0" 23 | } 24 | 25 | configurations { 26 | commonJava { 27 | canBeResolved = false 28 | canBeConsumed = true 29 | } 30 | commonResources { 31 | canBeResolved = false 32 | canBeConsumed = true 33 | } 34 | } 35 | 36 | artifacts { 37 | commonJava sourceSets.main.java.sourceDirectories.singleFile 38 | commonResources sourceSets.main.resources.sourceDirectories.singleFile 39 | } 40 | 41 | // Implement mcgradleconventions loader attribute 42 | def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) 43 | ['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> 44 | configurations.named("$variant") { 45 | attributes { 46 | attribute(loaderAttribute, 'common') 47 | } 48 | } 49 | } 50 | sourceSets.configureEach { 51 | [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName].each { variant-> 52 | configurations.named("$variant") { 53 | attributes { 54 | attribute(loaderAttribute, 'common') 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | exclusiveContent { 6 | forRepository { 7 | maven { 8 | name = 'Fabric' 9 | url = "https://maven.fabricmc.net" 10 | } 11 | } 12 | filter { 13 | includeGroupAndSubgroups("net.fabricmc") 14 | includeGroup("fabric-loom") 15 | } 16 | } 17 | exclusiveContent { 18 | forRepository { 19 | maven { 20 | name = 'NeoForge' 21 | url = "https://maven.neoforged.net/releases" 22 | } 23 | } 24 | filter { 25 | includeGroupAndSubgroups("net.neoforged") 26 | includeGroup("codechicken") 27 | } 28 | } 29 | exclusiveContent { 30 | forRepository { 31 | maven { 32 | name = 'Sponge Snapshots' 33 | url = "https://repo.spongepowered.org/repository/maven-public" 34 | } 35 | } 36 | filter { 37 | includeGroupAndSubgroups("org.spongepowered") 38 | includeGroupAndSubgroups("net.minecraftforge") 39 | } 40 | } 41 | } 42 | } 43 | 44 | plugins { 45 | id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' 46 | } 47 | 48 | def includeModloader(String name) { 49 | // If building with Github Actions, only include loader we want! 50 | String targetLoader = Boolean.getBoolean("CI") ?: System.getenv("TARGET_LOADER") 51 | if(targetLoader == null || targetLoader == name) { 52 | include(name) 53 | } 54 | } 55 | 56 | rootProject.name = 'GoldenHopper' 57 | include("common") 58 | includeModloader("fabric") 59 | includeModloader("neoforge") -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/mixin/DetectorRailBlockMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.mixin; 2 | 3 | import com.mrcrayfish.goldenhopper.entity.vehicle.GoldenHopperMinecart; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.core.Direction; 6 | import net.minecraft.world.entity.Entity; 7 | import net.minecraft.world.entity.EntitySelector; 8 | import net.minecraft.world.entity.vehicle.minecart.AbstractMinecart; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.level.block.DetectorRailBlock; 11 | import net.minecraft.world.level.block.state.BlockState; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 17 | 18 | import java.util.List; 19 | import java.util.function.Predicate; 20 | 21 | /** 22 | * Author: MrCrayfish 23 | */ 24 | @Mixin(DetectorRailBlock.class) 25 | public abstract class DetectorRailBlockMixin 26 | { 27 | @Shadow 28 | protected abstract List getInteractingMinecartOfType(Level level, BlockPos pos, Class minecartClass, Predicate predicate); 29 | 30 | @Inject(method = "getAnalogOutputSignal", at = @At(value = "HEAD"), cancellable = true) 31 | private void goldenhopperGetAnalogOutputSignal(BlockState state, Level level, BlockPos pos, Direction direction, CallbackInfoReturnable cir) 32 | { 33 | if(state.getValue(DetectorRailBlock.POWERED)) 34 | { 35 | List carts = this.getInteractingMinecartOfType(level, pos, AbstractMinecart.class, EntitySelector.CONTAINER_ENTITY_SELECTOR); 36 | if(!carts.isEmpty() && carts.get(0) instanceof GoldenHopperMinecart hopper) 37 | { 38 | cir.setReturnValue(hopper.getComparatorLevel()); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /neoforge/src/main/java/com/mrcrayfish/goldenhopper/client/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.client; 2 | 3 | import com.mrcrayfish.goldenhopper.Constants; 4 | import com.mrcrayfish.goldenhopper.core.ModContainers; 5 | import com.mrcrayfish.goldenhopper.core.ModEntities; 6 | import com.mrcrayfish.goldenhopper.data.*; 7 | import com.mrcrayfish.goldenhopper.inventory.GoldenHopperScreen; 8 | import net.minecraft.client.model.geom.ModelLayers; 9 | import net.minecraft.client.renderer.entity.EntityRenderers; 10 | import net.minecraft.client.renderer.entity.MinecartRenderer; 11 | import net.neoforged.api.distmarker.Dist; 12 | import net.neoforged.bus.api.SubscribeEvent; 13 | import net.neoforged.fml.common.EventBusSubscriber; 14 | import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; 15 | import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; 16 | import net.neoforged.neoforge.data.event.GatherDataEvent; 17 | 18 | /** 19 | * Author: MrCrayfish 20 | */ 21 | @EventBusSubscriber(modid = Constants.MOD_ID, value = Dist.CLIENT) 22 | public final class ClientHandler 23 | { 24 | @SubscribeEvent 25 | private static void onClientSetup(FMLClientSetupEvent event) 26 | { 27 | event.enqueueWork(() -> { 28 | EntityRenderers.register(ModEntities.GOLDEN_HOPPER_MINECART.get(), context -> 29 | new MinecartRenderer(context, ModelLayers.HOPPER_MINECART)); 30 | }); 31 | } 32 | 33 | @SubscribeEvent 34 | private static void onRegisterScreens(RegisterMenuScreensEvent event) 35 | { 36 | event.register(ModContainers.GOLDEN_HOPPER.get(), GoldenHopperScreen::new); 37 | } 38 | 39 | @SubscribeEvent 40 | private static void onGatherClientData(GatherDataEvent.Client event) 41 | { 42 | event.createProvider(CommonModelProvider::new); 43 | event.createProvider(CommonRecipeProvider.Runner::new); 44 | event.createProvider(CommonLootTableProvider::new); 45 | event.createProvider(CommonBlockTagsProvider::new); 46 | event.createProvider(CommonItemTagsProvider::new); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/mixin/BlockEntityMixin.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.mixin; 2 | 3 | import com.mrcrayfish.goldenhopper.blockentity.BlockEntityTypeSetter; 4 | import com.mrcrayfish.goldenhopper.blockentity.GoldenHopperBlockEntity; 5 | import com.mrcrayfish.goldenhopper.core.ModBlockEntities; 6 | import com.mrcrayfish.goldenhopper.core.ModBlocks; 7 | import net.minecraft.world.level.block.entity.BlockEntity; 8 | import net.minecraft.world.level.block.entity.BlockEntityType; 9 | import net.minecraft.world.level.block.state.BlockState; 10 | import org.spongepowered.asm.mixin.Final; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Mutable; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | /** 19 | * Author: MrCrayfish 20 | */ 21 | @Mixin(BlockEntity.class) 22 | public abstract class BlockEntityMixin implements BlockEntityTypeSetter 23 | { 24 | @Shadow 25 | @Final 26 | @Mutable 27 | private BlockEntityType type; 28 | 29 | @Override 30 | public void goldenHopper$SetType(BlockEntityType type) 31 | { 32 | this.type = type; 33 | } 34 | 35 | /* 36 | * Patches the validation for the block state. This validation happens before block entity 37 | * type can be injected. To fix this issue, we just patch the method and do validation ourselves 38 | * to avoid throwing an exception. 39 | */ 40 | @SuppressWarnings("DataFlowIssue") 41 | @Inject(method = "validateBlockState", at = @At(value = "HEAD"), cancellable = true) 42 | private void goldenHopper$validateFix(BlockState state, CallbackInfo ci) 43 | { 44 | BlockEntity entity = (BlockEntity) (Object) this; 45 | // getType() is overridden in GoldenHopperBlockEntity, so we can perform the correct check 46 | if(entity instanceof GoldenHopperBlockEntity && state.is(ModBlocks.GOLDEN_HOPPER.get()) && entity.getType().isValid(state)) 47 | { 48 | ci.cancel(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/data/CommonModelProvider.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.data; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModItems; 4 | import net.minecraft.client.data.models.ModelProvider; 5 | import net.minecraft.client.data.models.model.ItemModelUtils; 6 | import net.minecraft.client.data.models.model.ModelLocationUtils; 7 | import net.minecraft.client.renderer.item.ClientItem; 8 | import net.minecraft.client.renderer.item.ItemModel; 9 | import net.minecraft.data.CachedOutput; 10 | import net.minecraft.data.DataProvider; 11 | import net.minecraft.data.PackOutput; 12 | import net.minecraft.world.item.Item; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.concurrent.CompletableFuture; 17 | 18 | /** 19 | * Author: MrCrayfish 20 | */ 21 | public class CommonModelProvider implements DataProvider 22 | { 23 | private final PackOutput.PathProvider itemsPathProvider; 24 | private final Map clientItems = new HashMap<>(); 25 | 26 | public CommonModelProvider(PackOutput output) 27 | { 28 | this.itemsPathProvider = output.createPathProvider(PackOutput.Target.RESOURCE_PACK, "items"); 29 | } 30 | 31 | private void generate() 32 | { 33 | this.existingModel(ModItems.GOLDEN_HOPPER.get()); 34 | this.existingModel(ModItems.GOLDEN_HOPPER_MINECART.get()); 35 | } 36 | 37 | private void existingModel(Item item) 38 | { 39 | this.clientItems.put(item, this.defaultClientItem(ItemModelUtils.plainModel(ModelLocationUtils.getModelLocation(item)))); 40 | } 41 | 42 | private ClientItem defaultClientItem(ItemModel.Unbaked unbaked) 43 | { 44 | return new ClientItem(unbaked, ClientItem.Properties.DEFAULT); 45 | } 46 | 47 | @Override 48 | @SuppressWarnings("deprecation") 49 | public CompletableFuture run(CachedOutput output) 50 | { 51 | this.generate(); 52 | return DataProvider.saveAll(output, ClientItem.CODEC, item -> { 53 | return this.itemsPathProvider.json(item.builtInRegistryHolder().key().identifier()); 54 | }, this.clientItems); 55 | } 56 | 57 | @Override 58 | public String getName() 59 | { 60 | return "Model Definitions"; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Golden Hopper Banner](https://i.imgur.com/3NbvLuh.png) 2 | 3 | [![Forge](https://mrcrayfish.com/img/forge.svg)](https://files.minecraftforge.net) 4 | [![Download](https://img.shields.io/static/v1?label=&message=Website&color=2d2d2d&labelColor=cc923d&style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjCGJ1kDAAAAzUlEQVRYR8WOAQ7CMAwD9/9PD2XqpsZxWdrUcNIhaoSd45zH/rNN+5gllFS8v8ziSireX2ZxJRX7h4p+I4iBAtxwYqACdx5ZqIJt0QNMBWxneICpIOyEoKkibIWgqcRtuUezyluf+909mhVY31dZuArrehWDVbAnLQarYE9aDFbBnrQYrII9aTGogF0pWViB9Y00th9ww3pNhB5g/oq/HnBt4XCvmmsHR3uVPDv9IFOB23APogK34R4DdxL6QzBwF6E7BAN3wHrTB4g8jw82Wi+WaJjRLwAAAABJRU5ErkJggg==)](https://mrcrayfish.com/mods?id=goldenhopper) 5 | [![YouTube](https://img.shields.io/static/v1?label=&message=YouTube&color=2d2d2d&labelColor=cc923d&style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjCGJ1kDAAAA4UlEQVQ4T62UMQrCQBREtxQ8gI1niZBT2HkOS69hIygexD52HkBEKzuTVJrVeckPKBsCshl4yc7+v0PYZOOQ9z4RG3EQJ3ERd5GLUjwNxsxRo4de1rA2acNm4vWOFBlkEbizuWiR5XTPGjuIMp7waiaQauzZoq/nW/QRmJsPpFph+zwWK1FaqVNk0VyZD6RaHdhKfiq2wlvLjzRd/Rs4EWvR+VWQRVNhPhA1gjQcabwUj6bSrbpfl5v5QKrxUubibFO9IovAo/lokUXg3ny0yCIwFUMdvbR9e5zn2J/DzDnnPrzcFo62/tq3AAAAAElFTkSuQmCC)](https://www.youtube.com/MrCrayfishMinecraft) 6 | [![Discord](https://img.shields.io/discord/336389026586165261?label=&color=2d2d2d&labelColor=cc923d&style=for-the-badge&logo=Discord&logoColor=ffffff)](https://discord.gg/mrcrayfish) 7 | [![Curseforge](http://cf.way2muchnoise.eu/full_374736_downloads.svg?badge_style=for_the_badge)](https://www.curseforge.com/minecraft/mc-mods/golden-hopper) 8 | 9 | ## 📖 About: 10 | 11 | Golden Hopper is a very simple mod that adds, as the mod name states, golden hoppers! They are just like a regular hopper but you can filter which item can be pulled and push. It tries to mimic the classes redstone hopper filtering design by only allowing one item to be filtered and you must also use one of the items in the filter, not just a reference. This mod is intentionally simple to keep the feel of a vanilla game. 12 | 13 | ### 🔨 Crafting: 14 | 15 | ![Golden Hopper Crafting](https://i.imgur.com/5T8VaXg.png) 16 | ![Golden Hopper Minecart Crafting](https://i.imgur.com/ch692Bv.png) 17 | 18 | ### 📷 Screenshots: 19 | 20 | ![Screenshot 1](https://i.imgur.com/mY48udw.png) 21 | ![Screenshot 2](https://i.imgur.com/hieW4Tw.png) 22 | ![Screenshot 2](https://i.imgur.com/5oofACO.png) 23 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/data/CommonRecipeProvider.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.data; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModBlocks; 4 | import com.mrcrayfish.goldenhopper.core.ModItems; 5 | import com.mrcrayfish.goldenhopper.platform.Services; 6 | import net.minecraft.advancements.Criterion; 7 | import net.minecraft.core.HolderLookup; 8 | import net.minecraft.core.registries.Registries; 9 | import net.minecraft.data.PackOutput; 10 | import net.minecraft.data.recipes.RecipeCategory; 11 | import net.minecraft.data.recipes.RecipeOutput; 12 | import net.minecraft.data.recipes.RecipeProvider; 13 | import net.minecraft.data.recipes.ShapedRecipeBuilder; 14 | import net.minecraft.tags.TagKey; 15 | import net.minecraft.world.item.Item; 16 | import net.minecraft.world.item.Items; 17 | import net.minecraft.world.level.ItemLike; 18 | 19 | import java.util.concurrent.CompletableFuture; 20 | import java.util.function.Function; 21 | 22 | /** 23 | * Author: MrCrayfish 24 | */ 25 | public class CommonRecipeProvider extends RecipeProvider 26 | { 27 | private final HolderLookup.RegistryLookup items; 28 | 29 | public CommonRecipeProvider(HolderLookup.Provider provider, RecipeOutput output) 30 | { 31 | super(provider, output); 32 | this.items = provider.lookupOrThrow(Registries.ITEM); 33 | } 34 | 35 | @Override 36 | public void buildRecipes() 37 | { 38 | ShapedRecipeBuilder.shaped(this.items, RecipeCategory.REDSTONE, ModBlocks.GOLDEN_HOPPER.get()) 39 | .pattern("ICI") 40 | .pattern("IHI") 41 | .pattern("RIR") 42 | .define('I', Services.PLATFORM.getGoldIngotsTag()) 43 | .define('R', Items.REDSTONE) 44 | .define('H', Items.HOPPER) 45 | .define('C', Items.COMPARATOR) 46 | .unlockedBy("has_gold_ingot", this.has(Services.PLATFORM.getGoldIngotsTag())) 47 | .unlockedBy("has_redstone", this.has(Items.REDSTONE)) 48 | .unlockedBy("has_comparator", this.has(Items.COMPARATOR)) 49 | .unlockedBy("has_hopper", this.has(Items.HOPPER)) 50 | .save(this.output); 51 | 52 | ShapedRecipeBuilder.shaped(this.items, RecipeCategory.TRANSPORTATION, ModItems.GOLDEN_HOPPER_MINECART.get()) 53 | .pattern("A") 54 | .pattern("B") 55 | .define('A', ModBlocks.GOLDEN_HOPPER.get()) 56 | .define('B', Items.MINECART) 57 | .unlockedBy("has_gold_ingot", this.has(Services.PLATFORM.getGoldIngotsTag())) 58 | .unlockedBy("has_minecart", this.has(Items.MINECART)) 59 | .save(this.output); 60 | } 61 | 62 | public static final class Runner extends RecipeProvider.Runner 63 | { 64 | public Runner(PackOutput output, CompletableFuture completableFuture) 65 | { 66 | super(output, completableFuture); 67 | } 68 | 69 | @Override 70 | protected RecipeProvider createRecipeProvider(HolderLookup.Provider provider, RecipeOutput output) 71 | { 72 | return new CommonRecipeProvider(provider, output); 73 | } 74 | 75 | @Override 76 | public String getName() 77 | { 78 | return "Golden Hopper Recipes"; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/inventory/GoldenHopperMenu.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.inventory; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModContainers; 4 | import com.mrcrayfish.goldenhopper.inventory.slot.FilterSlot; 5 | import net.minecraft.world.Container; 6 | import net.minecraft.world.SimpleContainer; 7 | import net.minecraft.world.entity.player.Inventory; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.minecraft.world.inventory.AbstractContainerMenu; 10 | import net.minecraft.world.inventory.Slot; 11 | import net.minecraft.world.item.ItemStack; 12 | 13 | /** 14 | * Author: MrCrayfish 15 | */ 16 | public class GoldenHopperMenu extends AbstractContainerMenu 17 | { 18 | private final Container container; 19 | 20 | public GoldenHopperMenu(int windowId, Inventory playerInventory) 21 | { 22 | this(windowId, playerInventory, new SimpleContainer(6)); 23 | } 24 | 25 | public GoldenHopperMenu(int windowId, Inventory playerInventory, Container container) 26 | { 27 | super(ModContainers.GOLDEN_HOPPER.get(), windowId); 28 | this.container = container; 29 | checkContainerSize(container, 6); 30 | container.startOpen(playerInventory.player); 31 | 32 | this.addSlot(new FilterSlot(container, 0, 26, 20)); 33 | 34 | for(int i = 0; i < 5; i++) 35 | { 36 | this.addSlot(new Slot(container, i + 1, 62 + i * 18, 20)); 37 | } 38 | 39 | for(int i = 0; i < 3; i++) 40 | { 41 | for(int j = 0; j < 9; j++) 42 | { 43 | this.addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, i * 18 + 51)); 44 | } 45 | } 46 | 47 | for(int i = 0; i < 9; i++) 48 | { 49 | this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 109)); 50 | } 51 | 52 | } 53 | 54 | @Override 55 | public boolean stillValid(Player player) 56 | { 57 | return this.container.stillValid(player); 58 | } 59 | 60 | @Override 61 | public ItemStack quickMoveStack(Player player, int index) 62 | { 63 | ItemStack result = ItemStack.EMPTY; 64 | Slot slot = this.slots.get(index); 65 | if(slot.hasItem()) 66 | { 67 | ItemStack slotStack = slot.getItem(); 68 | result = slotStack.copy(); 69 | if(index < this.container.getContainerSize()) 70 | { 71 | if(!this.moveItemStackTo(slotStack, this.container.getContainerSize(), this.slots.size(), true)) 72 | { 73 | return ItemStack.EMPTY; 74 | } 75 | } 76 | else if(!this.moveItemStackTo(slotStack, 1, this.container.getContainerSize(), false)) 77 | { 78 | return ItemStack.EMPTY; 79 | } 80 | 81 | if(slotStack.isEmpty()) 82 | { 83 | slot.set(ItemStack.EMPTY); 84 | } 85 | else 86 | { 87 | slot.setChanged(); 88 | } 89 | } 90 | 91 | return result; 92 | } 93 | 94 | @Override 95 | public void removed(Player player) 96 | { 97 | super.removed(player); 98 | this.container.stopOpen(player); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/block/GoldenHopperBlock.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.block; 2 | 3 | import com.mojang.serialization.MapCodec; 4 | import com.mrcrayfish.goldenhopper.blockentity.GoldenHopperBlockEntity; 5 | import com.mrcrayfish.goldenhopper.core.ModBlockEntities; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.core.Direction; 8 | import net.minecraft.stats.Stats; 9 | import net.minecraft.util.Mth; 10 | import net.minecraft.world.InteractionResult; 11 | import net.minecraft.world.entity.player.Player; 12 | import net.minecraft.world.item.ItemStack; 13 | import net.minecraft.world.level.Level; 14 | import net.minecraft.world.level.block.HopperBlock; 15 | import net.minecraft.world.level.block.entity.BlockEntity; 16 | import net.minecraft.world.level.block.entity.BlockEntityTicker; 17 | import net.minecraft.world.level.block.entity.BlockEntityType; 18 | import net.minecraft.world.level.block.entity.HopperBlockEntity; 19 | import net.minecraft.world.level.block.state.BlockState; 20 | import net.minecraft.world.phys.BlockHitResult; 21 | import org.jetbrains.annotations.Nullable; 22 | 23 | import java.util.stream.IntStream; 24 | 25 | /** 26 | * Author: MrCrayfish 27 | */ 28 | public class GoldenHopperBlock extends HopperBlock 29 | { 30 | public static final MapCodec CODEC = simpleCodec(GoldenHopperBlock::new); 31 | 32 | public GoldenHopperBlock(Properties properties) 33 | { 34 | super(properties); 35 | } 36 | 37 | @Override 38 | public MapCodec codec() 39 | { 40 | return CODEC; 41 | } 42 | 43 | @Override 44 | public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) 45 | { 46 | if(!level.isClientSide()) 47 | { 48 | BlockEntity entity = level.getBlockEntity(pos); 49 | if(entity instanceof GoldenHopperBlockEntity hopper) 50 | { 51 | player.openMenu(hopper); 52 | player.awardStat(Stats.INSPECT_HOPPER); 53 | } 54 | return InteractionResult.CONSUME; 55 | } 56 | return InteractionResult.SUCCESS; 57 | } 58 | 59 | @Nullable 60 | @Override 61 | public BlockEntity newBlockEntity(BlockPos pos, BlockState state) 62 | { 63 | return new GoldenHopperBlockEntity(pos, state); 64 | } 65 | 66 | @Nullable 67 | @Override 68 | public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType type) 69 | { 70 | return !level.isClientSide() ? createTickerHelper(type, ModBlockEntities.GOLDEN_HOPPER.get(), HopperBlockEntity::pushItemsTick) : null; 71 | } 72 | 73 | @Override 74 | protected int getAnalogOutputSignal(BlockState state, Level level, BlockPos pos, Direction direction) 75 | { 76 | if(level.getBlockEntity(pos) instanceof GoldenHopperBlockEntity hopper) 77 | { 78 | float strength = (float) IntStream.range(0, hopper.getContainerSize()) 79 | .filter(slot -> slot != GoldenHopperBlockEntity.FILTER_SLOT_INDEX) // Ignore the filter slot 80 | .mapToObj(hopper::getItem) // Get the stack in the slot 81 | .mapToDouble(stack -> Math.clamp((double) stack.getCount() / hopper.getMaxStackSize(stack), 0, 1)) // Get normalised value of count vs max stack size 82 | .average().orElse(0); // Get the average of all the results 83 | return Mth.lerpDiscrete(strength, 0, 15); 84 | } 85 | return 0; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /neoforge/build.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.commons.io.FilenameUtils 2 | 3 | plugins { 4 | id 'multiloader-loader' 5 | id 'net.neoforged.moddev' 6 | } 7 | 8 | neoForge { 9 | enable { 10 | version = neoforge_version 11 | disableRecompilation = System.getenv("CI") == "true" 12 | } 13 | 14 | def at = file('src/main/resources/META-INF/accesstransformer.cfg') 15 | if (at.exists()) { 16 | accessTransformers.from(at.absolutePath) 17 | } 18 | 19 | parchment { 20 | minecraftVersion = parchment_minecraft 21 | mappingsVersion = parchment_version 22 | } 23 | 24 | mods { 25 | "${mod_id}" { 26 | sourceSet sourceSets.main 27 | } 28 | } 29 | 30 | runs { 31 | configureEach { 32 | systemProperty('neoforge.enabledGameTestNamespaces', mod_id) 33 | ideName = "NeoForge ${it.name.capitalize()} (${project.path})" // Unify the run config names with fabric 34 | } 35 | client { 36 | client() 37 | } 38 | data { 39 | clientData() 40 | 41 | // DataGen can be run by - "./gradlew :neoforge:runData" in Terminal. 42 | // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. 43 | programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() 44 | } 45 | server { 46 | server() 47 | } 48 | } 49 | } 50 | 51 | sourceSets.main.resources { srcDir 'src/generated/resources' } 52 | 53 | dependencies { 54 | implementation "com.mrcrayfish:framework-neoforge:${minecraft_version}-${framework_version}" 55 | } 56 | 57 | tasks.register("signJar") { 58 | dependsOn jar 59 | onlyIf { 60 | findProperty('keyStore') || System.getenv("KEYSTORE") 61 | } 62 | doLast { 63 | def unsignedJar = tasks.jar.archiveFile.get().asFile 64 | def name = FilenameUtils.removeExtension(unsignedJar.getName()) 65 | def signedJar = layout.buildDirectory.file("libs/%s-signed.jar".formatted(name)).get().asFile 66 | def signingKeyAlias = findProperty('keyStoreAlias') ?: System.getenv("KEYSTORE_ALIAS") 67 | def signingKeystore = findProperty('keyStore') ?: System.getenv("KEYSTORE") 68 | def signingPassword = findProperty('keyStorePass') ?: System.getenv("KEYSTORE_PASS") 69 | ant.signjar( 70 | jar: unsignedJar, 71 | signedJar: signedJar, 72 | alias: signingKeyAlias, 73 | keyStore: signingKeystore, 74 | storePass: signingPassword, 75 | storetype: 'jks' 76 | ) 77 | println "Signed jar created at: $signedJar" 78 | } 79 | } 80 | 81 | jar.finalizedBy 'signJar' 82 | 83 | // Implement mcgradleconventions loader attribute 84 | def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) 85 | ['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> 86 | configurations.named("$variant") { 87 | attributes { 88 | attribute(loaderAttribute, 'neoforge') 89 | } 90 | } 91 | } 92 | sourceSets.configureEach { 93 | [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName, it.getTaskName(null, 'jarJar')].each { variant-> 94 | configurations.named("$variant") { 95 | attributes { 96 | attribute(loaderAttribute, 'neoforge') 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /fabric/build.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.commons.io.FilenameUtils 2 | 3 | plugins { 4 | id 'multiloader-loader' 5 | id 'fabric-loom' 6 | } 7 | 8 | repositories { 9 | exclusiveContent { 10 | forRepository { 11 | maven { 12 | name = "TerraformersMC" 13 | url = "https://maven.terraformersmc.com/" 14 | } 15 | } 16 | filter { 17 | includeGroup("com.terraformersmc") 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | minecraft "com.mojang:minecraft:${minecraft_version}" 24 | mappings loom.officialMojangMappings() 25 | modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}" 26 | modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}" 27 | modImplementation "com.mrcrayfish:framework-fabric:${minecraft_version}-${framework_version}" 28 | modRuntimeOnly "com.terraformersmc:modmenu:${modmenu_version}" 29 | } 30 | 31 | sourceSets.main.resources { srcDir 'src/generated/resources' } 32 | 33 | fabricApi { 34 | configureDataGeneration { 35 | client = true 36 | outputDirectory.set(file("src/generated")) 37 | createRunConfiguration = true 38 | } 39 | } 40 | 41 | loom { 42 | def aw = project(":common").file("src/main/resources/${mod_id}.accesswidener") 43 | if (aw.exists()) { 44 | accessWidenerPath.set(aw) 45 | } 46 | mixin { 47 | defaultRefmapName.set("${mod_id}.refmap.json") 48 | } 49 | runs { 50 | client { 51 | client() 52 | setConfigName('Fabric Client') 53 | ideConfigGenerated(true) 54 | runDir('runs/client') 55 | } 56 | server { 57 | server() 58 | setConfigName('Fabric Server') 59 | ideConfigGenerated(true) 60 | runDir('runs/server') 61 | } 62 | } 63 | } 64 | 65 | tasks.register("signJar") { 66 | dependsOn remapJar 67 | onlyIf { 68 | findProperty('keyStore') || System.getenv("KEYSTORE") 69 | } 70 | doLast { 71 | def unsignedJar = remapJar.archiveFile.get().asFile 72 | def name = FilenameUtils.removeExtension(unsignedJar.getName()) 73 | def signedJar = layout.buildDirectory.file("libs/%s-signed.jar".formatted(name)).get().asFile 74 | def signingKeyAlias = findProperty('keyStoreAlias') ?: System.getenv("KEYSTORE_ALIAS") 75 | def signingKeystore = findProperty('keyStore') ?: System.getenv("KEYSTORE") 76 | def signingPassword = findProperty('keyStorePass') ?: System.getenv("KEYSTORE_PASS") 77 | ant.signjar( 78 | jar: unsignedJar, 79 | signedJar: signedJar, 80 | alias: signingKeyAlias, 81 | keyStore: signingKeystore, 82 | storePass: signingPassword, 83 | storetype: 'jks' 84 | ) 85 | println "Signed jar created at: $signedJar" 86 | } 87 | } 88 | 89 | build.dependsOn signJar 90 | 91 | // Implement mcgradleconventions loader attribute 92 | def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) 93 | ['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements', 'includeInternal', 'modCompileClasspath'].each { variant -> 94 | configurations.named("$variant") { 95 | attributes { 96 | attribute(loaderAttribute, 'fabric') 97 | } 98 | } 99 | } 100 | sourceSets.configureEach { 101 | [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName].each { variant-> 102 | configurations.named("$variant") { 103 | attributes { 104 | attribute(loaderAttribute, 'fabric') 105 | } 106 | } 107 | } 108 | } 109 | loom.remapConfigurations.configureEach { 110 | configurations.named(it.name) { 111 | attributes { 112 | attribute(loaderAttribute, 'fabric') 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/entity/vehicle/GoldenHopperMinecart.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.entity.vehicle; 2 | 3 | import com.mrcrayfish.goldenhopper.blockentity.GoldenHopperBlockEntity; 4 | import com.mrcrayfish.goldenhopper.core.ModBlocks; 5 | import com.mrcrayfish.goldenhopper.core.ModEntities; 6 | import com.mrcrayfish.goldenhopper.core.ModItems; 7 | import com.mrcrayfish.goldenhopper.inventory.GoldenHopperMenu; 8 | import net.minecraft.core.Direction; 9 | import net.minecraft.util.Mth; 10 | import net.minecraft.world.Container; 11 | import net.minecraft.world.WorldlyContainer; 12 | import net.minecraft.world.entity.EntityType; 13 | import net.minecraft.world.entity.player.Inventory; 14 | import net.minecraft.world.entity.vehicle.minecart.MinecartHopper; 15 | import net.minecraft.world.inventory.AbstractContainerMenu; 16 | import net.minecraft.world.item.Item; 17 | import net.minecraft.world.item.ItemStack; 18 | import net.minecraft.world.level.Level; 19 | import net.minecraft.world.level.block.state.BlockState; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | import java.util.stream.IntStream; 23 | 24 | /** 25 | * Author: MrCrayfish 26 | */ 27 | public class GoldenHopperMinecart extends MinecartHopper implements WorldlyContainer 28 | { 29 | private static final int CONTAINER_SIZE = 6; 30 | private static final int FILTER_SLOT_INDEX = 0; 31 | 32 | public GoldenHopperMinecart(EntityType type, Level level) 33 | { 34 | super(type, level); 35 | } 36 | 37 | public GoldenHopperMinecart(Level level, double x, double y, double z) 38 | { 39 | super(ModEntities.GOLDEN_HOPPER_MINECART.get(), level); 40 | this.setPos(x, y, z); 41 | this.xo = x; 42 | this.yo = y; 43 | this.zo = z; 44 | } 45 | 46 | @Override 47 | public int getContainerSize() 48 | { 49 | return CONTAINER_SIZE; 50 | } 51 | 52 | @Override 53 | public AbstractContainerMenu createMenu(int windowId, Inventory playerInventory) 54 | { 55 | return new GoldenHopperMenu(windowId, playerInventory, this); 56 | } 57 | 58 | @Override 59 | public BlockState getDefaultDisplayBlockState() 60 | { 61 | return ModBlocks.GOLDEN_HOPPER.get().defaultBlockState(); 62 | } 63 | 64 | @Override 65 | public ItemStack getPickResult() 66 | { 67 | return new ItemStack(ModItems.GOLDEN_HOPPER_MINECART.get()); 68 | } 69 | 70 | @Override 71 | protected Item getDropItem() 72 | { 73 | return ModItems.GOLDEN_HOPPER_MINECART.get(); 74 | } 75 | 76 | @Override 77 | public boolean canPlaceItem(int index, ItemStack stack) 78 | { 79 | return GoldenHopperBlockEntity.canInsertIntoSlot(index, FILTER_SLOT_INDEX, this.getItem(FILTER_SLOT_INDEX), stack); 80 | } 81 | 82 | @Override 83 | public int[] getSlotsForFace(Direction side) 84 | { 85 | return IntStream.range(0, this.getContainerSize()).filter(value -> value != FILTER_SLOT_INDEX).toArray(); 86 | } 87 | 88 | @Override 89 | public boolean canPlaceItemThroughFace(int index, ItemStack stack, @Nullable Direction direction) 90 | { 91 | return index != FILTER_SLOT_INDEX && this.canPlaceItem(index, stack); 92 | } 93 | 94 | @Override 95 | public boolean canTakeItemThroughFace(int index, ItemStack stack, Direction direction) 96 | { 97 | return index != FILTER_SLOT_INDEX; 98 | } 99 | 100 | @Override 101 | public boolean canTakeItem(Container container, int index, ItemStack stack) 102 | { 103 | return index != FILTER_SLOT_INDEX; 104 | } 105 | 106 | public int getComparatorLevel() 107 | { 108 | float filled = IntStream.range(1, this.getContainerSize()) 109 | .mapToObj(this::getItem) 110 | .filter(stack -> !stack.isEmpty()) 111 | .map(stack -> stack.getCount() / (float) Math.min(this.getMaxStackSize(), stack.getMaxStackSize())) 112 | .reduce(0F, Float::sum); 113 | filled /= (this.getContainerSize() - 1.0F); 114 | return Mth.floor(filled * 14.0F) + (filled > 0 ? 1 : 0); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | ARGV=("$@") 176 | eval set -- $DEFAULT_JVM_OPTS 177 | 178 | IFS=$' 179 | ' read -rd '' -a JAVA_OPTS_ARR <<< "$(echo $JAVA_OPTS | xargs -n1)" 180 | IFS=$' 181 | ' read -rd '' -a GRADLE_OPTS_ARR <<< "$(echo $GRADLE_OPTS | xargs -n1)" 182 | 183 | exec "$JAVACMD" "$@" "${JAVA_OPTS_ARR[@]}" "${GRADLE_OPTS_ARR[@]}" "-Dorg.gradle.appname=$APP_BASE_NAME" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "${ARGV[@]}" 184 | -------------------------------------------------------------------------------- /common/src/main/java/com/mrcrayfish/goldenhopper/blockentity/GoldenHopperBlockEntity.java: -------------------------------------------------------------------------------- 1 | package com.mrcrayfish.goldenhopper.blockentity; 2 | 3 | import com.mrcrayfish.goldenhopper.core.ModBlockEntities; 4 | import com.mrcrayfish.goldenhopper.inventory.GoldenHopperMenu; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.core.Direction; 7 | import net.minecraft.core.Holder; 8 | import net.minecraft.core.NonNullList; 9 | import net.minecraft.core.component.DataComponents; 10 | import net.minecraft.network.chat.Component; 11 | import net.minecraft.world.Container; 12 | import net.minecraft.world.WorldlyContainer; 13 | import net.minecraft.world.entity.player.Inventory; 14 | import net.minecraft.world.inventory.AbstractContainerMenu; 15 | import net.minecraft.world.item.ItemStack; 16 | import net.minecraft.world.item.alchemy.Potion; 17 | import net.minecraft.world.item.alchemy.PotionContents; 18 | import net.minecraft.world.item.enchantment.ItemEnchantments; 19 | import net.minecraft.world.level.block.entity.BlockEntityType; 20 | import net.minecraft.world.level.block.entity.HopperBlockEntity; 21 | import net.minecraft.world.level.block.state.BlockState; 22 | import org.jetbrains.annotations.Nullable; 23 | 24 | import java.util.Optional; 25 | import java.util.stream.IntStream; 26 | 27 | /** 28 | * Author: MrCrayfish 29 | */ 30 | public class GoldenHopperBlockEntity extends HopperBlockEntity implements WorldlyContainer 31 | { 32 | public static final int CONTAINER_SIZE = 6; 33 | public static final int FILTER_SLOT_INDEX = 0; 34 | public static final int[] TRANSFERABLE_SLOTS = IntStream.range(1, CONTAINER_SIZE).toArray(); 35 | public static boolean ejecting = false; 36 | 37 | public GoldenHopperBlockEntity(BlockPos pos, BlockState state) 38 | { 39 | super(pos, state); 40 | this.setItems(NonNullList.withSize(CONTAINER_SIZE, ItemStack.EMPTY)); 41 | ((BlockEntityTypeSetter) this).goldenHopper$SetType(ModBlockEntities.GOLDEN_HOPPER.get()); 42 | } 43 | 44 | @Override 45 | public BlockEntityType getType() 46 | { 47 | return ModBlockEntities.GOLDEN_HOPPER.get(); 48 | } 49 | 50 | @Override 51 | protected Component getDefaultName() 52 | { 53 | return Component.translatable("container.goldenhopper.golden_hopper"); 54 | } 55 | 56 | @Override 57 | protected AbstractContainerMenu createMenu(int windowId, Inventory playerInventory) 58 | { 59 | return new GoldenHopperMenu(windowId, playerInventory, this); 60 | } 61 | 62 | @Override 63 | public ItemStack getItem(int slot) 64 | { 65 | if(ejecting && slot == FILTER_SLOT_INDEX) 66 | { 67 | return ItemStack.EMPTY; 68 | } 69 | return super.getItem(slot); 70 | } 71 | 72 | @Override 73 | public int[] getSlotsForFace(Direction side) 74 | { 75 | return TRANSFERABLE_SLOTS; 76 | } 77 | 78 | @Override 79 | public boolean canPlaceItem(int index, ItemStack stack) 80 | { 81 | return canInsertIntoSlot(index, FILTER_SLOT_INDEX, this.getItems().get(FILTER_SLOT_INDEX), stack); 82 | } 83 | 84 | @Override 85 | public boolean canPlaceItemThroughFace(int index, ItemStack stack, @Nullable Direction direction) 86 | { 87 | return index != FILTER_SLOT_INDEX && this.canPlaceItem(index, stack); 88 | } 89 | 90 | @Override 91 | public boolean canTakeItemThroughFace(int index, ItemStack stack, Direction direction) 92 | { 93 | return index != FILTER_SLOT_INDEX; 94 | } 95 | 96 | @Override 97 | public boolean canTakeItem(Container container, int index, ItemStack stack) 98 | { 99 | return index != FILTER_SLOT_INDEX; 100 | } 101 | 102 | public static boolean canInsertIntoSlot(int index, int filterIndex, ItemStack filter, ItemStack stack) 103 | { 104 | if(index != filterIndex) 105 | { 106 | if(filter.isEmpty()) 107 | return true; 108 | 109 | if(!stack.is(filter.getItem())) 110 | return false; 111 | 112 | // Potion contents must match if filter has potion contents 113 | if(filter.has(DataComponents.POTION_CONTENTS)) 114 | { 115 | if(!stack.has(DataComponents.POTION_CONTENTS)) 116 | return false; 117 | 118 | PotionContents first = filter.get(DataComponents.POTION_CONTENTS); 119 | PotionContents second = stack.get(DataComponents.POTION_CONTENTS); 120 | if(first != null && second != null && !comparePotions(first, second)) 121 | return false; 122 | } 123 | 124 | // Item enchantments must match if filter has potion contents 125 | if(filter.has(DataComponents.STORED_ENCHANTMENTS)) 126 | { 127 | if(!stack.has(DataComponents.STORED_ENCHANTMENTS)) 128 | return false; 129 | 130 | ItemEnchantments first = filter.get(DataComponents.STORED_ENCHANTMENTS); 131 | ItemEnchantments second = stack.get(DataComponents.STORED_ENCHANTMENTS); 132 | if(first != null && second != null && !compareEnchantments(first, second)) 133 | return false; 134 | } 135 | 136 | return true; 137 | } 138 | return false; 139 | } 140 | 141 | public static boolean comparePotions(PotionContents first, PotionContents second) 142 | { 143 | // Compare the potion value 144 | Optional> firstOptional = first.potion(); 145 | if(firstOptional.isPresent()) 146 | { 147 | Optional> secondOptional = second.potion(); 148 | if(secondOptional.isEmpty()) 149 | return false; 150 | 151 | Potion firstPotion = firstOptional.get().value(); 152 | Potion secondPotion = secondOptional.get().value(); 153 | if(firstPotion != secondPotion) 154 | return false; 155 | } 156 | 157 | // The matching stack must have at least the base effects of the filter. 158 | for(var instance : first.customEffects()) 159 | { 160 | if(second.customEffects().stream().noneMatch(effect -> effect.getEffect().equals(instance.getEffect()))) 161 | { 162 | return false; 163 | } 164 | } 165 | return true; 166 | } 167 | 168 | public static boolean compareEnchantments(ItemEnchantments first, ItemEnchantments second) 169 | { 170 | // The matching stack must have at least the enchantment types of the filter. 171 | for(var holder : first.keySet()) 172 | { 173 | if(second.getLevel(holder) <= 0) 174 | { 175 | return false; 176 | } 177 | } 178 | return true; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Mod 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | environment: Build 10 | permissions: 11 | contents: write 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | fetch-tags: true 17 | - name: Set up JDK 21 18 | uses: actions/setup-java@v4 19 | with: 20 | java-version: '21' 21 | distribution: 'temurin' 22 | - name: Validate Gradle Wrapper 23 | uses: gradle/actions/wrapper-validation@v4 24 | - name: Read mod data from gradle.properties 25 | id: mod_data 26 | uses: christian-draeger/read-properties@1.1.1 27 | with: 28 | path: './gradle.properties' 29 | properties: 'mod_id mod_version minecraft_version' 30 | - name: Validate tag format 31 | run: | 32 | tag=${{ github.ref_name }} 33 | expected_mc_version=${{ steps.mod_data.outputs.minecraft_version }} 34 | expected_mod_version=${{ steps.mod_data.outputs.mod_version }} 35 | mc_version=$(echo "${tag:1}" | cut -d '-' -f 1) 36 | mod_version=$(echo "${tag:1}" | cut -d '-' -f 2) 37 | if [[ "${tag:0:1}" != "v" ]]; then 38 | echo "Tag must start with v" 39 | exit 1 40 | elif [[ "$mc_version" != "$expected_mc_version" ]]; then 41 | echo "The minecraft version in the tag does not match the gradle property" 42 | exit 1 43 | elif [[ "$mod_version" != "$expected_mod_version" ]]; then 44 | echo "The mod version in the tag does not match the gradle property" 45 | exit 1 46 | fi 47 | echo "Tag validation sucessful!" 48 | - name: Cache 49 | uses: actions/cache@v4.2.0 50 | with: 51 | path: ~/.gradle/caches 52 | key: gradle-minecraft-${{ steps.mod_data.outputs.minecraft_version }} 53 | restore-keys: | 54 | gradle-minecraft-${{ steps.mod_data.outputs.minecraft_version }} 55 | - name: Load Keystore 56 | id: load_keystore 57 | env: 58 | JKS: ${{ secrets.MRCRAYFISH_JKS }} 59 | if: ${{ env.JKS != '' }} 60 | uses: timheuer/base64-to-file@v1.2 61 | with: 62 | fileName: 'keystore.jks' 63 | encodedString: ${{ env.JKS }} 64 | - name: Make gradlew executable 65 | run: chmod +x ./gradlew 66 | - name: Data Generation (NeoForge) 67 | env: 68 | GPR_USER: "MrCrayfish" 69 | GPR_KEY: ${{ secrets.GPR_TOKEN }} 70 | PACKAGES_REPO: ${{ vars.PACKAGES_REPOSITORY }} 71 | TARGET_LOADER: "neoforge" 72 | run: ./gradlew :neoforge:runData 73 | - name: Data Generation (Fabric) 74 | env: 75 | GPR_USER: "MrCrayfish" 76 | GPR_KEY: ${{ secrets.GPR_TOKEN }} 77 | PACKAGES_REPO: ${{ vars.PACKAGES_REPOSITORY }} 78 | TARGET_LOADER: "fabric" 79 | run: ./gradlew :fabric:runDatagen 80 | - name: Build (Common) 81 | env: 82 | GPR_USER: "MrCrayfish" 83 | GPR_KEY: ${{ secrets.GPR_TOKEN }} 84 | PACKAGES_REPO: ${{ vars.PACKAGES_REPOSITORY }} 85 | TARGET_LOADER: "common" 86 | run: ./gradlew :common:build 87 | - name: Build (NeoForge) 88 | env: 89 | GPR_USER: "MrCrayfish" 90 | GPR_KEY: ${{ secrets.GPR_TOKEN }} 91 | PACKAGES_REPO: ${{ vars.PACKAGES_REPOSITORY }} 92 | KEYSTORE: ${{ steps.load_keystore.outputs.filePath }} 93 | KEYSTORE_ALIAS: ${{ secrets.MRCRAYFISH_JKS_ALIAS }} 94 | KEYSTORE_PASS: ${{ secrets.MRCRAYFISH_JKS_PASSPHRASE }} 95 | TARGET_LOADER: "neoforge" 96 | run: ./gradlew :neoforge:build 97 | - name: Build (Fabric) 98 | env: 99 | GPR_USER: "MrCrayfish" 100 | GPR_KEY: ${{ secrets.GPR_TOKEN }} 101 | PACKAGES_REPO: ${{ vars.PACKAGES_REPOSITORY }} 102 | KEYSTORE: ${{ steps.load_keystore.outputs.filePath }} 103 | KEYSTORE_ALIAS: ${{ secrets.MRCRAYFISH_JKS_ALIAS }} 104 | KEYSTORE_PASS: ${{ secrets.MRCRAYFISH_JKS_PASSPHRASE }} 105 | TARGET_LOADER: "fabric" 106 | run: ./gradlew :fabric:build 107 | - name: Construct artifact files 108 | id: artifacts 109 | run: | 110 | echo "neoforge=neoforge/build/libs/${{ steps.mod_data.outputs.mod_id }}-neoforge-${{ steps.mod_data.outputs.minecraft_version }}-${{ steps.mod_data.outputs.mod_version }}-signed.jar" >> "$GITHUB_OUTPUT" 111 | echo "fabric=fabric/build/libs/${{ steps.mod_data.outputs.mod_id }}-fabric-${{ steps.mod_data.outputs.minecraft_version }}-${{ steps.mod_data.outputs.mod_version }}-signed.jar" >> "$GITHUB_OUTPUT" 112 | - name: Create Changelog Files 113 | run: | 114 | echo "" > CHANGELOG.txt 115 | echo "### Technical Changelog:" >> RELEASE_BODY.md 116 | tag_count=$(git tag --merged | wc -l) 117 | if [ "$tag_count" -ge 2 ]; then 118 | latest_tag=$(git describe --tags --abbrev=0) 119 | previous_tag=$(git describe --tags --abbrev=0 --exclude=$latest_tag) 120 | git log "$previous_tag..$latest_tag^" --oneline --pretty="- %s (%H)" >> RELEASE_BODY.md 121 | git log "$previous_tag..$latest_tag^" --oneline --pretty="%s" >> CHANGELOG.txt 122 | else 123 | echo "No changes" 124 | fi 125 | echo "### Download:" >> RELEASE_BODY.md 126 | echo "- Official Website: https://mrcrayfish.com/mods/${{ steps.mod_data.outputs.mod_id }}" >> RELEASE_BODY.md 127 | echo "- CurseForge: https://curseforge.com/minecraft/mc-mods/golden-hopper" >> RELEASE_BODY.md 128 | - name: Import GPG 129 | env: 130 | SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} 131 | SIGNING_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 132 | if: ${{ env.SIGNING_KEY != '' && env.SIGNING_PASSPHRASE != '' }} 133 | uses: crazy-max/ghaction-import-gpg@v6.2.0 134 | with: 135 | gpg_private_key: ${{ env.SIGNING_KEY }} 136 | passphrase: ${{ env.SIGNING_PASSPHRASE }} 137 | - name: GPG Sign 138 | env: 139 | SIGNING_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 140 | if: ${{ env.SIGNING_PASSPHRASE != '' }} 141 | run: | 142 | gpg --batch --yes --passphrase "${{ env.SIGNING_PASSPHRASE }}" --armor --detach-sign ${{ steps.artifacts.outputs.neoforge }} 143 | gpg --batch --yes --passphrase "${{ env.SIGNING_PASSPHRASE }}" --armor --detach-sign ${{ steps.artifacts.outputs.fabric }} 144 | - name: Publish to GitHub Packages 145 | env: 146 | GPR_USER: "MrCrayfish" 147 | GPR_KEY: ${{ secrets.GPR_TOKEN }} 148 | PACKAGES_REPO: ${{ vars.PACKAGES_REPOSITORY }} 149 | SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} 150 | SIGNING_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 151 | run: ./gradlew publish 152 | - name: Stop Gradle 153 | run: ./gradlew --stop 154 | - name: Create Release 155 | uses: ncipollo/release-action@v1 156 | with: 157 | name: '${{ steps.mod_data.outputs.minecraft_version }}-${{ steps.mod_data.outputs.mod_version }}' 158 | artifacts: "${{ steps.artifacts.outputs.neoforge }},${{ steps.artifacts.outputs.neoforge }}.asc,${{ steps.artifacts.outputs.fabric }},${{ steps.artifacts.outputs.fabric }}.asc,CHANGELOG.txt" 159 | bodyFile: "RELEASE_BODY.md" 160 | - name: Prepare Artifacts 161 | run: | 162 | mkdir artifacts 163 | mv ${{ steps.artifacts.outputs.neoforge }} artifacts 164 | mv ${{ steps.artifacts.outputs.neoforge }}.asc artifacts 165 | mv ${{ steps.artifacts.outputs.fabric }} artifacts 166 | mv ${{ steps.artifacts.outputs.fabric }}.asc artifacts 167 | - name: Upload Artifacts 168 | uses: actions/upload-artifact@v4 169 | with: 170 | name: '${{ steps.mod_data.outputs.mod_id }}-${{ steps.mod_data.outputs.minecraft_version }}-${{ steps.mod_data.outputs.mod_version }}' 171 | path: artifacts 172 | - name: Delete tag on fail 173 | if: failure() 174 | run: | 175 | echo "Cleaning up tag..." 176 | git push origin :refs/tags/${{ github.ref_name }} --------------------------------------------------------------------------------