diff --git a/src/client/java/aztech/modern_industrialization/compat/viewer/impl/MachineScreenPredicateTest.java b/src/client/java/aztech/modern_industrialization/compat/viewer/impl/MachineScreenPredicateTest.java index cd198d1fe..16b638aaf 100644 --- a/src/client/java/aztech/modern_industrialization/compat/viewer/impl/MachineScreenPredicateTest.java +++ b/src/client/java/aztech/modern_industrialization/compat/viewer/impl/MachineScreenPredicateTest.java @@ -24,24 +24,24 @@ package aztech.modern_industrialization.compat.viewer.impl; import aztech.modern_industrialization.compat.rei.machines.ReiMachineRecipes; -import aztech.modern_industrialization.machines.gui.GuiComponentClient; import aztech.modern_industrialization.machines.gui.MachineScreen; import aztech.modern_industrialization.machines.guicomponents.CraftingMultiblockGuiClient; +import java.util.Optional; public class MachineScreenPredicateTest { public static boolean test(ReiMachineRecipes.MachineScreenPredicate predicate, MachineScreen screen) { return switch (predicate) { case ANY -> true; - case MULTIBLOCK -> { - for (GuiComponentClient client : screen.getMenu().components) { - if (client instanceof CraftingMultiblockGuiClient cmGui) { - if (cmGui.isShapeValid) { - yield true; + case MULTIBLOCK -> screen.getMenu().components.findOrDefault( + client -> { + if (client instanceof CraftingMultiblockGuiClient cmGui) { + if (cmGui.isShapeValid) { + return Optional.of(true); + } } - } - } - yield false; - } + return Optional.empty(); + }, + false); }; } } diff --git a/src/client/java/aztech/modern_industrialization/datagen/model/MachineCasingsProvider.java b/src/client/java/aztech/modern_industrialization/datagen/model/MachineCasingsProvider.java index cabf5cb9f..af7196993 100644 --- a/src/client/java/aztech/modern_industrialization/datagen/model/MachineCasingsProvider.java +++ b/src/client/java/aztech/modern_industrialization/datagen/model/MachineCasingsProvider.java @@ -81,16 +81,16 @@ protected void registerModels() { } private void imitateBlock(MachineCasing casing, Block block) { - getBuilder(casing.name) + getBuilder(casing.key.toString()) .customLoader((bmb, existingFileHelper) -> new UseBlockModelModelBuilder<>(block, bmb, existingFileHelper)); } private void cubeBottomTop(MachineCasing casing, String side, String bottom, String top) { - cubeBottomTop(casing.name, MI.id(side), MI.id(bottom), MI.id(top)); + cubeBottomTop(casing.key.toString(), MI.id(side), MI.id(bottom), MI.id(top)); } private void cubeAll(MachineCasing casing, String side) { - cubeAll(casing.name, MI.id(side)); + cubeAll(casing.key.toString(), MI.id(side)); } @Override diff --git a/src/client/java/aztech/modern_industrialization/machines/MachineBlockEntityRenderer.java b/src/client/java/aztech/modern_industrialization/machines/MachineBlockEntityRenderer.java index 411d0daaa..ef31f3a4d 100644 --- a/src/client/java/aztech/modern_industrialization/machines/MachineBlockEntityRenderer.java +++ b/src/client/java/aztech/modern_industrialization/machines/MachineBlockEntityRenderer.java @@ -69,7 +69,7 @@ private BakedQuad getCachedQuad(MachineModelClientData data, Direction d) { var cachedQuads = quadCache.computeIfAbsent(casing, c -> new Object[36]); if (cachedQuads[cachedQuadIndex] == null) { - TextureAtlasSprite sprite = model == null ? null : MachineBakedModel.getSprite(model.getSprites(casing), d, facing, true); + TextureAtlasSprite sprite = model == null ? null : model.getSprite(model.getSprites(casing), d, facing, true); if (sprite != null) { var vc = new QuadBakingVertexConsumer(); cachedQuads[cachedQuadIndex] = ModelHelper.bakeSprite(vc, d, sprite, -2 * MachineBakedModel.Z_OFFSET); diff --git a/src/client/java/aztech/modern_industrialization/machines/gui/MachineMenuClient.java b/src/client/java/aztech/modern_industrialization/machines/gui/MachineMenuClient.java index 694019608..a4bee1d3a 100644 --- a/src/client/java/aztech/modern_industrialization/machines/gui/MachineMenuClient.java +++ b/src/client/java/aztech/modern_industrialization/machines/gui/MachineMenuClient.java @@ -27,6 +27,7 @@ import aztech.modern_industrialization.inventory.ConfigurableItemStack; import aztech.modern_industrialization.inventory.MIInventory; import aztech.modern_industrialization.inventory.SlotPositions; +import aztech.modern_industrialization.machines.ComponentStorage; import aztech.modern_industrialization.machines.GuiComponentsClient; import aztech.modern_industrialization.util.NbtHelper; import java.util.ArrayList; @@ -52,11 +53,11 @@ public static MachineMenuClient create(int syncId, Inventory playerInventory, Re SlotPositions fluidPositions = SlotPositions.read(buf); MIInventory inventory = new MIInventory(itemStacks, fluidStacks, itemPositions, fluidPositions); // Components - List components = new ArrayList<>(); + ComponentStorage components = new ComponentStorage<>(); int componentCount = buf.readInt(); for (int i = 0; i < componentCount; ++i) { ResourceLocation id = buf.readResourceLocation(); - components.add(GuiComponentsClient.get(id).createFromInitialData(buf)); + components.register(GuiComponentsClient.get(id).createFromInitialData(buf)); } // GUI params MachineGuiParameters guiParams = MachineGuiParameters.read(buf); @@ -64,9 +65,9 @@ public static MachineMenuClient create(int syncId, Inventory playerInventory, Re return new MachineMenuClient(syncId, playerInventory, inventory, components, guiParams); } - public final List components; + public final ComponentStorage components; - private MachineMenuClient(int syncId, Inventory playerInventory, MIInventory inventory, List components, + private MachineMenuClient(int syncId, Inventory playerInventory, MIInventory inventory, ComponentStorage components, MachineGuiParameters guiParams) { super(syncId, playerInventory, inventory, guiParams, components); this.components = components; @@ -74,12 +75,7 @@ private MachineMenuClient(int syncId, Inventory playerInventory, MIInventory inv @Nullable public T getComponent(Class klass) { - for (GuiComponentClient component : components) { - if (klass.isInstance(component)) { - return (T) component; - } - } - return null; + return components.get(klass).orElse(null); } @Override diff --git a/src/client/java/aztech/modern_industrialization/machines/gui/MachineScreen.java b/src/client/java/aztech/modern_industrialization/machines/gui/MachineScreen.java index ab75a6164..27b22c9ef 100644 --- a/src/client/java/aztech/modern_industrialization/machines/gui/MachineScreen.java +++ b/src/client/java/aztech/modern_industrialization/machines/gui/MachineScreen.java @@ -64,9 +64,7 @@ public class MachineScreen extends MIHandledScreen implements public MachineScreen(MachineMenuClient handler, Inventory inventory, Component title) { super(handler, inventory, title); - for (GuiComponentClient component : handler.components) { - renderers.add(component.createRenderer(this)); - } + handler.components.forEach(component -> renderers.add(component.createRenderer(this))); this.imageHeight = handler.guiParams.backgroundHeight; this.imageWidth = handler.guiParams.backgroundWidth; @@ -211,10 +209,15 @@ protected void renderBg(GuiGraphics guiGraphics, float delta, int mouseX, int mo private void renderConfigurableSlotBackgrounds(GuiGraphics guiGraphics) { for (Slot slot : this.menu.slots) { - if (slot instanceof BackgroundRenderedSlot brs) { + if (slot.isActive() && slot instanceof BackgroundRenderedSlot brs) { int px = leftPos + slot.x - 1; int py = topPos + slot.y - 1; - guiGraphics.blit(SLOT_ATLAS, px, py, brs.getBackgroundU(), brs.getBackgroundV(), 18, 18); + if (slot.getItem().isEmpty()) { + var atlas = brs.getBackgroundAtlasLocation(); + guiGraphics.blit(atlas == null ? SLOT_ATLAS : atlas, px, py, brs.getBackgroundU(), brs.getBackgroundV(), 18, 18); + } else { + guiGraphics.blit(SLOT_ATLAS, px, py, 0, 0, 18, 18); + } } } } diff --git a/src/client/java/aztech/modern_industrialization/machines/models/MachineBakedModel.java b/src/client/java/aztech/modern_industrialization/machines/models/MachineBakedModel.java index 8012f6007..ce537a945 100644 --- a/src/client/java/aztech/modern_industrialization/machines/models/MachineBakedModel.java +++ b/src/client/java/aztech/modern_industrialization/machines/models/MachineBakedModel.java @@ -23,7 +23,6 @@ */ package aztech.modern_industrialization.machines.models; -import aztech.modern_industrialization.MI; import aztech.modern_industrialization.util.ModelHelper; import java.util.ArrayList; import java.util.List; @@ -39,6 +38,7 @@ import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; @@ -57,7 +57,8 @@ public class MachineBakedModel implements IDynamicBakedModel { public static final String CASING_FOLDER = "machine_casing"; public static ModelResourceLocation getCasingModelId(MachineCasing casing) { - return ModelResourceLocation.standalone(MI.id(CASING_FOLDER + "/" + casing.name)); + return ModelResourceLocation + .standalone(ResourceLocation.fromNamespaceAndPath(casing.key.getNamespace(), CASING_FOLDER + "/" + casing.key.getPath())); } public static BakedModel getCasingModel(MachineCasing casing) { @@ -65,14 +66,16 @@ public static BakedModel getCasingModel(MachineCasing casing) { } private final MachineCasing baseCasing; + private final int[] outputOverlayIndexes; private final TextureAtlasSprite[] defaultOverlays; - private final Map tieredOverlays; + private final Map tieredOverlays; private final MachineModelClientData defaultData; - MachineBakedModel(MachineCasing baseCasing, - TextureAtlasSprite[] defaultOverlays, - Map tieredOverlays) { + public MachineBakedModel(MachineCasing baseCasing, + int[] outputOverlayIndexes, TextureAtlasSprite[] defaultOverlays, + Map tieredOverlays) { this.baseCasing = baseCasing; + this.outputOverlayIndexes = outputOverlayIndexes; this.defaultOverlays = defaultOverlays; this.tieredOverlays = tieredOverlays; this.defaultData = new MachineModelClientData(baseCasing, Direction.NORTH); @@ -86,14 +89,14 @@ public TextureAtlasSprite[] getSprites(@Nullable MachineCasing casing) { if (casing == null) { return defaultOverlays; } - return tieredOverlays.getOrDefault(casing.name, defaultOverlays); + return tieredOverlays.getOrDefault(casing.key, defaultOverlays); } /** * Returns null if nothing should be rendered. */ @Nullable - public static TextureAtlasSprite getSprite(TextureAtlasSprite[] sprites, Direction side, Direction facingDirection, boolean isActive) { + public TextureAtlasSprite getSprite(TextureAtlasSprite[] sprites, Direction side, Direction facingDirection, boolean isActive) { int spriteId; if (side.getAxis().isHorizontal()) { spriteId = (facingDirection.get2DDataValue() - side.get2DDataValue() + 4) % 4 * 2; @@ -121,44 +124,50 @@ public ModelData getModelData(BlockAndTintGetter level, BlockPos pos, BlockState return getCasingModel(casing).getModelData(level, pos, state, modelData); } - @Override - public @NotNull List getQuads(@Nullable BlockState state, @Nullable Direction side, @NotNull RandomSource rand, - @NotNull ModelData extraData, @Nullable RenderType renderType) { - var data = extraData.get(MachineModelClientData.KEY); - if (data == null) { - data = defaultData; - } - - MachineCasing casing = Objects.requireNonNullElse(data.casing, baseCasing); - var sprites = getSprites(casing); - + protected @NotNull List getQuads(@Nullable BlockState state, @Nullable Direction side, @NotNull RandomSource rand, + @NotNull ModelData extraData, @Nullable RenderType renderType, + @NotNull MachineModelClientData data, @NotNull MachineCasing casing, + @NotNull TextureAtlasSprite[] sprites, @NotNull QuadBakingVertexConsumer vertexConsumer) { List quads = new ArrayList<>(); - var vc = new QuadBakingVertexConsumer(); if (side != null) { - // Casing quads.addAll(getCasingModel(casing).getQuads(state, side, rand, extraData, renderType)); - // Machine overlays + TextureAtlasSprite sprite = getSprite(sprites, side, data.frontDirection, false); if (sprite != null) { - quads.add(ModelHelper.bakeSprite(vc, side, sprite, -Z_OFFSET)); + quads.add(ModelHelper.bakeSprite(vertexConsumer, side, sprite, -Z_OFFSET)); } } - // Output overlays if (data.outputDirection != null && side == data.outputDirection) { - quads.add(ModelHelper.bakeSprite(vc, data.outputDirection, sprites[24], -3 * Z_OFFSET)); + quads.add(ModelHelper.bakeSprite(vertexConsumer, data.outputDirection, sprites[outputOverlayIndexes[0]], -3 * Z_OFFSET)); if (data.itemAutoExtract) { - quads.add(ModelHelper.bakeSprite(vc, data.outputDirection, sprites[25], -3 * Z_OFFSET)); + quads.add(ModelHelper.bakeSprite(vertexConsumer, data.outputDirection, sprites[outputOverlayIndexes[1]], -3 * Z_OFFSET)); } if (data.fluidAutoExtract) { - quads.add(ModelHelper.bakeSprite(vc, data.outputDirection, sprites[26], -3 * Z_OFFSET)); + quads.add(ModelHelper.bakeSprite(vertexConsumer, data.outputDirection, sprites[outputOverlayIndexes[2]], -3 * Z_OFFSET)); } } return quads; } + @Override + public @NotNull List getQuads(@Nullable BlockState state, @Nullable Direction side, @NotNull RandomSource rand, + @NotNull ModelData extraData, @Nullable RenderType renderType) { + var data = extraData.get(MachineModelClientData.KEY); + if (data == null) { + data = defaultData; + } + + MachineCasing casing = Objects.requireNonNullElse(data.casing, baseCasing); + var sprites = getSprites(casing); + + var vc = new QuadBakingVertexConsumer(); + + return getQuads(state, side, rand, extraData, renderType, data, casing, sprites, vc); + } + @Override public boolean useAmbientOcclusion() { return true; diff --git a/src/client/java/aztech/modern_industrialization/machines/models/MachineModelBaker.java b/src/client/java/aztech/modern_industrialization/machines/models/MachineModelBaker.java new file mode 100644 index 000000000..ebe775847 --- /dev/null +++ b/src/client/java/aztech/modern_industrialization/machines/models/MachineModelBaker.java @@ -0,0 +1,34 @@ +/* + * MIT License + * + * Copyright (c) 2020 Azercoco & Technici4n + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package aztech.modern_industrialization.machines.models; + +import java.util.Map; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.resources.ResourceLocation; + +public interface MachineModelBaker { + MachineBakedModel bake(MachineCasing baseCasing, + int[] outputOverlayIndexes, TextureAtlasSprite[] defaultOverlays, + Map tieredOverlays); +} diff --git a/src/client/java/aztech/modern_industrialization/machines/models/MachineOverlaysJson.java b/src/client/java/aztech/modern_industrialization/machines/models/MachineOverlaysJson.java new file mode 100644 index 000000000..4ea8cca2e --- /dev/null +++ b/src/client/java/aztech/modern_industrialization/machines/models/MachineOverlaysJson.java @@ -0,0 +1,70 @@ +/* + * MIT License + * + * Copyright (c) 2020 Azercoco & Technici4n + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package aztech.modern_industrialization.machines.models; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import java.lang.reflect.Field; +import net.minecraft.client.resources.model.Material; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.inventory.InventoryMenu; + +public interface MachineOverlaysJson { + Material[] toSpriteIds(); + + int[] getOutputSpriteIndexes(); + + Gson GSON = new GsonBuilder().registerTypeAdapter(ResourceLocation.class, new ResourceLocation.Serializer()).create(); + + static O parse(Class clazz, JsonObject json, MachineOverlaysJson defaultOverlay) { + O overlays = GSON.fromJson(json, clazz); + + if (defaultOverlay != null) { + try { + for (Field field : clazz.getDeclaredFields()) { + if (field.get(overlays) == null) { + field.set(overlays, field.get(defaultOverlay)); + } + } + } catch (IllegalAccessException ex) { + throw new RuntimeException("Failed to copy fields from default overlay", ex); + } + } + + return overlays; + } + + /** + * Select first non-null id, and convert it to a sprite id. + */ + default Material select(ResourceLocation... candidates) { + for (ResourceLocation id : candidates) { + if (id != null) { + return new Material(InventoryMenu.BLOCK_ATLAS, id); + } + } + return null; + } +} diff --git a/src/client/java/aztech/modern_industrialization/machines/models/MachineUnbakedModel.java b/src/client/java/aztech/modern_industrialization/machines/models/MachineUnbakedModel.java index 15a5ddd01..3029f47a8 100644 --- a/src/client/java/aztech/modern_industrialization/machines/models/MachineUnbakedModel.java +++ b/src/client/java/aztech/modern_industrialization/machines/models/MachineUnbakedModel.java @@ -24,8 +24,6 @@ package aztech.modern_industrialization.machines.models; import aztech.modern_industrialization.MI; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import java.util.HashMap; import java.util.Map; @@ -38,34 +36,36 @@ import net.minecraft.client.resources.model.ModelState; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.GsonHelper; -import net.minecraft.world.inventory.InventoryMenu; import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import org.jetbrains.annotations.Nullable; -public class MachineUnbakedModel implements IUnbakedGeometry { +public class MachineUnbakedModel implements IUnbakedGeometry> { public static final ResourceLocation LOADER_ID = MI.id("machine"); - public static final IGeometryLoader LOADER = (jsonObject, deserializationContext) -> { - return new MachineUnbakedModel(jsonObject); + public static final IGeometryLoader> LOADER = (jsonObject, deserializationContext) -> { + return new MachineUnbakedModel(OverlaysJson.class, MachineBakedModel::new, jsonObject); }; - private static final Gson GSON = new GsonBuilder().registerTypeAdapter(ResourceLocation.class, new ResourceLocation.Serializer()).create(); - + private final MachineModelBaker modelBaker; private final MachineCasing baseCasing; + private final int[] outputOverlayIndexes; private final Material[] defaultOverlays; - private final Map tieredOverlays = new HashMap<>(); + private final Map tieredOverlays = new HashMap<>(); + + public MachineUnbakedModel(Class overlayClass, MachineModelBaker modelBaker, JsonObject obj) { + this.modelBaker = modelBaker; - private MachineUnbakedModel(JsonObject obj) { this.baseCasing = MachineCasings.get(GsonHelper.getAsString(obj, "casing")); - var defaultOverlaysJson = OverlaysJson.parse(GsonHelper.getAsJsonObject(obj, "default_overlays"), null); + var defaultOverlaysJson = MachineOverlaysJson.parse(overlayClass, GsonHelper.getAsJsonObject(obj, "default_overlays"), null); + this.outputOverlayIndexes = defaultOverlaysJson.getOutputSpriteIndexes(); this.defaultOverlays = defaultOverlaysJson.toSpriteIds(); var tieredOverlays = GsonHelper.getAsJsonObject(obj, "tiered_overlays", new JsonObject()); for (var casingTier : tieredOverlays.keySet()) { - var casingOverlaysJson = OverlaysJson.parse(GsonHelper.getAsJsonObject(tieredOverlays, casingTier), defaultOverlaysJson); - this.tieredOverlays.put(casingTier, casingOverlaysJson.toSpriteIds()); + var casingOverlaysJson = MachineOverlaysJson.parse(overlayClass, GsonHelper.getAsJsonObject(tieredOverlays, casingTier), + defaultOverlaysJson); + this.tieredOverlays.put(ResourceLocation.parse(casingTier), casingOverlaysJson.toSpriteIds()); } } @@ -73,11 +73,11 @@ private MachineUnbakedModel(JsonObject obj) { public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, ItemOverrides overrides) { var defaultOverlays = loadSprites(spriteGetter, this.defaultOverlays); - var tieredOverlays = new HashMap(); + var tieredOverlays = new HashMap(); for (var entry : this.tieredOverlays.entrySet()) { tieredOverlays.put(entry.getKey(), loadSprites(spriteGetter, entry.getValue())); } - return new MachineBakedModel(baseCasing, defaultOverlays, tieredOverlays); + return modelBaker.bake(baseCasing, outputOverlayIndexes, defaultOverlays, tieredOverlays); } private static TextureAtlasSprite[] loadSprites(Function textureGetter, Material[] ids) { @@ -90,7 +90,7 @@ private static TextureAtlasSprite[] loadSprites(Function Component.literal("Successfully built multiblock at position %s. %d blocks updated.".formatted( diff --git a/src/main/java/aztech/modern_industrialization/inventory/BackgroundRenderedSlot.java b/src/main/java/aztech/modern_industrialization/inventory/BackgroundRenderedSlot.java index f0cb1f6a3..7a0af9d53 100644 --- a/src/main/java/aztech/modern_industrialization/inventory/BackgroundRenderedSlot.java +++ b/src/main/java/aztech/modern_industrialization/inventory/BackgroundRenderedSlot.java @@ -23,10 +23,21 @@ */ package aztech.modern_industrialization.inventory; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; + /** * Implement this on a slot to render its background automatically. */ public interface BackgroundRenderedSlot { + /** + * @return the {@link ResourceLocation} of the slot atlas texture to use for the slot background. When null, uses the MI slot atlas + */ + @Nullable + default ResourceLocation getBackgroundAtlasLocation() { + return null; + } + default int getBackgroundU() { return 0; } diff --git a/src/main/java/aztech/modern_industrialization/machines/ComponentStorage.java b/src/main/java/aztech/modern_industrialization/machines/ComponentStorage.java new file mode 100644 index 000000000..d174f62d6 --- /dev/null +++ b/src/main/java/aztech/modern_industrialization/machines/ComponentStorage.java @@ -0,0 +1,136 @@ +/* + * MIT License + * + * Copyright (c) 2020 Azercoco & Technici4n + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package aztech.modern_industrialization.machines; + +import aztech.modern_industrialization.machines.gui.GuiComponent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import net.minecraft.resources.ResourceLocation; + +public sealed class ComponentStorage permits ComponentStorage.GuiServer, ComponentStorage.Server { + protected final List components = new ArrayList<>(); + + @SafeVarargs + public final void register(C... components) { + Collections.addAll(this.components, components); + } + + @SafeVarargs + public final void unregister(C... components) { + for (C component : components) { + this.components.remove(component); + } + } + + public final int size() { + return components.size(); + } + + public final C get(int index) { + return components.get(index); + } + + public final void forEach(Consumer action) { + components.forEach(action); + } + + public final void forEachIndexed(BiConsumer action) { + for (int i = 0; i < components.size(); i++) { + action.accept(i, components.get(i)); + } + } + + public final Optional get(Class clazz) { + for (C component : components) { + if (clazz.isInstance(component)) { + return Optional.of((T) component); + } + } + return Optional.empty(); + } + + public final List tryGet(Class clazz) { + List components = new ArrayList<>(); + for (C component : this.components) { + if (clazz.isInstance(component)) { + components.add((T) component); + } + } + return components; + } + + public final void forType(Class clazz, Consumer action) { + List component = tryGet(clazz); + for (T c : component) { + action.accept(c); + } + } + + public final R mapOrDefault(Class clazz, Function action, R defaultValue) { + List components = tryGet(clazz); + if (components.isEmpty()) { + return defaultValue; + } else if (components.size() == 1) { + return action.apply(components.get(0)); + } else { + throw new RuntimeException("Multiple components of type " + clazz.getName() + " found"); + } + } + + public final R findOrDefault(Function> action, R defaultValue) { + for (C component : components) { + Optional result = action.apply(component); + if (result.isPresent()) { + return result.get(); + } + } + return defaultValue; + } + + public final R findOrDefault(Class clazz, Function> action, R defaultValue) { + return findOrDefault(component -> clazz.isInstance(component) ? action.apply((T) component) : Optional.empty(), defaultValue); + } + + public static final class GuiServer extends ComponentStorage { + /** + * @throws RuntimeException if the component doesn't exist. + */ + public S get(ResourceLocation componentId) { + for (GuiComponent.Server component : components) { + if (component.getId().equals(componentId)) { + return (S) component; + } + } + throw new RuntimeException("Couldn't find component " + componentId); + } + } + + public static final class Server extends ComponentStorage { + } +} diff --git a/src/main/java/aztech/modern_industrialization/machines/MachineBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/MachineBlockEntity.java index 026f30410..c9994ef50 100644 --- a/src/main/java/aztech/modern_industrialization/machines/MachineBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/MachineBlockEntity.java @@ -37,10 +37,7 @@ import aztech.modern_industrialization.util.NbtHelper; import aztech.modern_industrialization.util.WorldHelper; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; import net.minecraft.Util; import net.minecraft.core.Direction; import net.minecraft.core.HolderLookup; @@ -50,7 +47,6 @@ 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.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.ItemInteractionResult; @@ -75,8 +71,8 @@ @SuppressWarnings("rawtypes") public abstract class MachineBlockEntity extends FastBlockEntity implements MenuProvider, WrenchableBlockEntity { - public final List guiComponents = new ArrayList<>(); - private final List icomponents = new ArrayList<>(); + protected final ComponentStorage.GuiServer guiComponents = new ComponentStorage.GuiServer(); + protected final ComponentStorage.Server icomponents = new ComponentStorage.Server(); public final MachineGuiParameters guiParams; /** * Server-side only: true if the next call to sync() will trigger a remesh. @@ -101,11 +97,11 @@ public MachineBlockEntity(BEP bep, MachineGuiParameters guiParams, OrientationCo } protected final void registerGuiComponent(GuiComponent.Server... components) { - Collections.addAll(guiComponents, components); + guiComponents.register(components); } protected final void registerComponents(IComponent... components) { - Collections.addAll(icomponents, components); + icomponents.register(components); } /** @@ -113,45 +109,12 @@ protected final void registerComponents(IComponent... components) { */ public abstract MIInventory getInventory(); - /** - * @throws RuntimeException if the component doesn't exist. - */ - @SuppressWarnings("unchecked") - public S getComponent(ResourceLocation componentId) { - for (GuiComponent.Server component : guiComponents) { - if (component.getId().equals(componentId)) { - return (S) component; - } - } - throw new RuntimeException("Couldn't find component " + componentId); - } - - private List tryGetComponent(Class clazz) { - List components = new ArrayList<>(); - for (var component : icomponents) { - if (clazz.isInstance(component)) { - components.add((T) component); - } - } - return components; + public final ComponentStorage.GuiServer getGuiComponents() { + return guiComponents; } - public final void forComponentType(Class clazz, Consumer action) { - List component = tryGetComponent(clazz); - for (T c : component) { - action.accept(c); - } - } - - public R mapComponentOrDefault(Class clazz, Function action, R defaultValue) { - List components = tryGetComponent(clazz); - if (components.isEmpty()) { - return defaultValue; - } else if (components.size() == 1) { - return action.apply(components.get(0)); - } else { - throw new RuntimeException("Multiple components of type " + clazz.getName() + " found"); - } + public final ComponentStorage.Server getComponents() { + return icomponents; } @Override @@ -176,10 +139,10 @@ public final void writeScreenOpeningData(RegistryFriendlyByteBuf buf) { inv.fluidPositions.write(buf); buf.writeInt(guiComponents.size()); // Write components - for (GuiComponent.Server component : guiComponents) { + guiComponents.forEach(component -> { buf.writeResourceLocation(component.getId()); component.writeInitialData(buf); - } + }); // Write GUI params guiParams.write(buf); } @@ -238,17 +201,13 @@ public CompoundTag getUpdateTag(HolderLookup.Provider registries) { CompoundTag tag = new CompoundTag(); tag.putBoolean("remesh", syncCausesRemesh); syncCausesRemesh = false; - for (IComponent component : icomponents) { - component.writeClientNbt(tag, registries); - } + icomponents.forEach(component -> component.writeClientNbt(tag, registries)); return tag; } @Override public final void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { - for (IComponent component : icomponents) { - component.writeNbt(tag, registries); - } + icomponents.forEach(component -> component.writeNbt(tag, registries)); } @Override @@ -258,14 +217,10 @@ public final void loadAdditional(CompoundTag tag, HolderLookup.Provider registri public final void load(CompoundTag tag, HolderLookup.Provider registries, boolean isUpgradingMachine) { if (!tag.contains("remesh")) { - for (IComponent component : icomponents) { - component.readNbt(tag, registries, isUpgradingMachine); - } + icomponents.forEach(component -> component.readNbt(tag, registries, isUpgradingMachine)); } else { boolean forceChunkRemesh = tag.getBoolean("remesh"); - for (IComponent component : icomponents) { - component.readClientNbt(tag, registries); - } + icomponents.forEach(component -> component.readClientNbt(tag, registries)); if (forceChunkRemesh) { WorldHelper.forceChunkRemesh(level, worldPosition); requestModelDataUpdate(); @@ -300,7 +255,7 @@ public static void registerFluidApi(BlockEntityType bet) { public List dropExtra() { List drops = new ArrayList<>(); - forComponentType(DropableComponent.class, u -> drops.add(u.getDrop())); + icomponents.forType(DropableComponent.class, u -> drops.add(u.getDrop())); return drops; } diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/hatches/EnergyHatch.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/hatches/EnergyHatch.java index 94805acf5..ce499efc1 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/hatches/EnergyHatch.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/hatches/EnergyHatch.java @@ -25,6 +25,7 @@ import aztech.modern_industrialization.MICapabilities; import aztech.modern_industrialization.api.energy.CableTier; +import aztech.modern_industrialization.api.energy.CableTierHolder; import aztech.modern_industrialization.api.energy.EnergyApi; import aztech.modern_industrialization.api.energy.MIEnergyStorage; import aztech.modern_industrialization.api.machine.holder.EnergyComponentHolder; @@ -39,12 +40,13 @@ import java.util.List; import net.minecraft.world.level.block.entity.BlockEntityType; -public class EnergyHatch extends HatchBlockEntity implements EnergyComponentHolder { +public class EnergyHatch extends HatchBlockEntity implements EnergyComponentHolder, CableTierHolder { public EnergyHatch(BEP bep, String name, boolean input, CableTier tier) { super(bep, new MachineGuiParameters.Builder(name, false).build(), new OrientationComponent.Params(!input, false, false)); this.input = input; + this.tier = tier; this.energy = new EnergyComponent(this, 30 * 20 * tier.getEu()); insertable = energy.buildInsertable((CableTier tier2) -> tier2 == tier); @@ -55,7 +57,8 @@ public EnergyHatch(BEP bep, String name, boolean input, CableTier tier) { this.registerComponents(energy); } - private final boolean input; + protected final boolean input; + protected final CableTier tier; protected final EnergyComponent energy; protected final MIEnergyStorage insertable; @@ -71,6 +74,11 @@ public boolean upgradesToSteel() { return false; } + @Override + public CableTier getCableTier() { + return tier; + } + @Override public MIInventory getInventory() { return MIInventory.EMPTY; diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/AbstractCraftingMultiblockBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/AbstractCraftingMultiblockBlockEntity.java index 530d0b7ae..e36ffcd0a 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/AbstractCraftingMultiblockBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/AbstractCraftingMultiblockBlockEntity.java @@ -32,10 +32,8 @@ import aztech.modern_industrialization.machines.guicomponents.ReiSlotLocking; import aztech.modern_industrialization.machines.models.MachineModelClientData; import aztech.modern_industrialization.machines.multiblocks.MultiblockMachineBlockEntity; -import aztech.modern_industrialization.machines.multiblocks.ShapeMatcher; import aztech.modern_industrialization.machines.multiblocks.ShapeTemplate; import aztech.modern_industrialization.util.Tickable; -import org.jetbrains.annotations.Nullable; public abstract class AbstractCraftingMultiblockBlockEntity extends MultiblockMachineBlockEntity implements Tickable, MultiblockInventoryComponentHolder, CrafterComponentHolder { @@ -56,8 +54,6 @@ public AbstractCraftingMultiblockBlockEntity(BEP bep, String name, OrientationCo */ protected abstract CrafterComponent.Behavior getBehavior(); - @Nullable - private ShapeMatcher shapeMatcher = null; private OperatingState operatingState = OperatingState.NOT_MATCHED; protected final ActiveShapeComponent activeShape; @@ -65,8 +61,6 @@ public AbstractCraftingMultiblockBlockEntity(BEP bep, String name, OrientationCo protected final CrafterComponent crafter; private final IsActiveComponent isActive; - protected abstract void onSuccessfulMatch(ShapeMatcher shapeMatcher); - public ShapeTemplate getActiveShape() { return activeShape.getActiveShape(); } @@ -121,37 +115,15 @@ public void tickExtra() { } - protected final void link() { - if (shapeMatcher == null) { - shapeMatcher = new ShapeMatcher(level, worldPosition, orientation.facingDirection, getActiveShape()); - shapeMatcher.registerListeners(level); - } - if (shapeMatcher.needsRematch()) { - operatingState = OperatingState.NOT_MATCHED; - shapeValid.shapeValid = false; - shapeMatcher.rematch(level); - - if (shapeMatcher.isMatchSuccessful()) { - inventory.rebuild(shapeMatcher); - - onSuccessfulMatch(shapeMatcher); - shapeValid.shapeValid = true; - operatingState = OperatingState.TRYING_TO_RESUME; - } - - if (shapeValid.update()) { - sync(false); - } - } + @Override + protected void onRematch() { + operatingState = OperatingState.NOT_MATCHED; } @Override - public final void unlink() { - if (shapeMatcher != null) { - shapeMatcher.unlinkHatches(); - shapeMatcher.unregisterListeners(level); - shapeMatcher = null; - } + protected void onMatchSuccessful() { + inventory.rebuild(shapeMatcher); + operatingState = OperatingState.TRYING_TO_RESUME; } private enum OperatingState { diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/AbstractElectricCraftingMultiblockBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/AbstractElectricCraftingMultiblockBlockEntity.java index 1b564feaf..764b600d0 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/AbstractElectricCraftingMultiblockBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/AbstractElectricCraftingMultiblockBlockEntity.java @@ -28,7 +28,6 @@ import aztech.modern_industrialization.machines.components.*; import aztech.modern_industrialization.machines.guicomponents.CraftingMultiblockGui; import aztech.modern_industrialization.machines.multiblocks.HatchBlockEntity; -import aztech.modern_industrialization.machines.multiblocks.ShapeMatcher; import aztech.modern_industrialization.machines.multiblocks.ShapeTemplate; import aztech.modern_industrialization.util.Simulation; import java.util.ArrayList; @@ -62,7 +61,9 @@ public List getEnergyComponents() { } @Override - protected void onSuccessfulMatch(ShapeMatcher shapeMatcher) { + protected void onMatchSuccessful() { + super.onMatchSuccessful(); + energyInputs.clear(); for (HatchBlockEntity hatch : shapeMatcher.getMatchedHatches()) { hatch.appendEnergyInputs(energyInputs); @@ -76,13 +77,13 @@ protected ItemInteractionResult useItemOn(Player player, InteractionHand hand, D result = LubricantHelper.onUse(this.crafter, player, hand); } if (!result.consumesAction()) { - result = mapComponentOrDefault(UpgradeComponent.class, upgrade -> upgrade.onUse(this, player, hand), result); + result = icomponents.mapOrDefault(UpgradeComponent.class, upgrade -> upgrade.onUse(this, player, hand), result); } if (!result.consumesAction()) { result = redstoneControl.onUse(this, player, hand); } if (!result.consumesAction()) { - result = mapComponentOrDefault(OverdriveComponent.class, overdrive -> overdrive.onUse(this, player, hand), result); + result = icomponents.mapOrDefault(OverdriveComponent.class, overdrive -> overdrive.onUse(this, player, hand), result); } return result; } diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/GeneratorMultiblockBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/GeneratorMultiblockBlockEntity.java index 196047c34..9bfb1b2af 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/GeneratorMultiblockBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/GeneratorMultiblockBlockEntity.java @@ -33,14 +33,12 @@ import aztech.modern_industrialization.machines.models.MachineModelClientData; import aztech.modern_industrialization.machines.multiblocks.HatchBlockEntity; import aztech.modern_industrialization.machines.multiblocks.MultiblockMachineBlockEntity; -import aztech.modern_industrialization.machines.multiblocks.ShapeMatcher; import aztech.modern_industrialization.machines.multiblocks.ShapeTemplate; import aztech.modern_industrialization.util.Simulation; import aztech.modern_industrialization.util.Tickable; import java.util.ArrayList; import java.util.List; import net.minecraft.network.chat.Component; -import org.jetbrains.annotations.Nullable; public class GeneratorMultiblockBlockEntity extends MultiblockMachineBlockEntity implements Tickable, EnergyListComponentHolder, MultiblockInventoryComponentHolder { @@ -64,8 +62,6 @@ public GeneratorMultiblockBlockEntity(BEP bep, registerGuiComponent(new SlotPanel.Server(this).withRedstoneControl(redstoneControl)); } - @Nullable - private ShapeMatcher shapeMatcher = null; private boolean allowNormalOperation = false; private final ActiveShapeComponent activeShape; @@ -89,13 +85,6 @@ public MultiblockInventoryComponent getMultiblockInventoryComponent() { return inventory; } - protected void onSuccessfulMatch(ShapeMatcher shapeMatcher) { - energyOutputs.clear(); - for (HatchBlockEntity hatch : shapeMatcher.getMatchedHatches()) { - hatch.appendEnergyOutputs(energyOutputs); - } - } - @Override public final MIInventory getInventory() { return MIInventory.EMPTY; @@ -143,36 +132,19 @@ public long insertEnergy(long value, Simulation simulation) { return inserted; } - protected final void link() { - if (shapeMatcher == null) { - shapeMatcher = new ShapeMatcher(level, worldPosition, orientation.facingDirection, getActiveShape()); - shapeMatcher.registerListeners(level); - } - if (shapeMatcher.needsRematch()) { - allowNormalOperation = false; - shapeValid.shapeValid = false; - shapeMatcher.rematch(level); - - if (shapeMatcher.isMatchSuccessful()) { - inventory.rebuild(shapeMatcher); - - onSuccessfulMatch(shapeMatcher); - shapeValid.shapeValid = true; - allowNormalOperation = true; - } - - if (shapeValid.update()) { - sync(false); - } - } + @Override + protected void onRematch() { + allowNormalOperation = false; } @Override - public final void unlink() { - if (shapeMatcher != null) { - shapeMatcher.unlinkHatches(); - shapeMatcher.unregisterListeners(level); - shapeMatcher = null; + protected void onMatchSuccessful() { + inventory.rebuild(shapeMatcher); + allowNormalOperation = true; + + energyOutputs.clear(); + for (HatchBlockEntity hatch : shapeMatcher.getMatchedHatches()) { + hatch.appendEnergyOutputs(energyOutputs); } } diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java index b34af8ff4..b244851c1 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/LargeTankMultiblockBlockEntity.java @@ -54,7 +54,6 @@ import net.neoforged.neoforge.fluids.FluidType; import net.neoforged.neoforge.fluids.capability.IFluidHandler; import net.neoforged.neoforge.fluids.capability.templates.EmptyFluidHandler; -import org.jetbrains.annotations.Nullable; public class LargeTankMultiblockBlockEntity extends MultiblockMachineBlockEntity implements Tickable, FluidStorageComponentHolder { @@ -148,9 +147,6 @@ private static ShapeTemplate buildShape(int index) { return templateBuilder.build(); } - @Nullable - private ShapeMatcher shapeMatcher = null; - private final ActiveShapeComponent activeShape; private final FluidStorageComponent fluidStorage; @@ -223,36 +219,6 @@ public FluidStorageComponent getFluidStorageComponent() { @Override protected MachineModelClientData getMachineModelData() { return new MachineModelClientData(null, orientation.facingDirection); - - } - - protected final void link() { - if (shapeMatcher == null) { - shapeMatcher = new ShapeMatcher(level, worldPosition, orientation.facingDirection, getActiveShape()); - shapeMatcher.registerListeners(level); - } - if (shapeMatcher.needsRematch()) { - shapeValid.shapeValid = false; - shapeMatcher.rematch(level); - - if (shapeMatcher.isMatchSuccessful()) { - shapeValid.shapeValid = true; - onMatchSuccessful(); - } - - if (shapeValid.update()) { - sync(false); - } - } - } - - @Override - public final void unlink() { - if (shapeMatcher != null) { - shapeMatcher.unlinkHatches(); - shapeMatcher.unregisterListeners(level); - shapeMatcher = null; - } } @Override @@ -275,7 +241,8 @@ public static long getCapacityFromComponents(int xIndex, int yIndex, int zIndex) return volume * BUCKET_PER_STRUCTURE_BLOCK * FluidType.BUCKET_VOLUME; } - private void onMatchSuccessful() { + @Override + protected void onMatchSuccessful() { int index = activeShape.getActiveShapeIndex(); long capacity = getCapacityFromComponents(getXComponent(index), getYComponent(index), getZComponent(index)); fluidStorage.setCapacity(capacity); diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/NuclearReactorMultiblockBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/NuclearReactorMultiblockBlockEntity.java index c383de415..323494ac2 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/NuclearReactorMultiblockBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/NuclearReactorMultiblockBlockEntity.java @@ -58,7 +58,6 @@ public class NuclearReactorMultiblockBlockEntity extends MultiblockMachineBlockE private final RedstoneControlComponent redstoneControl; private final IsActiveComponent isActive; private final NuclearEfficiencyHistoryComponent efficiencyHistory; - private ShapeMatcher shapeMatcher; private NuclearGrid nuclearGrid; private Supplier dataSupplier; @@ -125,7 +124,18 @@ public void tick() { } } - protected void onSuccessfulMatch(ShapeMatcher shapeMatcher) { + @Override + public ShapeTemplate getActiveShape() { + return activeShape.getActiveShape(); + } + + @Override + protected void onRematch() { + nuclearGrid = null; + } + + @Override + protected void onMatchSuccessful() { shapeValid.shapeValid = true; int size = gridLayout[activeShape.getActiveShapeIndex()].length; NuclearHatch[][] hatchesGrid = new NuclearHatch[size][size]; @@ -174,41 +184,6 @@ protected void onSuccessfulMatch(ShapeMatcher shapeMatcher) { }; } - @Override - public ShapeTemplate getActiveShape() { - return activeShape.getActiveShape(); - } - - protected final void link() { - if (shapeMatcher == null) { - shapeMatcher = new ShapeMatcher(level, worldPosition, orientation.facingDirection, getActiveShape()); - shapeMatcher.registerListeners(level); - } - if (shapeMatcher.needsRematch()) { - shapeValid.shapeValid = false; - nuclearGrid = null; - shapeMatcher.rematch(level); - - if (shapeMatcher.isMatchSuccessful()) { - shapeValid.shapeValid = true; - onSuccessfulMatch(shapeMatcher); - } - - if (shapeValid.update()) { - sync(false); - } - } - } - - @Override - public final void unlink() { - if (shapeMatcher != null) { - shapeMatcher.unlinkHatches(); - shapeMatcher.unregisterListeners(level); - shapeMatcher = null; - } - } - public static void registerReiShapes() { for (int i = 0; i < shapeTemplates.length; ++i) { ReiMachineRecipes.registerMultiblockShape("nuclear_reactor", shapeTemplates[i], "" + i); diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/SteamBoilerMultiblockBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/SteamBoilerMultiblockBlockEntity.java index 418e1ef49..0c3623775 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/SteamBoilerMultiblockBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/SteamBoilerMultiblockBlockEntity.java @@ -35,7 +35,6 @@ import aztech.modern_industrialization.machines.guicomponents.TemperatureBar; import aztech.modern_industrialization.machines.models.MachineModelClientData; import aztech.modern_industrialization.machines.multiblocks.MultiblockMachineBlockEntity; -import aztech.modern_industrialization.machines.multiblocks.ShapeMatcher; import aztech.modern_industrialization.machines.multiblocks.ShapeTemplate; import aztech.modern_industrialization.util.Tickable; import java.util.List; @@ -44,7 +43,6 @@ public class SteamBoilerMultiblockBlockEntity extends MultiblockMachineBlockEntity implements Tickable { - private ShapeMatcher shapeMatcher; private final ShapeTemplate shapeTemplate; private final IsActiveComponent isActiveComponent; private final RedstoneControlComponent redstoneControl; @@ -81,33 +79,9 @@ public SteamBoilerMultiblockBlockEntity(BEP bep, ShapeTemplate shapeTemplate, St } - protected final void link() { - if (shapeMatcher == null) { - shapeMatcher = new ShapeMatcher(level, worldPosition, orientation.facingDirection, shapeTemplate); - shapeMatcher.registerListeners(level); - } - if (shapeMatcher.needsRematch()) { - shapeValid.shapeValid = false; - shapeMatcher.rematch(level); - - if (shapeMatcher.isMatchSuccessful()) { - inventory.rebuild(shapeMatcher); - shapeValid.shapeValid = true; - } - - if (shapeValid.update()) { - sync(false); - } - } - } - @Override - public final void unlink() { - if (shapeMatcher != null) { - shapeMatcher.unlinkHatches(); - shapeMatcher.unregisterListeners(level); - shapeMatcher = null; - } + protected void onMatchSuccessful() { + inventory.rebuild(shapeMatcher); } @Override diff --git a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/SteamCraftingMultiblockBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/SteamCraftingMultiblockBlockEntity.java index f7d26d5ea..469e3b628 100644 --- a/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/SteamCraftingMultiblockBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/blockentities/multiblocks/SteamCraftingMultiblockBlockEntity.java @@ -30,7 +30,6 @@ import aztech.modern_industrialization.machines.guicomponents.CraftingMultiblockGui; import aztech.modern_industrialization.machines.helper.SteamHelper; import aztech.modern_industrialization.machines.multiblocks.HatchBlockEntity; -import aztech.modern_industrialization.machines.multiblocks.ShapeMatcher; import aztech.modern_industrialization.machines.multiblocks.ShapeTemplate; import aztech.modern_industrialization.machines.recipe.MachineRecipeType; import aztech.modern_industrialization.util.Simulation; @@ -68,7 +67,9 @@ protected CrafterComponent.Behavior getBehavior() { private boolean steelTier; @Override - protected void onSuccessfulMatch(ShapeMatcher shapeMatcher) { + protected void onMatchSuccessful() { + super.onMatchSuccessful(); + steelTier = false; for (HatchBlockEntity hatch : shapeMatcher.getMatchedHatches()) { diff --git a/src/main/java/aztech/modern_industrialization/machines/components/CasingComponent.java b/src/main/java/aztech/modern_industrialization/machines/components/CasingComponent.java index aaabf953e..be1ed0c49 100644 --- a/src/main/java/aztech/modern_industrialization/machines/components/CasingComponent.java +++ b/src/main/java/aztech/modern_industrialization/machines/components/CasingComponent.java @@ -24,6 +24,7 @@ package aztech.modern_industrialization.machines.components; import aztech.modern_industrialization.api.energy.CableTier; +import aztech.modern_industrialization.api.energy.CableTierHolder; import aztech.modern_industrialization.machines.IComponent; import aztech.modern_industrialization.machines.MachineBlockEntity; import aztech.modern_industrialization.machines.models.MachineCasing; @@ -42,15 +43,15 @@ import net.minecraft.world.level.block.Blocks; import org.jetbrains.annotations.Nullable; -public class CasingComponent implements IComponent, DropableComponent { +public class CasingComponent implements IComponent, DropableComponent, CableTierHolder { - private ItemStack casingStack = ItemStack.EMPTY; - private CableTier currentTier = CableTier.LV; + protected ItemStack casingStack = ItemStack.EMPTY; + protected CableTier currentTier = CableTier.LV; /** * Sets the current casing stack and update {@link #currentTier} accordingly. */ - private void setCasingStack(ItemStack stack) { + protected void setCasingStack(ItemStack stack) { casingStack = stack; // Compute tier @@ -109,7 +110,7 @@ public ItemInteractionResult onUse(MachineBlockEntity be, Player player, Interac return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; } - private void playCasingPlaceSound(MachineBlockEntity be) { + protected void playCasingPlaceSound(MachineBlockEntity be) { var blockKey = currentTier.itemKey; if (blockKey == null) { return; // no sound for LV @@ -141,6 +142,11 @@ public ItemStack getDrop() { return casingStack; } + @Override + public CableTier getCableTier() { + return currentTier; + } + public void setCasingServer(MachineBlockEntity be, ItemStack casing) { setCasingStack(casing); be.setChanged(); diff --git a/src/main/java/aztech/modern_industrialization/machines/components/OrientationComponent.java b/src/main/java/aztech/modern_industrialization/machines/components/OrientationComponent.java index a91b0241f..2e5229be6 100644 --- a/src/main/java/aztech/modern_industrialization/machines/components/OrientationComponent.java +++ b/src/main/java/aztech/modern_industrialization/machines/components/OrientationComponent.java @@ -89,7 +89,7 @@ public boolean useWrench(Player player, InteractionHand hand, Direction face) { return true; } } else { - if (face.getAxis().isHorizontal()) { + if (params.canBeVertical || face.getAxis().isHorizontal()) { facingDirection = face; } // We consume the event to prevent the GUI from opening. @@ -100,7 +100,7 @@ public boolean useWrench(Player player, InteractionHand hand, Direction face) { public void onPlaced(@Nullable LivingEntity placer, ItemStack itemStack) { // The placer can be null using some mods' automatic placement: pick NORTH arbitrarily. - Direction dir = placer != null ? placer.getDirection() : Direction.NORTH; + Direction dir = placer != null ? (params.canBeVertical ? placer.getNearestViewDirection() : placer.getDirection()) : Direction.NORTH; facingDirection = dir.getOpposite(); if (params.hasOutput) { outputDirection = dir; @@ -111,11 +111,17 @@ public static class Params { public final boolean hasOutput; public final boolean hasExtractItems; public final boolean hasExtractFluids; + public final boolean canBeVertical; - public Params(boolean hasOutput, boolean hasExtractItems, boolean hasExtractFluids) { + public Params(boolean hasOutput, boolean hasExtractItems, boolean hasExtractFluids, boolean canBeVertical) { this.hasOutput = hasOutput; this.hasExtractItems = hasExtractItems; this.hasExtractFluids = hasExtractFluids; + this.canBeVertical = canBeVertical; + } + + public Params(boolean hasOutput, boolean hasExtractItems, boolean hasExtractFluids) { + this(hasOutput, hasExtractItems, hasExtractFluids, false); } } diff --git a/src/main/java/aztech/modern_industrialization/machines/gui/MachineMenuCommon.java b/src/main/java/aztech/modern_industrialization/machines/gui/MachineMenuCommon.java index 591efa882..634aff0a6 100644 --- a/src/main/java/aztech/modern_industrialization/machines/gui/MachineMenuCommon.java +++ b/src/main/java/aztech/modern_industrialization/machines/gui/MachineMenuCommon.java @@ -29,7 +29,7 @@ import aztech.modern_industrialization.inventory.ConfigurableScreenHandler; import aztech.modern_industrialization.inventory.MIInventory; import aztech.modern_industrialization.inventory.SlotGroup; -import java.util.List; +import aztech.modern_industrialization.machines.ComponentStorage; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.Slot; @@ -38,7 +38,7 @@ public abstract class MachineMenuCommon extends ConfigurableScreenHandler implem public final MachineGuiParameters guiParams; MachineMenuCommon(int syncId, Inventory playerInventory, MIInventory inventory, MachineGuiParameters guiParams, - List guiComponents) { + ComponentStorage guiComponents) { super(MIRegistries.MACHINE_MENU.get(), syncId, playerInventory, inventory); this.guiParams = guiParams; @@ -53,9 +53,7 @@ public abstract class MachineMenuCommon extends ConfigurableScreenHandler implem } // Gui components first (we want to prioritize them with shift click) - for (var component : guiComponents) { - component.setupMenu(this); - } + guiComponents.forEach(component -> component.setupMenu(this)); // Configurable slots for (int i = 0; i < inventory.getItemStacks().size(); ++i) { diff --git a/src/main/java/aztech/modern_industrialization/machines/gui/MachineMenuServer.java b/src/main/java/aztech/modern_industrialization/machines/gui/MachineMenuServer.java index 44b635de6..4f700d1ce 100644 --- a/src/main/java/aztech/modern_industrialization/machines/gui/MachineMenuServer.java +++ b/src/main/java/aztech/modern_industrialization/machines/gui/MachineMenuServer.java @@ -39,19 +39,16 @@ public class MachineMenuServer extends MachineMenuCommon { protected final List trackedData; public MachineMenuServer(int syncId, Inventory playerInventory, MachineBlockEntity blockEntity, MachineGuiParameters guiParams) { - super(syncId, playerInventory, blockEntity.getInventory(), guiParams, blockEntity.guiComponents); + super(syncId, playerInventory, blockEntity.getInventory(), guiParams, blockEntity.getGuiComponents()); this.blockEntity = blockEntity; trackedData = new ArrayList<>(); - for (GuiComponent.Server component : blockEntity.guiComponents) { - trackedData.add(component.copyData()); - } + blockEntity.getGuiComponents().forEach(component -> trackedData.add(component.copyData())); } @Override public void broadcastChanges() { super.broadcastChanges(); - for (int i = 0; i < blockEntity.guiComponents.size(); ++i) { - GuiComponent.Server component = blockEntity.guiComponents.get(i); + blockEntity.getGuiComponents().forEachIndexed((i, component) -> { if (component.needsSync(trackedData.get(i))) { var buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), blockEntity.getLevel().registryAccess()); component.writeCurrentData(buf); @@ -61,7 +58,7 @@ public void broadcastChanges() { trackedData.set(i, component.copyData()); buf.release(); } - } + }); } @Override diff --git a/src/main/java/aztech/modern_industrialization/machines/models/MachineCasing.java b/src/main/java/aztech/modern_industrialization/machines/models/MachineCasing.java index dd1fa9b5b..df6f8f259 100644 --- a/src/main/java/aztech/modern_industrialization/machines/models/MachineCasing.java +++ b/src/main/java/aztech/modern_industrialization/machines/models/MachineCasing.java @@ -23,10 +23,12 @@ */ package aztech.modern_industrialization.machines.models; +import net.minecraft.resources.ResourceLocation; + public class MachineCasing { - public final String name; + public final ResourceLocation key; - MachineCasing(String name) { - this.name = name; + MachineCasing(ResourceLocation key) { + this.key = key; } } diff --git a/src/main/java/aztech/modern_industrialization/machines/models/MachineCasings.java b/src/main/java/aztech/modern_industrialization/machines/models/MachineCasings.java index 53f961336..ae5c9a324 100644 --- a/src/main/java/aztech/modern_industrialization/machines/models/MachineCasings.java +++ b/src/main/java/aztech/modern_industrialization/machines/models/MachineCasings.java @@ -23,13 +23,15 @@ */ package aztech.modern_industrialization.machines.models; +import aztech.modern_industrialization.MI; import aztech.modern_industrialization.compat.kubejs.KubeJSProxy; import java.util.HashMap; import java.util.Map; +import net.minecraft.resources.ResourceLocation; public class MachineCasings { - public static final Map registeredCasings = new HashMap<>(); + public static final Map registeredCasings = new HashMap<>(); public static final MachineCasing BRICKED_BRONZE = create("bricked_bronze"); public static final MachineCasing BRICKED_STEEL = create("bricked_steel"); @@ -54,22 +56,30 @@ public class MachineCasings { KubeJSProxy.instance.fireRegisterMachineCasingsEvent(); } - public static MachineCasing create(String name) { - if (registeredCasings.containsKey(name)) { - throw new IllegalArgumentException("Duplicate machine casing definition: " + name); + public static MachineCasing create(ResourceLocation key) { + if (registeredCasings.containsKey(key)) { + throw new IllegalArgumentException("Duplicate machine casing definition: " + key); } - MachineCasing casing = new MachineCasing(name); - registeredCasings.put(name, casing); + MachineCasing casing = new MachineCasing(key); + registeredCasings.put(key, casing); return casing; } - public static MachineCasing get(String name) { - MachineCasing casing = registeredCasings.get(name); + public static MachineCasing create(String name) { + return create(MI.id(name)); + } + + public static MachineCasing get(ResourceLocation key) { + MachineCasing casing = registeredCasings.get(key); if (casing != null) { return casing; } else { - throw new IllegalArgumentException("Machine casing model \"" + name + "\" does not exist."); + throw new IllegalArgumentException("Machine casing model \"" + key + "\" does not exist."); } } + + public static MachineCasing get(String name) { + return get(ResourceLocation.isValidPath(name) ? MI.id(name) : ResourceLocation.parse(name)); + } } diff --git a/src/main/java/aztech/modern_industrialization/machines/multiblocks/HatchBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/multiblocks/HatchBlockEntity.java index 43cbdbaff..8c81c74ad 100644 --- a/src/main/java/aztech/modern_industrialization/machines/multiblocks/HatchBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/multiblocks/HatchBlockEntity.java @@ -39,6 +39,7 @@ import java.util.Objects; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; @@ -51,19 +52,19 @@ public HatchBlockEntity(BEP bep, MachineGuiParameters guiParams, OrientationComp @Override public void writeClientNbt(CompoundTag tag, HolderLookup.Provider registries) { if (matchedCasing != null) { - tag.putString("matchedCasing", matchedCasing); + tag.putString("matchedCasing", matchedCasing.toString()); } } @Override public void readClientNbt(CompoundTag tag, HolderLookup.Provider registries) { - matchedCasing = tag.contains("matchedCasing") ? tag.getString("matchedCasing") : null; + matchedCasing = tag.contains("matchedCasing") ? ResourceLocation.tryParse(tag.getString("matchedCasing")) : null; } }); } - private String lastSyncedMachineCasing = null; - private String matchedCasing = null; + private ResourceLocation lastSyncedMachineCasing = null; + private ResourceLocation matchedCasing = null; public abstract HatchType getHatchType(); @@ -89,7 +90,7 @@ public void unlink() { } public void link(MachineCasing casing) { - matchedCasing = casing.name; + matchedCasing = casing.key; } protected void clearMachineLock() { diff --git a/src/main/java/aztech/modern_industrialization/machines/multiblocks/MultiblockMachineBlockEntity.java b/src/main/java/aztech/modern_industrialization/machines/multiblocks/MultiblockMachineBlockEntity.java index d68a51de4..d18ab0a2e 100644 --- a/src/main/java/aztech/modern_industrialization/machines/multiblocks/MultiblockMachineBlockEntity.java +++ b/src/main/java/aztech/modern_industrialization/machines/multiblocks/MultiblockMachineBlockEntity.java @@ -33,6 +33,8 @@ import net.minecraft.world.phys.BlockHitResult; public abstract class MultiblockMachineBlockEntity extends MachineBlockEntity { + protected ShapeMatcher shapeMatcher; + public MultiblockMachineBlockEntity(BEP bep, MachineGuiParameters guiParams, OrientationComponent.Params orientationParams) { super(bep, guiParams, orientationParams); this.shapeValid = new ShapeValidComponent(); @@ -45,7 +47,61 @@ public boolean isShapeValid() { return shapeValid.shapeValid; } - public abstract void unlink(); + public ShapeMatcher getShapeMatcher() { + return shapeMatcher; + } + + public ShapeMatcher createShapeMatcher() { + return new ShapeMatcher(level, worldPosition, orientation.facingDirection, getActiveShape()); + } + + protected void onLink() { + } + + protected void onUnlink() { + } + + protected void onRematch() { + } + + protected void onMatchSuccessful() { + } + + protected void onMatchFailure() { + } + + protected void link() { + if (shapeMatcher == null) { + shapeMatcher = createShapeMatcher(); + shapeMatcher.registerListeners(level); + onLink(); + } + if (shapeMatcher.needsRematch()) { + shapeValid.shapeValid = false; + shapeMatcher.rematch(level); + onRematch(); + + if (shapeMatcher.isMatchSuccessful()) { + onMatchSuccessful(); + shapeValid.shapeValid = true; + } else { + onMatchFailure(); + } + + if (shapeValid.update()) { + sync(false); + } + } + } + + public void unlink() { + if (shapeMatcher != null) { + shapeMatcher.unlinkHatches(); + shapeMatcher.unregisterListeners(level); + onUnlink(); + shapeMatcher = null; + } + } @Override public boolean useWrench(Player player, InteractionHand hand, BlockHitResult hitResult) { @@ -59,7 +115,7 @@ public boolean useWrench(Player player, InteractionHand hand, BlockHitResult hit } @Override - public final void setRemoved() { + public void setRemoved() { super.setRemoved(); if (!level.isClientSide) { unlink(); diff --git a/src/main/java/aztech/modern_industrialization/machines/multiblocks/ShapeMatcher.java b/src/main/java/aztech/modern_industrialization/machines/multiblocks/ShapeMatcher.java index f8c8eae8f..448b99cec 100644 --- a/src/main/java/aztech/modern_industrialization/machines/multiblocks/ShapeMatcher.java +++ b/src/main/java/aztech/modern_industrialization/machines/multiblocks/ShapeMatcher.java @@ -47,14 +47,14 @@ public ShapeMatcher(Level world, BlockPos controllerPos, Direction controllerDir this.hatchFlags = toWorldPos(controllerPos, controllerDirection, template.hatchFlags); } - private final BlockPos controllerPos; - private final ShapeTemplate template; - private final Map simpleMembers; - private final Map hatchFlags; + protected final BlockPos controllerPos; + protected final ShapeTemplate template; + protected final Map simpleMembers; + protected final Map hatchFlags; - private boolean needsRematch = true; - private boolean matchSuccessful = false; - private final List matchedHatches = new ArrayList<>(); + protected boolean needsRematch = true; + protected boolean matchSuccessful = false; + protected final List matchedHatches = new ArrayList<>(); /** * Convert a relative position in the shape template to the real position in the @@ -73,7 +73,7 @@ else if (controllerDirection == EAST) return rotatedPos.offset(controllerPos); } - private static Map toWorldPos(BlockPos controllerPos, Direction controllerDirection, Map templateMap) { + protected static Map toWorldPos(BlockPos controllerPos, Direction controllerDirection, Map templateMap) { Map result = new HashMap<>(); for (Map.Entry entry : templateMap.entrySet()) { result.put(toWorldPos(controllerPos, controllerDirection, entry.getKey()), entry.getValue()); diff --git a/src/main/java/aztech/modern_industrialization/network/machines/ChangeShapePacket.java b/src/main/java/aztech/modern_industrialization/network/machines/ChangeShapePacket.java index b92809545..84aacfeb2 100644 --- a/src/main/java/aztech/modern_industrialization/network/machines/ChangeShapePacket.java +++ b/src/main/java/aztech/modern_industrialization/network/machines/ChangeShapePacket.java @@ -50,7 +50,7 @@ public void handle(Context ctx) { AbstractContainerMenu menu = ctx.getPlayer().containerMenu; if (menu.containerId == syncId && menu instanceof MachineMenuServer machineMenu) { - ShapeSelection.Server shapeSelection = machineMenu.blockEntity.getComponent(GuiComponents.SHAPE_SELECTION); + ShapeSelection.Server shapeSelection = machineMenu.blockEntity.getGuiComponents().get(GuiComponents.SHAPE_SELECTION); shapeSelection.behavior.handleClick(shapeLine, clickedLeftButton ? -1 : +1); } } diff --git a/src/main/java/aztech/modern_industrialization/network/machines/ReiLockSlotsPacket.java b/src/main/java/aztech/modern_industrialization/network/machines/ReiLockSlotsPacket.java index 378aec6b6..9f0689be5 100644 --- a/src/main/java/aztech/modern_industrialization/network/machines/ReiLockSlotsPacket.java +++ b/src/main/java/aztech/modern_industrialization/network/machines/ReiLockSlotsPacket.java @@ -48,7 +48,7 @@ public void handle(Context ctx) { AbstractContainerMenu sh = ctx.getPlayer().containerMenu; if (sh.containerId == containedId && sh instanceof MachineMenuServer screenHandler) { // Check that locking the slots is allowed in the first place - ReiSlotLocking.Server slotLocking = screenHandler.blockEntity.getComponent(GuiComponents.REI_SLOT_LOCKING); + ReiSlotLocking.Server slotLocking = screenHandler.blockEntity.getGuiComponents().get(GuiComponents.REI_SLOT_LOCKING); if (!slotLocking.allowLocking.get()) return; diff --git a/src/main/java/aztech/modern_industrialization/network/machines/SetAutoExtractPacket.java b/src/main/java/aztech/modern_industrialization/network/machines/SetAutoExtractPacket.java index 24ee14798..b8ef3181d 100644 --- a/src/main/java/aztech/modern_industrialization/network/machines/SetAutoExtractPacket.java +++ b/src/main/java/aztech/modern_industrialization/network/machines/SetAutoExtractPacket.java @@ -50,7 +50,7 @@ public void handle(Context ctx) { if (ctx.getPlayer().containerMenu.containerId == syncId) { var screenHandler = (MachineMenuServer) ctx.getPlayer().containerMenu; - AutoExtract.Server autoExtract = screenHandler.blockEntity.getComponent(GuiComponents.AUTO_EXTRACT); + AutoExtract.Server autoExtract = screenHandler.blockEntity.getGuiComponents().get(GuiComponents.AUTO_EXTRACT); OrientationComponent orientation = autoExtract.getOrientation(); if (isItem) { orientation.extractItems = isExtract;