diff --git a/src/client/java/work/lclpnet/notica/impl/PendingSong.java b/src/client/java/work/lclpnet/notica/impl/PendingSong.java index d8e0bd0..f574398 100644 --- a/src/client/java/work/lclpnet/notica/impl/PendingSong.java +++ b/src/client/java/work/lclpnet/notica/impl/PendingSong.java @@ -18,14 +18,20 @@ public class PendingSong implements Song { private final Instruments instruments; private final boolean stereo; private final byte signature; + private int startTick; public PendingSong(SongHeader header) { + this(header, 0); + } + + public PendingSong(SongHeader header, int startTick) { this.durationTicks = header.durationTicks(); this.ticksPerSecond = header.ticksPerSecond(); this.loopConfig = header.loopConfig(); this.instruments = header.instruments(); this.stereo = header.stereo(); this.signature = header.signature(); + this.startTick = startTick; var layerInfo = header.layerInfo(); var layers = new HashMap(layerInfo.size()); @@ -86,6 +92,8 @@ public byte signature() { * @param slice The song slice. */ public void accept(SongSlice slice) { + startTick = Math.max(0, Math.min(slice.tickStart(), startTick)); + for (NoteEvent noteEvent : slice) { MutableLayer layer = layers.get(noteEvent.layer()); @@ -94,4 +102,8 @@ public void accept(SongSlice slice) { layer.accept(noteEvent); } } + + public int getStartTick() { + return startTick; + } } diff --git a/src/client/java/work/lclpnet/notica/networking/NoticaClientNetworking.java b/src/client/java/work/lclpnet/notica/networking/NoticaClientNetworking.java index ab99aeb..b7b7264 100644 --- a/src/client/java/work/lclpnet/notica/networking/NoticaClientNetworking.java +++ b/src/client/java/work/lclpnet/notica/networking/NoticaClientNetworking.java @@ -59,29 +59,53 @@ private CompletableFuture onQueryVersion(MinecraftClient client, private void onPlaySong(PlaySongS2CPacket packet, ClientPlayerEntity player, PacketSender sender) { Identifier songId = packet.getSongId(); byte[] checksum = packet.getChecksum(); + int startTick = packet.getStartTick(); PendingSong song = songRepository.get(checksum); if (song == null) { - logger.debug("Song {} ({}) is not cached, requesting it", songId, ByteHelper.toHexString(checksum, 32)); + acceptUnknownSong(packet, songId, checksum, startTick); + } else if (startTick < song.getStartTick()) { + // the cached song is missing parts before its old start + acceptUnknownRegion(packet, song, songId); + } - // song is not cached, create a new instance - song = new PendingSong(packet.getHeader()); + controller.playSong(songId, packet.getVolume(), startTick); + } - SongSlice slice = packet.getSlice(); + private void acceptUnknownSong(PlaySongS2CPacket packet, Identifier songId, byte[] checksum, int startTick) { + logger.debug("Song {} ({}) is not cached, requesting it...", songId, ByteHelper.toHexString(checksum, 32)); - logger.debug("Got initial slice {} for song {}", slice, songId); + // song is not cached, create a new instance + PendingSong song = new PendingSong(packet.getHeader(), startTick); - song.accept(slice); + SongSlice slice = packet.getSlice(); - if (!packet.isLast()) { - requestNext(songId, slice); - } + logger.debug("Got initial slice {} for song {}", slice, songId); - songRepository.add(songId, checksum, song); + song.accept(slice); + + if (song.loopConfig().enabled() && startTick > 0) { + // songs with looping enabled that start with an offset need to be fetched completely + request(songId, 0, 0); + } else if (!packet.isLast()) { + // request next song part + requestNext(songId, slice); } - controller.playSong(songId, packet.getVolume(), packet.getStartTick()); + songRepository.add(songId, checksum, song); + } + + private void acceptUnknownRegion(PlaySongS2CPacket packet, PendingSong song, Identifier songId) { + SongSlice slice = packet.getSlice(); + + song.accept(slice); + + if (packet.isLast()) return; + + logger.debug("Cached song is missing parts, requesting the song from the beginning..."); + + request(songId, 0, 0); } private void onStopSong(StopSongBidiPacket packet, ClientPlayerEntity player, PacketSender sender) { diff --git a/src/main/java/work/lclpnet/notica/util/PlayerConfigContainer.java b/src/main/java/work/lclpnet/notica/util/PlayerConfigContainer.java index 1724477..93c9392 100644 --- a/src/main/java/work/lclpnet/notica/util/PlayerConfigContainer.java +++ b/src/main/java/work/lclpnet/notica/util/PlayerConfigContainer.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -20,7 +21,7 @@ public class PlayerConfigContainer { private final Path directory; private final Logger logger; - private final Map entries = new HashMap<>(); + private final Map entries = Collections.synchronizedMap(new HashMap<>()); public PlayerConfigContainer(Path directory, Logger logger) { this.directory = directory;