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
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
# 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)
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
+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
+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
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
+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
+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
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(
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) {
+ " 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
+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
+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
+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
"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
{"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}
]
}