From 681a39723d805d028fcf378dccf2c64a5ddffce1 Mon Sep 17 00:00:00 2001 From: Rythian Smythe Date: Wed, 15 Jul 2026 10:03:23 +0100 Subject: [PATCH 1/3] Sable Compatibility - Added "Sable Companion" dependency. - Added "SableHelper" class. This class will safely no-op if sable is absent, or if a computer is not placed onto a sublevel. - Added "Sable" to `APAddon` , along with an integration plugin to load previously deprecated Environment Detector functions: `scanShips(radius)`, `scanShipCost(radius)`, and a new `getShip()` which will return the information of the ship the computer is currently placed on. - Updated IPeripheralOwner to refer to `SableHelper` for `getPhysicsPos()` and `getDirection()` - Updated BasePeripheral to refer to `SableHelper` for `isOnShip()` --- .gitignore | 1 + build.gradle | 11 +++ gradle.properties | 1 + .../common/addons/APAddon.java | 3 +- .../computercraft/owner/IPeripheralOwner.java | 14 +--- .../peripheral/ColonyPeripheral.java | 2 +- .../peripheral/PlayerDetectorPeripheral.java | 24 +++--- .../common/addons/sable/Integration.java | 11 +++ .../common/addons/sable/SableHelper.java | 82 +++++++++++++++++++ .../addons/sable/ShipScannerPlugin.java | 62 ++++++++++++++ .../lib/peripherals/BasePeripheral.java | 4 +- 11 files changed, 188 insertions(+), 27 deletions(-) create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/sable/Integration.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/sable/SableHelper.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/sable/ShipScannerPlugin.java diff --git a/.gitignore b/.gitignore index 3dbbd5d4a..f59a9e288 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ forge*changelog.txt test-files logs/ +/.vscode diff --git a/build.gradle b/build.gradle index e93d7516d..1fb3900fc 100644 --- a/build.gradle +++ b/build.gradle @@ -207,6 +207,10 @@ repositories { includeModule("thedarkcolour", "kotlinforforge") } } + maven { + name = "RyanHCode Maven" + url = "https://maven.ryanhcode.dev/releases" + } exclusiveContent { forRepository { maven { @@ -249,6 +253,13 @@ dependencies { // Minimal requirements end + // Sable Companion + jarJar(api("dev.ryanhcode.sable-companion:sable-companion-common-1.21.1:[${sable_companion_version},)")) { + version { + prefer project.sable_companion_version + } + } + compileOnly "com.refinedmods.refinedstorage:refinedstorage-neoforge:${refinedstorage_version}" if (!minimumRuntime) { runtimeOnly("com.refinedmods.refinedstorage:refinedstorage-neoforge:${refinedstorage_version}") { diff --git a/gradle.properties b/gradle.properties index 287de9da3..601339f5b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -41,6 +41,7 @@ patchouli_version=1.21-88 powah_version=6143661 refinedstorage_mekanism_version=1.1.0 refinedstorage_version=2.0.0 +sable_companion_version=1.6.0 # Mod dependencies which are needed for other mods # dimstorage diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/APAddon.java b/src/main/java/de/srendi/advancedperipherals/common/addons/APAddon.java index 13d30feca..357919566 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/APAddon.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/APAddon.java @@ -17,7 +17,8 @@ public enum APAddon { PATCHOULI("patchouli"), POWAH("powah"), REFINEDSTORAGE("refinedstorage"), - REFINEDSTORAGE_MEKANISM("refinedstorage_mekanism_integration"); + REFINEDSTORAGE_MEKANISM("refinedstorage_mekanism_integration"), + SABLE("sable"); private final String modId; private boolean loaded; diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/IPeripheralOwner.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/IPeripheralOwner.java index 9fdb9a29f..ba71395e9 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/IPeripheralOwner.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/IPeripheralOwner.java @@ -2,6 +2,7 @@ import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.peripheral.IPeripheral; +import de.srendi.advancedperipherals.common.addons.sable.SableHelper; import de.srendi.advancedperipherals.common.util.StringUtil; import de.srendi.advancedperipherals.common.util.fakeplayer.APFakePlayer; import de.srendi.advancedperipherals.lib.peripherals.IPeripheralOperation; @@ -62,22 +63,13 @@ default Vec3 getCenterPos() { @NotNull default Vec3 getPhysicsPos() { - Vec3 pos = this.getCenterPos(); - return pos; - // if (!APAddons.vs2Loaded) { - // return pos; - // } - // return ValkyrienSkies.transformToWorldPos(getLevel(), getPos(), pos); + return SableHelper.projectOutOfSubLevel(getLevel(), this.getCenterPos()); } @NotNull default Vec3 getDirection() { Vec3 dir = Vec3.atLowerCornerOf(getFacing().getNormal()); - return dir; - // if (!APAddons.vs2Loaded) { - // return dir; - // } - // return ValkyrienSkies.transformToWorldDir(getLevel(), getPos(), dir); + return SableHelper.transformDirectionOutOfSubLevel(getLevel(), getPos(), dir); } @NotNull Direction getFacing(); diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/ColonyPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/ColonyPeripheral.java index edd8f2b20..d6e1a5105 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/ColonyPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/ColonyPeripheral.java @@ -287,6 +287,6 @@ private IColony getColonyOrThrow() throws LuaException { @Nullable private IColony getColonyWithoutPermission() { IMinecoloniesAPI api = IMinecoloniesAPI.getInstance(); - return api.getColonyManager().getColonyByPosFromWorld(getLevel(), getPos()); + return api.getColonyManager().getColonyByPosFromWorld(getLevel(), getPhysicsBlockPos()); } } 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 0207f0778..c3944ad05 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 getPlayersInCoords(Map firstCoord, Map sec BlockPos secondPos = LuaConverter.convertToBlockPos(secondCoord); return getPlayers() - .filter(player -> CoordUtil.isInRange(getCenterPos(), player, getLevel(), firstPos, secondPos, MAX_RANGE)) + .filter(player -> CoordUtil.isInRange(getPhysicsPos(), player, getLevel(), firstPos, secondPos, MAX_RANGE)) .map(player -> player.getGameProfile().getName()) .toList(); } @@ -105,7 +105,7 @@ public final List getPlayersInCoords(Map firstCoord, Map sec @LuaFunction(mainThread = true) public final List getPlayersInCubic(int x, int y, int z) { return getPlayers() - .filter(player -> CoordUtil.isInRange(getCenterPos(), getLevel(), player, x, y, z, MAX_RANGE)) + .filter(player -> CoordUtil.isInRange(getPhysicsPos(), getLevel(), player, x, y, z, MAX_RANGE)) .map(player -> player.getGameProfile().getName()) .toList(); } @@ -113,7 +113,7 @@ public final List getPlayersInCubic(int x, int y, int z) { @LuaFunction(mainThread = true) public final List getPlayersInRange(int range) { return getPlayers() - .filter(player -> CoordUtil.isInRange(getCenterPos(), getLevel(), player, range, MAX_RANGE)) + .filter(player -> CoordUtil.isInRange(getPhysicsPos(), getLevel(), player, range, MAX_RANGE)) .map(player -> player.getGameProfile().getName()) .toList(); } @@ -124,19 +124,19 @@ public final boolean isPlayersInCoords(Map firstCoord, Map secondCoo BlockPos secondPos = LuaConverter.convertToBlockPos(secondCoord); return getPlayers() - .anyMatch(player -> CoordUtil.isInRange(getCenterPos(), player, getLevel(), firstPos, secondPos, MAX_RANGE)); + .anyMatch(player -> CoordUtil.isInRange(getPhysicsPos(), player, getLevel(), firstPos, secondPos, MAX_RANGE)); } @LuaFunction(mainThread = true) public final boolean isPlayersInCubic(int x, int y, int z) { return getPlayers() - .anyMatch(player -> CoordUtil.isInRange(getCenterPos(), getLevel(), player, x, y, z, MAX_RANGE)); + .anyMatch(player -> CoordUtil.isInRange(getPhysicsPos(), getLevel(), player, x, y, z, MAX_RANGE)); } @LuaFunction(mainThread = true) public final boolean isPlayersInRange(int range) { return getPlayers() - .anyMatch(player -> CoordUtil.isInRange(getCenterPos(), getLevel(), player, range, MAX_RANGE)); + .anyMatch(player -> CoordUtil.isInRange(getPhysicsPos(), getLevel(), player, range, MAX_RANGE)); } @LuaFunction(mainThread = true) @@ -145,19 +145,19 @@ public final boolean isPlayerInCoords(Map firstCoord, Map secondCoor BlockPos secondPos = LuaConverter.convertToBlockPos(secondCoord); ServerPlayer player = getPlayer(username); - return player != null && CoordUtil.isInRange(getCenterPos(), player, getLevel(), firstPos, secondPos, MAX_RANGE); + return player != null && CoordUtil.isInRange(getPhysicsPos(), player, getLevel(), firstPos, secondPos, MAX_RANGE); } @LuaFunction(mainThread = true) public final boolean isPlayerInCubic(int x, int y, int z, String username) { ServerPlayer player = getPlayer(username); - return player != null && CoordUtil.isInRange(getCenterPos(), getLevel(), player, x, y, z, MAX_RANGE); + return player != null && CoordUtil.isInRange(getPhysicsPos(), getLevel(), player, x, y, z, MAX_RANGE); } @LuaFunction(mainThread = true) public final boolean isPlayerInRange(int range, String username) { ServerPlayer player = getPlayer(username); - return player != null && CoordUtil.isInRange(getCenterPos(), getLevel(), player, range, MAX_RANGE); + return player != null && CoordUtil.isInRange(getPhysicsPos(), getLevel(), player, range, MAX_RANGE); } @LuaFunction(value = "getPlayer", mainThread = true) @@ -168,7 +168,7 @@ public final Map getPlayerLua(String playerName, Optional { if (message.restrictedRange() && MAX_RANGE != -1) { ServerPlayer player = level.getServer().getPlayerList().getPlayer(message.playerId()); - if (player == null || !CoordUtil.isInRange(getCenterPos(), getLevel(), player, MAX_RANGE, MAX_RANGE)) { + if (player == null || !CoordUtil.isInRange(getPhysicsPos(), getLevel(), player, MAX_RANGE, MAX_RANGE)) { return; } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/sable/Integration.java b/src/main/java/de/srendi/advancedperipherals/common/addons/sable/Integration.java new file mode 100644 index 000000000..f452fd233 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/sable/Integration.java @@ -0,0 +1,11 @@ +package de.srendi.advancedperipherals.common.addons.sable; + +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.EnvironmentDetectorPeripheral; + +public class Integration implements Runnable { + + @Override + public void run() { + EnvironmentDetectorPeripheral.addIntegrationPlugin(ShipScannerPlugin::new); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/sable/SableHelper.java b/src/main/java/de/srendi/advancedperipherals/common/addons/sable/SableHelper.java new file mode 100644 index 000000000..663504886 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/sable/SableHelper.java @@ -0,0 +1,82 @@ +package de.srendi.advancedperipherals.common.addons.sable; + +import dev.ryanhcode.sable.companion.SableCompanion; +import dev.ryanhcode.sable.companion.SubLevelAccess; +import dev.ryanhcode.sable.companion.math.BoundingBox3d; +import dev.ryanhcode.sable.companion.math.BoundingBox3dc; +import dev.ryanhcode.sable.companion.math.Pose3dc; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Position; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import org.joml.Quaterniondc; +import org.joml.Vector3d; +import org.joml.Vector3dc; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class SableHelper { + + private SableHelper() {} + + public static Vec3 projectOutOfSubLevel(Level level, Vec3 pos) { + return SableCompanion.INSTANCE.projectOutOfSubLevel(level, (Position) pos); + } + + public static Vec3 transformDirectionOutOfSubLevel(Level level, BlockPos pos, Vec3 direction) { + SubLevelAccess subLevel = SableCompanion.INSTANCE.getContaining(level, pos); + if (subLevel == null) { + return direction; + } + return subLevel.logicalPose().transformNormal(direction); + } + + public static boolean isOnSubLevel(Level level, BlockPos pos) { + return SableCompanion.INSTANCE.getContaining(level, pos) != null; + } + + public static Map getContainingSubLevel(Level level, BlockPos pos, Vec3 center) { + SubLevelAccess subLevel = SableCompanion.INSTANCE.getContaining(level, pos); + if (subLevel == null) { + return null; + } + return subLevelToObject(level, subLevel, center); + } + + public static List> scanSubLevels(Level level, Vec3 center, double radius) { + BoundingBox3d bounds = new BoundingBox3d( + center.x - radius, center.y - radius, center.z - radius, + center.x + radius, center.y + radius, center.z + radius); + + List> result = new ArrayList<>(); + for (SubLevelAccess subLevel : SableCompanion.INSTANCE.getAllIntersecting(level, bounds)) { + result.add(subLevelToObject(level, subLevel, center)); + } + return result; + } + + private static Map subLevelToObject(Level level, SubLevelAccess subLevel, Vec3 center) { + Pose3dc pose = subLevel.logicalPose(); + Vector3dc pos = pose.position(); + Quaterniondc rot = pose.orientation(); + BoundingBox3dc box = subLevel.boundingBox(); + Vector3d velocity = SableCompanion.INSTANCE.getVelocity(level, new Vector3d(pos)); + + Map data = new HashMap<>(); + data.put("id", subLevel.getUniqueId().toString()); + String name = subLevel.getName(); + if (name != null) { + data.put("name", name); + } + data.put("x", pos.x() - center.x); + data.put("y", pos.y() - center.y); + data.put("z", pos.z() - center.z); + data.put("rotate", Map.of("x", rot.x(), "y", rot.y(), "z", rot.z(), "w", rot.w())); + data.put("size", Map.of("x", box.maxX() - box.minX(), "y", box.maxY() - box.minY(), "z", box.maxZ() - box.minZ())); + data.put("velocity", Map.of("x", velocity.x(), "y", velocity.y(), "z", velocity.z())); + return data; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/sable/ShipScannerPlugin.java b/src/main/java/de/srendi/advancedperipherals/common/addons/sable/ShipScannerPlugin.java new file mode 100644 index 000000000..5cf1a00ed --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/sable/ShipScannerPlugin.java @@ -0,0 +1,62 @@ +package de.srendi.advancedperipherals.common.addons.sable; + +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.MethodResult; +import de.srendi.advancedperipherals.common.addons.computercraft.operations.SphereOperationContext; +import de.srendi.advancedperipherals.common.addons.computercraft.owner.IPeripheralOwner; +import de.srendi.advancedperipherals.lib.peripherals.BasePeripheralPlugin; +import de.srendi.advancedperipherals.lib.peripherals.IPeripheralOperation; + +import java.util.Map; + +import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SphereOperation.SCAN_SHIPS; + +public class ShipScannerPlugin extends BasePeripheralPlugin { + + public ShipScannerPlugin(IPeripheralOwner owner) { + super(owner); + } + + @Override + public IPeripheralOperation[] getOperations() { + return new IPeripheralOperation[]{SCAN_SHIPS}; + } + + @LuaFunction(mainThread = true) + public final MethodResult scanShips(int radius) throws LuaException { + return withOperation(SCAN_SHIPS, new SphereOperationContext(radius), context -> { + return context.getRadius() > SCAN_SHIPS.getMaxCostRadius() ? MethodResult.of(null, "Radius exceeds max value") : null; + }, context -> { + return MethodResult.of(SableHelper.scanSubLevels(owner.getLevel(), owner.getPhysicsPos(), context.getRadius())); + }, null); + } + + @LuaFunction(mainThread = true) + public final Map getShip() throws LuaException { + Map ship = SableHelper.getContainingSubLevel(owner.getLevel(), owner.getPos(), owner.getPhysicsPos()); + if (ship == null) { + throw new LuaException("There is no ship here"); + } + return ship; + } + + @LuaFunction + public final MethodResult scanShipCost(int radius) { + int estimatedCost = estimateShipCost(radius); + if (estimatedCost < 0) { + return MethodResult.of(null, "Radius exceeds max value"); + } + return MethodResult.of(estimatedCost); + } + + private static int estimateShipCost(int radius) { + if (radius <= SCAN_SHIPS.getMaxFreeRadius()) { + return 0; + } + if (radius > SCAN_SHIPS.getMaxCostRadius()) { + return -1; + } + return SCAN_SHIPS.getCost(SphereOperationContext.of(radius)); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/lib/peripherals/BasePeripheral.java b/src/main/java/de/srendi/advancedperipherals/lib/peripherals/BasePeripheral.java index 42d0be10f..5b91d6efe 100644 --- a/src/main/java/de/srendi/advancedperipherals/lib/peripherals/BasePeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/lib/peripherals/BasePeripheral.java @@ -13,6 +13,7 @@ import de.srendi.advancedperipherals.common.addons.computercraft.owner.IPeripheralOwner; import de.srendi.advancedperipherals.common.addons.computercraft.owner.OperationAbility; import de.srendi.advancedperipherals.common.addons.computercraft.owner.PeripheralOwnerAbility; +import de.srendi.advancedperipherals.common.addons.sable.SableHelper; import de.srendi.advancedperipherals.common.util.CoordUtil; import de.srendi.advancedperipherals.common.util.inventory.ChemicalUtil; import de.srendi.advancedperipherals.common.util.inventory.FluidUtil; @@ -152,8 +153,7 @@ public Vec3 getCenterPos() { } public boolean isOnShip() { - return false; - // return APAddons.isBlockOnShip(owner.getLevel(), owner.getPos()); + return SableHelper.isOnSubLevel(owner.getLevel(), owner.getPos()); } public Vec3 getPhysicsPos() { From 9de260945b3860681308bf2ee65269348a2f3197 Mon Sep 17 00:00:00 2001 From: Rythian Smythe Date: Wed, 15 Jul 2026 10:22:04 +0100 Subject: [PATCH 2/3] Pocket Computer & Smart Glasses. Fixes issue where Pocket Computer and Smart Glasses peripherals would be unable to use modified peripheral functions. --- .../computercraft/owner/IPeripheralOwner.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/IPeripheralOwner.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/IPeripheralOwner.java index ba71395e9..feecb6e87 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/IPeripheralOwner.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/owner/IPeripheralOwner.java @@ -63,13 +63,23 @@ default Vec3 getCenterPos() { @NotNull default Vec3 getPhysicsPos() { - return SableHelper.projectOutOfSubLevel(getLevel(), this.getCenterPos()); + Level level = this.getLevel(); + Vec3 pos = this.getCenterPos(); + if (level == null || pos == null) { + return pos; + } + return SableHelper.projectOutOfSubLevel(level, pos); } @NotNull default Vec3 getDirection() { Vec3 dir = Vec3.atLowerCornerOf(getFacing().getNormal()); - return SableHelper.transformDirectionOutOfSubLevel(getLevel(), getPos(), dir); + Level level = this.getLevel(); + Vec3 center = this.getCenterPos(); + if (level == null || center == null) { + return dir; + } + return SableHelper.transformDirectionOutOfSubLevel(level, getPos(), dir); } @NotNull Direction getFacing(); From 6713fda42cd05b7dfcc7daa64d982410d6aace1d Mon Sep 17 00:00:00 2001 From: Rythian Smythe Date: Wed, 15 Jul 2026 10:37:12 +0100 Subject: [PATCH 3/3] Update .gitignore Remove vscode directory Signed-off-by: Rythian Smythe --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index f59a9e288..3dbbd5d4a 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,3 @@ forge*changelog.txt test-files logs/ -/.vscode