commit 546a78f77fa588fbd1f48d14c4b0ee16273abe26 from: rohanverma2007 date: Sat Apr 25 09:24:26 2026 UTC edit compile time stubs commit - 8807bf3c95b0c4b284897192e9d22bbebad9cbcd commit + 546a78f77fa588fbd1f48d14c4b0ee16273abe26 blob - 8f5ecc2c4322359fbb7f1749fbc698b91d3e7bba blob + 4c4a00eabdddf1d4b78d64f886807a1fd8c43b3b --- src/main/java/com/github/alexthe666/citadel/server/entity/pathfinding/raycoms/AbstractAdvancedPathNavigate.java +++ src/main/java/com/github/alexthe666/citadel/server/entity/pathfinding/raycoms/AbstractAdvancedPathNavigate.java @@ -2,18 +2,6 @@ package com.github.alexthe666.citadel.server.entity.pa import net.minecraft.world.entity.Mob; -/** - * COMPILE-TIME STUB of - * {@code com.github.alexthe666.citadel.server.entity.pathfinding.raycoms.AbstractAdvancedPathNavigate}. - * - *

The real class at runtime is provided by Citadel and declares - * {@code protected final Mob ourEntity} and {@code public Mob getOurEntity()}. - * We only need {@code getOurEntity()} for the Citadel mixin to compile; - * the field itself is not referenced from our code. - * - *

Excluded from the output jar via {@code jar { exclude 'com/github/**' }} - * in build.gradle so it can never shadow the real class at runtime. - */ public abstract class AbstractAdvancedPathNavigate { public Mob getOurEntity() { blob - 42dcdfb8fc946f8a4bea02b6ba9115b95a22103c blob + 6203a0f9ed2fe09ca5f5cedf1a4dc3a4c343406f --- src/main/java/com/github/alexthe666/citadel/server/entity/pathfinding/raycoms/AdvancedPathNavigate.java +++ src/main/java/com/github/alexthe666/citadel/server/entity/pathfinding/raycoms/AdvancedPathNavigate.java @@ -1,19 +1,5 @@ package com.github.alexthe666.citadel.server.entity.pathfinding.raycoms; -/** - * COMPILE-TIME STUB of - * {@code com.github.alexthe666.citadel.server.entity.pathfinding.raycoms.AdvancedPathNavigate}. - * - *

The real class at runtime extends {@code AbstractAdvancedPathNavigate} - * (which holds the {@code ourEntity} field and the {@code getOurEntity()} - * accessor we use). The mixin doesn't need to reference any fields directly - * — it casts {@code this} to the parent at runtime — so this stub only needs - * the inheritance link to be present so {@code (AbstractAdvancedPathNavigate) - * (Object) this} compiles cleanly. - * - *

Excluded from the output jar via {@code jar { exclude 'com/github/**' }} - * in build.gradle so it can never shadow the real class at runtime. - */ public abstract class AdvancedPathNavigate extends AbstractAdvancedPathNavigate { protected PathResult moveToXYZ(final double x, final double y, final double z, final double speedFactor) { blob - 42992ad16c531461a662b90b737a84de2085c9b8 blob + 46b08b679f27076c025ef10486f80d9db726e0d5 --- src/main/java/com/github/alexthe666/citadel/server/entity/pathfinding/raycoms/PathResult.java +++ src/main/java/com/github/alexthe666/citadel/server/entity/pathfinding/raycoms/PathResult.java @@ -1,13 +1,4 @@ package com.github.alexthe666.citadel.server.entity.pathfinding.raycoms; -/** - * COMPILE-TIME STUB of - * {@code com.github.alexthe666.citadel.server.entity.pathfinding.raycoms.PathResult}. - * - *

The real class at runtime is provided by Citadel. This stub only needs - * to exist to satisfy the compiler for AeronauticsCompat's mixin; no fields - * or methods from it are used by the mixin itself. Excluded from the output - * jar via the {@code jar { exclude 'com/github/**' }} rule in build.gradle. - */ public class PathResult { } blob - b4b7effa2d6806ca8669964bd3b23a907c211ab0 blob + f6988de35ef0cc6e4a49bc78d80b232c6ca9bd36 --- src/main/java/dev/ryanhcode/sable/sound/MovingSoundInstanceDelegate.java +++ src/main/java/dev/ryanhcode/sable/sound/MovingSoundInstanceDelegate.java @@ -1,17 +1,4 @@ package dev.ryanhcode.sable.sound; -/** - * COMPILE-TIME STUB of Sable's - * {@code dev.ryanhcode.sable.sound.MovingSoundInstanceDelegate}. - * - *

This stub exists only so Etchtube's mixin can refer to the type name. - * The mixin stores a reference via {@link SoundInstanceDelegated#setDelegate} - * and reads it back via {@link SoundInstanceDelegated#getDelegate}; it never - * touches any methods on this class, so a no-body stub is sufficient. - * - *

At runtime, Sable itself provides the real class. This stub is built - * into a separate source set whose output is excluded from the Etchtube jar - * (see {@code build.gradle}). - */ public class MovingSoundInstanceDelegate { } blob - 752e29c85150b03e64d304506518bf364af5fa2e blob + 9ceaa254805a4f52f5dfcbe83c122bb4fdccc6a8 --- src/main/java/dev/ryanhcode/sable/sound/SoundInstanceDelegated.java +++ src/main/java/dev/ryanhcode/sable/sound/SoundInstanceDelegated.java @@ -1,17 +1,5 @@ package dev.ryanhcode.sable.sound; -/** - * COMPILE-TIME STUB of Sable's {@code dev.ryanhcode.sable.sound.SoundInstanceDelegated}. - * - *

This file exists purely so Etchtube's mixin can reference this interface at - * compile time. It is built into a separate {@code sableStubs} source set whose - * output is explicitly excluded from the Etchtube jar (see {@code build.gradle}). - * At runtime, Sable itself provides the real interface; if Sable isn't loaded, - * the mixin that depends on it is disabled by {@code EtchtubeMixinPlugin}. - * - *

Keep this shape in sync with upstream Sable. If the upstream interface - * changes its method signatures, this stub must be updated to match. - */ public interface SoundInstanceDelegated { MovingSoundInstanceDelegate getDelegate(); blob - 8241691230773707dfbb36c24bcba50a5ec3f511 blob + f200478d61a0fe9d773a0a293719886b40acb70c --- src/main/java/me/srrapero720/waterframes/WaterFrames.java +++ src/main/java/me/srrapero720/waterframes/WaterFrames.java @@ -4,19 +4,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Position; import net.minecraft.world.level.Level; -/** - * COMPILE-TIME STUB of - * {@code me.srrapero720.waterframes.WaterFrames}. - * - *

Only the method signature that AeronauticsCompat's mixin targets is - * declared. The real WaterFrames class at runtime provides the full - * implementation. This stub is built into a separate source set whose - * output is excluded from the AeronauticsCompat jar (see {@code build.gradle}). - * - *

Keep the {@code getDistance} signature in sync with upstream. The - * mixin targets the three-argument {@code (Level, BlockPos, Position)} - * overload by its full JVM descriptor. - */ public class WaterFrames { public static double getDistance(Level level, BlockPos pos, Position position) { blob - 6f30b702572ce3c60dedc454f83b4ce528abb29c blob + 8c7b4ce76fcf9af35197a9d934de2a3a0f09338a --- src/main/java/sh/rsap/aeronauticscompat/AeronauticsCompat.java +++ src/main/java/sh/rsap/aeronauticscompat/AeronauticsCompat.java @@ -8,15 +8,6 @@ import net.neoforged.fml.common.Mod; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import org.slf4j.Logger; -/** - * AeronauticsCompat entry point. - * - *

All of this mod's actual behavior lives in its mixins — see - * {@code aeronauticscompat.mixins.json} and - * {@link sh.rsap.aeronauticscompat.mixin.AeronauticsCompatMixinPlugin} for the - * list of patches and their per-mod gates. This class just logs what's on the - * classpath at common setup time so crash reports are easier to read. - */ @Mod(AeronauticsCompat.MOD_ID) public final class AeronauticsCompat { blob - 0767b5fca0ee1a1e17b034b21949dd8212888137 blob + 02389948c4dae6e1a03518a1dcb919b2dbd9b323 --- src/main/java/sh/rsap/aeronauticscompat/compat/SableBridge.java +++ src/main/java/sh/rsap/aeronauticscompat/compat/SableBridge.java @@ -9,11 +9,6 @@ import org.slf4j.Logger; import java.lang.reflect.Field; import java.lang.reflect.Method; -/** - * Reflective bridge to Sable's {@code SableCompanion}. We don't compile - * against Sable; lookups short-circuit to a no-op when the class isn't - * present so the mod stays loadable without Sable. - */ public final class SableBridge { private static final Logger LOGGER = LogUtils.getLogger(); @@ -78,11 +73,6 @@ public final class SableBridge { return INSTANCE != null && DIST_SQ_METHOD != null; } - /** - * Compute the squared distance between two points including any sub-level - * transforms. Returns {@code Double.NaN} when Sable isn't available — the - * caller should fall back to vanilla distance in that case. - */ public static double distanceSquaredWithSubLevels(Level level, Position a, Position b) { if (!isAvailable()) return Double.NaN; try { @@ -92,18 +82,6 @@ public final class SableBridge { } } - /** - * Whether the given entity is currently inside a Sable sub-level (i.e. - * standing on a contraption / ship). Used by the Citadel pathfinding - * guard to avoid rejecting legitimate paths between two entities that - * are both on the same contraption — those endpoints have huge - * world-space coords by design and would otherwise look identical to - * the sub-level entity bleed-through that the guard exists to catch. - * - *

Returns {@code false} if Sable isn't loaded, the membership lookup - * isn't available, or the call throws — all of which are fail-safe: - * the caller will then fall back to the conservative distance check. - */ public static boolean isInSubLevel(Entity entity) { if (entity == null || GET_CONTAINING_ENTITY_METHOD == null) return false; try { blob - cb534c34f5b64b8a83873d3d7afb2cc161819a6f blob + 65cd64a8d97e1d3216bf8cade6b59dd7a7703c4e --- src/main/java/sh/rsap/aeronauticscompat/mixin/AeronauticsCompatMixinPlugin.java +++ src/main/java/sh/rsap/aeronauticscompat/mixin/AeronauticsCompatMixinPlugin.java @@ -8,13 +8,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -/** - * Gates each mixin on the required target mod(s) being present. A mixin is - * applied only if every marker class it depends on resolves. - * - *

If a required mod is missing the mixin is silently skipped — the rest - * of AeronauticsCompat (config, logs, other mixins) keeps working. - */ public class AeronauticsCompatMixinPlugin implements IMixinConfigPlugin { private static final String SABLE = "dev.ryanhcode.sable.sound.SoundInstanceDelegated"; @@ -54,11 +47,6 @@ public class AeronauticsCompatMixinPlugin implements I } private static boolean resolves(String fqn, ClassLoader cl) { - // IMPORTANT: don't use Class.forName here — it *defines* the class even - // with initialize=false, which trips Mixin's "target loaded too early" - // check if we happen to probe a class that another mixin wants to - // transform. getResource only looks for the .class file on the - // classpath; it does not load it. String resource = fqn.replace('.', '/') + ".class"; return cl.getResource(resource) != null; } blob - 3faec0a4989be5bea853b1659507a0d278745e94 blob + 3702a34bef1209621ffad955cea8e1eaa29cc176 --- src/main/java/sh/rsap/aeronauticscompat/mixin/citadel/CitadelAdvancedPathNavigateMixin.java +++ src/main/java/sh/rsap/aeronauticscompat/mixin/citadel/CitadelAdvancedPathNavigateMixin.java @@ -12,62 +12,10 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -/** - * Citadel-from-contraption OOM guard. - * - *

When Sable is installed, - * {@code dev.ryanhcode.sable.util.SubLevelInclusiveLevelEntityGetter} causes - * {@code Level#getEntities(AABB, ...)} to also scan entities living in Sable - * sub-levels. Those entities' raw positions live in sub-level-local - * coordinates — often tens of millions of blocks from any world-space mob - * that sees them. - * - *

When a Citadel-powered mob (Alex's Mobs, Alex's Caves, Cataclysm, etc.) - * calls {@link AdvancedPathNavigate#moveToXYZ} with one of these targets, - * Citadel's {@code AbstractPathJob} allocates a {@code ChunkCache} sized by - * the bounding box between the mob's world-space start position and - * the sub-level-space end position: - * - *

- *   final int minX = Math.min(start.getX(), end.getX()) - range/2;
- *   final int maxX = Math.max(start.getX(), end.getX()) + range/2;
- *   // ... same for Z, then a LevelChunk[dx/16 + 1][dz/16 + 1] is allocated.
- * 
- * - * With one coordinate near origin and the other in the tens of millions, the - * chunk array is sized in the trillions of entries and the JVM OOMs. This - * happens on servers too — the path job runs on the server thread. - * - *

The span-based guard. We measure the span between the - * mob's current position (what {@code AbstractPathJob.prepareStart} will use - * as {@code start}) and the requested target {@code (x, y, z)} (the - * {@code end}). If the horizontal span exceeds - * {@link #AERONAUTICSCOMPAT$MAX_REASONABLE_SPAN} blocks, the resulting - * {@code ChunkCache} would be too large to allocate safely — this is - * definitionally the bleed-through pattern, regardless of whether the mob - * is on a contraption or in world space. We reject the path and return - * {@code null}; next tick the goal will pick a saner target. - * - *

Crucially this guard is symmetric: it doesn't matter whether - * start is tiny and end is huge, or vice-versa — we only care about the - * span. That means legitimate mob-to-mob paths entirely within a single - * sub-level (both endpoints at, say, 20000001 and 20000005) are allowed - * through, because the span is small. And legitimate world-space paths - * (start and end both near world origin) are also allowed. Only the - * pathological cross-domain case is rejected. - */ @Pseudo @Mixin(value = AdvancedPathNavigate.class, remap = false) public abstract class CitadelAdvancedPathNavigateMixin { - /** - * Maximum horizontal span (blocks, per axis) between a mob's current - * position and a prospective path target before the call is rejected. - * Chosen so that {@code (span / 16 + 1)²} stays comfortably below - * anything that could exhaust a typical server heap — 4096 blocks yields - * a ~256×256 chunk array (65k entries), still plenty of headroom for - * any real pathing job while catching anything in the millions. - */ @Unique private static final double AERONAUTICSCOMPAT$MAX_REASONABLE_SPAN = 4096.0; blob - ffaba628e15d07a3e732d46ad4bc0441545f7284 blob + de1bb51dad23c19336c34528764af4847ffacc69 --- src/main/java/sh/rsap/aeronauticscompat/mixin/etched/EtchedStopListeningSoundMixin.java +++ src/main/java/sh/rsap/aeronauticscompat/mixin/etched/EtchedStopListeningSoundMixin.java @@ -6,39 +6,6 @@ import gg.moonflower.etched.api.sound.StopListeningSou import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; -/** - * Fixes the "jukebox music never stops on a Sable/Create: Aeronautics - * contraption" bug that affects every music disc (vanilla, - * datapack, Etched) as long as Etched is installed. - * - *

Root cause. When a jukebox sound is played on a - * block sitting in a Sable sub-level (i.e. a moving contraption), Sable's - * {@code SoundEngineMixin.sable$play} wraps the sound in a - * {@link MovingSoundInstanceDelegate} at play-time — OpenAL's channel is - * keyed by that wrapper. On stop, Sable's {@code sable$stop} unwraps back - * to the wrapper via {@link SoundInstanceDelegated}, so the lookup hits - * and the channel is closed. - * - *

Etched, however, installs its own wrapper over every - * jukebox sound via {@code LevelRendererMixin.modifySoundInstance}, which - * rewrites the local variable in {@code LevelRenderer.playRecord} to be a - * {@link StopListeningSound}. {@code StopListeningSound} does not extend - * {@code AbstractSoundInstance}, so it doesn't inherit Sable's - * {@code SoundInstanceDelegated} mixin. Consequence: at stop time, - * Sable's unwrap check ({@code instance instanceof SoundInstanceDelegated}) - * fails, the {@link StopListeningSound} is passed through to OpenAL, no - * channel matches it, and the sound plays forever. - * - *

Fix. Make {@code StopListeningSound} implement - * {@link SoundInstanceDelegated}. Sable's constructor sets the back-ref - * on us at play-time; Sable's stop-time unwrap reads it back. Now both - * ends of the Sable pipeline see a matched channel key and the sound - * stops when the disc is ejected. - * - *

This mixin is conditionally applied (see - * {@code sh.rsap.etchtube.mixin.EtchtubeMixinPlugin}): if Sable isn't - * loaded, the mixin isn't applied, and Etchtube runs without it. - */ @Mixin(StopListeningSound.class) public abstract class EtchedStopListeningSoundMixin implements SoundInstanceDelegated { blob - e039ac8160c40beeac10f16935c1e04dddc8715b blob + c243ed167a2fe7b685f55f1770f0b2dbfaf486bf --- src/main/java/sh/rsap/aeronauticscompat/mixin/waterframes/WaterFramesSableDistanceMixin.java +++ src/main/java/sh/rsap/aeronauticscompat/mixin/waterframes/WaterFramesSableDistanceMixin.java @@ -11,25 +11,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import sh.rsap.aeronauticscompat.compat.SableBridge; -/** - * Makes WaterFrames' TVs audible when they ride a Sable / Create: Aeronautics - * contraption. - * - *

Bug. WaterFrames attenuates audio by the distance between the - * TV's block position and the player. On a Sable contraption the block lives - * in a sub-level at coordinates around 20 million blocks from the player's - * world-space position — so the computed distance is astronomical and the - * TV is silent no matter how close you stand. - * - *

Fix. WaterFrames already has an exact analog for Valkyrien Skies - * ({@code VSCompat.getSquaredDistance}). This mixin adds the Sable equivalent - * at HEAD: if Sable is loaded, short-circuit to its - * {@code distanceSquaredWithSubLevels}, which projects both endpoints out - * of any enclosing sub-level before measuring. - * - *

Gated on both WaterFrames and Sable being loaded — see - * {@code AeronauticsCompatMixinPlugin}. - */ @Mixin(WaterFrames.class) public abstract class WaterFramesSableDistanceMixin {