diff --git a/patches/net/minecraft/client/sounds/MusicManager.java.patch b/patches/net/minecraft/client/sounds/MusicManager.java.patch new file mode 100644 index 0000000000..9772e14aed --- /dev/null +++ b/patches/net/minecraft/client/sounds/MusicManager.java.patch @@ -0,0 +1,19 @@ +--- a/net/minecraft/client/sounds/MusicManager.java ++++ b/net/minecraft/client/sounds/MusicManager.java +@@ -24,7 +_,15 @@ + } + + public void tick() { +- Music music = this.minecraft.getSituationalMusic(); ++ Music music = net.neoforged.neoforge.client.ClientHooks.selectMusic(this.minecraft.getSituationalMusic(), this.currentMusic); ++ if (music == null) { ++ if (this.currentMusic != null) { ++ this.stopPlaying(); ++ } ++ this.nextSongDelay = 0; ++ return; ++ } ++ + if (this.currentMusic != null) { + if (!music.getEvent().value().getLocation().equals(this.currentMusic.getLocation()) && music.replaceCurrentMusic()) { + this.minecraft.getSoundManager().stop(this.currentMusic); diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java index ddef24010a..767063d3d1 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java @@ -102,6 +102,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ReloadableResourceManager; +import net.minecraft.sounds.Music; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; @@ -164,6 +165,7 @@ import net.neoforged.neoforge.client.event.RenderTooltipEvent; import net.neoforged.neoforge.client.event.ScreenEvent; import net.neoforged.neoforge.client.event.ScreenshotEvent; +import net.neoforged.neoforge.client.event.SelectMusicEvent; import net.neoforged.neoforge.client.event.TextureAtlasStitchedEvent; import net.neoforged.neoforge.client.event.ToastAddEvent; import net.neoforged.neoforge.client.event.ViewportEvent; @@ -392,6 +394,13 @@ public static SoundInstance playSound(SoundEngine manager, SoundInstance sound) return e.getSound(); } + @Nullable + public static Music selectMusic(Music situational, @Nullable SoundInstance playing) { + SelectMusicEvent e = new SelectMusicEvent(situational, playing); + NeoForge.EVENT_BUS.post(e); + return e.getMusic(); + } + public static void drawScreen(Screen screen, GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { guiGraphics.pose().pushPose(); guiLayers.forEach(layer -> { diff --git a/src/main/java/net/neoforged/neoforge/client/event/SelectMusicEvent.java b/src/main/java/net/neoforged/neoforge/client/event/SelectMusicEvent.java new file mode 100644 index 0000000000..e0d20a712b --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/event/SelectMusicEvent.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.event; + +import net.minecraft.client.resources.sounds.SoundInstance; +import net.minecraft.sounds.Music; +import net.neoforged.bus.api.Event; +import net.neoforged.bus.api.ICancellableEvent; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.common.NeoForge; +import org.jetbrains.annotations.Nullable; + +/** + * Fired when the {@link net.minecraft.client.sounds.MusicManager} checks what situational music should be used. This fires before the music begins playing.
+ * If the music is set to {@code null} by a modder, it will cancel any music that was already playing.
+ *
+ * Note that the higher priority you make your event listener, the earlier the music will be set.
+ * Because of this, if you want your music to take precedence over others (perhaps you want to have seperate nighttime music for a biome for instance) then you may want it to have a lower priority.
+ *
+ * To make your music instantly play rather than waiting for the playing music to stop, set the music to one that {@linkplain Music#replaceCurrentMusic() is set to replace the current music.}
+ *
+ * Higher priorities would likely be better suited for biome-based or dimension-based musics, whereas lower priority is likely good for specific structures or situations.
+ *
+ * This event is {@linkplain ICancellableEvent cancellable}, and does not {@linkplain HasResult have a result}.
+ * If the event is canceled, then whatever the latest music set was will be used as the music. + *
+ * This event is fired on the {@linkplain NeoForge#EVENT_BUS main Forge event bus},
+ * only on the {@linkplain LogicalSide#CLIENT logical client}. + * + */ +public class SelectMusicEvent extends Event implements ICancellableEvent { + private @Nullable Music music; + private final Music originalMusic; + private final @Nullable SoundInstance playingMusic; + + public SelectMusicEvent(Music music, @Nullable SoundInstance playingMusic) { + this.music = music; + this.originalMusic = music; + this.playingMusic = playingMusic; + } + + /** + * {@return the original situational music that was selected} + */ + public Music getOriginalMusic() { + return originalMusic; + } + + /** + * {@return the current track that the {@link net.minecraft.client.sounds.MusicManager} is playing, or {@code null} if there is none} + */ + @Nullable + public SoundInstance getPlayingMusic() { + return playingMusic; + } + + /** + * {@return the Music to be played, or {@code null} if any playing music should be cancelled} + */ + @Nullable + public Music getMusic() { + return music; + } + + /** + * Changes the situational music. If this is set to {@code null}, any currently playing music will be cancelled.
+ * If this was {@code null} but on the next tick isn't, the music given will be immediately played.
+ *
+ */ + public void setMusic(@Nullable Music newMusic) { + this.music = newMusic; + } + + /** + * Sets the music and then cancels the event so that other listeners will not be invoked.
+ * Note that listeners using {@link SubscribeEvent#receiveCanceled()} will still be able to override this, but by default they will not + */ + public void overrideMusic(@Nullable Music newMusic) { + this.music = newMusic; + this.setCanceled(true); + } +}