When playing a jukebox at a low TPS, for example, 10. The timer to find out when the music disk ends and update the comparator takes twice as long as the song, since the client sound manager is unaffected by TPS.
Code analysis
The relevant code is in JukeboxBlockEntity
private long tickCount;
private long recordStartedTick;
private boolean isPlaying;
private void tick(Level level, BlockPos pos, BlockState state) {
++this.ticksSinceLastEvent;
if (this.isRecordPlaying()) {
Item var5 = this.getFirstItem().getItem();
if (var5 instanceof RecordItem recordItem) {
if (this.shouldRecordStopPlaying(recordItem)) {
this.stopPlaying();
} else if (this.shouldSendJukeboxPlayingEvent()) {
this.ticksSinceLastEvent = 0;
world.gameEvent(GameEvent.JUKEBOX_PLAY, pos, GameEvent.Context.of(state));
this.spawnMusicParticles(world, pos);
}
}
}
++this.tickCount;
}
private boolean shouldRecordStopPlaying(RecordItem disc) {
return this.tickCount >= this.recordStartedTick + (long)disc.getLengthInTicks() + 20L;
}
As you can see, there is no accounting for TPS.
My solution would be to converting tickCount to a double, replacing ++this.tickCount;
with this.tickCount += 20 / tps
; so each tick can actually account for more than a single tick.
When serializing to NBT, this can be kept backwards compatible by just rounding back to long (how it is saved), as at most this can lose a single tick per save/load cycle.
I don't think this is actually a bug, being an undeterministical-length (which your solution would do, more/less ticks based on tick speed) makes it hard to create contraptions with and alike. Consistent timing is always better, if anything, your source of lag is the issue here.
There's only 1 thing that uses real-time on the server, and that isthe world border.