From 6bf40126b01460137dc91b4c960866b125fa170d Mon Sep 17 00:00:00 2001 From: awakaxis Date: Fri, 4 Sep 2026 03:26:46 -0700 Subject: [PATCH 1/4] fix: fix distance check logic when placing a MetaMachineBlock on a Sable SubLevel --- .../com/gregtechceu/gtceu/api/block/MetaMachineBlock.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java index 52e652bfd59..d172b1b91e4 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java +++ b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java @@ -20,6 +20,8 @@ import com.gregtechceu.gtceu.common.machine.owner.MachineOwner; import com.gregtechceu.gtceu.utils.ExtendedUseOnContext; +import dev.ryanhcode.sable.companion.SableCompanion; +import dev.ryanhcode.sable.companion.SubLevelAccess; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -162,6 +164,11 @@ public BlockState getStateForPlacement(BlockPlaceContext context) { } Vec3 pos = player.position(); + + // transform the players "global" position into the sublevel plot's local space for the distance checks + SubLevelAccess subLevel = SableCompanion.INSTANCE.getContaining(player.level(), blockPos); + if (subLevel != null) pos = subLevel.logicalPose().transformPositionInverse(pos); + if (Math.abs(pos.x - (double) ((float) blockPos.getX() + 0.5F)) < 2.0D && Math.abs(pos.z - (double) ((float) blockPos.getZ() + 0.5F)) < 2.0D) { double d0 = pos.y + (double) player.getEyeHeight(); From 5ce78c654d6d7cfe507c35c50110ee38064d7816 Mon Sep 17 00:00:00 2001 From: awakaxis Date: Fri, 4 Sep 2026 03:29:50 -0700 Subject: [PATCH 2/4] fix: fix muffler particle coordinate space issue when on a Sable Sublevel --- .../multiblock/part/MufflerPartMachine.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java index 63e3642130d..6e2cee0ae7e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java @@ -18,6 +18,9 @@ import com.gregtechceu.gtceu.common.machine.trait.hazard.EnvironmentalHazardEmitterTrait; import com.gregtechceu.gtceu.utils.GTUtil; +import dev.ryanhcode.sable.companion.ClientSubLevelAccess; +import dev.ryanhcode.sable.companion.SableCompanion; +import dev.ryanhcode.sable.companion.math.JOMLConversion; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.world.item.ItemStack; @@ -32,6 +35,7 @@ import lombok.Getter; import org.jetbrains.annotations.MustBeInvokedByOverriders; import org.jetbrains.annotations.Nullable; +import org.joml.Vector3d; import java.util.stream.IntStream; @@ -146,6 +150,8 @@ public boolean isFrontFaceFree() { public void emitPollutionParticles() { var pos = getBlockPos(); var facing = getFrontFacing(); + Vector3d normal = new Vector3d( + facing.getStepX(), facing.getStepY(), facing.getStepZ()); IHazardParticleContainer container = GTCapabilityHelper.getHazardContainer(getLevel(), pos.relative(facing), facing.getOpposite()); @@ -155,14 +161,22 @@ public void emitPollutionParticles() { } var center = pos.getCenter(); + + ClientSubLevelAccess clientSubLevelAccess = SableCompanion.INSTANCE.getContainingClient(pos); + if (clientSubLevelAccess != null) { + // if in a sublevel, we should transform our center and normal to match the sublevel's pose + center = JOMLConversion.toMojang(clientSubLevelAccess.renderPose().transformPosition(JOMLConversion.toJOML(center))); + clientSubLevelAccess.renderPose().transformNormal(normal); + } + var offset = .75f; - var xPos = (float) (center.x + facing.getStepX() * offset + (GTValues.RNG.nextFloat() - .5f) * .35f); - var yPos = (float) (center.y + facing.getStepY() * offset + (GTValues.RNG.nextFloat() - .5f) * .35f); - var zPos = (float) (center.z + facing.getStepZ() * offset + (GTValues.RNG.nextFloat() - .5f) * .35f); + var xPos = (float) (center.x + normal.x * offset + (GTValues.RNG.nextFloat() - .5f) * .35f); + var yPos = (float) (center.y + normal.y * offset + (GTValues.RNG.nextFloat() - .5f) * .35f); + var zPos = (float) (center.z + normal.z * offset + (GTValues.RNG.nextFloat() - .5f) * .35f); - var ySpd = facing.getStepY() + (GTValues.RNG.nextFloat() - .15f) * .5f; - var xSpd = facing.getStepX() + (GTValues.RNG.nextFloat() - .5f) * .5f; - var zSpd = facing.getStepZ() + (GTValues.RNG.nextFloat() - .5f) * .5f; + var ySpd = normal.y + (GTValues.RNG.nextFloat() - .15f) * .5f; + var xSpd = normal.x + (GTValues.RNG.nextFloat() - .5f) * .5f; + var zSpd = normal.z + (GTValues.RNG.nextFloat() - .5f) * .5f; getLevel().addParticle(GTParticleTypes.MUFFLER_PARTICLE.get(), xPos, yPos, zPos, xSpd, ySpd, zSpd); From e17066cf307c511a27a252c1407b1d69ee34fa21 Mon Sep 17 00:00:00 2001 From: awakaxis Date: Fri, 4 Sep 2026 05:10:20 -0700 Subject: [PATCH 3/4] fix: add static subclass methods for sable compats --- .../gtceu/api/block/MetaMachineBlock.java | 16 +++++++++++--- .../multiblock/part/MufflerPartMachine.java | 21 ++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java index d172b1b91e4..fed1ea297b4 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java +++ b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java @@ -165,9 +165,9 @@ public BlockState getStateForPlacement(BlockPlaceContext context) { Vec3 pos = player.position(); - // transform the players "global" position into the sublevel plot's local space for the distance checks - SubLevelAccess subLevel = SableCompanion.INSTANCE.getContaining(player.level(), blockPos); - if (subLevel != null) pos = subLevel.logicalPose().transformPositionInverse(pos); + if (GTCEu.Mods.isSableLoaded()) { + pos = SableUtils.transformIntoSubLevel(pos, player.level(), blockPos); + } if (Math.abs(pos.x - (double) ((float) blockPos.getX() + 0.5F)) < 2.0D && Math.abs(pos.z - (double) ((float) blockPos.getZ() + 0.5F)) < 2.0D) { @@ -591,4 +591,14 @@ public static int colorTinted(BlockState blockState, @Nullable BlockAndTintGette public final BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return getDefinition().getBlockEntityType().create(pos, state); } + + private static class SableUtils { + public static Vec3 transformIntoSubLevel(Vec3 pos, Level level, BlockPos blockPos) { + // transform the players "global" position into the sublevel plot's local space for the distance checks + SubLevelAccess subLevel = SableCompanion.INSTANCE.getContaining(level, blockPos); + if (subLevel != null) return subLevel.logicalPose().transformPositionInverse(pos); + + return pos; + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java index 6e2cee0ae7e..c4bc99b4ca8 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.common.machine.multiblock.part; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.blockentity.BlockEntityCreationInfo; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; @@ -24,6 +25,7 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; import net.neoforged.neoforge.items.ItemHandlerHelper; @@ -162,11 +164,8 @@ public void emitPollutionParticles() { var center = pos.getCenter(); - ClientSubLevelAccess clientSubLevelAccess = SableCompanion.INSTANCE.getContainingClient(pos); - if (clientSubLevelAccess != null) { - // if in a sublevel, we should transform our center and normal to match the sublevel's pose - center = JOMLConversion.toMojang(clientSubLevelAccess.renderPose().transformPosition(JOMLConversion.toJOML(center))); - clientSubLevelAccess.renderPose().transformNormal(normal); + if (GTCEu.Mods.isSableLoaded()) { + center = SableUtils.transformPositionAndNormal(center, normal); } var offset = .75f; @@ -193,4 +192,16 @@ public void buildMainUI(ParentWidget mainWidget, PosGuiData guiData, PanelSyn .child(createSquareSlotGroupFromInventory(inventory, "muffler_inventory", syncManager).margin(10) .center()); } + + private static class SableUtils { + public static Vec3 transformPositionAndNormal(Vec3 pos, Vector3d normal) { + ClientSubLevelAccess clientSubLevelAccess = SableCompanion.INSTANCE.getContainingClient(pos); + if (clientSubLevelAccess != null) { + // if in a sublevel, we should transform our center and normal to match the sublevel's pose + clientSubLevelAccess.renderPose().transformNormal(normal); + return JOMLConversion.toMojang(clientSubLevelAccess.renderPose().transformPosition(JOMLConversion.toJOML(pos))); + } + return pos; + } + } } From 6b790a830d04092d477f6bc475f10aa6dc080617 Mon Sep 17 00:00:00 2001 From: awakaxis Date: Fri, 4 Sep 2026 16:52:34 -0700 Subject: [PATCH 4/4] formatting: spotless --- .../gregtechceu/gtceu/api/block/MetaMachineBlock.java | 5 +++-- .../machine/multiblock/part/MufflerPartMachine.java | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java index fed1ea297b4..189c720a2a9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java +++ b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java @@ -20,8 +20,6 @@ import com.gregtechceu.gtceu.common.machine.owner.MachineOwner; import com.gregtechceu.gtceu.utils.ExtendedUseOnContext; -import dev.ryanhcode.sable.companion.SableCompanion; -import dev.ryanhcode.sable.companion.SubLevelAccess; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -60,6 +58,8 @@ import appeng.api.AECapabilities; import appeng.api.networking.IInWorldGridNodeHost; +import dev.ryanhcode.sable.companion.SableCompanion; +import dev.ryanhcode.sable.companion.SubLevelAccess; import lombok.Getter; import org.jetbrains.annotations.Nullable; @@ -593,6 +593,7 @@ public final BlockEntity newBlockEntity(BlockPos pos, BlockState state) { } private static class SableUtils { + public static Vec3 transformIntoSubLevel(Vec3 pos, Level level, BlockPos blockPos) { // transform the players "global" position into the sublevel plot's local space for the distance checks SubLevelAccess subLevel = SableCompanion.INSTANCE.getContaining(level, blockPos); diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java index c4bc99b4ca8..4ea2b9a1188 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MufflerPartMachine.java @@ -19,9 +19,6 @@ import com.gregtechceu.gtceu.common.machine.trait.hazard.EnvironmentalHazardEmitterTrait; import com.gregtechceu.gtceu.utils.GTUtil; -import dev.ryanhcode.sable.companion.ClientSubLevelAccess; -import dev.ryanhcode.sable.companion.SableCompanion; -import dev.ryanhcode.sable.companion.math.JOMLConversion; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.world.item.ItemStack; @@ -34,6 +31,9 @@ import brachy.modularui.screen.UISettings; import brachy.modularui.value.sync.PanelSyncManager; import brachy.modularui.widget.ParentWidget; +import dev.ryanhcode.sable.companion.ClientSubLevelAccess; +import dev.ryanhcode.sable.companion.SableCompanion; +import dev.ryanhcode.sable.companion.math.JOMLConversion; import lombok.Getter; import org.jetbrains.annotations.MustBeInvokedByOverriders; import org.jetbrains.annotations.Nullable; @@ -194,12 +194,14 @@ public void buildMainUI(ParentWidget mainWidget, PosGuiData guiData, PanelSyn } private static class SableUtils { + public static Vec3 transformPositionAndNormal(Vec3 pos, Vector3d normal) { ClientSubLevelAccess clientSubLevelAccess = SableCompanion.INSTANCE.getContainingClient(pos); if (clientSubLevelAccess != null) { // if in a sublevel, we should transform our center and normal to match the sublevel's pose clientSubLevelAccess.renderPose().transformNormal(normal); - return JOMLConversion.toMojang(clientSubLevelAccess.renderPose().transformPosition(JOMLConversion.toJOML(pos))); + return JOMLConversion + .toMojang(clientSubLevelAccess.renderPose().transformPosition(JOMLConversion.toJOML(pos))); } return pos; }