From 8a1067940d43d943a8c36222a908bfce73bac01b Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 19 Dec 2021 16:29:06 +0000 Subject: [PATCH] Account for the game being paused when tracking sound progress When the game is paused in SSP world, speakers are not ticked. However, System.nanoTime() continues to increase, which means the next tick speakers believe there has been a big jump and so schedule a bunch of extra audio. To avoid this, we keep track of how long the game has been paused offset nanoTime by that amount. Fixes #994 --- .../shared/peripheral/speaker/DfpwmState.java | 5 +- .../peripheral/speaker/SpeakerPeripheral.java | 3 +- .../shared/util/PauseAwareTimer.java | 53 +++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/main/java/dan200/computercraft/shared/util/PauseAwareTimer.java diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/DfpwmState.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/DfpwmState.java index 47638a970..af53ced42 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/DfpwmState.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/DfpwmState.java @@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.speaker; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaTable; +import dan200.computercraft.shared.util.PauseAwareTimer; import net.minecraft.util.math.MathHelper; import javax.annotation.Nonnull; @@ -36,7 +37,7 @@ class DfpwmState private boolean previousBit = false; private boolean unplayed = true; - private long clientEndTime = System.nanoTime(); + private long clientEndTime = PauseAwareTimer.getTime(); private float pendingVolume = 1.0f; private ByteBuffer pendingAudio; @@ -107,7 +108,7 @@ class DfpwmState boolean isPlaying() { - return unplayed || clientEndTime >= System.nanoTime(); + return unplayed || clientEndTime >= PauseAwareTimer.getTime(); } float getVolume() diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java index d361b4d75..01fbf5e06 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java @@ -17,6 +17,7 @@ import dan200.computercraft.shared.network.client.SpeakerAudioClientMessage; import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage; import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage; import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; +import dan200.computercraft.shared.util.PauseAwareTimer; import net.minecraft.network.play.server.SPlaySoundPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.state.properties.NoteBlockInstrument; @@ -119,7 +120,7 @@ public abstract class SpeakerPeripheral implements IPeripheral return; } - long now = System.nanoTime(); + long now = PauseAwareTimer.getTime(); if( sound != null ) { lastPlayTime = clock; diff --git a/src/main/java/dan200/computercraft/shared/util/PauseAwareTimer.java b/src/main/java/dan200/computercraft/shared/util/PauseAwareTimer.java new file mode 100644 index 000000000..9443168e6 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/util/PauseAwareTimer.java @@ -0,0 +1,53 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.util; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.Util; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +/** + * A monotonically increasing clock which accounts for the game being paused. + */ +@Mod.EventBusSubscriber( Dist.CLIENT ) +public final class PauseAwareTimer +{ + private static boolean paused; + private static long pauseTime; + private static long pauseOffset; + + private PauseAwareTimer() + { + } + + public static long getTime() + { + return (paused ? pauseTime : Util.getNanos()) - pauseOffset; + } + + @SubscribeEvent + public static void tick( TickEvent.RenderTickEvent event ) + { + if( event.phase != TickEvent.Phase.START ) return; + + boolean isPaused = Minecraft.getInstance().isPaused(); + if( isPaused == paused ) return; + + if( isPaused ) + { + pauseTime = Util.getNanos(); + paused = true; + } + else + { + pauseOffset += Util.getNanos() - pauseTime; + paused = false; + } + } +}