From 5907a04f35244171d7bcc561923fbce46311e512 Mon Sep 17 00:00:00 2001 From: John Lemme Date: Fri, 3 Oct 2025 18:45:56 -0400 Subject: [PATCH 1/9] feat: First version of the pattern grid turtle, can craft both types of pattern and get detailed information --- .../turtle_upgrade/pattern_turtle.json | 4 + .../client/ClientRegistry.java | 1 + .../peripheral/PatternGridPeripheral.java | 291 ++++++++++++++++++ .../turtles/TurtlePatternGridUpgrade.java | 40 +++ .../common/data/EnUsLanguageProvider.java | 1 + .../common/setup/CCRegistration.java | 3 + .../models/block/turtle_pattern_left.json | 4 + .../models/block/turtle_pattern_right.json | 4 + .../item/turtle_pattern_upgrade_left.json | 6 + .../item/turtle_pattern_upgrade_right.json | 6 + .../textures/block/pattern_grid.png | Bin 0 -> 647 bytes .../textures/block/pattern_grid_small.png | Bin 0 -> 594 bytes 12 files changed, 360 insertions(+) create mode 100644 src/generated/resources/data/advancedperipherals/computercraft/turtle_upgrade/pattern_turtle.json create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/turtles/TurtlePatternGridUpgrade.java create mode 100644 src/main/resources/assets/advancedperipherals/models/block/turtle_pattern_left.json create mode 100644 src/main/resources/assets/advancedperipherals/models/block/turtle_pattern_right.json create mode 100644 src/main/resources/assets/advancedperipherals/models/item/turtle_pattern_upgrade_left.json create mode 100644 src/main/resources/assets/advancedperipherals/models/item/turtle_pattern_upgrade_right.json create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/pattern_grid.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/pattern_grid_small.png diff --git a/src/generated/resources/data/advancedperipherals/computercraft/turtle_upgrade/pattern_turtle.json b/src/generated/resources/data/advancedperipherals/computercraft/turtle_upgrade/pattern_turtle.json new file mode 100644 index 000000000..21b79a957 --- /dev/null +++ b/src/generated/resources/data/advancedperipherals/computercraft/turtle_upgrade/pattern_turtle.json @@ -0,0 +1,4 @@ +{ + "type": "advancedperipherals:pattern_turtle", + "item": "refinedstorage:pattern_grid" +} \ No newline at end of file diff --git a/src/main/java/de/srendi/advancedperipherals/client/ClientRegistry.java b/src/main/java/de/srendi/advancedperipherals/client/ClientRegistry.java index ece61b712..ecdffb6ba 100644 --- a/src/main/java/de/srendi/advancedperipherals/client/ClientRegistry.java +++ b/src/main/java/de/srendi/advancedperipherals/client/ClientRegistry.java @@ -24,6 +24,7 @@ public static void menuRegister(RegisterMenuScreensEvent event) { public static void onUpgradeModeller(RegisterTurtleModellersEvent event) { event.register(CCRegistration.CHUNKY_TURTLE.get(), TurtleUpgradeModeller.flatItem()); event.register(CCRegistration.COMPASS_TURTLE.get(), TurtleUpgradeModeller.flatItem()); + event.register(CCRegistration.PATTERN_TURTLE.get(), TurtleUpgradeModeller.sided(AdvancedPeripherals.getRL("block/turtle_pattern_left"), AdvancedPeripherals.getRL("block/turtle_pattern_right"))); event.register(CCRegistration.CHAT_BOX_TURTLE.get(), TurtleUpgradeModeller.sided(AdvancedPeripherals.getRL("block/turtle_chatty_left"), AdvancedPeripherals.getRL("block/turtle_chatty_right"))); event.register(CCRegistration.ENVIRONMENT_TURTLE.get(), TurtleUpgradeModeller.sided(AdvancedPeripherals.getRL("block/turtle_environment_left"), AdvancedPeripherals.getRL("block/turtle_environment_right"))); event.register(CCRegistration.GEO_SCANNER_TURTLE.get(), TurtleUpgradeModeller.sided(AdvancedPeripherals.getRL("block/turtle_geoscanner_left"), AdvancedPeripherals.getRL("block/turtle_geoscanner_right"))); diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java new file mode 100644 index 000000000..758fdd73b --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -0,0 +1,291 @@ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral; + +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; +import com.refinedmods.refinedstorage.common.api.autocrafting.PatternProviderItem; +import com.refinedmods.refinedstorage.common.autocrafting.CraftingPatternState; +import com.refinedmods.refinedstorage.common.autocrafting.PatternState; +import com.refinedmods.refinedstorage.common.autocrafting.ProcessingPatternState; +import com.refinedmods.refinedstorage.common.autocrafting.patterngrid.PatternType; +import com.refinedmods.refinedstorage.common.content.DataComponents; +import com.refinedmods.refinedstorage.common.content.Items; +import com.refinedmods.refinedstorage.common.support.resource.FluidResource; +import com.refinedmods.refinedstorage.common.support.resource.ItemResource; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.TurtleSide; +import de.srendi.advancedperipherals.common.addons.APAddon; +import de.srendi.advancedperipherals.common.addons.computercraft.owner.TurtlePeripheralOwner; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSApi; +import de.srendi.advancedperipherals.common.configuration.APConfig; +import de.srendi.advancedperipherals.lib.peripherals.BasePeripheral; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.Container; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.CraftingInput; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; + +public class PatternGridPeripheral extends BasePeripheral { + + public static final String PERIPHERAL_TYPE = "pattern_grid"; + + protected PatternGridPeripheral(TurtlePeripheralOwner owner) { + super(PERIPHERAL_TYPE, owner); + } + + public PatternGridPeripheral(ITurtleAccess turtle, TurtleSide side) { + this(new TurtlePeripheralOwner(turtle, side)); + } + + @Override + public boolean isEnabled() { + return APAddon.REFINEDSTORAGE.isLoaded() && APConfig.PERIPHERALS_CONFIG.enableCompassTurtle.get(); + } + + // Returns information about a pattern, in the style of the RSBridge.getPattern() method. + public Map getDetailsForItem(ItemStack target) throws RuntimeException { + if(!target.is(Items.INSTANCE.getPattern())) { + throw new RuntimeException("Not a pattern"); + } + + Optional pattern = Optional.empty(); + if (target.getItem() instanceof PatternProviderItem patternProvider) { + pattern = patternProvider.getPattern(target, this.getLevel()); + } + + if(pattern.isEmpty()) { + throw new RuntimeException("Pattern is empty"); + } + else { + return (Map) RSApi.parsePattern(pattern.get(), null); + } + } + + @LuaFunction(mainThread = true) + public MethodResult getDetails(Optional slot) { + try { + ITurtleAccess turtle = this.getPeripheralOwner().getTurtle(); + // Note: the hack at the end of the line here converts between CC slot numbering (from 1) to Java slot numbering (from 0). + // It's broken out like this so we can call getDetailsInSlot from the builder functions later, and not have to care + // what Lua thinks about array indexing. + ItemStack target = turtle.getInventory().getItem(slot.orElse(turtle.getSelectedSlot() + 1) - 1); + return MethodResult.of(getDetailsForItem(target)); + } + catch(RuntimeException e) { + // Did you know: passing a naked RuntimeException to the Lua interpreter causes a bizarre hang + return MethodResult.of(false, e.getMessage()); + } + } + + // ----------------------------------------------------------------------------------------------------------------- + + // Make sure we have patterns available. + public int verifyPatternsInSlot(int slot) throws RuntimeException { + Container turtleInventory = this.getPeripheralOwner().getTurtle().getInventory(); + ItemStack selected = turtleInventory.getItem(slot); + if(selected.is(Items.INSTANCE.getPattern()) && selected.getCount() > 0) { + return selected.getCount(); + } + else { + // TODO: do I even care to do this here + throw new RuntimeException("No pattern available"); + } + } + + // Find a slot to insert a newly built pattern. + public int getFreeSlotAfterBuild(int source) throws RuntimeException { + // We assume that we'll consume one of whatever is in the source slot. + Container turtleInventory = this.getPeripheralOwner().getTurtle().getInventory(); + + if(turtleInventory.getItem(source).getCount() <= 1) { + // We're using the last of something, so feel free to take its place. + return source; + } + else { + // Otherwise... are there any free slots? + for(int i = 0; i < turtleInventory.getContainerSize(); i++) { + if (turtleInventory.getItem(i).getCount() == 0) { + return i; + } + } + // Turtle's full, keep movin' + throw new RuntimeException("No room in destination"); + } + } + + public ResourceKey parseResourceName(String resourceName) throws RuntimeException { + try { + ResourceLocation location = ResourceLocation.parse(resourceName); + // Try as item first + Item item = BuiltInRegistries.ITEM.get(location); + if (!item.equals(BuiltInRegistries.ITEM.get(ResourceLocation.parse("minecraft:air")))) { + return new ItemResource(item); + } + // Try as fluid + Fluid fluid = BuiltInRegistries.FLUID.get(location); + if (!fluid.equals(Fluids.EMPTY)) { + return new FluidResource(fluid); + } + throw new RuntimeException("Couldn't find item or fluid: " + resourceName); + } catch(Exception e) { + throw new RuntimeException("Couldn't find item or fluid: " + resourceName); + } + } + + // ----------------------------------------------------------------------------------------------------------------- + + // Build a crafting pattern from a table of slots. + // TODO: replace with Lua exceptions + @LuaFunction(mainThread = true) + public MethodResult buildCrafting(Map recipeInput, Optional fuzzy) { + ITurtleAccess turtle = this.getPeripheralOwner().getTurtle(); + + try { + verifyPatternsInSlot(turtle.getSelectedSlot()); + int destinationSlot = getFreeSlotAfterBuild(turtle.getSelectedSlot()); + + ItemStack patternStack = new ItemStack(Items.INSTANCE.getPattern()); + PatternState patternState = new PatternState(UUID.randomUUID(), PatternType.CRAFTING); + patternStack.set(DataComponents.INSTANCE.getPatternState(), patternState); + + List ingredients = new ArrayList<>(Collections.nCopies(9, ItemStack.EMPTY)); + + for (Object o : recipeInput.keySet()) { + // Note: I'm assuming that Lua returns all numbers as doubles. + // Only care about slots 1 through 9 in the input; we'll just ignore everything else. + if(o instanceof Double) { + int slot = ((Double) o).intValue() - 1; + if(slot >= 0 && slot < 9) { + if(recipeInput.get(o) instanceof String) { + ingredients.set(slot, ((ItemResource) parseResourceName((String) recipeInput.get(o))).toItemStack()); + } else { + return MethodResult.of(false, "Couldn't parse item in slot " + slot); + } + } + } + } + + CraftingInput craftingInput = CraftingInput.of(3, 3, ingredients); + CraftingInput.Positioned positioned = new CraftingInput.Positioned(craftingInput, 0, 0); + CraftingPatternState craftingState = new CraftingPatternState(fuzzy.orElse(false), positioned); + patternStack.set(DataComponents.INSTANCE.getCraftingPatternState(), craftingState); + + // Kismet: a bad recipe will be caught automatically + Map result; + try { + result = getDetailsForItem(patternStack); + } + catch(Exception e) { + throw new RuntimeException("Bad recipe"); + } + + turtle.getInventory().removeItem(turtle.getSelectedSlot(), 1); + turtle.getInventory().setItem(destinationSlot, patternStack); + + return MethodResult.of(true, result); + } + catch(Exception e) { + // I like exceptions but the other peripherals in this mod don't use them. So just return errors as strings. + return MethodResult.of(false, e.getMessage()); + } + } + + // ----------------------------------------------------------------------------------------------------------------- + + // Build a processing pattern from a list of ingredients. + @LuaFunction(mainThread = true) + public MethodResult buildProcessing(Map recipeInput) { + ITurtleAccess turtle = this.getPeripheralOwner().getTurtle(); + + try { + verifyPatternsInSlot(turtle.getSelectedSlot()); + int destinationSlot = getFreeSlotAfterBuild(turtle.getSelectedSlot()); + + ItemStack patternStack = new ItemStack(Items.INSTANCE.getPattern()); + PatternState patternState = new PatternState(UUID.randomUUID(), PatternType.PROCESSING); + patternStack.set(DataComponents.INSTANCE.getPatternState(), patternState); + + List> ingredients = new ArrayList<>(); + List> results = new ArrayList<>(); + + /* Attempt to parse our recipe. We should be passed in a table that looks like this: + * + * { + * inputs = { + * [1] = { name = "", count = }, + * [2] = { name = "", alts = { [1] = "", ... }, count = }, + * ... + * }, + * outputs = { + * [1] = { name = "", count = }, + * ... + * } + * } + * + * Note that input names can be passed as a string or as an array of strings (for alternates). + * That's a lot to validate, so barring better options, we'll just catch and error out. + */ + try { + // Lua sends in all numbers as doubles. We don't really care about the index. + Map> inputMap = (Map>) recipeInput.get("inputs"); + for(Map thisInput : inputMap.values()) { + int thisCount = ((Double) thisInput.get("count")).intValue(); + ResourceKey thisItem = parseResourceName((String) thisInput.get("name")); + List theseAlts = new ArrayList<>(); + + if(thisInput.containsKey("alts")) { + theseAlts = ((Map) thisInput.get("alts")).values().stream().map( + ResourceLocation::parse).toList(); + } + + ingredients.add(Optional.of(new ProcessingPatternState.ProcessingIngredient(new ResourceAmount(thisItem, thisCount), theseAlts))); + } + + Map> outputMap = (Map>) recipeInput.get("outputs"); + for(Map thisOutput : outputMap.values()) { + int thisCount = ((Double) thisOutput.get("count")).intValue(); + ResourceKey thisItem = parseResourceName((String) thisOutput.get("name")); + results.add(Optional.of(new ResourceAmount(thisItem, thisCount))); + } + } + catch(Exception e) { + throw new RuntimeException("Recipe is malformed"); + } + + ProcessingPatternState processingState = new ProcessingPatternState(ingredients, results); + patternStack.set(DataComponents.INSTANCE.getProcessingPatternState(), processingState); + + // Kismet: a bad recipe will be caught automatically + Map result; + try { + result = getDetailsForItem(patternStack); + } + catch(Exception e) { + throw new RuntimeException("Pattern couldn't be built"); + } + + turtle.getInventory().removeItem(turtle.getSelectedSlot(), 1); + turtle.getInventory().setItem(destinationSlot, patternStack); + + // And now the reversal of the ugly hack in getDetails above. + return MethodResult.of(true, result); + } + catch(Exception e) { + // I like exceptions but the other peripherals in this mod don't use them. So just return errors as strings. + return MethodResult.of(false, e.getMessage()); + } + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/turtles/TurtlePatternGridUpgrade.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/turtles/TurtlePatternGridUpgrade.java new file mode 100644 index 000000000..2953db57a --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/turtles/TurtlePatternGridUpgrade.java @@ -0,0 +1,40 @@ +package de.srendi.advancedperipherals.common.addons.computercraft.turtles; + +import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.api.upgrades.UpgradeType; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.PatternGridPeripheral; +import de.srendi.advancedperipherals.common.setup.CCRegistration; +import de.srendi.advancedperipherals.lib.turtle.PeripheralTurtleUpgrade; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class TurtlePatternGridUpgrade extends PeripheralTurtleUpgrade { + + public TurtlePatternGridUpgrade(ItemStack stack) { + super(CCRegistration.ID.PATTERN_TURTLE, stack); + } + + @Override + public ModelResourceLocation getLeftModel() { + return new ModelResourceLocation(AdvancedPeripherals.getRL("turtle_pattern_upgrade_left"), "inventory"); + } + + @Override + public ModelResourceLocation getRightModel() { + return new ModelResourceLocation(AdvancedPeripherals.getRL("turtle_pattern_upgrade_right"), "inventory"); + } + + @Override + protected PatternGridPeripheral buildPeripheral(@NotNull ITurtleAccess turtle, @NotNull TurtleSide side) { + return new PatternGridPeripheral(turtle, side); + } + + @Override + public UpgradeType getType() { + return CCRegistration.PATTERN_TURTLE.get(); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java b/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java index edc89370b..4d0980de9 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java +++ b/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java @@ -72,6 +72,7 @@ private void addTurtles() { addTurtle(CCRegistration.ID.ENVIRONMENT_TURTLE, "Environment"); addTurtle(CCRegistration.ID.PLAYER_TURTLE, "Player Detector"); addTurtle(CCRegistration.ID.GEOSCANNER_TURTLE, "Geo"); + addTurtle(CCRegistration.ID.PATTERN_TURTLE, "Pattern Grid"); addTurtle(CCRegistration.ID.COMPASS_TURTLE, "Compass"); addTurtle(CCRegistration.ID.WEAK_AUTOMATA, "Weak automata"); addTurtle(CCRegistration.ID.HUSBANDRY_AUTOMATA, "Husbandry automata"); diff --git a/src/main/java/de/srendi/advancedperipherals/common/setup/CCRegistration.java b/src/main/java/de/srendi/advancedperipherals/common/setup/CCRegistration.java index c05b0402a..9194b6046 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/setup/CCRegistration.java +++ b/src/main/java/de/srendi/advancedperipherals/common/setup/CCRegistration.java @@ -17,6 +17,7 @@ import de.srendi.advancedperipherals.common.addons.computercraft.turtles.TurtleCompassUpgrade; import de.srendi.advancedperipherals.common.addons.computercraft.turtles.TurtleEnvironmentDetectorUpgrade; import de.srendi.advancedperipherals.common.addons.computercraft.turtles.TurtleGeoScannerUpgrade; +import de.srendi.advancedperipherals.common.addons.computercraft.turtles.TurtlePatternGridUpgrade; import de.srendi.advancedperipherals.common.addons.computercraft.turtles.TurtlePlayerDetectorUpgrade; import de.srendi.advancedperipherals.common.addons.computercraft.turtles.metaphysics.EndAutomata; import de.srendi.advancedperipherals.common.addons.computercraft.turtles.metaphysics.HusbandryAutomata; @@ -35,6 +36,7 @@ public class CCRegistration { public static final DeferredHolder, UpgradeType> CHUNKY_TURTLE = Registration.TURTLE_SERIALIZER.register(ID.CHUNKY_TURTLE.getPath(), () -> UpgradeType.simpleWithCustomItem(TurtleChunkyUpgrade::new)); public static final DeferredHolder, UpgradeType> GEO_SCANNER_TURTLE = Registration.TURTLE_SERIALIZER.register(ID.GEOSCANNER_TURTLE.getPath(), () -> UpgradeType.simpleWithCustomItem(TurtleGeoScannerUpgrade::new)); public static final DeferredHolder, UpgradeType> COMPASS_TURTLE = Registration.TURTLE_SERIALIZER.register(ID.COMPASS_TURTLE.getPath(), () -> UpgradeType.simpleWithCustomItem(TurtleCompassUpgrade::new)); + public static final DeferredHolder, UpgradeType> PATTERN_TURTLE = Registration.TURTLE_SERIALIZER.register(ID.PATTERN_TURTLE.getPath(), () -> UpgradeType.simpleWithCustomItem(TurtlePatternGridUpgrade::new)); public static final DeferredHolder, UpgradeType> WEAK_TURTLE = Registration.TURTLE_SERIALIZER.register(ID.WEAK_AUTOMATA.getPath(), () -> UpgradeType.simpleWithCustomItem(WeakAutomata::new)); public static final DeferredHolder, UpgradeType> END_TURTLE = Registration.TURTLE_SERIALIZER.register(ID.END_AUTOMATA.getPath(), () -> UpgradeType.simpleWithCustomItem(EndAutomata::new)); public static final DeferredHolder, UpgradeType> HUSBANDRY_TURTLE = Registration.TURTLE_SERIALIZER.register(ID.HUSBANDRY_AUTOMATA.getPath(), () -> UpgradeType.simpleWithCustomItem(HusbandryAutomata::new)); @@ -61,6 +63,7 @@ public static class ID { public static final ResourceLocation CHUNKY_TURTLE = AdvancedPeripherals.getRL("chunky_turtle"); public static final ResourceLocation GEOSCANNER_TURTLE = AdvancedPeripherals.getRL("geoscanner_turtle"); public static final ResourceLocation COMPASS_TURTLE = AdvancedPeripherals.getRL("compass_turtle"); + public static final ResourceLocation PATTERN_TURTLE = AdvancedPeripherals.getRL("pattern_turtle"); public static final ResourceLocation WEAK_AUTOMATA = AdvancedPeripherals.getRL("weak_automata"); public static final ResourceLocation END_AUTOMATA = AdvancedPeripherals.getRL("end_automata"); public static final ResourceLocation HUSBANDRY_AUTOMATA = AdvancedPeripherals.getRL("husbandry_automata"); diff --git a/src/main/resources/assets/advancedperipherals/models/block/turtle_pattern_left.json b/src/main/resources/assets/advancedperipherals/models/block/turtle_pattern_left.json new file mode 100644 index 000000000..fc87a80e1 --- /dev/null +++ b/src/main/resources/assets/advancedperipherals/models/block/turtle_pattern_left.json @@ -0,0 +1,4 @@ +{ + "parent": "computercraft:block/turtle_upgrade_base_left", + "textures": {"texture": "advancedperipherals:block/pattern_grid_small"} +} \ No newline at end of file diff --git a/src/main/resources/assets/advancedperipherals/models/block/turtle_pattern_right.json b/src/main/resources/assets/advancedperipherals/models/block/turtle_pattern_right.json new file mode 100644 index 000000000..107c410d3 --- /dev/null +++ b/src/main/resources/assets/advancedperipherals/models/block/turtle_pattern_right.json @@ -0,0 +1,4 @@ +{ + "parent": "computercraft:block/turtle_upgrade_base_right", + "textures": {"texture": "advancedperipherals:block/pattern_grid_small"} +} \ No newline at end of file diff --git a/src/main/resources/assets/advancedperipherals/models/item/turtle_pattern_upgrade_left.json b/src/main/resources/assets/advancedperipherals/models/item/turtle_pattern_upgrade_left.json new file mode 100644 index 000000000..62f95a889 --- /dev/null +++ b/src/main/resources/assets/advancedperipherals/models/item/turtle_pattern_upgrade_left.json @@ -0,0 +1,6 @@ +{ + "parent": "advancedperipherals:block/turtle_upgrade_base_left", + "textures": { + "texture": "advancedperipherals:block/pattern_grid_small" + } +} diff --git a/src/main/resources/assets/advancedperipherals/models/item/turtle_pattern_upgrade_right.json b/src/main/resources/assets/advancedperipherals/models/item/turtle_pattern_upgrade_right.json new file mode 100644 index 000000000..6ec2d358a --- /dev/null +++ b/src/main/resources/assets/advancedperipherals/models/item/turtle_pattern_upgrade_right.json @@ -0,0 +1,6 @@ +{ + "parent": "advancedperipherals:block/turtle_upgrade_base_right", + "textures": { + "texture": "advancedperipherals:block/pattern_grid_small" + } +} diff --git a/src/main/resources/assets/advancedperipherals/textures/block/pattern_grid.png b/src/main/resources/assets/advancedperipherals/textures/block/pattern_grid.png new file mode 100644 index 0000000000000000000000000000000000000000..02e9e85d15d1541b9e7f336be15dc25739441a3c GIT binary patch literal 647 zcmV;20(kw2P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vGf6951U69E94oEQKA0q{vgK~y-)b(1@96G0S(zn$In zdQ1>D5fZoxQqq9Z(<2HfK|(1gpyMHEprfRs1W{0?pr%3aClEqHP$3j46o8drJ8SPe zc4vkH*4lN*Y39xy&AsP7PIUF!^}s(UrD_u?uC(E$L0MN-Y0IV@B ze~s~E{5L>-X`;?7rEJ+X>ZD2YQ}5n$bkDMxbUB_z1P7jokrPaO%x*kK|EO5qezZs; zlu`?-SrQ}Dm;mI~F*OzAq9V5rz@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vGf6951U69E94oEQKA0lP^=K~y-)t&_it6G0TmKRcQ1 z-o%Ss7UXg{ixaf4xK6A@1UpLwu{4Ds*!XwYh+=iMe}RHnh}hXF7FLHGh=mDUHo5$G z+1>1{jkCAAIXho7GjHC!`M&qQU+?a{`#FD2MBEl*%$l$4ccuY4TOHi$2LVYO1KF)8xkPd1ju+wcSk)-h3j;C)DGWtIK5^ ztAN*o1#_!-_j5|NYVh`Gfog}?^a3JM#2aHyo9G7tB0`Ide)k+CDYtu@9Ho{!do2Lw zc3OclEfq0+?OPOrFhx;RS<0RR9107*qoM6N<$f-Q0Y5dZ)H literal 0 HcmV?d00001 From 86507ede4c82e927e1815f14f4e7a7a46b1c4d10 Mon Sep 17 00:00:00 2001 From: John Lemme Date: Fri, 3 Oct 2025 18:56:24 -0400 Subject: [PATCH 2/9] feat: Added Javadocs to internal functions --- .../peripheral/PatternGridPeripheral.java | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java index 758fdd73b..00328c9bf 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -55,7 +55,13 @@ public boolean isEnabled() { return APAddon.REFINEDSTORAGE.isLoaded() && APConfig.PERIPHERALS_CONFIG.enableCompassTurtle.get(); } - // Returns information about a pattern, in the style of the RSBridge.getPattern() method. + /** + * Returns information about a pattern, in the style of the RS Bridge ".getPattern()" method. + * + * @param target the pattern item to examine + * @return a Map containing the properties as returned by the RSApi + * @throws RuntimeException if the pattern is blank, or if the item isn't a pattern at all + */ public Map getDetailsForItem(ItemStack target) throws RuntimeException { if(!target.is(Items.INSTANCE.getPattern())) { throw new RuntimeException("Not a pattern"); @@ -67,7 +73,7 @@ public Map getDetailsForItem(ItemStack target) throws RuntimeExc } if(pattern.isEmpty()) { - throw new RuntimeException("Pattern is empty"); + throw new RuntimeException("Pattern is blank"); } else { return (Map) RSApi.parsePattern(pattern.get(), null); @@ -92,8 +98,14 @@ public MethodResult getDetails(Optional slot) { // ----------------------------------------------------------------------------------------------------------------- - // Make sure we have patterns available. - public int verifyPatternsInSlot(int slot) throws RuntimeException { + /** + * Ensures that a slot contains patterns. + * + * @param slot the slot number to look in + * @return how many patterns are in the slot + * @throws RuntimeException if the slot doesn't contain any patterns + */ + private int verifyPatternsInSlot(int slot) throws RuntimeException { Container turtleInventory = this.getPeripheralOwner().getTurtle().getInventory(); ItemStack selected = turtleInventory.getItem(slot); if(selected.is(Items.INSTANCE.getPattern()) && selected.getCount() > 0) { @@ -105,8 +117,14 @@ public int verifyPatternsInSlot(int slot) throws RuntimeException { } } - // Find a slot to insert a newly built pattern. - public int getFreeSlotAfterBuild(int source) throws RuntimeException { + /** + * Finds a free slot in which to insert the result of a pattern build. + * + * @param source the slot containing our source pattern + * @return which slot to store the built pattern in + * @throws RuntimeException if there are no free slots in the turtle + */ + private int getFreeSlotAfterBuild(int source) throws RuntimeException { // We assume that we'll consume one of whatever is in the source slot. Container turtleInventory = this.getPeripheralOwner().getTurtle().getInventory(); @@ -126,9 +144,16 @@ public int getFreeSlotAfterBuild(int source) throws RuntimeException { } } - public ResourceKey parseResourceName(String resourceName) throws RuntimeException { + /** + * Attempts to match a String resource name to an ItemResource or FluidResource. + * + * @param name the resource name + * @return a ResourceKey representing the resource + * @throws RuntimeException if the name doesn't match a resource + */ + private ResourceKey parseResourceName(String name) throws RuntimeException { try { - ResourceLocation location = ResourceLocation.parse(resourceName); + ResourceLocation location = ResourceLocation.parse(name); // Try as item first Item item = BuiltInRegistries.ITEM.get(location); if (!item.equals(BuiltInRegistries.ITEM.get(ResourceLocation.parse("minecraft:air")))) { @@ -139,16 +164,15 @@ public ResourceKey parseResourceName(String resourceName) throws RuntimeExceptio if (!fluid.equals(Fluids.EMPTY)) { return new FluidResource(fluid); } - throw new RuntimeException("Couldn't find item or fluid: " + resourceName); + throw new RuntimeException("Couldn't find item or fluid: " + name); } catch(Exception e) { - throw new RuntimeException("Couldn't find item or fluid: " + resourceName); + throw new RuntimeException("Couldn't find item or fluid: " + name); } } // ----------------------------------------------------------------------------------------------------------------- // Build a crafting pattern from a table of slots. - // TODO: replace with Lua exceptions @LuaFunction(mainThread = true) public MethodResult buildCrafting(Map recipeInput, Optional fuzzy) { ITurtleAccess turtle = this.getPeripheralOwner().getTurtle(); From b2b083b170ae8b4ecb4b8ca2459ada48523e6487 Mon Sep 17 00:00:00 2001 From: John Lemme Date: Tue, 7 Oct 2025 11:23:51 -0400 Subject: [PATCH 3/9] fix: a comment --- .../addons/computercraft/peripheral/PatternGridPeripheral.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java index 00328c9bf..fd05713fd 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -156,6 +156,7 @@ private ResourceKey parseResourceName(String name) throws RuntimeException { ResourceLocation location = ResourceLocation.parse(name); // Try as item first Item item = BuiltInRegistries.ITEM.get(location); + // The parser seems to be greedy... sometimes it'll map an invalid Item to air. Make sure it behaves. if (!item.equals(BuiltInRegistries.ITEM.get(ResourceLocation.parse("minecraft:air")))) { return new ItemResource(item); } From 9a4c4fe46f60bb45ff64515091b12f670a7ca5f4 Mon Sep 17 00:00:00 2001 From: John Lemme Date: Tue, 7 Oct 2025 11:45:45 -0400 Subject: [PATCH 4/9] fix: Set up CheckStyle and linted previous commits --- .../peripheral/PatternGridPeripheral.java | 86 ++++++++----------- 1 file changed, 36 insertions(+), 50 deletions(-) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java index fd05713fd..59e541ad8 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -13,12 +13,7 @@ import com.refinedmods.refinedstorage.common.support.resource.FluidResource; import com.refinedmods.refinedstorage.common.support.resource.ItemResource; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.lua.MethodResult; @@ -62,9 +57,9 @@ public boolean isEnabled() { * @return a Map containing the properties as returned by the RSApi * @throws RuntimeException if the pattern is blank, or if the item isn't a pattern at all */ - public Map getDetailsForItem(ItemStack target) throws RuntimeException { - if(!target.is(Items.INSTANCE.getPattern())) { - throw new RuntimeException("Not a pattern"); + public Map getDetailsForItem(ItemStack target) throws IllegalArgumentException, IllegalStateException { + if (!target.is(Items.INSTANCE.getPattern())) { + throw new IllegalArgumentException("Not a pattern"); } Optional pattern = Optional.empty(); @@ -72,10 +67,9 @@ public Map getDetailsForItem(ItemStack target) throws RuntimeExc pattern = patternProvider.getPattern(target, this.getLevel()); } - if(pattern.isEmpty()) { - throw new RuntimeException("Pattern is blank"); - } - else { + if (pattern.isEmpty()) { + throw new IllegalStateException("Pattern is blank"); + } else { return (Map) RSApi.parsePattern(pattern.get(), null); } } @@ -85,12 +79,11 @@ public MethodResult getDetails(Optional slot) { try { ITurtleAccess turtle = this.getPeripheralOwner().getTurtle(); // Note: the hack at the end of the line here converts between CC slot numbering (from 1) to Java slot numbering (from 0). - // It's broken out like this so we can call getDetailsInSlot from the builder functions later, and not have to care + // It's broken out like this so we can call getDetailsForItem from the builder functions later, and not have to care // what Lua thinks about array indexing. ItemStack target = turtle.getInventory().getItem(slot.orElse(turtle.getSelectedSlot() + 1) - 1); return MethodResult.of(getDetailsForItem(target)); - } - catch(RuntimeException e) { + } catch (RuntimeException e) { // Did you know: passing a naked RuntimeException to the Lua interpreter causes a bizarre hang return MethodResult.of(false, e.getMessage()); } @@ -105,15 +98,14 @@ public MethodResult getDetails(Optional slot) { * @return how many patterns are in the slot * @throws RuntimeException if the slot doesn't contain any patterns */ - private int verifyPatternsInSlot(int slot) throws RuntimeException { + private int verifyPatternsInSlot(int slot) throws IllegalStateException { Container turtleInventory = this.getPeripheralOwner().getTurtle().getInventory(); ItemStack selected = turtleInventory.getItem(slot); - if(selected.is(Items.INSTANCE.getPattern()) && selected.getCount() > 0) { + if (selected.is(Items.INSTANCE.getPattern()) && selected.getCount() > 0) { return selected.getCount(); - } - else { + } else { // TODO: do I even care to do this here - throw new RuntimeException("No pattern available"); + throw new IllegalStateException("No pattern available"); } } @@ -124,23 +116,22 @@ private int verifyPatternsInSlot(int slot) throws RuntimeException { * @return which slot to store the built pattern in * @throws RuntimeException if there are no free slots in the turtle */ - private int getFreeSlotAfterBuild(int source) throws RuntimeException { + private int getFreeSlotAfterBuild(int source) throws IllegalStateException { // We assume that we'll consume one of whatever is in the source slot. Container turtleInventory = this.getPeripheralOwner().getTurtle().getInventory(); - if(turtleInventory.getItem(source).getCount() <= 1) { + if (turtleInventory.getItem(source).getCount() <= 1) { // We're using the last of something, so feel free to take its place. return source; - } - else { + } else { // Otherwise... are there any free slots? - for(int i = 0; i < turtleInventory.getContainerSize(); i++) { + for (int i = 0; i < turtleInventory.getContainerSize(); i++) { if (turtleInventory.getItem(i).getCount() == 0) { return i; } } // Turtle's full, keep movin' - throw new RuntimeException("No room in destination"); + throw new IllegalStateException("No room in destination"); } } @@ -151,7 +142,7 @@ private int getFreeSlotAfterBuild(int source) throws RuntimeException { * @return a ResourceKey representing the resource * @throws RuntimeException if the name doesn't match a resource */ - private ResourceKey parseResourceName(String name) throws RuntimeException { + private ResourceKey parseResourceName(String name) throws IllegalArgumentException { try { ResourceLocation location = ResourceLocation.parse(name); // Try as item first @@ -165,9 +156,9 @@ private ResourceKey parseResourceName(String name) throws RuntimeException { if (!fluid.equals(Fluids.EMPTY)) { return new FluidResource(fluid); } - throw new RuntimeException("Couldn't find item or fluid: " + name); - } catch(Exception e) { - throw new RuntimeException("Couldn't find item or fluid: " + name); + throw new IllegalArgumentException("Couldn't find item or fluid: " + name); + } catch (Exception e) { + throw new IllegalArgumentException("Couldn't find item or fluid: " + name); } } @@ -191,10 +182,10 @@ public MethodResult buildCrafting(Map recipeInput, Optional fuzzy for (Object o : recipeInput.keySet()) { // Note: I'm assuming that Lua returns all numbers as doubles. // Only care about slots 1 through 9 in the input; we'll just ignore everything else. - if(o instanceof Double) { + if (o instanceof Double) { int slot = ((Double) o).intValue() - 1; - if(slot >= 0 && slot < 9) { - if(recipeInput.get(o) instanceof String) { + if (slot >= 0 && slot < 9) { + if (recipeInput.get(o) instanceof String) { ingredients.set(slot, ((ItemResource) parseResourceName((String) recipeInput.get(o))).toItemStack()); } else { return MethodResult.of(false, "Couldn't parse item in slot " + slot); @@ -211,9 +202,8 @@ public MethodResult buildCrafting(Map recipeInput, Optional fuzzy // Kismet: a bad recipe will be caught automatically Map result; try { - result = getDetailsForItem(patternStack); - } - catch(Exception e) { + result = getDetailsForItem(patternStack); + } catch (Exception e) { throw new RuntimeException("Bad recipe"); } @@ -221,8 +211,7 @@ public MethodResult buildCrafting(Map recipeInput, Optional fuzzy turtle.getInventory().setItem(destinationSlot, patternStack); return MethodResult.of(true, result); - } - catch(Exception e) { + } catch (Exception e) { // I like exceptions but the other peripherals in this mod don't use them. So just return errors as strings. return MethodResult.of(false, e.getMessage()); } @@ -266,12 +255,12 @@ public MethodResult buildProcessing(Map recipeInput) { try { // Lua sends in all numbers as doubles. We don't really care about the index. Map> inputMap = (Map>) recipeInput.get("inputs"); - for(Map thisInput : inputMap.values()) { + for (Map thisInput : inputMap.values()) { int thisCount = ((Double) thisInput.get("count")).intValue(); ResourceKey thisItem = parseResourceName((String) thisInput.get("name")); List theseAlts = new ArrayList<>(); - - if(thisInput.containsKey("alts")) { + + if (thisInput.containsKey("alts")) { theseAlts = ((Map) thisInput.get("alts")).values().stream().map( ResourceLocation::parse).toList(); } @@ -280,25 +269,23 @@ public MethodResult buildProcessing(Map recipeInput) { } Map> outputMap = (Map>) recipeInput.get("outputs"); - for(Map thisOutput : outputMap.values()) { + for (Map thisOutput : outputMap.values()) { int thisCount = ((Double) thisOutput.get("count")).intValue(); ResourceKey thisItem = parseResourceName((String) thisOutput.get("name")); results.add(Optional.of(new ResourceAmount(thisItem, thisCount))); } - } - catch(Exception e) { + } catch (Exception e) { throw new RuntimeException("Recipe is malformed"); } ProcessingPatternState processingState = new ProcessingPatternState(ingredients, results); patternStack.set(DataComponents.INSTANCE.getProcessingPatternState(), processingState); - + // Kismet: a bad recipe will be caught automatically Map result; try { result = getDetailsForItem(patternStack); - } - catch(Exception e) { + } catch (Exception e) { throw new RuntimeException("Pattern couldn't be built"); } @@ -307,8 +294,7 @@ public MethodResult buildProcessing(Map recipeInput) { // And now the reversal of the ugly hack in getDetails above. return MethodResult.of(true, result); - } - catch(Exception e) { + } catch (Exception e) { // I like exceptions but the other peripherals in this mod don't use them. So just return errors as strings. return MethodResult.of(false, e.getMessage()); } From 3f8d1625417a7ff0a6d0518ae012aa9785365b5f Mon Sep 17 00:00:00 2001 From: John Lemme Date: Tue, 7 Oct 2025 14:14:39 -0400 Subject: [PATCH 5/9] Update src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java Co-authored-by: Kevin Z Signed-off-by: John Lemme --- .../addons/computercraft/peripheral/PatternGridPeripheral.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java index 59e541ad8..1010281eb 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -148,7 +148,7 @@ private ResourceKey parseResourceName(String name) throws IllegalArgumentExcepti // Try as item first Item item = BuiltInRegistries.ITEM.get(location); // The parser seems to be greedy... sometimes it'll map an invalid Item to air. Make sure it behaves. - if (!item.equals(BuiltInRegistries.ITEM.get(ResourceLocation.parse("minecraft:air")))) { + if (!item.equals(Items.AIR)) { return new ItemResource(item); } // Try as fluid From 0478bbb7062e64f6513628815a97c8b3f66dc905 Mon Sep 17 00:00:00 2001 From: John Lemme Date: Wed, 8 Oct 2025 00:35:45 -0400 Subject: [PATCH 6/9] fix: Now utilizing LuaTable --- .../peripheral/PatternGridPeripheral.java | 121 +++++++++--------- 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java index 59e541ad8..9e8de4413 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -13,10 +13,11 @@ import com.refinedmods.refinedstorage.common.support.resource.FluidResource; import com.refinedmods.refinedstorage.common.support.resource.ItemResource; -import java.util.*; - +import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.LuaTable; import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.lua.ObjectLuaTable; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleSide; import de.srendi.advancedperipherals.common.addons.APAddon; @@ -33,6 +34,13 @@ import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + public class PatternGridPeripheral extends BasePeripheral { public static final String PERIPHERAL_TYPE = "pattern_grid"; @@ -55,7 +63,8 @@ public boolean isEnabled() { * * @param target the pattern item to examine * @return a Map containing the properties as returned by the RSApi - * @throws RuntimeException if the pattern is blank, or if the item isn't a pattern at all + * @throws IllegalArgumentException if the item isn't a pattern + * @throws IllegalStateException if the pattern is blank */ public Map getDetailsForItem(ItemStack target) throws IllegalArgumentException, IllegalStateException { if (!target.is(Items.INSTANCE.getPattern())) { @@ -69,9 +78,9 @@ public Map getDetailsForItem(ItemStack target) throws IllegalArg if (pattern.isEmpty()) { throw new IllegalStateException("Pattern is blank"); - } else { - return (Map) RSApi.parsePattern(pattern.get(), null); } + + return (Map) RSApi.parsePattern(pattern.get(), null); } @LuaFunction(mainThread = true) @@ -83,8 +92,7 @@ public MethodResult getDetails(Optional slot) { // what Lua thinks about array indexing. ItemStack target = turtle.getInventory().getItem(slot.orElse(turtle.getSelectedSlot() + 1) - 1); return MethodResult.of(getDetailsForItem(target)); - } catch (RuntimeException e) { - // Did you know: passing a naked RuntimeException to the Lua interpreter causes a bizarre hang + } catch (IllegalStateException | IllegalArgumentException e) { return MethodResult.of(false, e.getMessage()); } } @@ -96,7 +104,7 @@ public MethodResult getDetails(Optional slot) { * * @param slot the slot number to look in * @return how many patterns are in the slot - * @throws RuntimeException if the slot doesn't contain any patterns + * @throws IllegalStateException if the slot doesn't contain any patterns */ private int verifyPatternsInSlot(int slot) throws IllegalStateException { Container turtleInventory = this.getPeripheralOwner().getTurtle().getInventory(); @@ -114,7 +122,7 @@ private int verifyPatternsInSlot(int slot) throws IllegalStateException { * * @param source the slot containing our source pattern * @return which slot to store the built pattern in - * @throws RuntimeException if there are no free slots in the turtle + * @throws IllegalStateException if there are no free slots in the turtle */ private int getFreeSlotAfterBuild(int source) throws IllegalStateException { // We assume that we'll consume one of whatever is in the source slot. @@ -140,26 +148,22 @@ private int getFreeSlotAfterBuild(int source) throws IllegalStateException { * * @param name the resource name * @return a ResourceKey representing the resource - * @throws RuntimeException if the name doesn't match a resource + * @throws IllegalArgumentException if the name doesn't match a resource */ private ResourceKey parseResourceName(String name) throws IllegalArgumentException { - try { - ResourceLocation location = ResourceLocation.parse(name); - // Try as item first - Item item = BuiltInRegistries.ITEM.get(location); - // The parser seems to be greedy... sometimes it'll map an invalid Item to air. Make sure it behaves. - if (!item.equals(BuiltInRegistries.ITEM.get(ResourceLocation.parse("minecraft:air")))) { - return new ItemResource(item); - } - // Try as fluid - Fluid fluid = BuiltInRegistries.FLUID.get(location); - if (!fluid.equals(Fluids.EMPTY)) { - return new FluidResource(fluid); - } - throw new IllegalArgumentException("Couldn't find item or fluid: " + name); - } catch (Exception e) { - throw new IllegalArgumentException("Couldn't find item or fluid: " + name); + ResourceLocation location = ResourceLocation.parse(name); + // Try as item first + Item item = BuiltInRegistries.ITEM.get(location); + // The parser seems to be greedy... sometimes it'll map an invalid Item to air. Make sure it behaves. + if (!item.equals(BuiltInRegistries.ITEM.get(ResourceLocation.parse("minecraft:air")))) { + return new ItemResource(item); } + // Try as fluid + Fluid fluid = BuiltInRegistries.FLUID.get(location); + if (!fluid.equals(Fluids.EMPTY)) { + return new FluidResource(fluid); + } + throw new IllegalArgumentException("Couldn't find item or fluid: " + name); } // ----------------------------------------------------------------------------------------------------------------- @@ -199,19 +203,19 @@ public MethodResult buildCrafting(Map recipeInput, Optional fuzzy CraftingPatternState craftingState = new CraftingPatternState(fuzzy.orElse(false), positioned); patternStack.set(DataComponents.INSTANCE.getCraftingPatternState(), craftingState); - // Kismet: a bad recipe will be caught automatically + // Kismet: a bad recipe will be caught automatically, since it'll stay a blank pattern Map result; try { result = getDetailsForItem(patternStack); - } catch (Exception e) { - throw new RuntimeException("Bad recipe"); + } catch (IllegalStateException e) { + throw new IllegalStateException("Bad recipe"); } turtle.getInventory().removeItem(turtle.getSelectedSlot(), 1); turtle.getInventory().setItem(destinationSlot, patternStack); return MethodResult.of(true, result); - } catch (Exception e) { + } catch (IllegalStateException | IllegalArgumentException e) { // I like exceptions but the other peripherals in this mod don't use them. So just return errors as strings. return MethodResult.of(false, e.getMessage()); } @@ -221,8 +225,9 @@ public MethodResult buildCrafting(Map recipeInput, Optional fuzzy // Build a processing pattern from a list of ingredients. @LuaFunction(mainThread = true) - public MethodResult buildProcessing(Map recipeInput) { + public MethodResult buildProcessing(Map recipeArg) { ITurtleAccess turtle = this.getPeripheralOwner().getTurtle(); + LuaTable recipeTable = new ObjectLuaTable(recipeArg); try { verifyPatternsInSlot(turtle.getSelectedSlot()); @@ -249,52 +254,50 @@ public MethodResult buildProcessing(Map recipeInput) { * } * } * - * Note that input names can be passed as a string or as an array of strings (for alternates). - * That's a lot to validate, so barring better options, we'll just catch and error out. */ - try { - // Lua sends in all numbers as doubles. We don't really care about the index. - Map> inputMap = (Map>) recipeInput.get("inputs"); - for (Map thisInput : inputMap.values()) { - int thisCount = ((Double) thisInput.get("count")).intValue(); - ResourceKey thisItem = parseResourceName((String) thisInput.get("name")); - List theseAlts = new ArrayList<>(); - - if (thisInput.containsKey("alts")) { - theseAlts = ((Map) thisInput.get("alts")).values().stream().map( - ResourceLocation::parse).toList(); - } - ingredients.add(Optional.of(new ProcessingPatternState.ProcessingIngredient(new ResourceAmount(thisItem, thisCount), theseAlts))); - } + // Lua sends in all numbers as doubles. We don't really care about the index. + LuaTable inputsTable = new ObjectLuaTable(recipeTable.getTable("inputs")); + + for (LuaTable thisInput : inputsTable.values().stream().map(Map.class::cast).map(ObjectLuaTable::new).toList()) { + int thisCount = thisInput.getInt("count"); + ResourceKey thisItem = parseResourceName(thisInput.getString("name")); - Map> outputMap = (Map>) recipeInput.get("outputs"); - for (Map thisOutput : outputMap.values()) { - int thisCount = ((Double) thisOutput.get("count")).intValue(); - ResourceKey thisItem = parseResourceName((String) thisOutput.get("name")); - results.add(Optional.of(new ResourceAmount(thisItem, thisCount))); + List theseAlts = new ArrayList<>(); + + if (thisInput.containsKey("alts")) { + theseAlts = thisInput.getTable("alts").values().stream() + .map(String.class::cast).map(ResourceLocation::parse).toList(); } - } catch (Exception e) { - throw new RuntimeException("Recipe is malformed"); + + ingredients.add(Optional.of( + new ProcessingPatternState.ProcessingIngredient(new ResourceAmount(thisItem, thisCount), theseAlts))); + } + + LuaTable outputsTable = new ObjectLuaTable(recipeTable.getTable("outputs")); + + for (LuaTable thisOutput : outputsTable.values().stream().map(Map.class::cast).map(ObjectLuaTable::new).toList()) { + int thisCount = thisOutput.getInt("count"); + ResourceKey thisItem = parseResourceName(thisOutput.getString("name")); + + results.add(Optional.of(new ResourceAmount(thisItem, thisCount))); } ProcessingPatternState processingState = new ProcessingPatternState(ingredients, results); patternStack.set(DataComponents.INSTANCE.getProcessingPatternState(), processingState); - // Kismet: a bad recipe will be caught automatically Map result; try { result = getDetailsForItem(patternStack); - } catch (Exception e) { - throw new RuntimeException("Pattern couldn't be built"); + } catch (IllegalStateException e) { + throw new IllegalStateException("Bad recipe"); } turtle.getInventory().removeItem(turtle.getSelectedSlot(), 1); turtle.getInventory().setItem(destinationSlot, patternStack); - // And now the reversal of the ugly hack in getDetails above. return MethodResult.of(true, result); - } catch (Exception e) { + } catch (IllegalStateException | IllegalArgumentException | LuaException e) { // I like exceptions but the other peripherals in this mod don't use them. So just return errors as strings. return MethodResult.of(false, e.getMessage()); } From ae0e4da162c18413bac855efa24a9c98d3e24385 Mon Sep 17 00:00:00 2001 From: John Lemme Date: Wed, 8 Oct 2025 03:01:47 -0400 Subject: [PATCH 7/9] fix: Classpath items --- .../addons/computercraft/peripheral/PatternGridPeripheral.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java index 9e8de4413..83afc8871 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -155,7 +155,7 @@ private ResourceKey parseResourceName(String name) throws IllegalArgumentExcepti // Try as item first Item item = BuiltInRegistries.ITEM.get(location); // The parser seems to be greedy... sometimes it'll map an invalid Item to air. Make sure it behaves. - if (!item.equals(BuiltInRegistries.ITEM.get(ResourceLocation.parse("minecraft:air")))) { + if (!item.equals(net.minecraft.world.item.Items.AIR)) { return new ItemResource(item); } // Try as fluid From 9e489c3d42113e8c287fc340d212eb64238c5224 Mon Sep 17 00:00:00 2001 From: John Lemme Date: Wed, 8 Oct 2025 12:01:54 -0400 Subject: [PATCH 8/9] feat: Added toggle to config handler --- .../computercraft/peripheral/PatternGridPeripheral.java | 2 +- .../common/configuration/PeripheralsConfig.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java index 83afc8871..3bd4a516d 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -55,7 +55,7 @@ public PatternGridPeripheral(ITurtleAccess turtle, TurtleSide side) { @Override public boolean isEnabled() { - return APAddon.REFINEDSTORAGE.isLoaded() && APConfig.PERIPHERALS_CONFIG.enableCompassTurtle.get(); + return APAddon.REFINEDSTORAGE.isLoaded() && APConfig.PERIPHERALS_CONFIG.enablePatternGridTurtle.get(); } /** diff --git a/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java b/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java index 62a061140..2af8386e8 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java +++ b/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java @@ -58,6 +58,9 @@ public class PeripheralsConfig implements IAPConfig { public final ModConfigSpec.BooleanValue enableRSBridge; public final ModConfigSpec.IntValue rsConsumption; + // Pattern Grid turtle + public final ModConfigSpec.BooleanValue enablePatternGridTurtle; + // Environment Detector public final ModConfigSpec.BooleanValue enableEnvironmentDetector; @@ -162,6 +165,9 @@ public PeripheralsConfig() { enableRSBridge = builder.comment("Enable the Rs Bridge or not.").define("enableRsBridge", true); rsConsumption = builder.comment("Power consumption per tick.").defineInRange("rsPowerConsumption", 10, 0, Integer.MAX_VALUE); + pop("Pattern_Grid_Turtle", builder); + enablePatternGridTurtle = builder.comment("Enable the RS Pattern Grid turtle or not.").define("enablePatternGridTurtle", true); + pop("Environment_Detector", builder); enableEnvironmentDetector = builder.comment("Enable the Environment Detector or not.").define("enableEnvironmentDetector", true); From b730a97111fd5f27f5ba6c986c5708accad9f0ea Mon Sep 17 00:00:00 2001 From: Srendi Date: Mon, 20 Oct 2025 17:34:57 +0200 Subject: [PATCH 9/9] Replace Object return types to the actual return type --- .../computercraft/peripheral/PatternGridPeripheral.java | 2 +- .../common/addons/refinedstorage/RSApi.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java index 3bd4a516d..23f399d85 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PatternGridPeripheral.java @@ -80,7 +80,7 @@ public Map getDetailsForItem(ItemStack target) throws IllegalArg throw new IllegalStateException("Pattern is blank"); } - return (Map) RSApi.parsePattern(pattern.get(), null); + return RSApi.parsePattern(pattern.get(), null); } @LuaFunction(mainThread = true) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSApi.java b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSApi.java index 59307eb42..685e1a910 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSApi.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSApi.java @@ -606,7 +606,7 @@ public static boolean isSameResource(ResourceKey resource, ResourceKey toCompare return false; } - public static Object parseDiskDrive(StorageNetworkNode diskDrive, InWorldNetworkNodeContainer nodeContainer) { + public static Map parseDiskDrive(StorageNetworkNode diskDrive, InWorldNetworkNodeContainer nodeContainer) { Map properties = new HashMap<>(); StorageConfiguration storageConfiguration = diskDrive.getStorageConfiguration(); @@ -631,7 +631,7 @@ public static Object parseDiskDrive(StorageNetworkNode diskDrive, InWorldNetwork return properties; } - public static Object parseStorageDisk(StateTrackedStorage disk) { + public static Map parseStorageDisk(StateTrackedStorage disk) { Map properties = new HashMap<>(); properties.put("used", disk.getStored()); @@ -654,7 +654,7 @@ public static Object parseStorageDisk(StateTrackedStorage disk) { return properties; } - public static Object parseCraftingTask(@Nullable RSCraftJob task, TaskStatus status, @Nullable AutocraftingNetworkComponent autocraftingComponent) { + public static Map parseCraftingTask(@Nullable RSCraftJob task, TaskStatus status, @Nullable AutocraftingNetworkComponent autocraftingComponent) { Map properties = new HashMap<>(); properties.put("bridge_id", task == null ? -1 : task.getId()); @@ -669,7 +669,7 @@ public static Object parseCraftingTask(@Nullable RSCraftJob task, TaskStatus sta return properties; } - public static Object parsePattern(Pattern pattern, @Nullable AutocraftingNetworkComponent autocraftingComponent) { + public static Map parsePattern(Pattern pattern, @Nullable AutocraftingNetworkComponent autocraftingComponent) { if (pattern == null) return null;