Compare commits

..
2 Commits
Author SHA1 Message Date
dnorberg 7d335f5952 Bug Fix: Enviromental Audio from Immersive Portals
Build / build (push) Canceled after 0s
2026-09-23 15:32:24 -04:00
dnorberg 211aa0733b Mute Immersive Portals preview world audio
Build / build (push) Canceled after 0s
2026-09-23 15:20:07 -04:00
6 changed files with 150 additions and 1 deletions

No files matched your search

+1 -1
View File
@@ -35,7 +35,7 @@ mod_name=Rivencraft
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=LGPL-3.0-or-later
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
mod_version=0.3.2.1
mod_version=0.3.2.3
immersive_portals_version=6.0.7
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
@@ -42,7 +42,11 @@ public final class ImmersivePortalsDestinationPreviewRenderer implements Destina
@Override
public void accept(LinkPreviewPayload payload) {
if (destination != null) {
ImmersivePortalsPreviewAudioGuard.clear(destination.dimension());
}
destination = payload;
ImmersivePortalsPreviewAudioGuard.mute(payload.dimension());
resetView();
}
@@ -118,6 +122,7 @@ public final class ImmersivePortalsDestinationPreviewRenderer implements Destina
FRAMEBUFFER.clear(true);
minecraft.getMainRenderTarget().bindWrite(true);
MyRenderHelper.restoreViewPort();
ImmersivePortalsPreviewAudioGuard.markCameraRestorePending();
GuiPortalRendering.submitNextFrameRendering(info, FRAMEBUFFER);
hasCompletedFrame = true;
graphics.renderOutline(left, top, width, height, exploring ? 0xFFE0C36A : 0xFF7B6AA8);
@@ -234,5 +239,8 @@ public final class ImmersivePortalsDestinationPreviewRenderer implements Destina
public void close() {
clearMovement();
hasCompletedFrame = false;
if (destination != null) {
ImmersivePortalsPreviewAudioGuard.clear(destination.dimension());
}
}
}
@@ -0,0 +1,62 @@
package net.dnorberg.rivencraft.client.preview;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
/** Prevents an Immersive Portals preview world from contributing sounds to a book screen. */
public final class ImmersivePortalsPreviewAudioGuard {
private static ResourceKey<Level> mutedDimension;
private static boolean cameraRestorePending;
private ImmersivePortalsPreviewAudioGuard() {
}
public static void mute(ResourceKey<Level> dimension) {
mutedDimension = dimension;
}
public static void clear(ResourceKey<Level> dimension) {
if (dimension.equals(mutedDimension)) {
mutedDimension = null;
}
}
public static boolean shouldMute(ClientLevel level) {
Minecraft minecraft = Minecraft.getInstance();
return mutedDimension != null
&& level.dimension().equals(mutedDimension)
&& (minecraft.player == null || level != minecraft.player.level());
}
public static void markCameraRestorePending() {
cameraRestorePending = true;
}
/**
* Immersive Portals' GUI renderer leaves the shared main camera in the
* remote world after its end-of-frame render. Restore it before the next
* frame uses that camera for rain effects and the sound listener.
*/
public static void restorePlayerCamera() {
Minecraft minecraft = Minecraft.getInstance();
if (!cameraRestorePending) {
return;
}
cameraRestorePending = false;
if (minecraft.level == null || minecraft.getCameraEntity() == null) {
return;
}
Camera camera = minecraft.gameRenderer.getMainCamera();
camera.setup(
minecraft.level,
minecraft.getCameraEntity(),
!minecraft.options.getCameraType().isFirstPerson(),
minecraft.options.getCameraType().isMirrored(),
camera.getPartialTickTime()
);
}
}
@@ -0,0 +1,59 @@
package net.dnorberg.rivencraft.mixin.client;
import net.dnorberg.rivencraft.client.preview.ImmersivePortalsPreviewAudioGuard;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Holder;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/** Mutes only the remote client world displayed by a Rivencraft book preview. */
@Mixin(value = ClientLevel.class, priority = 2000)
public abstract class ClientLevelSoundMixin {
@Inject(
method = "playSound(DDDLnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FFZJ)V",
at = @At("HEAD"),
cancellable = true
)
private void rivencraft$mutePreviewPositionSound(
double x, double y, double z, SoundEvent sound, SoundSource source,
float volume, float pitch, boolean distanceDelay, long seed, CallbackInfo callback
) {
rivencraft$cancelPreviewSound(callback);
}
@Inject(
method = "playLocalSound(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V",
at = @At("HEAD"),
cancellable = true
)
private void rivencraft$mutePreviewLocalEntitySound(
Entity entity, SoundEvent sound, SoundSource source, float volume, float pitch,
CallbackInfo callback
) {
rivencraft$cancelPreviewSound(callback);
}
@Inject(
method = "playSeededSound(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/core/Holder;Lnet/minecraft/sounds/SoundSource;FFJ)V",
at = @At("HEAD"),
cancellable = true
)
private void rivencraft$mutePreviewSeededEntitySound(
Player player, Entity entity, Holder<SoundEvent> sound, SoundSource source,
float volume, float pitch, long seed, CallbackInfo callback
) {
rivencraft$cancelPreviewSound(callback);
}
private void rivencraft$cancelPreviewSound(CallbackInfo callback) {
if (ImmersivePortalsPreviewAudioGuard.shouldMute((ClientLevel)(Object)this)) {
callback.cancel();
}
}
}
@@ -0,0 +1,18 @@
package net.dnorberg.rivencraft.mixin.client;
import net.dnorberg.rivencraft.client.preview.ImmersivePortalsPreviewAudioGuard;
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.CallbackInfo;
/** Restores the player camera after Immersive Portals finishes GUI-world rendering. */
@Pseudo
@Mixin(targets = "qouteall.imm_ptl.core.render.GuiPortalRendering", remap = false)
public abstract class ImmersivePortalsGuiRenderingMixin {
@Inject(method = "_onGameRenderEnd", at = @At("RETURN"), remap = false)
private static void rivencraft$restorePlayerCamera(CallbackInfo callback) {
ImmersivePortalsPreviewAudioGuard.restorePlayerCamera();
}
}
@@ -4,6 +4,8 @@
"package": "net.dnorberg.rivencraft.mixin",
"compatibilityLevel": "JAVA_21",
"client": [
"client.ClientLevelSoundMixin",
"client.ImmersivePortalsGuiRenderingMixin",
"client.LevelRendererMixin"
],
"injectors": {