Rohan Verma



Commit Diff


commit - 12b6fd0e76508533048d936c805c36e021ae4a86
commit + 5b39389340654ac191111465e83dcac8eafcbe5b
blob - /dev/null
blob + c2222ae5e9839f0e389bb21baee9db127ced4f32 (mode 644)
Binary files /dev/null and .DS_Store differ
blob - ecaf3c0caf6d160e40e3a4cad3453072f2aee257
blob + b38974d9a0ee2dc668a7b8386fecc071ad752f2e
--- build.gradle
+++ build.gradle
@@ -95,6 +95,8 @@ jar {
     exclude 'me/desht/**'
     exclude 'net/mehvahdjukaar/**'
     exclude 'com/cobblemon/**'
+    exclude 'com/jaquadro/**'
+    exclude 'com/texelsaurus/**'
 }
 
 var generateModMetadata = tasks.register("generateModMetadata", ProcessResources) {
blob - decb1f156e06922e6cc40cd9f16187ec7d775ed8
blob + af248c516b7809421a6dce09c4ebe6e5c0110151
--- gradle.properties
+++ gradle.properties
@@ -17,7 +17,7 @@ loader_version_range=[1,)
 
 # Optional dependencies. All integrations are soft: missing mods are simply skipped.
 etched_version_range=[5.0.0,6.0.0)
-sable_version_range=[1.0.0,)
+sable_version_range=[2.0.3,)
 waterframes_version_range=[2.1.0,3.0.0)
 citadel_version_range=[2.0.0,3.0.0)
 
@@ -25,7 +25,7 @@ citadel_version_range=[2.0.0,3.0.0)
 mod_id=aeronauticscompat
 mod_name=AeronauticsCompat
 mod_license=MIT
-mod_version=1.1.2
+mod_version=1.1.3
 mod_authors=rohan
 mod_description=Compatibility fixes for Create: Aeronautics / Sable sub-levels. Per-mod patches apply only when the target mod is installed.
 mod_group_id=sh.rsap.aeronauticscompat
blob - /dev/null
blob + e0fb753522fbb96d5964df700ba1197bb09997f8 (mode 644)
--- /dev/null
+++ src/main/java/com/jaquadro/minecraft/storagedrawers/util/WorldUtils.java
@@ -0,0 +1,14 @@
+package com.jaquadro.minecraft.storagedrawers.util;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.BlockHitResult;
+
+public final class WorldUtils {
+    private WorldUtils() {}
+
+    public static BlockHitResult rayTraceEyes(Level level, Player player, BlockPos blockPos) {
+        return null;
+    }
+}
blob - /dev/null
blob + 4426aad1ec61d6f5950cf7a1a7948c7378f147d1 (mode 644)
--- /dev/null
+++ src/main/java/com/texelsaurus/minecraft/chameleon/util/WorldUtils.java
@@ -0,0 +1,14 @@
+package com.texelsaurus.minecraft.chameleon.util;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.BlockHitResult;
+
+public final class WorldUtils {
+    private WorldUtils() {}
+
+    public static BlockHitResult rayTraceEyes(Level level, Player player, BlockPos blockPos) {
+        return null;
+    }
+}
blob - 11bbad628318b2cd789c4b028b825414064c45d1
blob + 494276892609aa2a4e1911b9d6ddb6171e5567dd
--- src/main/java/dev/ryanhcode/sable/api/SubLevelAssemblyHelper.java
+++ src/main/java/dev/ryanhcode/sable/api/SubLevelAssemblyHelper.java
@@ -1,6 +1,21 @@
 package dev.ryanhcode.sable.api;
 
+import dev.ryanhcode.sable.companion.math.BoundingBox3ic;
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.phys.Vec3;
+
 public final class SubLevelAssemblyHelper {
     private SubLevelAssemblyHelper() {}
+
     public static void moveBlocks() {}
+
+    public static void moveOtherStuff(ServerLevel level, AssemblyTransform transform,
+                                      Iterable<BlockPos> blocks, BoundingBox3ic bounds) {}
+
+    public static class AssemblyTransform {
+        public Vec3 apply(Vec3 pos) {
+            return pos;
+        }
+    }
 }
blob - /dev/null
blob + 31f876ea28c65b672567188556f4e311b887e54e (mode 644)
--- /dev/null
+++ src/main/java/dev/ryanhcode/sable/companion/math/BoundingBox3ic.java
@@ -0,0 +1,7 @@
+package dev.ryanhcode.sable.companion.math;
+
+import net.minecraft.world.phys.AABB;
+
+public interface BoundingBox3ic {
+    AABB toAABB();
+}
blob - /dev/null
blob + 6ff4afc63e4b201ec5b6559a6436f5b160800a62 (mode 644)
--- /dev/null
+++ src/main/java/sh/rsap/aeronauticscompat/compat/SubLevelRayTrace.java
@@ -0,0 +1,57 @@
+package sh.rsap.aeronauticscompat.compat;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.ClipContext;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.BlockHitResult;
+import net.minecraft.world.phys.Vec3;
+
+/**
+ * Replacement for eye-to-block raytraces whose ray length is derived from the
+ * raw distance between the player and the target block. For a block on a Sable
+ * sub-level that distance is measured against the far-away plot coordinates,
+ * producing a multi-million-block ray that {@code Level.clip} walks block by
+ * block, synchronously loading chunks until the server watchdog kills the
+ * process.
+ */
+public final class SubLevelRayTrace {
+
+    /**
+     * Any genuine interaction happens within interaction range; a target
+     * further away than this can only be a sub-level plot position.
+     */
+    private static final double NEAR_DISTANCE_SQ = 64 * 64;
+
+    /** Upper bound on the replacement ray, well above any interaction range. */
+    private static final double MAX_RAY_LENGTH = 64;
+
+    private SubLevelRayTrace() {}
+
+    /**
+     * Returns a clip result whose ray length is computed through Sable's
+     * sub-level aware distance, or null when the target block is nearby and
+     * the caller's own raytrace is safe to run unchanged.
+     *
+     * <p>Sable's {@code BlockGetter.clip} overwrite projects the short ray
+     * into any sub-level it crosses and returns the hit in plot coordinates,
+     * so callers comparing the hit pos against the plot-space block pos keep
+     * working.
+     */
+    public static BlockHitResult rayTraceEyesBounded(Level level, Player player, BlockPos pos) {
+        Vec3 eyes = player.getEyePosition(1F);
+        Vec3 center = Vec3.atCenterOf(pos);
+        if (eyes.distanceToSqr(center) <= NEAR_DISTANCE_SQ) {
+            return null;
+        }
+
+        double apparentSq = SableBridge.distanceSquaredWithSubLevels(level, eyes, center);
+        double length = Double.isNaN(apparentSq)
+                ? MAX_RAY_LENGTH
+                : Math.min(Math.sqrt(apparentSq), MAX_RAY_LENGTH);
+
+        Vec3 end = eyes.add(player.getViewVector(1F).scale(length + 1));
+        return level.clip(new ClipContext(eyes, end,
+                ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player));
+    }
+}
blob - 5dafc0ede48fbe660bd393799047a007478f8558
blob + a0b2375eda38dd9d7f1b46913fd1a1bb95a3a0a4
--- src/main/java/sh/rsap/aeronauticscompat/mixin/AeronauticsCompatMixinPlugin.java
+++ src/main/java/sh/rsap/aeronauticscompat/mixin/AeronauticsCompatMixinPlugin.java
@@ -19,6 +19,9 @@ public class AeronauticsCompatMixinPlugin implements I
     private static final String SIMULATED = "dev.simulated_team.simulated.util.SimAssemblyHelper";
     private static final String PNEUMATIC_CRAFT = "me.desht.pneumaticcraft.common.block.entity.utility.SentryTurretBlockEntity";
     private static final String COBBLEMON = "com.cobblemon.mod.common.api.storage.pc.link.ProximityPCLink";
+    private static final String STORAGE_DRAWERS = "com.jaquadro.minecraft.storagedrawers.util.WorldUtils";
+    private static final String CHAMELEON = "com.texelsaurus.minecraft.chameleon.util.WorldUtils";
+    private static final String CAMERA = "de.maxhenkel.camera.entities.ImageEntity";
 
     /** Per-mixin marker-class requirements (all must resolve to apply). */
     private static final Map<String, String[]> REQUIREMENTS = Map.ofEntries(
@@ -49,7 +52,13 @@ public class AeronauticsCompatMixinPlugin implements I
         Map.entry("sh.rsap.aeronauticscompat.mixin.cobblemon.PastureLinkManagerMixin",
                 new String[]{SABLE, COBBLEMON}),
         Map.entry("sh.rsap.aeronauticscompat.mixin.cobblemon.PokemonEntityRidingMixin",
-                new String[]{SABLE, COBBLEMON})
+                new String[]{SABLE, COBBLEMON}),
+        Map.entry("sh.rsap.aeronauticscompat.mixin.storagedrawers.StorageDrawersRayTraceMixin",
+                new String[]{SABLE, STORAGE_DRAWERS}),
+        Map.entry("sh.rsap.aeronauticscompat.mixin.storagedrawers.ChameleonRayTraceMixin",
+                new String[]{SABLE, CHAMELEON}),
+        Map.entry("sh.rsap.aeronauticscompat.mixin.camera.CameraFrameAssemblyMixin",
+                new String[]{SABLE, CAMERA})
     );
     @Override
     public void onLoad(String mixinPackage) {
@@ -63,7 +72,10 @@ public class AeronauticsCompatMixinPlugin implements I
                 + " bnb=" + resolves(BITS_N_BOBS, cl)
                 + " simulated=" + resolves(SIMULATED, cl)
                 + " pneumaticcraft=" + resolves(PNEUMATIC_CRAFT, cl)
-                + " cobblemon=" + resolves(COBBLEMON, cl));
+                + " cobblemon=" + resolves(COBBLEMON, cl)
+                + " storagedrawers=" + resolves(STORAGE_DRAWERS, cl)
+                + " chameleon=" + resolves(CHAMELEON, cl)
+                + " camera=" + resolves(CAMERA, cl));
     }
 
     @Override
blob - /dev/null
blob + f7e09a4fce44046d8afe7dd02b71dae5159eda93 (mode 644)
--- /dev/null
+++ src/main/java/sh/rsap/aeronauticscompat/mixin/camera/CameraFrameAssemblyMixin.java
@@ -0,0 +1,63 @@
+package sh.rsap.aeronauticscompat.mixin.camera;
+
+import dev.ryanhcode.sable.api.SubLevelAssemblyHelper;
+import dev.ryanhcode.sable.companion.math.BoundingBox3ic;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.phys.AABB;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Pseudo;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Sable's moveOtherStuff only carries HangingEntity subclasses into the
+ * sub-level during assembly. Camera Mod's image frame extends Entity
+ * directly, so frames placed on an unassembled structure get left behind
+ * (and then self-destruct once their wall is gone). This appends the same
+ * treatment for frames attached to assembled blocks. Frames are matched by
+ * entity-type id so no Camera Mod classes are referenced.
+ */
+@Pseudo
+@Mixin(value = SubLevelAssemblyHelper.class, remap = false)
+public abstract class CameraFrameAssemblyMixin {
+
+    @Unique
+    private static final ResourceLocation AERONAUTICSCOMPAT$IMAGE_FRAME =
+            ResourceLocation.fromNamespaceAndPath("camera", "image_frame");
+
+    @Inject(method = "moveOtherStuff", at = @At("TAIL"), remap = false)
+    private static void aeronauticscompat$moveImageFrames(
+            ServerLevel level, SubLevelAssemblyHelper.AssemblyTransform transform,
+            Iterable<BlockPos> blocks, BoundingBox3ic bounds,
+            CallbackInfo ci
+    ) {
+        Set<BlockPos> assembled = new HashSet<>();
+        for (BlockPos pos : blocks) {
+            assembled.add(pos.immutable());
+        }
+        if (assembled.isEmpty()) return;
+
+        for (Entity entity : level.getEntitiesOfClass(Entity.class, bounds.toAABB().inflate(2.0))) {
+            if (!AERONAUTICSCOMPAT$IMAGE_FRAME.equals(BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()))) {
+                continue;
+            }
+            // The frame's box sits flush against its supporting wall; a small
+            // inflation reaches the wall blocks, mirroring what Sable checks
+            // via HangingEntity.calculateSupportBox for paintings.
+            AABB support = entity.getBoundingBox().inflate(2.0 / 16.0);
+            boolean attached = BlockPos.betweenClosedStream(support).anyMatch(assembled::contains);
+            if (attached) {
+                entity.setPos(transform.apply(entity.position()));
+            }
+        }
+    }
+}
blob - /dev/null
blob + d48f1893dab2ef5c13574ab027b4080e27108750 (mode 644)
--- /dev/null
+++ src/main/java/sh/rsap/aeronauticscompat/mixin/storagedrawers/ChameleonRayTraceMixin.java
@@ -0,0 +1,35 @@
+package sh.rsap.aeronauticscompat.mixin.storagedrawers;
+
+import com.texelsaurus.minecraft.chameleon.util.WorldUtils;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.BlockHitResult;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Pseudo;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import sh.rsap.aeronauticscompat.compat.SubLevelRayTrace;
+
+/**
+ * The Chameleon library bundles an identical copy of Storage Drawers'
+ * WorldUtils.rayTraceEyes; the creative-mode left-click handler on NeoForge
+ * goes through this copy. Same unbounded-ray hang as
+ * {@link StorageDrawersRayTraceMixin}.
+ */
+@Pseudo
+@Mixin(value = WorldUtils.class, remap = false)
+public abstract class ChameleonRayTraceMixin {
+
+    @Inject(method = "rayTraceEyes", at = @At("HEAD"), cancellable = true, remap = false)
+    private static void aeronauticscompat$boundedRayTrace(
+            Level level, Player player, BlockPos blockPos,
+            CallbackInfoReturnable<BlockHitResult> cir
+    ) {
+        BlockHitResult hit = SubLevelRayTrace.rayTraceEyesBounded(level, player, blockPos);
+        if (hit != null) {
+            cir.setReturnValue(hit);
+        }
+    }
+}
blob - /dev/null
blob + f1e1d1f0d47555f11b97ce64f64dba8459d53487 (mode 644)
--- /dev/null
+++ src/main/java/sh/rsap/aeronauticscompat/mixin/storagedrawers/StorageDrawersRayTraceMixin.java
@@ -0,0 +1,36 @@
+package sh.rsap.aeronauticscompat.mixin.storagedrawers;
+
+import com.jaquadro.minecraft.storagedrawers.util.WorldUtils;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.BlockHitResult;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Pseudo;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import sh.rsap.aeronauticscompat.compat.SubLevelRayTrace;
+
+/**
+ * Storage Drawers resolves left-clicks (item extraction) by raytracing from
+ * the player's eyes toward the block, with the ray length taken from the raw
+ * player-to-block distance. On a sub-level the block pos is in plot space, so
+ * the ray becomes millions of blocks long and hangs the server. See
+ * {@link SubLevelRayTrace}.
+ */
+@Pseudo
+@Mixin(value = WorldUtils.class, remap = false)
+public abstract class StorageDrawersRayTraceMixin {
+
+    @Inject(method = "rayTraceEyes", at = @At("HEAD"), cancellable = true, remap = false)
+    private static void aeronauticscompat$boundedRayTrace(
+            Level level, Player player, BlockPos blockPos,
+            CallbackInfoReturnable<BlockHitResult> cir
+    ) {
+        BlockHitResult hit = SubLevelRayTrace.rayTraceEyesBounded(level, player, blockPos);
+        if (hit != null) {
+            cir.setReturnValue(hit);
+        }
+    }
+}
blob - 379f4b03199b71307a52758029ad6bd066516bc1
blob + bc9834bcd9813f80a6aa7972c0a4dc505b85e2fd
--- src/main/resources/aeronauticscompat.mixins.json
+++ src/main/resources/aeronauticscompat.mixins.json
@@ -14,7 +14,10 @@
     "pneumaticcraft.AbstractPneumaticCraftBlockMixin",
     "cobblemon.ProximityPCLinkMixin",
     "cobblemon.PastureLinkManagerMixin",
-    "cobblemon.PokemonEntityRidingMixin"
+    "cobblemon.PokemonEntityRidingMixin",
+    "storagedrawers.StorageDrawersRayTraceMixin",
+    "storagedrawers.ChameleonRayTraceMixin",
+    "camera.CameraFrameAssemblyMixin"
   ],
   "client": [
     "etched.EtchedStopListeningSoundMixin",
blob - 00dcf5a434c21254ed8390f063265781066d0876
blob + 5d540c411271bd6f2741b0f1e2d00d2e997e076c
--- src/main/resources/data/sable/tags/entity_type/retain_in_sub_level.json
+++ src/main/resources/data/sable/tags/entity_type/retain_in_sub_level.json
@@ -6,6 +6,7 @@
     {"id": "immersive_paintings:glow_painting", "required": false},
     {"id": "immersive_paintings:graffiti", "required": false},
     {"id": "immersive_paintings:glow_graffiti", "required": false},
-    {"id": "sleep_tight:bed_entity", "required": false}
+    {"id": "sleep_tight:bed_entity", "required": false},
+    {"id": "camera:image_frame", "required": false}
   ]
 }