diff --git a/gradle.properties b/gradle.properties index 0590a3a..509c932 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,8 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://modmuss50.me/fabric.html -minecraft_version=1.20.6 -yarn_mappings=1.20.6+build.1 +minecraft_version=1.21 +yarn_mappings=1.21+build.2 loader_version=0.15.11 # Mod Properties mod_version=1.5.0 @@ -11,4 +11,4 @@ maven_group=semmiedev archives_base_name=disc_jockey # Dependencies # check this on https://modmuss50.me/fabric.html -fabric_version=0.98.0+1.20.6 +fabric_version=0.100.1+1.21 diff --git a/src/main/java/semmiedev/disc_jockey/Config.java b/src/main/java/semmiedev/disc_jockey/Config.java index 1f49df2..b8c8124 100644 --- a/src/main/java/semmiedev/disc_jockey/Config.java +++ b/src/main/java/semmiedev/disc_jockey/Config.java @@ -12,6 +12,32 @@ public class Config implements ConfigData { @ConfigEntry.Gui.Tooltip(count = 2) public boolean disableAsyncPlayback; @ConfigEntry.Gui.Tooltip(count = 2) public boolean omnidirectionalNoteBlockSounds = true; + public enum ExpectedServerVersion { + All, + v1_20_4_Or_Earlier, + v1_20_5_Or_Later; + + @Override + public String toString() { + if(this == All) { + return "All (universal)"; + }else if(this == v1_20_4_Or_Earlier) { + return "≤1.20.4"; + }else if (this == v1_20_5_Or_Later) { + return "≥1.20.5"; + }else { + return super.toString(); + } + } + } + + @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) + @ConfigEntry.Gui.Tooltip(count = 4) + public ExpectedServerVersion expectedServerVersion = ExpectedServerVersion.All; + + @ConfigEntry.Gui.Tooltip(count = 1) + public float delayPlaybackStartBySecs = 0.0f; + @ConfigEntry.Gui.Excluded public ArrayList favorites = new ArrayList<>(); } diff --git a/src/main/java/semmiedev/disc_jockey/DiscjockeyCommand.java b/src/main/java/semmiedev/disc_jockey/DiscjockeyCommand.java index 33898dc..9c23dbb 100644 --- a/src/main/java/semmiedev/disc_jockey/DiscjockeyCommand.java +++ b/src/main/java/semmiedev/disc_jockey/DiscjockeyCommand.java @@ -5,11 +5,10 @@ import com.mojang.brigadier.arguments.FloatArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.block.enums.Instrument; +import net.minecraft.block.enums.NoteBlockInstrument; import net.minecraft.client.MinecraftClient; import net.minecraft.command.CommandSource; import net.minecraft.text.Text; -import net.minecraft.util.Formatting; import org.jetbrains.annotations.Nullable; import semmiedev.disc_jockey.gui.screen.DiscJockeyScreen; @@ -26,7 +25,7 @@ public class DiscjockeyCommand { public static void register(CommandDispatcher commandDispatcher) { final ArrayList instrumentNames = new ArrayList<>(); - for (Instrument instrument : Instrument.values()) { + for (NoteBlockInstrument instrument : NoteBlockInstrument.values()) { instrumentNames.add(instrument.toString().toLowerCase()); } final ArrayList instrumentNamesAndAll = new ArrayList<>(instrumentNames); @@ -127,8 +126,8 @@ public class DiscjockeyCommand { .executes(context -> { String originalInstrumentStr = StringArgumentType.getString(context, "originalInstrument"); String newInstrumentStr = StringArgumentType.getString(context, "newInstrument"); - @Nullable Instrument originalInstrument = null, newInstrument = null; - for(Instrument maybeInstrument : Instrument.values()) { + @Nullable NoteBlockInstrument originalInstrument = null, newInstrument = null; + for(NoteBlockInstrument maybeInstrument : NoteBlockInstrument.values()) { if(maybeInstrument.toString().equalsIgnoreCase(originalInstrumentStr)) { originalInstrument = maybeInstrument; } @@ -152,7 +151,7 @@ public class DiscjockeyCommand { if(originalInstrument == null) { // All instruments - for(Instrument instrument : Instrument.values()) { + for(NoteBlockInstrument instrument : NoteBlockInstrument.values()) { Main.SONG_PLAYER.instrumentMap.put(instrument, newInstrument); } context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".instrument_mapped_all", newInstrumentStr.toLowerCase())); @@ -171,8 +170,8 @@ public class DiscjockeyCommand { .executes(context -> { String instrumentStr = StringArgumentType.getString(context, "instrument"); - Instrument instrument = null; - for(Instrument maybeInstrument : Instrument.values()) { + NoteBlockInstrument instrument = null; + for(NoteBlockInstrument maybeInstrument : NoteBlockInstrument.values()) { if(maybeInstrument.toString().equalsIgnoreCase(instrumentStr)) { instrument = maybeInstrument; break; @@ -198,7 +197,7 @@ public class DiscjockeyCommand { } StringBuilder maps = new StringBuilder(); - for(Map.Entry entry : Main.SONG_PLAYER.instrumentMap.entrySet()) { + for(Map.Entry entry : Main.SONG_PLAYER.instrumentMap.entrySet()) { if(maps.length() > 0) { maps.append(", "); } diff --git a/src/main/java/semmiedev/disc_jockey/Note.java b/src/main/java/semmiedev/disc_jockey/Note.java index b6209df..1653780 100644 --- a/src/main/java/semmiedev/disc_jockey/Note.java +++ b/src/main/java/semmiedev/disc_jockey/Note.java @@ -2,52 +2,53 @@ package semmiedev.disc_jockey; import net.minecraft.block.Block; import net.minecraft.block.Blocks; -import net.minecraft.block.enums.Instrument; +import net.minecraft.block.enums.NoteBlockInstrument; import java.util.HashMap; -public record Note(Instrument instrument, byte note) { - public static final HashMap INSTRUMENT_BLOCKS = new HashMap<>(); +public record Note(NoteBlockInstrument instrument, byte note) { + public static final HashMap INSTRUMENT_BLOCKS = new HashMap<>(); public static final byte LAYER_SHIFT = Short.SIZE; public static final byte INSTRUMENT_SHIFT = Short.SIZE * 2; public static final byte NOTE_SHIFT = Short.SIZE * 2 + Byte.SIZE; - public static final Instrument[] INSTRUMENTS = new Instrument[]{ - Instrument.HARP, - Instrument.BASS, - Instrument.BASEDRUM, - Instrument.SNARE, - Instrument.HAT, - Instrument.GUITAR, - Instrument.FLUTE, - Instrument.BELL, - Instrument.CHIME, - Instrument.XYLOPHONE, - Instrument.IRON_XYLOPHONE, - Instrument.COW_BELL, - Instrument.DIDGERIDOO, - Instrument.BIT, - Instrument.BANJO, - Instrument.PLING + public static final NoteBlockInstrument[] INSTRUMENTS = new NoteBlockInstrument[]{ + NoteBlockInstrument.HARP, + NoteBlockInstrument.BASS, + NoteBlockInstrument.BASEDRUM, + NoteBlockInstrument.SNARE, + NoteBlockInstrument.HAT, + NoteBlockInstrument.GUITAR, + NoteBlockInstrument.FLUTE, + NoteBlockInstrument.BELL, + NoteBlockInstrument.CHIME, + NoteBlockInstrument.XYLOPHONE, + NoteBlockInstrument.IRON_XYLOPHONE, + NoteBlockInstrument.COW_BELL, + NoteBlockInstrument.DIDGERIDOO, + NoteBlockInstrument.BIT, + NoteBlockInstrument.BANJO, + NoteBlockInstrument.PLING + }; static { - INSTRUMENT_BLOCKS.put(Instrument.HARP, Blocks.AIR); - INSTRUMENT_BLOCKS.put(Instrument.BASEDRUM, Blocks.STONE); - INSTRUMENT_BLOCKS.put(Instrument.SNARE, Blocks.SAND); - INSTRUMENT_BLOCKS.put(Instrument.HAT, Blocks.GLASS); - INSTRUMENT_BLOCKS.put(Instrument.BASS, Blocks.OAK_PLANKS); - INSTRUMENT_BLOCKS.put(Instrument.FLUTE, Blocks.CLAY); - INSTRUMENT_BLOCKS.put(Instrument.BELL, Blocks.GOLD_BLOCK); - INSTRUMENT_BLOCKS.put(Instrument.GUITAR, Blocks.WHITE_WOOL); - INSTRUMENT_BLOCKS.put(Instrument.CHIME, Blocks.PACKED_ICE); - INSTRUMENT_BLOCKS.put(Instrument.XYLOPHONE, Blocks.BONE_BLOCK); - INSTRUMENT_BLOCKS.put(Instrument.IRON_XYLOPHONE, Blocks.IRON_BLOCK); - INSTRUMENT_BLOCKS.put(Instrument.COW_BELL, Blocks.SOUL_SAND); - INSTRUMENT_BLOCKS.put(Instrument.DIDGERIDOO, Blocks.PUMPKIN); - INSTRUMENT_BLOCKS.put(Instrument.BIT, Blocks.EMERALD_BLOCK); - INSTRUMENT_BLOCKS.put(Instrument.BANJO, Blocks.HAY_BLOCK); - INSTRUMENT_BLOCKS.put(Instrument.PLING, Blocks.GLOWSTONE); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.HARP, Blocks.AIR); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BASEDRUM, Blocks.STONE); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.SNARE, Blocks.SAND); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.HAT, Blocks.GLASS); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BASS, Blocks.OAK_PLANKS); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.FLUTE, Blocks.CLAY); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BELL, Blocks.GOLD_BLOCK); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.GUITAR, Blocks.WHITE_WOOL); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.CHIME, Blocks.PACKED_ICE); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.XYLOPHONE, Blocks.BONE_BLOCK); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.IRON_XYLOPHONE, Blocks.IRON_BLOCK); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.COW_BELL, Blocks.SOUL_SAND); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.DIDGERIDOO, Blocks.PUMPKIN); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BIT, Blocks.EMERALD_BLOCK); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BANJO, Blocks.HAY_BLOCK); + INSTRUMENT_BLOCKS.put(NoteBlockInstrument.PLING, Blocks.GLOWSTONE); } } diff --git a/src/main/java/semmiedev/disc_jockey/SongPlayer.java b/src/main/java/semmiedev/disc_jockey/SongPlayer.java index 269bb90..f906836 100644 --- a/src/main/java/semmiedev/disc_jockey/SongPlayer.java +++ b/src/main/java/semmiedev/disc_jockey/SongPlayer.java @@ -4,7 +4,7 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; -import net.minecraft.block.enums.Instrument; +import net.minecraft.block.enums.NoteBlockInstrument; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.hud.ChatHud; import net.minecraft.client.network.ClientPlayerEntity; @@ -18,11 +18,9 @@ import net.minecraft.util.Formatting; import net.minecraft.util.Hand; import net.minecraft.util.Pair; import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.*; import net.minecraft.world.GameMode; +import org.apache.commons.lang3.NotImplementedException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -37,7 +35,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { private int index; private double tick; // Aka song position - private HashMap> noteBlocks = null; + private HashMap> noteBlocks = null; public boolean tuned; private long lastPlaybackTickAt = -1L; @@ -65,12 +63,13 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { private HashMap> notePredictions = new HashMap<>(); public boolean didSongReachEnd = false; public boolean loopSong = false; + private long pausePlaybackUntil = -1L; // Set after tuning, if configured public SongPlayer() { Main.TICK_LISTENERS.add(this); } - public @NotNull HashMap instrumentMap = new HashMap<>(); // Toy + public @NotNull HashMap instrumentMap = new HashMap<>(); // Toy public synchronized void startPlaybackThread() { if(Main.config.disableAsyncPlayback) { playbackThread = null; @@ -155,6 +154,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { last100MsSpanEstimatedPackets = 0; } if(noteBlocks != null && tuned) { + if(pausePlaybackUntil != -1L && System.currentTimeMillis() <= pausePlaybackUntil) return; while (running) { MinecraftClient client = MinecraftClient.getInstance(); GameMode gameMode = client.interactionManager == null ? null : client.interactionManager.getCurrentGameMode(); @@ -254,16 +254,32 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { ClientPlayerEntity player = client.player; // Create list of available noteblock positions per used instrument - HashMap> noteblocksForInstrument = new HashMap<>(); - for(Instrument instrument : Instrument.values()) + HashMap> noteblocksForInstrument = new HashMap<>(); + for(NoteBlockInstrument instrument : NoteBlockInstrument.values()) noteblocksForInstrument.put(instrument, new ArrayList<>()); - final Vec3d playerPos = player.getEyePos(); - final int[] orderedOffsets = new int[] { 0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5, -6, 6, -7, 7 }; - for(Instrument instrument : noteblocksForInstrument.keySet().toArray(new Instrument[0])) { + final Vec3d playerEyePos = player.getEyePos(); + + final int maxOffset; // Rough estimates, of which blocks could be in reach + if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.v1_20_4_Or_Earlier) { + maxOffset = 7; + }else if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.v1_20_5_Or_Later) { + maxOffset = (int) Math.ceil(player.getBlockInteractionRange() + 1.0 + 1.0); + }else if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.All) { + maxOffset = Math.min(7, (int) Math.ceil(player.getBlockInteractionRange() + 1.0 + 1.0)); + }else { + throw new NotImplementedException("ExpectedServerVersion Value not implemented: " + Main.config.expectedServerVersion.name()); + } + final ArrayList orderedOffsets = new ArrayList<>(); + for(int offset = 0; offset <= maxOffset; offset++) { + orderedOffsets.add(offset); + if(offset != 0) orderedOffsets.add(offset * -1); + } + + for(NoteBlockInstrument instrument : noteblocksForInstrument.keySet().toArray(new NoteBlockInstrument[0])) { for (int y : orderedOffsets) { for (int x : orderedOffsets) { for (int z : orderedOffsets) { - Vec3d vec3d = playerPos.add(x, y, z); + Vec3d vec3d = playerEyePos.add(x, y, z); BlockPos blockPos = new BlockPos(MathHelper.floor(vec3d.x), MathHelper.floor(vec3d.y), MathHelper.floor(vec3d.z)); if (!canInteractWith(player, blockPos)) continue; @@ -280,9 +296,9 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { // Remap instruments for funzies if(!instrumentMap.isEmpty()) { - HashMap> newNoteblocksForInstrument = new HashMap<>(); - for(Instrument orig : noteblocksForInstrument.keySet()) { - Instrument mappedInstrument = instrumentMap.getOrDefault(orig, orig); + HashMap> newNoteblocksForInstrument = new HashMap<>(); + for(NoteBlockInstrument orig : noteblocksForInstrument.keySet()) { + NoteBlockInstrument mappedInstrument = instrumentMap.getOrDefault(orig, orig); if(mappedInstrument == null) { // Instrument got likely mapped to "nothing" newNoteblocksForInstrument.put(orig, null); @@ -332,7 +348,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { HashMap missing = new HashMap<>(); for (Note note : missingNotes) { - Instrument mappedInstrument = instrumentMap.getOrDefault(note.instrument(), note.instrument()); + NoteBlockInstrument mappedInstrument = instrumentMap.getOrDefault(note.instrument(), note.instrument()); if(mappedInstrument == null) continue; // Ignore if mapped to nothing Block block = Note.INSTRUMENT_BLOCKS.get(mappedInstrument); Integer got = missing.get(block); @@ -403,7 +419,9 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { // Wait roundrip + 100ms before considering tuned after changing notes (in case the server rejects an interact) if(lastInteractAt == -1 || System.currentTimeMillis() - lastInteractAt >= ping * 2 + 100) { tuned = true; + pausePlaybackUntil = System.currentTimeMillis() + (long) (Math.abs(Main.config.delayPlaybackStartBySecs) * 1000); tuneInitialUntunedBlocks = -1; + // Tuning finished } } @@ -467,13 +485,28 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { } } - private HashMap getNotes(Instrument instrument) { + private HashMap getNotes(NoteBlockInstrument instrument) { return noteBlocks.computeIfAbsent(instrument, k -> new HashMap<>()); } - // The server limits interacts to 6 Blocks from Player Eye to Block Center + // Before 1.20.5, the server limits interacts to 6 Blocks from Player Eye to Block Center + // With 1.20.5 and later, the server does a more complex check, to the closest point of a full block hitbox + // (max distance is BlockInteractRange + 1.0). private boolean canInteractWith(ClientPlayerEntity player, BlockPos blockPos) { - return player.getEyePos().squaredDistanceTo(new Vec3d(blockPos.getX() + 0.5, blockPos.getY() + 0.5, blockPos.getZ() + 0.5)) <= 6.0*6.0; + final Vec3d eyePos = player.getEyePos(); + if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.v1_20_4_Or_Earlier) { + return eyePos.squaredDistanceTo(blockPos.toCenterPos()) <= 6.0 * 6.0; + }else if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.v1_20_5_Or_Later) { + double blockInteractRange = player.getBlockInteractionRange() + 1.0; + return new Box(blockPos).squaredMagnitude(eyePos) < blockInteractRange * blockInteractRange; + }else if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.All) { + // Require both checks to succeed (aka use worst distance) + double blockInteractRange = player.getBlockInteractionRange() + 1.0; + return eyePos.squaredDistanceTo(blockPos.toCenterPos()) <= 6.0 * 6.0 + && new Box(blockPos).squaredMagnitude(eyePos) < blockInteractRange * blockInteractRange; + }else { + throw new NotImplementedException("ExpectedServerVersion Value not implemented: " + Main.config.expectedServerVersion.name()); + } } public double getSongElapsedSeconds() { diff --git a/src/main/java/semmiedev/disc_jockey/gui/SongListWidget.java b/src/main/java/semmiedev/disc_jockey/gui/SongListWidget.java index 6ce001b..c86dfcd 100644 --- a/src/main/java/semmiedev/disc_jockey/gui/SongListWidget.java +++ b/src/main/java/semmiedev/disc_jockey/gui/SongListWidget.java @@ -41,7 +41,7 @@ public class SongListWidget extends EntryListWidget { // TODO: 6/2/2022 Add a delete icon public static class SongEntry extends Entry { - private static final Identifier ICONS = new Identifier(Main.MOD_ID, "textures/gui/icons.png"); + private static final Identifier ICONS = Identifier.of(Main.MOD_ID, "textures/gui/icons.png"); public final int index; public final Song song; diff --git a/src/main/java/semmiedev/disc_jockey/gui/hud/BlocksOverlay.java b/src/main/java/semmiedev/disc_jockey/gui/hud/BlocksOverlay.java index 6edf6c2..e90563d 100644 --- a/src/main/java/semmiedev/disc_jockey/gui/hud/BlocksOverlay.java +++ b/src/main/java/semmiedev/disc_jockey/gui/hud/BlocksOverlay.java @@ -4,6 +4,7 @@ import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.render.RenderTickCounter; import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.item.ItemStack; import net.minecraft.util.math.ColorHelper; @@ -15,7 +16,7 @@ public class BlocksOverlay { private static final ItemStack NOTE_BLOCK = Blocks.NOTE_BLOCK.asItem().getDefaultStack(); - public static void render(DrawContext context, float tickDelta) { + public static void render(DrawContext context, RenderTickCounter tickCounter) { if (itemStacks != null) { context.fill(2, 2, 62, (itemStacks.length + 1) * 20 + 7, ColorHelper.Argb.getArgb(255, 22, 22, 27)); context.fill(4, 4, 60, (itemStacks.length + 1) * 20 + 5, ColorHelper.Argb.getArgb(255, 42, 42, 47)); diff --git a/src/main/resources/assets/disc_jockey/lang/en_us.json b/src/main/resources/assets/disc_jockey/lang/en_us.json index 720e150..e4e0f1c 100644 --- a/src/main/resources/assets/disc_jockey/lang/en_us.json +++ b/src/main/resources/assets/disc_jockey/lang/en_us.json @@ -44,5 +44,12 @@ "text.autoconfig.disc_jockey.option.disableAsyncPlayback.@Tooltip[1]": "This can lead to performance loss, especially when you client has low or inconsistent fps but can fix issues when playback does not happen at all.", "text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds": "Omnidirectional Note Block Sounds (clientside)", "text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds.@Tooltip[0]": "Makes all note block sounds when playing a song omnidirectional, creating a more pleasing listening experience", - "text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds.@Tooltip[1]": "If you don't know what that means, I recommend you just try it and hear the difference" + "text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds.@Tooltip[1]": "If you don't know what that means, I recommend you just try it and hear the difference", + "text.autoconfig.disc_jockey.option.expectedServerVersion": "Expected Server Version", + "text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[0]": "Select the server version, you expect this mod to be used on.", + "text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[1]": "This affects how reachable NoteBlocks are determined.", + "text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[2]": "Selecting the wrong version could cause you not to be able to play some distant note blocks which could break/worsen playback", + "text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[3]": "If you're unsure, or play on many different server versions and don't mind not reaching every possible note block, select \"All\"", + "text.autoconfig.disc_jockey.option.delayPlaybackStartBySecs": "Delay playback by (seconds)", + "text.autoconfig.disc_jockey.option.delayPlaybackStartBySecs.@Tooltip": "Delays playback for specified seconds, after tuning finished, if any (e.g. 0.5 for half a second delay)." } \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 933546d..2bef658 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -27,7 +27,7 @@ ], "depends": { "fabric": "*", - "minecraft": ">=1.20.5 <=1.20.6", + "minecraft": "~1.21", "cloth-config": "*" } }