From d60fa9e565a2811436b6e788dc602fa4a513f855 Mon Sep 17 00:00:00 2001 From: Face o' Cat Date: Sat, 7 Nov 2026 11:38:36 +0200 Subject: [PATCH 1/9] Harden machine inventories and add inherited screens with processor controls --- gradle.properties | 1 + scripts/companion.gradle | 8 +- scripts/generate_gui_sprites.py | 31 +- .../moddev/mc/companion/CompanionMod.java | 21 +- .../mc/companion/CreativePowerEntity.java | 8 +- .../moddev/mc/companion/ProcessorEntity.java | 130 +++--- .../mc/companion/RecoveryCapsuleItem.java | 14 +- .../mmdlib/screen/base_powered_machine.xml | 16 + .../resources/mmdlib/screen/crusher.xml | 5 +- .../resources/mmdlib/screen/dual_crusher.xml | 56 ++- .../resources/mmdlib/screen/transmuter.xml | 28 +- .../companion/test/CompanionClientSmoke.java | 14 +- .../mc/companion/test/CompanionGameTests.java | 139 ++++++ .../test/InventoryLifecycleTests.java | 144 +++++++ .../test/ProcessorButtonClientSmoke.java | 192 +++++++++ .../test/ReservationClientSmoke.java | 8 + .../mc/companion/test/StatsClientSmoke.java | 34 +- .../api/automation/AutomationInventory.java | 15 +- .../mc/lib/api/capabilities/FluidStorage.java | 129 +++++- .../mc/lib/api/capabilities/ItemStorage.java | 134 +++++- .../lib/api/capabilities/StorageRecovery.java | 29 ++ .../mc/lib/gui/client/MachineScreen.java | 36 +- .../mc/lib/gui/client/RedstoneDrawer.java | 16 +- .../moddev/mc/lib/gui/load/ScreenBinder.java | 16 +- .../moddev/mc/lib/gui/load/ScreenLoader.java | 63 ++- .../moddev/mc/lib/gui/load/ScreenReader.java | 176 ++++++-- .../mc/lib/gui/load/ScreenTemplates.java | 129 ++++++ .../zone/moddev/mc/lib/gui/model/Element.java | 15 +- .../lib/gui/model/ProcessorButtonState.java | 24 ++ .../zone/moddev/mc/lib/gui/model/Screen.java | 23 +- .../mc/lib/gui/runtime/AutomationMenu.java | 4 +- .../mc/lib/gui/runtime/MachineMenu.java | 30 ++ .../mc/lib/gui/runtime/RedstoneMenu.java | 18 + .../moddev/mc/lib/gui/runtime/ScreenHost.java | 5 + .../runtime/ScreenInventoryValidation.java | 55 +++ .../mc/lib/gui/runtime/ScreenSnapshots.java | 13 + .../mc/lib/machine/feature/FeatureHost.java | 36 +- .../lib/machine/feature/MachineFeature.java | 3 + .../lib/machine/feature/RecipeProcessor.java | 18 + .../neoforge/MachineInventoryLifecycle.java | 145 +++++++ .../machine/neoforge/MachineResources.java | 201 +++++++++ .../machine/neoforge/RecipeInputFilters.java | 79 ++++ .../lib/machine/neoforge/StorageGroups.java | 8 +- .../resources/assets/mmdlib/lang/en_us.json | 9 + .../gui/sprites/icon_processor_conflict.png | Bin 0 -> 83 bytes .../gui/sprites/icon_processor_pause.png | Bin 0 -> 97 bytes .../gui/sprites/icon_processor_play.png | Bin 0 -> 108 bytes .../gui/sprites/icon_processor_stop.png | Bin 0 -> 91 bytes .../gui/sprites/icon_redstone_paused.png | Bin 0 -> 96 bytes .../gui/sprites/icon_redstone_running.png | Bin 0 -> 96 bytes .../mmdlib/textures/gui/sprites/icon_stop.png | Bin 0 -> 96 bytes src/main/resources/mmdlib/schema/screen.xsd | 68 ++- .../lib/smoke/InventoryArchitectureSmoke.java | 400 ++++++++++++++++++ .../moddev/mc/lib/smoke/MMDLibGameTests.java | 34 +- .../mc/lib/gui/ProcessorButtonTest.java | 100 +++++ .../mc/lib/gui/ScreenInheritanceTest.java | 258 +++++++++++ .../moddev/mc/lib/gui/ScreenLoaderTest.java | 114 +++-- 57 files changed, 2940 insertions(+), 312 deletions(-) create mode 100644 src/companion/resources/mmdlib/screen/base_powered_machine.xml create mode 100644 src/companionTest/java/zone/moddev/mc/companion/test/InventoryLifecycleTests.java create mode 100644 src/companionTest/java/zone/moddev/mc/companion/test/ProcessorButtonClientSmoke.java create mode 100644 src/main/java/zone/moddev/mc/lib/api/capabilities/StorageRecovery.java create mode 100644 src/main/java/zone/moddev/mc/lib/gui/load/ScreenTemplates.java create mode 100644 src/main/java/zone/moddev/mc/lib/gui/model/ProcessorButtonState.java create mode 100644 src/main/java/zone/moddev/mc/lib/gui/runtime/ScreenInventoryValidation.java create mode 100644 src/main/java/zone/moddev/mc/lib/machine/neoforge/MachineInventoryLifecycle.java create mode 100644 src/main/java/zone/moddev/mc/lib/machine/neoforge/MachineResources.java create mode 100644 src/main/java/zone/moddev/mc/lib/machine/neoforge/RecipeInputFilters.java create mode 100644 src/main/resources/assets/mmdlib/textures/gui/sprites/icon_processor_conflict.png create mode 100644 src/main/resources/assets/mmdlib/textures/gui/sprites/icon_processor_pause.png create mode 100644 src/main/resources/assets/mmdlib/textures/gui/sprites/icon_processor_play.png create mode 100644 src/main/resources/assets/mmdlib/textures/gui/sprites/icon_processor_stop.png create mode 100644 src/main/resources/assets/mmdlib/textures/gui/sprites/icon_redstone_paused.png create mode 100644 src/main/resources/assets/mmdlib/textures/gui/sprites/icon_redstone_running.png create mode 100644 src/main/resources/assets/mmdlib/textures/gui/sprites/icon_stop.png create mode 100644 src/runtimeTest/java/zone/moddev/mc/lib/smoke/InventoryArchitectureSmoke.java create mode 100644 src/test/java/zone/moddev/mc/lib/gui/ProcessorButtonTest.java create mode 100644 src/test/java/zone/moddev/mc/lib/gui/ScreenInheritanceTest.java diff --git a/gradle.properties b/gradle.properties index 37d916dd..bee48734 100644 --- a/gradle.properties +++ b/gradle.properties @@ -36,6 +36,7 @@ curios_version = 9.5.1+1.21.1 curios_version_range = [9.5.1,10) sophisticated_backpacks_version = 1.21.1-3.26.20.2244 sophisticated_core_version = 1.21.1-1.5.28.2458 +jade_version = 15.10.6+neoforge # Gradle org.gradle.jvmargs = -Xmx3G diff --git a/scripts/companion.gradle b/scripts/companion.gradle index 161c0953..bcd58c75 100644 --- a/scripts/companion.gradle +++ b/scripts/companion.gradle @@ -7,7 +7,7 @@ sourceSets { compileClasspath += sourceSets.companion.output + sourceSets.companion.compileClasspath runtimeClasspath += sourceSets.companion.output + sourceSets.companion.runtimeClasspath } - // Mock GameTest connections cannot negotiate Curios' login-sync payloads. + // Mock GameTest connections cannot negotiate Curios/Jade login-sync payloads. // Reuse the compiled fixtures, but keep optional mods for real client/server smoke runs. companionServerTest { runtimeClasspath += sourceSets.companionTest.output + sourceSets.companion.output + sourceSets.main.output + sourceSets.main.runtimeClasspath @@ -18,6 +18,12 @@ neoForge.addModdingDependenciesTo(sourceSets.companion) neoForge.addModdingDependenciesTo(sourceSets.companionTest) neoForge.addModdingDependenciesTo(sourceSets.companionServerTest) +// Jade is present in playable companion and real-client smoke runs, including their integrated server. +// Keep mock GameTest connections isolated, as with Curios below; publish neither runtime fixture. +dependencies { + companionRuntimeOnly "maven.modrinth:jade:${jade_version}" +} + // Development fixtures only: do not add backpack APIs or required mod metadata to either jar. // -PwithoutBackpacks retains a companion run without Curios or backpack mods. if (!providers.gradleProperty('withoutBackpacks').isPresent()) { diff --git a/scripts/generate_gui_sprites.py b/scripts/generate_gui_sprites.py index 55e930b7..6382ba63 100644 --- a/scripts/generate_gui_sprites.py +++ b/scripts/generate_gui_sprites.py @@ -275,17 +275,44 @@ def glyph(img, rows, x, y, ink): if bit != ".": img[y + dy][x + dx] = ink pause = ["##...##"] * 9 + stop = ["#######"] * 7 play = ["#......", "###....", "#####..", "#######", "#######", "#####..", "###....", "#......"] letters = {"a": [".#.", "#.#", "###", "#.#", "#.#"], "b": ["##.", "#.#", "##.", "#.#", "##."]} - for name, rows, ink in (("pause", pause, (240, 198, 70, 255)), + for name, rows, ink in (("stop", stop, (232, 86, 72, 255)), + ("pause", pause, (240, 198, 70, 255)), ("resume", play, (100, 216, 112, 255))): - for lane in ("", "a", "b"): + for lane in (("",) if name == "stop" else ("", "a", "b")): img = canvas(16, 16) glyph(img, rows, 3 if lane else 4, 2 if lane else 3, ink) if lane: glyph(img, letters[lane], 11, 9, grey(245)) write_png("icon_" + name + ("_" + lane if lane else ""), img) + # Native 12px sprites for the compact processor controls: no 16-to-12 scaling. + # All three glyphs, including their shadow, occupy the same centered 8x8 box. + for name, rows, ink in ( + ("pause", ["##...##"] * 7, (240, 198, 70, 255)), + ("stop", ["#######"] * 7, (232, 86, 72, 255)), + ("play", ["#......", "###....", "#####..", "#######", "#####..", "###....", "#......"], (100, 216, 112, 255)), + ): + img = canvas(12, 12) + glyph(img, rows, 2, 2, ink) + write_png("icon_processor_" + name, img) + + for name, rows, ink in ( + ("running", ["#....", "###..", "#####", "###..", "#...."], (100, 216, 112, 255)), + ("paused", ["##.##"] * 5, (240, 198, 70, 255)), + ): + img = canvas(7, 7) + glyph(img, rows, 0, 0, ink) + write_png("icon_redstone_" + name, img) + + img = canvas(5, 5) + for y, row in enumerate([".###.", "#####", "#####", "#####", ".###."]): + for x, bit in enumerate(row): + if bit == "#": img[y][x] = (244, 55, 55, 255) + write_png("icon_processor_conflict", img) + torch = [".###.", "#####", "#####", ".###.", "..#..", "..#..", "..#..", "..#..", "..#.."] badges = {"ignore": ["#.#", ".#.", "#.#"], "high": [".#.", "###", ".#."], "low": ["...", "###", "..."]} diff --git a/src/companion/java/zone/moddev/mc/companion/CompanionMod.java b/src/companion/java/zone/moddev/mc/companion/CompanionMod.java index 187adaeb..218889ef 100644 --- a/src/companion/java/zone/moddev/mc/companion/CompanionMod.java +++ b/src/companion/java/zone/moddev/mc/companion/CompanionMod.java @@ -92,18 +92,15 @@ public CompanionMod(IEventBus bus) { id("tank_container"), MaterialEventTypes.GUI_CLICK, CompanionMod::transferTankContainer); - for (int lane = 0; lane < 2; lane++) { - int index = lane; - MaterialExtensions.GLOBAL - .events() - .registerCallback( - id("toggle_" + lane), - MaterialEventTypes.GUI_CLICK, - click -> { - if (click.menu().blockEntity instanceof ProcessorEntity machine) - machine.toggle(index); - }); - } + // The custom progress dial remains clickable; processor buttons have a library-owned action. + MaterialExtensions.GLOBAL + .events() + .registerCallback( + id("toggle_0"), + MaterialEventTypes.GUI_CLICK, + click -> { + if (click.menu().blockEntity instanceof ProcessorEntity machine) machine.toggle(0); + }); } private static void transferTankContainer(ScreenClick click) { diff --git a/src/companion/java/zone/moddev/mc/companion/CreativePowerEntity.java b/src/companion/java/zone/moddev/mc/companion/CreativePowerEntity.java index c2b68d86..faa9d4c8 100644 --- a/src/companion/java/zone/moddev/mc/companion/CreativePowerEntity.java +++ b/src/companion/java/zone/moddev/mc/companion/CreativePowerEntity.java @@ -41,8 +41,12 @@ public CreativePowerEntity(BlockEntityType type, BlockPos pos, BlockState sta java.util.List.of(0), java.util.List.of("energy"), new SideConfig(IoMode.OUTPUT)); - group.bind(this::facing, side -> energy.view(() -> group.mode(side))); - automation = new AutomationInventory(this, java.util.List.of(group)); + automation = + new zone.moddev.mc.lib.machine.neoforge.MachineResources(this, this::facing) + .energy(group, energy) + .build() + .automation(); + zone.moddev.mc.lib.gui.runtime.ScreenInventoryValidation.validate(this); } @Override diff --git a/src/companion/java/zone/moddev/mc/companion/ProcessorEntity.java b/src/companion/java/zone/moddev/mc/companion/ProcessorEntity.java index d80cce9b..d65c7741 100644 --- a/src/companion/java/zone/moddev/mc/companion/ProcessorEntity.java +++ b/src/companion/java/zone/moddev/mc/companion/ProcessorEntity.java @@ -6,14 +6,12 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.Clearable; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -32,10 +30,9 @@ import zone.moddev.mc.lib.gui.runtime.ScreenHost; import zone.moddev.mc.lib.machine.feature.FeatureHost; import zone.moddev.mc.lib.machine.feature.RecipeProcessor; -import zone.moddev.mc.lib.machine.inventory.MachineInventory; -import zone.moddev.mc.lib.machine.inventory.SlotPlan; import zone.moddev.mc.lib.machine.neoforge.FeatureSync; -import zone.moddev.mc.lib.machine.neoforge.StorageGroups; +import zone.moddev.mc.lib.machine.neoforge.MachineInventoryLifecycle; +import zone.moddev.mc.lib.machine.neoforge.MachineResources; /** The dual crusher deliberately runs two lanes over the same input and output groups. */ public final class ProcessorEntity extends DemoEntity @@ -49,6 +46,8 @@ public enum Kind { public final ItemStorage items; public final AutomationInventory automation; + public final MachineResources resources; + public final MachineInventoryLifecycle inventoryLifecycle; public final FluidStorage fluids = new FluidStorage().onChange(this::setChanged); public final List lanes; public final FeatureHost features = @@ -92,45 +91,30 @@ public ProcessorEntity(BlockEntityType type, BlockPos pos, BlockState state, if (kind == Kind.LOGISTICS) for (int i = 0; i < 4; i++) fluids.addTank(4000, i < 2 ? Role.INPUT : Role.OUTPUT); int[] outputSlots = kind == Kind.CRUSHER ? new int[] {1, 2} : new int[] {1}; - var inventory = MachineInventory.builder().group("energy", StorageGroups.energy(energy)); + resources = new MachineResources(this, this::facing); if (kind == Kind.TRANSMUTER) { // One key, and one item: the recipe consumes the whole slot before inserting gold. items = new ItemStorage(1).limit(1, 0).onChange(this::setChanged); - inventory.group("slot", StorageGroups.items(items, 0)); } else { items = new ItemStorage(1 + outputSlots.length) .role(Role.INPUT, 0) .role(kind == Kind.CRUSHER ? Role.STORAGE : Role.OUTPUT, outputSlots) .onChange(this::setChanged); - inventory - .group("input", StorageGroups.items(items, 0)) - .group("output", StorageGroups.items(items, outputSlots)); } - var shared = inventory.build(); var recipeType = kind == Kind.TRANSMUTER ? CompanionMod.TRANSMUTING : CompanionMod.CRUSHING; - var processors = new ArrayList(); - for (int i = 0; i < (kind == Kind.DUAL ? 2 : 1); i++) { - var processor = - new RecipeProcessor( - "lane_" + i, - recipeType, - shared, - Component.translatable("screen.mmdlib_companion.processor", i + 1)); - processors.add(processor); - features.add(processor); - } - lanes = List.copyOf(processors); - var groups = new ArrayList>(); - groups.add( - itemGroup("input", 0x568bd8, kind == Kind.TRANSMUTER ? IoMode.BOTH : IoMode.INPUT, 0)); + resources.items( + itemGroup("input", 0x568bd8, kind == Kind.TRANSMUTER ? IoMode.BOTH : IoMode.INPUT, 0), + items); if (kind != Kind.TRANSMUTER) - groups.add( + resources.items( itemGroup( - "output", 0xe48f45, kind == Kind.CRUSHER ? IoMode.BOTH : IoMode.OUTPUT, outputSlots)); + "output", 0xe48f45, kind == Kind.CRUSHER ? IoMode.BOTH : IoMode.OUTPUT, outputSlots), + items); + items.slotId(0, "input"); + for (int i = 1; i < items.getSlots(); i++) items.slotId(i, "output_" + i); int[] colours = {0x64bfc1, 0xa678cb, 0xe0c55d, 0xce668e}; for (int i = 0; i < fluids.getTanks(); i++) { - final int tank = i; var group = new ResourceGroup<>( "tank_" + i, @@ -142,10 +126,8 @@ public ProcessorEntity(BlockEntityType type, BlockPos pos, BlockState state, List.of(i == 0 ? "tank" : "tank_" + i), new SideConfig( kind == Kind.CRUSHER ? IoMode.BOTH : (i < 2 ? IoMode.INPUT : IoMode.OUTPUT))); - group.bind( - this::facing, - side -> fluids.view(index -> index == tank ? group.mode(side) : IoMode.DISABLED)); - groups.add(group); + resources.fluids(group, fluids); + fluids.tankId(i, group.id()); } var energyGroup = new ResourceGroup<>( @@ -158,11 +140,31 @@ public ProcessorEntity(BlockEntityType type, BlockPos pos, BlockState state, List.of("energy"), new SideConfig(kind == Kind.LOGISTICS ? IoMode.BOTH : IoMode.INPUT)); if (kind == Kind.LOGISTICS) energy.setRates(1000, 1000); - energyGroup.bind(this::facing, side -> energy.view(() -> energyGroup.mode(side))); - groups.add(energyGroup); - automation = new AutomationInventory(this, groups); + resources.energy(energyGroup, energy); + if (kind == Kind.TRANSMUTER) resources.recipeGroup("slot", "input"); + if (kind != Kind.LOGISTICS) + resources.recipeInputs(recipeType, kind == Kind.TRANSMUTER ? "slot" : "input"); + resources.build(); + automation = resources.automation(); + var processors = new ArrayList(); + for (int i = 0; i < (kind == Kind.DUAL ? 2 : 1); i++) { + var processor = + new RecipeProcessor( + "lane_" + i, + recipeType, + resources.recipes(), + Component.translatable("screen.mmdlib_companion.processor", i + 1)); + processors.add(processor); + features.add(processor); + } + lanes = List.copyOf(processors); + inventoryLifecycle = + new MachineInventoryLifecycle(this, features, automation, energy) + .items("items", items) + .fluids("fluids", fluids) + .energy("energy", energy); var outputParts = new ArrayList(); - for (var group : groups) + for (var group : automation.groups()) outputParts.add( RedstoneOutput.resource( group, @@ -190,6 +192,7 @@ public ProcessorEntity(BlockEntityType type, BlockPos pos, BlockState state, })); if (kind != Kind.LOGISTICS) outputParts.add(features); redstone = new RedstoneControl(this, outputParts); + zone.moddev.mc.lib.gui.runtime.ScreenInventoryValidation.validate(this); } private ResourceGroup itemGroup( @@ -206,9 +209,7 @@ private ResourceGroup itemGroup( .mapToObj(i -> i == 0 ? id : id + "_" + (i + 1)) .toList(), new SideConfig(mode)); - return group.bind( - this::facing, - side -> items.view(index -> group.contains(index) ? group.mode(side) : IoMode.DISABLED)); + return group; } @Override @@ -216,6 +217,11 @@ public RedstoneControl screenRedstone() { return redstone; } + @Override + public FeatureHost screenFeatures() { + return features; + } + public AutomationInventory screenAutomation() { return automation; } @@ -223,12 +229,10 @@ public AutomationInventory screenAutomation() { @Override public void serverTick() { redstone.readInputs(); - features.tick( - level, - kind != Kind.LOGISTICS - && redstoneAllowsRun() - && energy.stored() >= energy.capacity() * reservePercent / 100); - AutomationTransfers.tick(automation, energy); + inventoryLifecycle.tick( + kind != Kind.LOGISTICS && energy.stored() >= energy.capacity() * reservePercent / 100, + !redstoneAllowsRun(), + MachineInventoryLifecycle.PausePolicy.PROCESSING_ONLY); redstone.updateOutputs(); } @@ -251,11 +255,7 @@ private boolean redstoneAllowsRun() { public void toggle(int index) { if (index < 0 || index >= lanes.size()) return; var processor = lanes.get(index); - if (processor.paused()) processor.resume(); - else processor.interrupt(); - // Redstone can hold the feature tick, so persist and sync a manual toggle immediately. - setChanged(); - FeatureSync.send(this); + features.setProcessorPaused(processor.key(), !processor.paused()); } public int getProgress() { @@ -300,10 +300,6 @@ public boolean getPaused() { return lanes.getFirst().paused(); } - public boolean getSecondPaused() { - return lanes.size() == 2 && lanes.get(1).paused(); - } - public List getWarnings() { var warnings = new ArrayList(); if (energy.stored() < energy.capacity() * reservePercent / 100) @@ -338,8 +334,7 @@ public int[] screenRecipeSlots(ResourceLocation category) { @Override public void dropContents(Level level, BlockPos pos) { - items.dropAll(level, pos); - automation.dropAll( + inventoryLifecycle.dropContents( payload -> net.minecraft.world.Containers.dropItemStack( level, @@ -351,28 +346,17 @@ public void dropContents(Level level, BlockPos pos) { @Override public void clearContent() { - automation.clear(); - lanes.forEach(RecipeProcessor::reset); - for (int slot = 0; slot < items.getSlots(); slot++) { - items.setPlan(slot, SlotPlan.NONE); - items.setStackInSlot(slot, ItemStack.EMPTY); - } - energy.setStored(0); - for (int i = 0; i < fluids.getTanks(); i++) fluids.tank(i).setFluid(FluidStack.EMPTY); - setChanged(); + inventoryLifecycle.clear(); FeatureSync.send(this); } @Override protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.saveAdditional(tag, registries); - tag.put("items", items.serializeNBT(registries)); - tag.put("automation", automation.save(registries)); + inventoryLifecycle.save(tag, registries); tag.put("redstone_control", redstone.save(registries)); tag.putString("machine_name", machineName); tag.putInt("reserve_percent", reservePercent); - if (hasTank()) tag.put("fluids", fluids.serializeNBT(registries)); - tag.put("features", features.save(registries.createSerializationContext(NbtOps.INSTANCE))); } @Override @@ -392,11 +376,7 @@ protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) ? savedName.strip() : "Crusher"; reservePercent = Math.clamp(tag.getInt("reserve_percent"), 0, 100); - items.deserializeNBT(registries, tag.getCompound("items")); - automation.load(tag.getCompound("automation"), registries); - if (hasTank() && tag.contains("fluids")) - fluids.deserializeNBT(registries, tag.getCompound("fluids")); - features.load(registries.createSerializationContext(NbtOps.INSTANCE), tag.get("features")); + inventoryLifecycle.load(tag, registries); } @Override diff --git a/src/companion/java/zone/moddev/mc/companion/RecoveryCapsuleItem.java b/src/companion/java/zone/moddev/mc/companion/RecoveryCapsuleItem.java index 333e043e..5397a585 100644 --- a/src/companion/java/zone/moddev/mc/companion/RecoveryCapsuleItem.java +++ b/src/companion/java/zone/moddev/mc/companion/RecoveryCapsuleItem.java @@ -52,7 +52,19 @@ public static boolean restore(ItemStack stack, ProcessorEntity target) { var entry = resources.getCompound(i); var data = entry.getCompound("data"); String type = entry.getString("type"); - if (type.equals(ResourceType.FLUIDS.id().toString())) { + if (type.equals(ResourceType.ITEMS.id().toString())) { + var item = + ItemStack.parseOptional(target.getLevel().registryAccess(), data.getCompound("item")); + if (!item.isEmpty()) { + var remaining = + zone.moddev.mc.lib.api.capabilities.Transfer.insert(target.items, item, false); + if (remaining.getCount() < item.getCount()) { + changed = true; + if (remaining.isEmpty()) data.remove("item"); + else data.put("item", remaining.save(target.getLevel().registryAccess())); + } + } + } else if (type.equals(ResourceType.FLUIDS.id().toString())) { var fluid = FluidStack.parseOptional(target.getLevel().registryAccess(), data.getCompound("fluid")); int moved = target.fluids.fill(fluid, IFluidHandler.FluidAction.EXECUTE); diff --git a/src/companion/resources/mmdlib/screen/base_powered_machine.xml b/src/companion/resources/mmdlib/screen/base_powered_machine.xml new file mode 100644 index 00000000..2a1333a5 --- /dev/null +++ b/src/companion/resources/mmdlib/screen/base_powered_machine.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/companion/resources/mmdlib/screen/crusher.xml b/src/companion/resources/mmdlib/screen/crusher.xml index 22748392..dc15f28d 100644 --- a/src/companion/resources/mmdlib/screen/crusher.xml +++ b/src/companion/resources/mmdlib/screen/crusher.xml @@ -47,10 +47,7 @@ -