diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 15e3241ee..30df7e0bc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -41,11 +41,12 @@ body: multiple: false options: - Own compiled build. - - 1.20.1-0.7.39r (Latest 1.20.1) + - 1.20.1-0.7.39r (Latest 1.20.1) - 1.20.1-0.7.38r - 1.20.1-0.7.37r - - 1.20.1-0.7.36r - - 1.19.2-0.7.35r (Latest 1.19.2) + - 1.20.1-0.7.36r + + - 1.19.2-0.7.35r (Latest 1.19.2) - 1.19.2-0.7.34r - 1.19.2-0.7.33r - 1.19.2-0.7.32r diff --git a/README.md b/README.md index dd7741d5c..69ad5b0cb 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,9 @@ will close pull requests related to translations. [Documentation]: https://advancedperipherals.madefor.cc/ -[Contributors]: https://github.com/Seniorendi/AdvancedPeripherals/graphs/contributors +[Contributors]: https://github.com/IntelligenceModding/AdvancedPeripherals/graphs/contributors [CurseForge]: https://www.curseforge.com/minecraft/mc-mods/advanced-peripherals -[Actions]: https://github.com/Seniorendi/AdvancedPeripherals/actions +[Actions]: https://github.com/IntelligenceModding/AdvancedPeripherals/actions [Crowdin]: https://crowdin.com/project/advanced-peripherals [Discord]: https://discord.intelligence-modding.de [Banner]: https://www.bisecthosting.com/images/CF/Advanced_Peripherals/BH_AP_Header.png 'Advanced Peripherals' @@ -77,7 +77,7 @@ will close pull requests related to translations. [@FatalMerlin]: https://github.com/FatalMerlin [@SirEdvin]: https://github.com/SirEdvin -[@Srendi]: https://github.com/Seniorendi +[@Srendi]: https://github.com/IntelligenceModding [@Olfi01]: https://github.com/Olfi01 [License]: LICENSE diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/AppEngApi.java b/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/AppEngApi.java index 9f74a0711..a0f06eb4e 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/AppEngApi.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/AppEngApi.java @@ -355,6 +355,8 @@ public static long getTotalItemStorage(IGridNode node) { net.minecraft.world.level.Level level = bus.getLevel(); BlockPos connectedInventoryPos = bus.getHost().getBlockEntity().getBlockPos().relative(bus.getSide()); BlockEntity connectedInventoryEntity = level.getBlockEntity(connectedInventoryPos); + if (connectedInventoryEntity == null) + continue; LazyOptional itemHandler = connectedInventoryEntity.getCapability(ForgeCapabilities.ITEM_HANDLER); if (itemHandler.isPresent()) { @@ -410,6 +412,8 @@ public static long getTotalFluidStorage(IGridNode node) { net.minecraft.world.level.Level level = bus.getLevel(); BlockPos connectedInventoryPos = bus.getHost().getBlockEntity().getBlockPos().relative(bus.getSide()); BlockEntity connectedInventoryEntity = level.getBlockEntity(connectedInventoryPos); + if (connectedInventoryEntity == null) + continue; LazyOptional fluidHandler = connectedInventoryEntity.getCapability(ForgeCapabilities.FLUID_HANDLER); if (fluidHandler.isPresent()) { diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/botania/ManaFlowerIntegration.java b/src/main/java/de/srendi/advancedperipherals/common/addons/botania/ManaFlowerIntegration.java index 7db9f0559..ad3a19289 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/botania/ManaFlowerIntegration.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/botania/ManaFlowerIntegration.java @@ -3,6 +3,7 @@ import dan200.computercraft.api.lua.LuaFunction; import de.srendi.advancedperipherals.lib.peripherals.APGenericPeripheral; import vazkii.botania.api.block_entity.GeneratingFlowerBlockEntity; +import vazkii.botania.common.block.BotaniaBlocks; public class ManaFlowerIntegration implements APGenericPeripheral { diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/integrations/NoteBlockIntegration.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/integrations/NoteBlockIntegration.java index b0b5c83c7..ad5a653d3 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/integrations/NoteBlockIntegration.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/integrations/NoteBlockIntegration.java @@ -7,6 +7,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.NoteBlock; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; import org.jetbrains.annotations.NotNull; public class NoteBlockIntegration extends BlockIntegrationPeripheral { @@ -50,6 +51,7 @@ public final int getNote() { public final void playNote() { if (world.isEmptyBlock(pos.above())) { world.blockEvent(pos, getBlock(), 0, 0); + world.gameEvent(null, GameEvent.NOTE_BLOCK_PLAY, pos); } } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/operations/SingleOperation.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/operations/SingleOperation.java index 08ebd3c6b..90205db17 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/operations/SingleOperation.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/operations/SingleOperation.java @@ -13,7 +13,8 @@ public enum SingleOperation implements IPeripheralOperation 1), + LINEAR(d -> d), SQRT(d -> (int) Math.sqrt(d)); private final Function factorFunction; diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/TurtlePeripheralOwner.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/TurtlePeripheralOwner.java index d3a842800..2d7446e1c 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/TurtlePeripheralOwner.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/TurtlePeripheralOwner.java @@ -53,13 +53,10 @@ public Direction getFacing() { return turtle.getDirection(); } - /** - * Not used for turtles - */ @NotNull @Override public FrontAndTop getOrientation() { - return FrontAndTop.NORTH_UP; + return FrontAndTop.fromFrontAndTop(getFacing(), Direction.UP); } @Nullable diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/ChatBoxPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/ChatBoxPeripheral.java index 4c323d31c..81122adc0 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/ChatBoxPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/ChatBoxPeripheral.java @@ -18,6 +18,7 @@ import de.srendi.advancedperipherals.common.configuration.APConfig; import de.srendi.advancedperipherals.common.events.Events; import de.srendi.advancedperipherals.common.util.CoordUtil; +import de.srendi.advancedperipherals.common.util.StringUtil; import de.srendi.advancedperipherals.lib.peripherals.BasePeripheral; import de.srendi.advancedperipherals.lib.peripherals.IPeripheralFunction; import de.srendi.advancedperipherals.network.APNetworking; @@ -110,8 +111,7 @@ public final MethodResult sendFormattedMessage(@NotNull IArguments arguments) th return withChatOperation(ignored -> { String message = arguments.getString(0); int maxRange = APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get(); - int range = arguments.optInt(4, maxRange); - range = maxRange == -1 ? range : Math.min(range, APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get()); + int range = arguments.optInt(4, -1); ResourceKey dimension = getLevel().dimension(); MutableComponent component = Component.Serializer.fromJson(message); if (component == null) @@ -121,14 +121,14 @@ public final MethodResult sendFormattedMessage(@NotNull IArguments arguments) th return MethodResult.of(null, "incorrect bracket string (e.g. [], {}, <>, ...)"); MutableComponent preparedMessage = appendPrefix( - arguments.optString(1, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get()).replaceAll("&", "\u00a7"), + StringUtil.convertAndToSectionMark(arguments.optString(1, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get())), arguments.optString(2, "[]"), - arguments.optString(3, "").replaceAll("&", "\u00a7") + StringUtil.convertAndToSectionMark(arguments.optString(3, "")) ).append(component); for (ServerPlayer player : ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayers()) { if (!APConfig.PERIPHERALS_CONFIG.chatBoxMultiDimensional.get() && player.level().dimension() != dimension) continue; - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) player.sendSystemMessage(preparedMessage); } return MethodResult.of(true); @@ -140,21 +140,20 @@ public final MethodResult sendMessage(@NotNull IArguments arguments) throws LuaE return withChatOperation(ignored -> { String message = arguments.getString(0); int maxRange = APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get(); - int range = arguments.optInt(4, maxRange); - range = maxRange == -1 ? range : Math.min(range, APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get()); + int range = arguments.optInt(4, -1); ResourceKey dimension = getLevel().dimension(); if (checkBrackets(arguments.optString(2))) return MethodResult.of(null, "incorrect bracket string (e.g. [], {}, <>, ...)"); MutableComponent preparedMessage = appendPrefix( - arguments.optString(1, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get()).replaceAll("&", "\u00a7"), + StringUtil.convertAndToSectionMark(arguments.optString(1, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get())), arguments.optString(2, "[]"), - arguments.optString(3, "").replaceAll("&", "\u00a7") + StringUtil.convertAndToSectionMark(arguments.optString(3, "")) ).append(message); for (ServerPlayer player : ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayers()) { if (!APConfig.PERIPHERALS_CONFIG.chatBoxMultiDimensional.get() && player.level().dimension() != dimension) continue; - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) player.sendSystemMessage(preparedMessage); } return MethodResult.of(true); @@ -167,8 +166,7 @@ public final MethodResult sendFormattedMessageToPlayer(@NotNull IArguments argum String message = arguments.getString(0); String playerName = arguments.getString(1); int maxRange = APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get(); - int range = arguments.optInt(5, maxRange); - range = maxRange == -1 ? range : Math.min(range, APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get()); + int range = arguments.optInt(5, -1); ResourceKey dimension = getLevel().dimension(); ServerPlayer player = getPlayer(playerName); if (player == null) @@ -182,14 +180,14 @@ public final MethodResult sendFormattedMessageToPlayer(@NotNull IArguments argum return MethodResult.of(null, "incorrect bracket string (e.g. [], {}, <>, ...)"); MutableComponent preparedMessage = appendPrefix( - arguments.optString(2, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get()).replaceAll("&", "\u00a7"), + StringUtil.convertAndToSectionMark(arguments.optString(2, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get())), arguments.optString(3, "[]"), - arguments.optString(4, "").replaceAll("&", "\u00a7") + StringUtil.convertAndToSectionMark(arguments.optString(4, "")) ).append(component); if (!APConfig.PERIPHERALS_CONFIG.chatBoxMultiDimensional.get() && player.level().dimension() != dimension) return MethodResult.of(false, "NOT_SAME_DIMENSION"); - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) player.sendSystemMessage(preparedMessage); return MethodResult.of(true); }); @@ -203,8 +201,7 @@ public final MethodResult sendFormattedToastToPlayer(@NotNull IArguments argumen String title = arguments.getString(1); String playerName = arguments.getString(2); int maxRange = APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get(); - int range = arguments.optInt(6, maxRange); - range = maxRange == -1 ? range : Math.min(range, APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get()); + int range = arguments.optInt(6, -1); ResourceKey dimension = getLevel().dimension(); ServerPlayer player = getPlayer(playerName); if (player == null) @@ -222,15 +219,15 @@ public final MethodResult sendFormattedToastToPlayer(@NotNull IArguments argumen return MethodResult.of(null, "incorrect bracket string (e.g. [], {}, <>, ,,,)"); MutableComponent preparedMessage = appendPrefix( - arguments.optString(3, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get()).replaceAll("&", "\u00a7"), + StringUtil.convertAndToSectionMark(arguments.optString(3, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get())), arguments.optString(4, "[]"), - arguments.optString(5, "").replaceAll("&", "\u00a7") + StringUtil.convertAndToSectionMark(arguments.optString(5, "")) ).append(messageComponent); if (!APConfig.PERIPHERALS_CONFIG.chatBoxMultiDimensional.get() && player.level().dimension() != dimension) return MethodResult.of(false, "NOT_SAME_DIMENSION"); - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) { + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) { ToastToClientPacket packet = new ToastToClientPacket(titleComponent, preparedMessage); APNetworking.sendTo(packet, player); } @@ -245,8 +242,7 @@ public final MethodResult sendMessageToPlayer(@NotNull IArguments arguments) thr String message = arguments.getString(0); String playerName = arguments.getString(1); int maxRange = APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get(); - int range = arguments.optInt(5, maxRange); - range = maxRange == -1 ? range : Math.min(range, APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get()); + int range = arguments.optInt(5, -1); ResourceKey dimension = getLevel().dimension(); ServerPlayer player = getPlayer(playerName); if (player == null) @@ -256,14 +252,14 @@ public final MethodResult sendMessageToPlayer(@NotNull IArguments arguments) thr return MethodResult.of(null, "incorrect bracket string (e.g. [], {}, <>, ...)"); MutableComponent preparedMessage = appendPrefix( - arguments.optString(2, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get()).replaceAll("&", "\u00a7"), + StringUtil.convertAndToSectionMark(arguments.optString(2, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get())), arguments.optString(3, "[]"), - arguments.optString(4, "").replaceAll("&", "\u00a7") + StringUtil.convertAndToSectionMark(arguments.optString(4, "")) ).append(message); if (!APConfig.PERIPHERALS_CONFIG.chatBoxMultiDimensional.get() && player.level().dimension() != dimension) return MethodResult.of(false, "NOT_SAME_DIMENSION"); - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) player.sendSystemMessage(preparedMessage, false); return MethodResult.of(true); }); @@ -276,8 +272,7 @@ public final MethodResult sendToastToPlayer(@NotNull IArguments arguments) throw String title = arguments.getString(1); String playerName = arguments.getString(2); int maxRange = APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get(); - int range = arguments.optInt(6, maxRange); - range = maxRange == -1 ? range : Math.min(range, APConfig.PERIPHERALS_CONFIG.chatBoxMaxRange.get()); + int range = arguments.optInt(6, -1); ResourceKey dimension = getLevel().dimension(); ServerPlayer player = getPlayer(playerName); if (player == null) @@ -287,15 +282,15 @@ public final MethodResult sendToastToPlayer(@NotNull IArguments arguments) throw return MethodResult.of(null, "incorrect bracket string (e.g. [], {}, <>, ...)"); MutableComponent preparedMessage = appendPrefix( - arguments.optString(3, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get()).replaceAll("&", "\u00a7"), + StringUtil.convertAndToSectionMark(arguments.optString(3, APConfig.PERIPHERALS_CONFIG.defaultChatBoxPrefix.get())), arguments.optString(4, "[]"), - arguments.optString(5, "").replaceAll("&", "\u00a7") + StringUtil.convertAndToSectionMark(arguments.optString(5, "")) ).append(message); if (!APConfig.PERIPHERALS_CONFIG.chatBoxMultiDimensional.get() && player.level().dimension() != dimension) return MethodResult.of(false, "NOT_SAME_DIMENSION"); - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) { + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, maxRange)) { ToastToClientPacket packet = new ToastToClientPacket(Component.literal(title), preparedMessage); APNetworking.sendTo(packet, player); } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/CompassPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/CompassPeripheral.java index fb24c8c1a..5ee6e8eee 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/CompassPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/CompassPeripheral.java @@ -11,8 +11,12 @@ public class CompassPeripheral extends BasePeripheral { public static final String PERIPHERAL_TYPE = "compass"; + protected CompassPeripheral(TurtlePeripheralOwner owner) { + super(PERIPHERAL_TYPE, owner); + } + public CompassPeripheral(ITurtleAccess turtle, TurtleSide side) { - super(PERIPHERAL_TYPE, new TurtlePeripheralOwner(turtle, side)); + this(new TurtlePeripheralOwner(turtle, side)); } @Override diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PlayerDetectorPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PlayerDetectorPeripheral.java index f471ce846..15d725d75 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PlayerDetectorPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/PlayerDetectorPeripheral.java @@ -97,7 +97,7 @@ public final List getPlayersInRange(int range) { for (ServerPlayer player : getPlayers()) { if (!isAllowedMultiDimensional() && player.level().dimension() != dimension) continue; - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, MAX_RANGE)) + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, MAX_RANGE)) playersName.add(player.getName().getString()); } return playersName; @@ -142,7 +142,7 @@ public final boolean isPlayersInRange(int range) { for (ServerPlayer player : getPlayers()) { if (!isAllowedMultiDimensional() && player.level().dimension() != dimension) continue; - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, MAX_RANGE)) return true; + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, MAX_RANGE)) return true; } return false; } @@ -185,7 +185,7 @@ public final boolean isPlayerInRange(int range, String username) { for (Player player : getPlayers()) { if (!isAllowedMultiDimensional() && player.level().dimension() != dimension) continue; - if (range == -1 || CoordUtil.isInRange(getPos(), getLevel(), player, range, MAX_RANGE)) { + if (CoordUtil.isInRange(getPos(), getLevel(), player, range, MAX_RANGE)) { if(player.getName().getString().equals(username)) return true; } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataBlockHandPlugin.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataBlockHandPlugin.java index f4022a219..d708f53c7 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataBlockHandPlugin.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataBlockHandPlugin.java @@ -1,19 +1,51 @@ package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.plugins; +import dan200.computercraft.api.lua.IArguments; import dan200.computercraft.api.lua.LuaException; 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 dan200.computercraft.core.apis.TableHelper; +import dan200.computercraft.shared.turtle.core.TurtlePlayer; +import de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperationContext; import de.srendi.advancedperipherals.common.addons.computercraft.owner.TurtlePeripheralOwner; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.CompassPeripheral; +import de.srendi.advancedperipherals.common.configuration.APConfig; import de.srendi.advancedperipherals.common.util.Pair; +import de.srendi.advancedperipherals.common.util.StringUtil; import de.srendi.advancedperipherals.common.util.fakeplayer.APFakePlayer; import de.srendi.advancedperipherals.lib.peripherals.AutomataCorePeripheral; import de.srendi.advancedperipherals.lib.peripherals.IPeripheralOperation; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.SignItem; +import net.minecraft.world.item.context.DirectionalPlaceContext; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.level.block.entity.SignText; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; + +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Stream; import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperation.DIG; import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperation.USE_ON_BLOCK; +import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperation.ACCURE_PLACE; public class AutomataBlockHandPlugin extends AutomataCorePlugin { @@ -27,32 +59,181 @@ public AutomataBlockHandPlugin(AutomataCorePeripheral automataCore) { } @LuaFunction(mainThread = true) - public final MethodResult digBlock() throws LuaException { + public final MethodResult digBlock(@NotNull IArguments arguments) throws LuaException { + Map opts = arguments.count() > 0 ? arguments.getTable(0) : Collections.emptyMap(); + boolean sneak = TableHelper.optBooleanField(opts, "sneak", false); + float yaw = opts != null ? (float) TableHelper.optNumberField(opts, "yaw", 0) : 0; + float pitch = opts != null ? (float) TableHelper.optNumberField(opts, "pitch", 0) : 0; return automataCore.withOperation(DIG, context -> { TurtlePeripheralOwner owner = automataCore.getPeripheralOwner(); ItemStack selectedTool = owner.getToolInMainHand(); int previousDamageValue = selectedTool.getDamageValue(); - Pair result = owner.withPlayer(apFakePlayer -> apFakePlayer.digBlock(owner.getFacing().getOpposite())); + Pair result = owner.withPlayer(APFakePlayer.wrapActionWithShiftKey(sneak, APFakePlayer.wrapActionWithRot(yaw, pitch, APFakePlayer::digBlock))); if (!result.getLeft()) { return MethodResult.of(null, result.getRight()); } - if (automataCore.hasAttribute(AutomataCorePeripheral.ATTR_STORING_TOOL_DURABILITY)) + if (automataCore.hasAttribute(AutomataCorePeripheral.ATTR_STORING_TOOL_DURABILITY)) { selectedTool.setDamageValue(previousDamageValue); + } return MethodResult.of(true); }); } @LuaFunction(mainThread = true) - public final MethodResult useOnBlock() throws LuaException { + public final MethodResult useOnBlock(@NotNull IArguments arguments) throws LuaException { + Map opts = arguments.count() > 0 ? arguments.getTable(0) : Collections.emptyMap(); + boolean sneak = TableHelper.optBooleanField(opts, "sneak", false); + float yaw = opts != null ? (float) TableHelper.optNumberField(opts, "yaw", 0) : 0; + float pitch = opts != null ? (float) TableHelper.optNumberField(opts, "pitch", 0) : 0; return automataCore.withOperation(USE_ON_BLOCK, context -> { TurtlePeripheralOwner owner = automataCore.getPeripheralOwner(); ItemStack selectedTool = owner.getToolInMainHand(); int previousDamageValue = selectedTool.getDamageValue(); - InteractionResult result = owner.withPlayer(APFakePlayer::useOnBlock); - if (automataCore.hasAttribute(AutomataCorePeripheral.ATTR_STORING_TOOL_DURABILITY)) + InteractionResult result = owner.withPlayer(APFakePlayer.wrapActionWithShiftKey(sneak, APFakePlayer.wrapActionWithRot(yaw, pitch, APFakePlayer::useOnBlock))); + if (automataCore.hasAttribute(AutomataCorePeripheral.ATTR_STORING_TOOL_DURABILITY)) { selectedTool.setDamageValue(previousDamageValue); - return MethodResult.of(true, result.toString()); + } + return MethodResult.of(result.consumesAction(), result.toString()); }); } + /** + * placeBlock method will let turtle place a block with more details when compass has equipped. + * It should not able to place fluids / use any item, because compass do not recognize them. + * + * @param options A table contains how to place the block: + * x: the x offset relative to the turtle. Default 0 + * y: the y offset relative to the turtle. Default 0 + * z: the z offset relative to the turtle. Default 0 + * anchor: the direction the block is going to hanging on. Default is the direction of the turtle + * front: the direction the block is going to facing. Default is same as anchor + * top: the direction the block's top is going to facing. Default is TOP + * text: the text going to write on the sign's front side. Default is null + * backText: the text going to write on the sign's back side. Default is null + */ + @LuaFunction(mainThread = true) + public MethodResult placeBlock(@NotNull Map options) throws LuaException { + ITurtleAccess turtle = automataCore.getPeripheralOwner().getTurtle(); + CompassPeripheral compassPeripheral = Stream.of(TurtleSide.values()).map(side -> turtle.getPeripheral(side) instanceof CompassPeripheral compass ? compass : null).filter(peripheral -> peripheral != null).findFirst().orElse(null); + if (compassPeripheral == null || !compassPeripheral.isEnabled()) { + return MethodResult.of(false, "COMPASS_NOT_EQUIPPED"); + } + int x = TableHelper.optIntField(options, "x", 0); + int y = TableHelper.optIntField(options, "y", 0); + int z = TableHelper.optIntField(options, "z", 0); + final int maxDist = APConfig.PERIPHERALS_CONFIG.compassAccurePlaceRadius.get(); + final int freeDist = APConfig.PERIPHERALS_CONFIG.compassAccurePlaceFreeRadius.get(); + if (Math.abs(x) > maxDist || Math.abs(y) > maxDist || Math.abs(z) > maxDist) { + return MethodResult.of(null, "OUT_OF_RANGE"); + } + String anchor = TableHelper.optStringField(options, "anchor", null); + String front = TableHelper.optStringField(options, "front", null); + String top = TableHelper.optStringField(options, "top", null); + Direction anchorDir = anchor != null ? automataCore.validateSide(anchor) : null; + Direction frontDir = front != null ? automataCore.validateSide(front) : null; + Direction topDir = top != null ? automataCore.validateSide(top) : null; + + int distance = + Math.max(0, Math.abs(x) - freeDist) + + Math.max(0, Math.abs(y) - freeDist) + + Math.max(0, Math.abs(z) - freeDist); + return automataCore.withOperation(ACCURE_PLACE, new SingleOperationContext(1, distance), context -> { + ItemStack stack = turtle.getInventory().getItem(turtle.getSelectedSlot()); + if (stack.isEmpty()) { + return MethodResult.of(null, "EMPTY_SLOT"); + } + BlockPos position = turtle.getPosition().offset(x, y, z); + String err = deployOn(stack, position, anchorDir, frontDir, topDir, options); + if (err != null) { + return MethodResult.of(null, err); + } + return MethodResult.of(true); + }, null); + } + + /** + * @return A nullable string of the error. null means the operation is successful + */ + @Nullable + private String deployOn(ItemStack stack, BlockPos position, Direction anchor, Direction front, Direction top, Map options) throws LuaException { + ITurtleAccess turtle = automataCore.getPeripheralOwner().getTurtle(); + Level world = turtle.getLevel(); + if (anchor == null) { + anchor = turtle.getDirection(); + } + if (front == null) { + front = anchor; + } + if (top == null) { + top = Direction.UP; + } + TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition(turtle, position, front.getOpposite()); + BlockHitResult hit = BlockHitResult.miss(Vec3.atCenterOf(position), top, position); + AdvanceDirectionalPlaceContext context = new AdvanceDirectionalPlaceContext(world, position, anchor, front, stack, top); + PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock(turtlePlayer.player(), InteractionHand.MAIN_HAND, position, hit); + if (event.isCanceled()) { + return "EVENT_CANCELED"; + } + Item item = stack.getItem(); + if (!(item instanceof BlockItem)) { + return "NOT_BLOCK"; + } + BlockItem block = (BlockItem) item; + InteractionResult res = block.place(context); + if (!res.consumesAction()) { + return "CANNOT_PLACE"; + } + if (block instanceof SignItem) { + BlockEntity blockEntity = world.getBlockEntity(position); + if (blockEntity instanceof SignBlockEntity sign) { + String text = StringUtil.convertAndToSectionMark(TableHelper.optStringField(options, "text", null)); + setSignText(world, sign, text, true); + String backText = StringUtil.convertAndToSectionMark(TableHelper.optStringField(options, "backText", null)); + setSignText(world, sign, backText, false); + } + } + return null; + } + + private static void setSignText(Level world, SignBlockEntity block, String text, boolean front) { + SignText sign = block.getText(front); + if (text == null) { + for (int i = 0; i < SignText.LINES; i++) { + sign.setMessage(i, Component.literal("")); + } + } else { + String[] lines = text.split("\n"); + for (int i = 0; i < SignText.LINES; i++) { + sign.setMessage(i, Component.literal(i < lines.length ? lines[i] : "")); + } + } + block.setChanged(); + world.sendBlockUpdated(block.getBlockPos(), block.getBlockState(), block.getBlockState(), Block.UPDATE_ALL); + } + + private static class AdvanceDirectionalPlaceContext extends DirectionalPlaceContext { + private final Direction anchor; + + AdvanceDirectionalPlaceContext(Level world, BlockPos pos, Direction anchor, Direction front, ItemStack stack, Direction top) { + super(world, pos, front, stack, top); + this.anchor = anchor; + } + + @Override + public Direction getNearestLookingDirection() { + return this.anchor; + } + + @Override + public Direction[] getNearestLookingDirections() { + return switch (this.anchor) { + case DOWN -> new Direction[]{Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP}; + case UP -> new Direction[]{Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.DOWN}; + case NORTH -> new Direction[]{Direction.NORTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.DOWN, Direction.SOUTH}; + case SOUTH -> new Direction[]{Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.DOWN, Direction.NORTH}; + case WEST -> new Direction[]{Direction.WEST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.DOWN, Direction.EAST}; + case EAST -> new Direction[]{Direction.EAST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.DOWN, Direction.WEST}; + }; + } + } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataChargingPlugin.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataChargingPlugin.java index 2ffdb4d43..d47108ac4 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataChargingPlugin.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataChargingPlugin.java @@ -11,8 +11,8 @@ import de.srendi.advancedperipherals.lib.peripherals.AutomataCorePeripheral; import net.minecraft.world.item.ItemStack; import net.minecraftforge.common.capabilities.ForgeCapabilities; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NotNull; import java.util.Objects; public class AutomataChargingPlugin extends AutomataCorePlugin { diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataEntityHandPlugin.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataEntityHandPlugin.java index 1bc139df1..14dd9e6f1 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataEntityHandPlugin.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataEntityHandPlugin.java @@ -1,10 +1,13 @@ package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.plugins; +import dan200.computercraft.api.lua.IArguments; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.core.apis.TableHelper; import de.srendi.advancedperipherals.common.addons.computercraft.owner.TurtlePeripheralOwner; import de.srendi.advancedperipherals.common.util.LuaConverter; +import de.srendi.advancedperipherals.common.util.fakeplayer.APFakePlayer; import de.srendi.advancedperipherals.lib.peripherals.AutomataCorePeripheral; import de.srendi.advancedperipherals.lib.peripherals.IPeripheralOperation; import net.minecraft.core.BlockPos; @@ -15,9 +18,10 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Predicate; @@ -39,12 +43,16 @@ public AutomataEntityHandPlugin(AutomataCorePeripheral automataCore, Predicate opts = arguments.count() > 0 ? arguments.getTable(0) : Collections.emptyMap(); + boolean sneak = TableHelper.optBooleanField(opts, "sneak", false); + float yaw = opts != null ? (float) TableHelper.optNumberField(opts, "yaw", 0) : 0; + float pitch = opts != null ? (float) TableHelper.optNumberField(opts, "pitch", 0) : 0; return automataCore.withOperation(USE_ON_ANIMAL, context -> { TurtlePeripheralOwner owner = automataCore.getPeripheralOwner(); ItemStack selectedTool = owner.getToolInMainHand(); int previousDamageValue = selectedTool.getDamageValue(); - InteractionResult result = owner.withPlayer(player -> player.useOnFilteredEntity(suitableEntity)); + InteractionResult result = owner.withPlayer(APFakePlayer.wrapActionWithShiftKey(sneak, APFakePlayer.wrapActionWithRot(yaw, pitch, p -> p.useOnFilteredEntity(suitableEntity)))); if (automataCore.hasAttribute(AutomataCorePeripheral.ATTR_STORING_TOOL_DURABILITY)) selectedTool.setDamageValue(previousDamageValue); @@ -53,10 +61,14 @@ public final MethodResult useOnAnimal() throws LuaException { } @LuaFunction(mainThread = true) - public final MethodResult inspectAnimal() { + public final MethodResult inspectAnimal(@NotNull IArguments arguments) throws LuaException { + Map opts = arguments.count() > 0 ? arguments.getTable(0) : Collections.emptyMap(); + float yaw = opts != null ? (float) TableHelper.optNumberField(opts, "yaw", 0) : 0; + float pitch = opts != null ? (float) TableHelper.optNumberField(opts, "pitch", 0) : 0; + automataCore.addRotationCycle(); TurtlePeripheralOwner owner = automataCore.getPeripheralOwner(); - HitResult entityHit = owner.withPlayer(player -> player.findHit(false, true, suitableEntity)); + HitResult entityHit = owner.withPlayer(APFakePlayer.wrapActionWithRot(yaw, pitch, p -> p.findHit(false, true, suitableEntity))); if (entityHit.getType() == HitResult.Type.MISS) return MethodResult.of(null, "Nothing found"); @@ -73,9 +85,8 @@ public final MethodResult searchAnimals() { TurtlePeripheralOwner owner = automataCore.getPeripheralOwner(); BlockPos currentPos = owner.getPos(); AABB box = new AABB(currentPos); - List> entities = new ArrayList<>(); ItemStack itemInHand = owner.getToolInMainHand(); - owner.getLevel().getEntities((Entity) null, box.inflate(automataCore.getInteractionRadius()), suitableEntity).forEach(entity -> entities.add(LuaConverter.completeEntityWithPositionToLua(entity, itemInHand, currentPos))); + List> entities = owner.getLevel().getEntities((Entity) null, box.inflate(automataCore.getInteractionRadius()), suitableEntity).stream().map(entity -> LuaConverter.completeEntityWithPositionToLua(entity, itemInHand, currentPos)).toList(); return MethodResult.of(entities); } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataEntityTransferPlugin.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataEntityTransferPlugin.java index b99d53de3..e46ca4356 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataEntityTransferPlugin.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataEntityTransferPlugin.java @@ -1,10 +1,13 @@ package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.plugins; +import dan200.computercraft.api.lua.IArguments; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.core.apis.TableHelper; import de.srendi.advancedperipherals.common.addons.computercraft.owner.TurtlePeripheralOwner; import de.srendi.advancedperipherals.common.util.LuaConverter; +import de.srendi.advancedperipherals.common.util.fakeplayer.APFakePlayer; import de.srendi.advancedperipherals.lib.peripherals.AutomataCorePeripheral; import de.srendi.advancedperipherals.lib.peripherals.IPeripheralOperation; import net.minecraft.core.BlockPos; @@ -16,7 +19,10 @@ import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.Map; import java.util.function.Predicate; import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperation.CAPTURE_ANIMAL; @@ -69,8 +75,12 @@ protected Entity extractEntity() { @LuaFunction(mainThread = true) - public final MethodResult captureAnimal() throws LuaException { - HitResult entityHit = automataCore.getPeripheralOwner().withPlayer(player -> player.findHit(false, true, suitableEntity)); + public final MethodResult captureAnimal(@NotNull IArguments arguments) throws LuaException { + Map opts = arguments.count() > 0 ? arguments.getTable(0) : Collections.emptyMap(); + float yaw = opts != null ? (float) TableHelper.optNumberField(opts, "yaw", 0) : 0; + float pitch = opts != null ? (float) TableHelper.optNumberField(opts, "pitch", 0) : 0; + + HitResult entityHit = automataCore.getPeripheralOwner().withPlayer(APFakePlayer.wrapActionWithRot(yaw, pitch, p -> p.findHit(false, true, suitableEntity))); if (entityHit.getType() == HitResult.Type.MISS) return MethodResult.of(null, "Nothing found"); return automataCore.withOperation(CAPTURE_ANIMAL, context -> { diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataItemSuckPlugin.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataItemSuckPlugin.java index bcc87f781..526ffd532 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataItemSuckPlugin.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataItemSuckPlugin.java @@ -15,8 +15,8 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.AABB; import net.minecraftforge.registries.ForgeRegistries; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataLookPlugin.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataLookPlugin.java index c79019c53..2b27487bf 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataLookPlugin.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/plugins/AutomataLookPlugin.java @@ -1,9 +1,13 @@ package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.plugins; +import dan200.computercraft.api.lua.IArguments; +import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.core.apis.TableHelper; import de.srendi.advancedperipherals.common.addons.computercraft.owner.TurtlePeripheralOwner; import de.srendi.advancedperipherals.common.util.LuaConverter; +import de.srendi.advancedperipherals.common.util.fakeplayer.APFakePlayer; import de.srendi.advancedperipherals.lib.peripherals.AutomataCorePeripheral; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.state.BlockState; @@ -12,6 +16,8 @@ import net.minecraft.world.phys.HitResult; import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.NotNull; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -22,10 +28,14 @@ public AutomataLookPlugin(AutomataCorePeripheral automataCore) { } @LuaFunction(mainThread = true) - public final MethodResult lookAtBlock() { + public final MethodResult lookAtBlock(@NotNull IArguments arguments) throws LuaException { + Map opts = arguments.count() > 0 ? arguments.getTable(0) : Collections.emptyMap(); + float yaw = opts != null ? (float) TableHelper.optNumberField(opts, "yaw", 0) : 0; + float pitch = opts != null ? (float) TableHelper.optNumberField(opts, "pitch", 0) : 0; + automataCore.addRotationCycle(); TurtlePeripheralOwner owner = automataCore.getPeripheralOwner(); - HitResult result = owner.withPlayer(apFakePlayer -> apFakePlayer.findHit(true, false)); + HitResult result = owner.withPlayer(APFakePlayer.wrapActionWithRot(yaw, pitch, p -> p.findHit(true, false))); if (result.getType() == HitResult.Type.MISS) return MethodResult.of(null, "No block find"); @@ -40,9 +50,13 @@ public final MethodResult lookAtBlock() { } @LuaFunction(mainThread = true) - public final MethodResult lookAtEntity() { + public final MethodResult lookAtEntity(@NotNull IArguments arguments) throws LuaException { + Map opts = arguments.count() > 0 ? arguments.getTable(0) : Collections.emptyMap(); + float yaw = opts != null ? (float) TableHelper.optNumberField(opts, "yaw", 0) : 0; + float pitch = opts != null ? (float) TableHelper.optNumberField(opts, "pitch", 0) : 0; + automataCore.addRotationCycle(); - HitResult result = automataCore.getPeripheralOwner().withPlayer(apFakePlayer -> apFakePlayer.findHit(false, true)); + HitResult result = automataCore.getPeripheralOwner().withPlayer(APFakePlayer.wrapActionWithRot(yaw, pitch, p -> p.findHit(false, true))); if (result.getType() == HitResult.Type.MISS) return MethodResult.of(null, "No entity find"); diff --git a/src/main/java/de/srendi/advancedperipherals/common/configuration/GeneralConfig.java b/src/main/java/de/srendi/advancedperipherals/common/configuration/GeneralConfig.java index 57c32e43d..9ebf6a61d 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/configuration/GeneralConfig.java +++ b/src/main/java/de/srendi/advancedperipherals/common/configuration/GeneralConfig.java @@ -23,6 +23,11 @@ public class GeneralConfig implements IAPConfig { LibConfig.build(builder); + builder.pop(); + builder.push("Unsafe"); + + UnsafeConfig.build(builder); + builder.pop(); configSpec = builder.build(); 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 e9c7d0d33..a31183cf4 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java +++ b/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java @@ -10,7 +10,7 @@ @FieldsAreNonnullByDefault public class PeripheralsConfig implements IAPConfig { - //Player Detector + // Player Detector public final ForgeConfigSpec.IntValue playerDetMaxRange; public final ForgeConfigSpec.BooleanValue playerSpy; public final ForgeConfigSpec.BooleanValue morePlayerInformation; @@ -20,57 +20,60 @@ public class PeripheralsConfig implements IAPConfig { public final ForgeConfigSpec.IntValue playerSpyRandErrorAmount; public final ForgeConfigSpec.IntValue playerSpyPreciseMaxRange; - //Energy Detector + // Energy Detector public final ForgeConfigSpec.IntValue energyDetectorMaxFlow; public final ForgeConfigSpec.BooleanValue enableEnergyDetector; - //NBT Storage + // NBT Storage public final ForgeConfigSpec.IntValue nbtStorageMaxSize; public final ForgeConfigSpec.BooleanValue enableNBTStorage; - //Chunky turtle - public final ForgeConfigSpec.IntValue chunkLoadValidTime; + // Chunky turtle + public final ForgeConfigSpec.IntValue chunkLoadValidTime; public final ForgeConfigSpec.IntValue chunkyTurtleRadius; public final ForgeConfigSpec.BooleanValue enableChunkyTurtle; - //Chat box + + // Chat box public final ForgeConfigSpec.BooleanValue enableChatBox; public final ForgeConfigSpec.ConfigValue defaultChatBoxPrefix; public final ForgeConfigSpec.IntValue chatBoxMaxRange; public final ForgeConfigSpec.BooleanValue chatBoxMultiDimensional; - //ME Bridge + // ME Bridge public final ForgeConfigSpec.BooleanValue enableMEBridge; public final ForgeConfigSpec.IntValue meConsumption; - //Rs Bridge + // Rs Bridge public final ForgeConfigSpec.BooleanValue enableRSBridge; public final ForgeConfigSpec.IntValue rsConsumption; - //Environment Detector + // Environment Detector public final ForgeConfigSpec.BooleanValue enableEnvironmentDetector; - //AR Controller + // AR Controller public final ForgeConfigSpec.BooleanValue enableARGoggles; - //Inventory Manager + // Inventory Manager public final ForgeConfigSpec.BooleanValue enableInventoryManager; - //Redstone Integrator + // Redstone Integrator public final ForgeConfigSpec.BooleanValue enableRedstoneIntegrator; - //Block reader + // Block reader public final ForgeConfigSpec.BooleanValue enableBlockReader; - //Geo Scanner + // Geo Scanner public final ForgeConfigSpec.BooleanValue enableGeoScanner; - //Colony integrator + // Colony integrator public final ForgeConfigSpec.BooleanValue enableColonyIntegrator; - //Compass turtle + // Compass turtle public final ForgeConfigSpec.BooleanValue enableCompassTurtle; + public final ForgeConfigSpec.IntValue compassAccurePlaceRadius; + public final ForgeConfigSpec.IntValue compassAccurePlaceFreeRadius; - //Powered Peripherals + // Powered Peripherals public final ForgeConfigSpec.BooleanValue enablePoweredPeripherals; public final ForgeConfigSpec.BooleanValue disablePocketFuelConsumption; public final ForgeConfigSpec.IntValue poweredPeripheralMaxEnergyStorage; @@ -155,6 +158,8 @@ public PeripheralsConfig() { pop("Compass_Turtle", builder); enableCompassTurtle = builder.comment("Enable the compass turtle or not.").define("enableCompassTurtle", true); + compassAccurePlaceRadius = builder.comment("The maximum distance the compass can locate accurately with in each axis.").defineInRange("compassAccurePlaceRadius", 3, 0, 8); + compassAccurePlaceFreeRadius = builder.comment("The free distance the compass can locate accurately with in each axis.").defineInRange("compassAccurePlaceFreeRadius", 1, 0, 4); pop("Powered_Peripherals", builder); diff --git a/src/main/java/de/srendi/advancedperipherals/common/configuration/UnsafeConfig.java b/src/main/java/de/srendi/advancedperipherals/common/configuration/UnsafeConfig.java new file mode 100644 index 000000000..800ec28a4 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/configuration/UnsafeConfig.java @@ -0,0 +1,22 @@ +package de.srendi.advancedperipherals.common.configuration; + +import net.minecraftforge.common.ForgeConfigSpec; + +public class UnsafeConfig { + + private static ForgeConfigSpec.BooleanValue enableUnsafe; + private static ForgeConfigSpec.BooleanValue ignoreTurtlePeripheralItemNBT; + + public static void build(final ForgeConfigSpec.Builder builder) { + enableUnsafe = builder.comment("By setting this value to true, I understand all operations below are danger to my adventure, and if they caused unexpected behavior in my world, I will not consider it as AP's liability").define("enableUnsafe", false); + ignoreTurtlePeripheralItemNBT = builder.comment("Ignore turtle peripheral item's NBT when equipping. **YOU WILL LOSE ALL NBT ON THE ITEM**").define("ignoreTurtlePeripheralItemNBT", false); + } + + public static boolean enabled() { + return enableUnsafe.get(); + } + + public static boolean getIgnoreTurtlePeripheralItemNBT() { + return enabled() && ignoreTurtlePeripheralItemNBT.get(); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/CoordUtil.java b/src/main/java/de/srendi/advancedperipherals/common/util/CoordUtil.java index 8dfd5818d..300985509 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/CoordUtil.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/CoordUtil.java @@ -16,13 +16,35 @@ public class CoordUtil { + /** + * isInRange checks if the player is in the range + * + * @param pos the position to start check + * @param world the world to start check + * @param player the player going to be check + * @param range the range that user want to reach, must be -1, 0, or a positive number + * @param maxRange the maximum range the user can reach, must be -1, 0, or a positive number + * + * @return If the player is in the {@code range} as well as in the {@code maxRange}, or {@code range} and {@code maxRange} are -1 + */ public static boolean isInRange(@Nullable BlockPos pos, @Nullable Level world, @Nullable Player player, int range, int maxRange) { // There are rare cases where these are null. For example if a player detector pocket computer runs while not in a player inventory // Fixes https://github.com/SirEndii/AdvancedPeripherals/issues/356 - if (pos == null || world == null || player == null) + if (pos == null || world == null || player == null) { return false; + } - range = maxRange == -1 ? range : Math.min(Math.abs(range), maxRange); + if (range == 0 || maxRange == 0) { + return false; + } + if (range < 0) { + if (maxRange < 0) { + return true; + } + range = maxRange; + } else if (maxRange > 0 && range > maxRange) { + range = maxRange; + } return isPlayerInBlockRange(pos, world, player, (double) range); } diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/LuaConverter.java b/src/main/java/de/srendi/advancedperipherals/common/util/LuaConverter.java index ba2c2c85c..959836c2a 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/LuaConverter.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/LuaConverter.java @@ -47,7 +47,7 @@ public static Map livingEntityToLua(LivingEntity entity) { } public static Map animalToLua(Animal animal, ItemStack itemInHand) { - Map data = entityToLua(animal); + Map data = livingEntityToLua(animal); data.put("baby", animal.isBaby()); data.put("inLove", animal.isInLove()); data.put("aggressive", animal.isAggressive()); diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/StringUtil.java b/src/main/java/de/srendi/advancedperipherals/common/util/StringUtil.java index bc586425d..f166eda25 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/StringUtil.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/StringUtil.java @@ -12,4 +12,23 @@ public static String toHexString(byte[] bytes) { } return new String(hexChars); } + + /** + * This method will convert "&[0-9a-z]" to "§[0-9a-z]", then we can make colored message in CC easier + * If a '&' is behind reverse slash '\', it will be ignored. + * Note: In CC, you need to use "\\&" to get an unescaped '&' character + * If the character after '&' is not a digital number or lowercase letter, the & operator will not be escaped as well. + * + * Some convert example: + * "&a" -> "§a" + * "&" -> "&" + * "\\&" -> "&" + * "\\&a" -> "&a" + * "&A" -> "&A" + * "& a" -> "& a" + * "&&a" -> "&§a" + */ + public static String convertAndToSectionMark(String str) { + return str == null ? null : str.replaceAll("(? stat) { MinecraftServer server = level().getServer(); @@ -113,58 +119,92 @@ public float getEyeHeight(@NotNull Pose pose) { return 0; } + public static Function wrapActionWithRot(float yaw, float pitch, Function action) { + return player -> player.doActionWithRot(yaw, pitch, action); + } + + public T doActionWithRot(float yaw, float pitch, Function action) { + final float oldRot = this.getYRot(); + this.setRot(oldRot + yaw, pitch); + try { + return action.apply(this); + } finally { + this.setRot(oldRot, 0); + } + } + + public static Function wrapActionWithShiftKey(boolean shift, Function action) { + return player -> player.doActionWithShiftKey(shift, action); + } + + public T doActionWithShiftKey(boolean shift, Function action) { + boolean old = this.isShiftKeyDown(); + this.setShiftKeyDown(shift); + try { + return action.apply(this); + } finally { + this.setShiftKeyDown(old); + } + } + + @Deprecated(forRemoval = true) public Pair digBlock(Direction direction) { - Level world = level(); + return doActionWithRot(direction.toYRot() - this.getYRot(), direction == Direction.DOWN ? 90 : direction == Direction.UP ? -90 : 0, APFakePlayer::digBlock); + } + + public Pair digBlock() { + Level world = this.level(); HitResult hit = findHit(true, false); - if (hit.getType() == HitResult.Type.MISS) + if (hit.getType() == HitResult.Type.MISS) { return Pair.of(false, "Nothing to break"); - BlockPos pos = new BlockPos((int) hit.getLocation().x, (int) hit.getLocation().y, (int) hit.getLocation().z); + } + BlockPos pos = ((BlockHitResult) hit).getBlockPos(); BlockState state = world.getBlockState(pos); Block block = state.getBlock(); ItemStack tool = getInventory().getSelected(); - - if (tool.isEmpty()) + if (tool.isEmpty()) { return Pair.of(false, "Cannot dig without tool"); + } - - if (block != digBlock || !pos.equals(digPosition)) + if (block != digBlock || !pos.equals(digPosition)) { setState(block, pos); + } - if (!world.isEmptyBlock(pos)) { - if (block == Blocks.BEDROCK || state.getDestroySpeed(world, pos) <= -1) - return Pair.of(false, "Unbreakable block detected"); + Vec3 look = getLookAngle(); + Direction direction = Direction.getNearest(look.x, look.y, look.z).getOpposite(); - if (!(tool.getItem() instanceof DiggerItem) && !(tool.getItem() instanceof ShearsItem)) - return Pair.of(false, "Item should be digger tool"); + if (world.isEmptyBlock(pos) || state.getBlock() instanceof LiquidBlock) { + return Pair.of(false, "Nothing to dig here"); + } - if (tool.getItem() instanceof DiggerItem toolItem && !toolItem.isCorrectToolForDrops(tool, state)) - return Pair.of(false, "Tool cannot mine this block"); + if (block == Blocks.BEDROCK || state.getDestroySpeed(world, pos) <= -1) { + return Pair.of(false, "Unbreakable block detected"); + } - if (tool.getItem() instanceof ShearsItem shearsItem && shearsItem.isCorrectToolForDrops(state)) - return Pair.of(false, "Shear cannot mine this block"); + if (!tool.isCorrectToolForDrops(state)) { + return Pair.of(false, "Tool cannot mine this block"); + } - ServerPlayerGameMode manager = gameMode; - float breakSpeed = 0.5f * tool.getDestroySpeed(state) / state.getDestroySpeed(level(), pos) - 0.1f; - for (int i = 0; i < 10; i++) { - currentDamage += breakSpeed; + ServerPlayerGameMode manager = gameMode; + float breakSpeed = 0.5f * tool.getDestroySpeed(state) / state.getDestroySpeed(world, pos) - 0.1f; + for (int i = 0; i < 10; i++) { + currentDamage += breakSpeed; - world.destroyBlockProgress(getId(), pos, i); + world.destroyBlockProgress(getId(), pos, i); - if (currentDamage > 9) { - world.playSound(null, pos, state.getSoundType().getHitSound(), SoundSource.NEUTRAL, .25f, 1); - manager.handleBlockBreakAction(pos, ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK, direction.getOpposite(), 320, 1); - manager.destroyBlock(pos); - world.destroyBlockProgress(getId(), pos, -1); - setState(null, null); - break; - } + if (currentDamage > 9) { + world.playSound(null, pos, state.getSoundType().getHitSound(), SoundSource.NEUTRAL, .25f, 1); + manager.handleBlockBreakAction(pos, ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK, direction, 320, 1); + manager.destroyBlock(pos); + world.destroyBlockProgress(getId(), pos, -1); + setState(null, null); + break; } - - return Pair.of(true, "block"); } - return Pair.of(false, "Nothing to dig here"); + return Pair.of(true, "block"); + } public InteractionResult useOnBlock() { @@ -182,8 +222,9 @@ public InteractionResult useOnFilteredEntity(Predicate filter) { public InteractionResult useOnSpecificEntity(@NotNull Entity entity, HitResult result) { InteractionResult simpleInteraction = interactOn(entity, InteractionHand.MAIN_HAND); if (simpleInteraction == InteractionResult.SUCCESS) return simpleInteraction; - if (ForgeHooks.onInteractEntityAt(this, entity, result.getLocation(), InteractionHand.MAIN_HAND) != null) + if (ForgeHooks.onInteractEntityAt(this, entity, result.getLocation(), InteractionHand.MAIN_HAND) != null) { return InteractionResult.FAIL; + } return entity.interactAt(this, result.getLocation(), InteractionHand.MAIN_HAND); } @@ -196,44 +237,49 @@ public InteractionResult use(boolean skipEntity, boolean skipBlock, @Nullable Pr HitResult hit = findHit(skipEntity, skipBlock, entityFilter); if (hit instanceof BlockHitResult blockHit) { - ItemStack stack = getMainHandItem(); BlockPos pos = blockHit.getBlockPos(); PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock(this, InteractionHand.MAIN_HAND, pos, blockHit); - if (event.isCanceled()) + if (event.isCanceled()) { return event.getCancellationResult(); - - if (event.getUseItem() != Event.Result.DENY) { - InteractionResult result = stack.onItemUseFirst(new UseOnContext(level(), this, InteractionHand.MAIN_HAND, stack, blockHit)); - if (result != InteractionResult.PASS) - return result; } + boolean denied = event.getUseItem() == Event.Result.DENY; + if (!denied) { + InteractionResult result = stack.onItemUseFirst(new UseOnContext(this.level(), this, InteractionHand.MAIN_HAND, stack, blockHit)); + if (result != InteractionResult.PASS) { + return result; + } - boolean bypass = getMainHandItem().doesSneakBypassUse(level(), pos, this); - if (getPose() != Pose.CROUCHING || bypass || event.getUseBlock() == Event.Result.ALLOW) { - InteractionResult useType = gameMode.useItemOn(this, level(), stack, InteractionHand.MAIN_HAND, blockHit); - if (event.getUseBlock() != Event.Result.DENY && useType == InteractionResult.SUCCESS) - return InteractionResult.SUCCESS; - + boolean bypass = getMainHandItem().doesSneakBypassUse(this.level(), pos, this); + if (isShiftKeyDown() || bypass || event.getUseBlock() == Event.Result.ALLOW) { + InteractionResult useType = gameMode.useItemOn(this, this.level(), stack, InteractionHand.MAIN_HAND, blockHit); + if (useType == InteractionResult.SUCCESS) { + return InteractionResult.SUCCESS; + } + } } - if (stack.isEmpty() || getCooldowns().isOnCooldown(stack.getItem())) + if (stack.isEmpty() || getCooldowns().isOnCooldown(stack.getItem())) { return InteractionResult.PASS; + } if (stack.getItem() instanceof BlockItem blockItem) { Block block = blockItem.getBlock(); - if (block instanceof CommandBlock || block instanceof StructureBlock) + if (block instanceof CommandBlock || block instanceof StructureBlock) { return InteractionResult.FAIL; + } } - if (event.getUseItem() == Event.Result.DENY) + if (denied) { return InteractionResult.PASS; + } ItemStack copyBeforeUse = stack.copy(); - InteractionResult result = stack.useOn(new UseOnContext(level(), this, InteractionHand.MAIN_HAND, copyBeforeUse, blockHit)); - if (stack.isEmpty()) + InteractionResult result = stack.useOn(new UseOnContext(this.level(), this, InteractionHand.MAIN_HAND, copyBeforeUse, blockHit)); + if (stack.isEmpty()) { ForgeEventFactory.onPlayerDestroyItem(this, copyBeforeUse, InteractionHand.MAIN_HAND); + } return result; } else if (hit instanceof EntityHitResult entityHit) { return useOnSpecificEntity(entityHit.getEntity(), entityHit); @@ -263,9 +309,9 @@ public HitResult findHit(boolean skipEntity, boolean skipBlock, @Nullable Predic blockHit = BlockHitResult.miss(traceContext.getTo(), traceDirection, new BlockPos((int) traceContext.getTo().x, (int) traceContext.getTo().y, (int) traceContext.getTo().z)); } else { blockHit = BlockGetter.traverseBlocks(traceContext.getFrom(), traceContext.getTo(), traceContext, (rayTraceContext, blockPos) -> { - if (level().isEmptyBlock(blockPos)) + if (this.level().isEmptyBlock(blockPos) || blockPos.equals(blockPosition())) { return null; - + } return new BlockHitResult(new Vec3(blockPos.getX(), blockPos.getY(), blockPos.getZ()), traceDirection, blockPos, false); }, rayTraceContext -> BlockHitResult.miss(rayTraceContext.getTo(), traceDirection, new BlockPos((int) rayTraceContext.getTo().x, (int) rayTraceContext.getTo().y, (int) rayTraceContext.getTo().z))); } @@ -273,7 +319,7 @@ public HitResult findHit(boolean skipEntity, boolean skipBlock, @Nullable Predic if (skipEntity) return blockHit; - List entities = level().getEntities(this, getBoundingBox().expandTowards(look.x * range, look.y * range, look.z * range).inflate(1, 1, 1), collidablePredicate); + List entities = this.level().getEntities(this, getBoundingBox().expandTowards(look.x * range, look.y * range, look.z * range).inflate(1, 1, 1), collidablePredicate); LivingEntity closestEntity = null; Vec3 closestVec = null; diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/fakeplayer/FakePlayerProviderTurtle.java b/src/main/java/de/srendi/advancedperipherals/common/util/fakeplayer/FakePlayerProviderTurtle.java index 4cab4747f..f174a62e5 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/fakeplayer/FakePlayerProviderTurtle.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/fakeplayer/FakePlayerProviderTurtle.java @@ -6,7 +6,6 @@ import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.core.Vec3i; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.Container; import net.minecraft.world.InteractionHand; @@ -33,18 +32,10 @@ public static APFakePlayer getPlayer(ITurtleAccess turtle, GameProfile profile) public static void load(APFakePlayer player, ITurtleAccess turtle) { Direction direction = turtle.getDirection(); - player.setServerLevel((ServerLevel) turtle.getLevel()); - BlockPos position = turtle.getPosition(); + player.setLevel((ServerLevel) turtle.getLevel()); // Player position - float pitch = direction == Direction.UP ? -90 : direction == Direction.DOWN ? 90 : 0; - float yaw = direction == Direction.SOUTH ? 0 : direction == Direction.WEST ? 90 : direction == Direction.NORTH ? 180 : -90; - Vec3i sideVec = direction.getNormal(); - Direction.Axis a = direction.getAxis(); - Direction.AxisDirection ad = direction.getAxisDirection(); - double x = a == Direction.Axis.X && ad == Direction.AxisDirection.NEGATIVE ? -.5 : .5 + sideVec.getX() / 1.9D; - double y = 0.5 + sideVec.getY() / 1.9D; - double z = a == Direction.Axis.Z && ad == Direction.AxisDirection.NEGATIVE ? -.5 : .5 + sideVec.getZ() / 1.9D; - player.moveTo(position.getX() + x, position.getY() + y, position.getZ() + z, yaw, pitch); + BlockPos position = turtle.getPosition(); + player.moveTo(position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5, direction.toYRot(), 0); // Player inventory Inventory playerInventory = player.getInventory(); playerInventory.selected = 0; diff --git a/src/main/java/de/srendi/advancedperipherals/lib/peripherals/AutomataCorePeripheral.java b/src/main/java/de/srendi/advancedperipherals/lib/peripherals/AutomataCorePeripheral.java index 00618d94b..6327b87f6 100644 --- a/src/main/java/de/srendi/advancedperipherals/lib/peripherals/AutomataCorePeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/lib/peripherals/AutomataCorePeripheral.java @@ -10,7 +10,7 @@ import de.srendi.advancedperipherals.common.util.DataStorageUtil; import de.srendi.advancedperipherals.lib.metaphysics.IAutomataCoreTier; import net.minecraft.core.BlockPos; - +import net.minecraft.core.Direction; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -80,4 +80,8 @@ public boolean hasAttribute(String attribute) { public void setAttribute(String attribute) { attributes.put(attribute, true); } + + public Direction validateSide(String direction) throws LuaException { + return super.validateSide(direction); + } } diff --git a/src/main/java/de/srendi/advancedperipherals/lib/turtle/PeripheralTurtleUpgrade.java b/src/main/java/de/srendi/advancedperipherals/lib/turtle/PeripheralTurtleUpgrade.java index 2c1488eb0..2f06c5944 100644 --- a/src/main/java/de/srendi/advancedperipherals/lib/turtle/PeripheralTurtleUpgrade.java +++ b/src/main/java/de/srendi/advancedperipherals/lib/turtle/PeripheralTurtleUpgrade.java @@ -5,6 +5,7 @@ import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleUpgradeType; +import de.srendi.advancedperipherals.common.configuration.UnsafeConfig; import de.srendi.advancedperipherals.common.util.TranslationUtil; import de.srendi.advancedperipherals.lib.peripherals.DisabledPeripheral; import de.srendi.advancedperipherals.lib.peripherals.IBasePeripheral; @@ -21,7 +22,7 @@ protected PeripheralTurtleUpgrade(ResourceLocation id, ItemStack item) { super(id, TurtleUpgradeType.PERIPHERAL, TranslationUtil.turtle(id.getPath()), item); } - //TODO: Do we still need this with the new modeller system? + // TODO: Do we still need this with the new modeller system? public abstract ModelResourceLocation getLeftModel(); public abstract ModelResourceLocation getRightModel(); @@ -37,4 +38,13 @@ public IPeripheral createPeripheral(@NotNull ITurtleAccess turtle, @NotNull Turt } return peripheral; } + + @Override + public boolean isItemSuitable(@NotNull ItemStack stack) { + if (UnsafeConfig.getIgnoreTurtlePeripheralItemNBT()) { + // always accept NBTed items + return true; + } + return super.isItemSuitable(stack); + } }