From 54c9cc4acbf13eff273275488f4eeb7c87641abf Mon Sep 17 00:00:00 2001 From: BRanulf Date: Sat, 5 Jul 2025 21:32:11 +0800 Subject: [PATCH] =?UTF-8?q?=E6=BB=9A=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../disc_jockey_revive/KeyMappingManager.java | 172 ----- .../semmiedev/disc_jockey_revive/Main.java | 27 +- .../disc_jockey_revive/SongLoader.java | 2 +- .../disc_jockey_revive/SongPlayer.java | 724 +++++++----------- .../gui/KeyMappingListWidget.java | 136 ---- .../gui/screen/DiscJockeyScreen.java | 503 +++++------- .../gui/screen/EditKeyMappingsScreen.java | 150 ---- .../gui/screen/LiveDjScreen.java | 175 ----- .../gui/screen/SelectNoteScreen.java | 173 ----- .../gui/widget/ProgressBarWidget.java | 91 --- .../resources/assets/disc_jockey/icon1.png | Bin 0 -> 117871 bytes .../assets/disc_jockey/lang/en_us.json | 51 +- .../assets/disc_jockey/lang/zh_cn.json | 51 +- 14 files changed, 470 insertions(+), 1787 deletions(-) delete mode 100644 src/main/java/semmiedev/disc_jockey_revive/KeyMappingManager.java delete mode 100644 src/main/java/semmiedev/disc_jockey_revive/gui/KeyMappingListWidget.java delete mode 100644 src/main/java/semmiedev/disc_jockey_revive/gui/screen/EditKeyMappingsScreen.java delete mode 100644 src/main/java/semmiedev/disc_jockey_revive/gui/screen/LiveDjScreen.java delete mode 100644 src/main/java/semmiedev/disc_jockey_revive/gui/screen/SelectNoteScreen.java delete mode 100644 src/main/java/semmiedev/disc_jockey_revive/gui/widget/ProgressBarWidget.java create mode 100644 src/main/resources/assets/disc_jockey/icon1.png diff --git a/gradle.properties b/gradle.properties index 23c4965..f0c19d0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.21.4 yarn_mappings=1.21.4+build.8 loader_version=0.16.10 # Mod Properties -mod_version=1.14.514.028 +mod_version=1.14.514.032 maven_group=semmiedev archives_base_name=disc_jockey_revive # Dependencies diff --git a/src/main/java/semmiedev/disc_jockey_revive/KeyMappingManager.java b/src/main/java/semmiedev/disc_jockey_revive/KeyMappingManager.java deleted file mode 100644 index 60e9c64..0000000 --- a/src/main/java/semmiedev/disc_jockey_revive/KeyMappingManager.java +++ /dev/null @@ -1,172 +0,0 @@ -package semmiedev.disc_jockey_revive; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.util.InputUtil; -import net.minecraft.block.enums.NoteBlockInstrument; -import net.minecraft.text.Text; - -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; - -public class KeyMappingManager { - private static final File MAPPINGS_FILE = new File(FabricLoader.getInstance().getConfigDir().toFile(), "disc_jockey" + "/key_mappings.json"); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - private static final Type MAPPING_TYPE = new TypeToken>() {}.getType(); - - private Map mappings = new HashMap<>(); - - private static class NoteData { - String instrument; - byte note; - - public NoteData(Note note) { - this.instrument = note.instrument().name(); - this.note = note.note(); - } - - public Note toNote() { - try { - NoteBlockInstrument instrumentEnum = NoteBlockInstrument.valueOf(instrument); - return new Note(instrumentEnum, note); - } catch (IllegalArgumentException e) { - Main.LOGGER.error("键位映射中出现未知乐器 '{}' ", instrument); - return null; - } - } - } - - public KeyMappingManager() { - loadMappings(); - } - - public void loadMappings() { - if (!MAPPINGS_FILE.exists()) { - Main.LOGGER.info("未找到键映射文件,正在创建默认的类似于 FL Studio 的映射。"); - mappings.clear(); - - NoteBlockInstrument dirt = NoteBlockInstrument.HARP; - NoteBlockInstrument wood = NoteBlockInstrument.BASS; - NoteBlockInstrument gold = NoteBlockInstrument.BELL; - - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_Z, 0), new Note(wood, (byte) 6)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_S, 0), new Note(wood, (byte) 7)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_X, 0), new Note(wood, (byte) 8)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_D, 0), new Note(wood, (byte) 9)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_C, 0), new Note(wood, (byte) 10)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_V, 0), new Note(wood, (byte) 11)); - - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_G, 0), new Note(dirt, (byte) 0)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_B, 0), new Note(dirt, (byte) 1)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_H, 0), new Note(dirt, (byte) 2)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_N, 0), new Note(dirt, (byte) 3)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_J, 0), new Note(dirt, (byte) 4)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_M, 0), new Note(dirt, (byte) 5)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_COMMA, 0), new Note(dirt, (byte) 6)); - - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_Q, 0), new Note(dirt, (byte) 6)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_2, 0), new Note(dirt, (byte) 7)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_W, 0), new Note(dirt, (byte) 8)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_3, 0), new Note(dirt, (byte) 9)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_E, 0), new Note(dirt, (byte) 10)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_R, 0), new Note(dirt, (byte) 11)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_5, 0), new Note(dirt, (byte) 12)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_T, 0), new Note(dirt, (byte) 13)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_6, 0), new Note(dirt, (byte) 14)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_Y, 0), new Note(dirt, (byte) 15)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_7, 0), new Note(dirt, (byte) 16)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_U, 0), new Note(dirt, (byte) 17)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_I, 0), new Note(dirt, (byte) 18)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_9, 0), new Note(dirt, (byte) 19)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_O, 0), new Note(dirt, (byte) 20)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_0, 0), new Note(dirt, (byte) 21)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_P, 0), new Note(dirt, (byte) 22)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_LEFT_BRACKET, 0), new Note(dirt, (byte) 23)); - mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_EQUAL, 0), new Note(dirt, (byte) 24)); - - - saveMappings(); - return; - } - - try (FileReader reader = new FileReader(MAPPINGS_FILE)) { - HashMap loadedData = GSON.fromJson(reader, MAPPING_TYPE); - mappings.clear(); - if (loadedData != null) { - for (Map.Entry entry : loadedData.entrySet()) { - InputUtil.Key key = InputUtil.fromTranslationKey(entry.getKey()); - Note note = entry.getValue().toNote(); - if (key != InputUtil.UNKNOWN_KEY && note != null) { - mappings.put(key, note); - } - } - } - Main.LOGGER.info("已加载按键映射。"); - } catch (IOException e) { - Main.LOGGER.error("加载按键映射失败。", e); - } - } - - - - public void saveMappings() { - MAPPINGS_FILE.getParentFile().mkdirs(); - try (FileWriter writer = new FileWriter(MAPPINGS_FILE)) { - HashMap dataToSave = new HashMap<>(); - for (Map.Entry entry : mappings.entrySet()) { - dataToSave.put(entry.getKey().getTranslationKey(), new NoteData(entry.getValue())); - } - GSON.toJson(dataToSave, writer); - Main.LOGGER.info("已保存按键映射。"); - } catch (IOException e) { - Main.LOGGER.error("保存按键映射失败。", e); - } - } - - public Note getNoteForKey(InputUtil.Key key) { - return mappings.get(key); - } - - public void setMapping(InputUtil.Key key, Note note) { - mappings.put(key, note); - } - - public void removeMapping(InputUtil.Key key) { - mappings.remove(key); - } - - public Map getMappings() { - return new HashMap<>(mappings); - } - - public static String getNoteDisplayName(Note note) { - if (note == null) return "未设置"; - - String instrumentTranslationKey = "block.minecraft.note_block.instrument." + note.instrument().asString(); - String instrumentName = Text.translatable(instrumentTranslationKey).getString(); - - int pitch = note.note(); - String[] noteNames = {"F#", "G", "G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F"}; - - int octave; - if (pitch >= 19) { - octave = 1; - } else if (pitch >= 7) { - octave = 0; - } else { - octave = -1; - } - - int noteIndexInArray = (pitch - 7 + 12 * 2) % 12; - String noteName = noteNames[noteIndexInArray]; - - return instrumentName + " " + noteName + "(" + octave + ")"; - } -} diff --git a/src/main/java/semmiedev/disc_jockey_revive/Main.java b/src/main/java/semmiedev/disc_jockey_revive/Main.java index 27956ee..9fcbd6e 100644 --- a/src/main/java/semmiedev/disc_jockey_revive/Main.java +++ b/src/main/java/semmiedev/disc_jockey_revive/Main.java @@ -5,7 +5,6 @@ import me.shedaniel.autoconfig.ConfigHolder; import me.shedaniel.autoconfig.serializer.JanksonConfigSerializer; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents; @@ -23,7 +22,6 @@ import org.apache.logging.log4j.Logger; import org.lwjgl.glfw.GLFW; import semmiedev.disc_jockey_revive.gui.hud.BlocksOverlay; import semmiedev.disc_jockey_revive.gui.screen.DiscJockeyScreen; -import semmiedev.disc_jockey_revive.gui.screen.LiveDjScreen; import java.io.File; import java.util.ArrayList; @@ -35,15 +33,11 @@ public class Main implements ClientModInitializer { public static final ArrayList TICK_LISTENERS = new ArrayList<>(); public static final Previewer PREVIEWER = new Previewer(); public static final SongPlayer SONG_PLAYER = new SongPlayer(); - public static KeyMappingManager keyMappingManager; public static File songsFolder; public static ModConfig config; public static ConfigHolder configHolder; - private static KeyBinding openLiveDjScreenKeyBind; - - @Override public void onInitializeClient() { configHolder = AutoConfig.register(ModConfig.class, JanksonConfigSerializer::new); @@ -52,14 +46,10 @@ public class Main implements ClientModInitializer { songsFolder = new File(FabricLoader.getInstance().getConfigDir()+File.separator+"disc_jockey"+File.separator+"songs"); if (!songsFolder.isDirectory()) songsFolder.mkdirs(); - keyMappingManager = new KeyMappingManager(); - SongLoader.loadSongs(); KeyBinding openScreenKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(MOD_ID+".key_bind.open_screen", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_J, "key.category."+MOD_ID)); - openLiveDjScreenKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(MOD_ID+".key_bind.open_live_dj_screen", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, "key.category."+MOD_ID)); - ClientTickEvents.START_CLIENT_TICK.register(new ClientTickEvents.StartTick() { private ClientWorld prevWorld; @@ -79,22 +69,11 @@ public class Main implements ClientModInitializer { client.setScreen(new DiscJockeyScreen()); } } - - if (openLiveDjScreenKeyBind.wasPressed()) { - if (client.currentScreen == null || client.currentScreen instanceof DiscJockeyScreen) { - client.setScreen(new LiveDjScreen()); - } - } } }); ClientTickEvents.START_WORLD_TICK.register(world -> { - ArrayList listenersCopy = new ArrayList<>(TICK_LISTENERS); - for (ClientTickEvents.StartWorldTick listener : listenersCopy) { - if (TICK_LISTENERS.contains(listener)) { - listener.onStartTick(world); - } - } + for (ClientTickEvents.StartWorldTick listener : TICK_LISTENERS) listener.onStartTick(world); }); ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { @@ -107,9 +86,5 @@ public class Main implements ClientModInitializer { }); HudRenderCallback.EVENT.register(BlocksOverlay::render); - - ClientLifecycleEvents.CLIENT_STOPPING.register(client -> { - keyMappingManager.saveMappings(); - }); } } diff --git a/src/main/java/semmiedev/disc_jockey_revive/SongLoader.java b/src/main/java/semmiedev/disc_jockey_revive/SongLoader.java index 087073f..f7aff09 100644 --- a/src/main/java/semmiedev/disc_jockey_revive/SongLoader.java +++ b/src/main/java/semmiedev/disc_jockey_revive/SongLoader.java @@ -79,7 +79,7 @@ public class SongLoader { song.folder = songFolder; } } catch (Exception exception) { - Main.LOGGER.error("无法读取或解析歌曲 {}", file.getName(), exception); + Main.LOGGER.error("Unable to read or parse song {}", file.getName(), exception); } } } diff --git a/src/main/java/semmiedev/disc_jockey_revive/SongPlayer.java b/src/main/java/semmiedev/disc_jockey_revive/SongPlayer.java index 2f80b89..abb4ac0 100644 --- a/src/main/java/semmiedev/disc_jockey_revive/SongPlayer.java +++ b/src/main/java/semmiedev/disc_jockey_revive/SongPlayer.java @@ -33,9 +33,9 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { public boolean running; public Song song; - public int index; - public double tick; - public HashMap> noteBlocks = null; + private int index; + private double tick; // Aka song position + private HashMap> noteBlocks = null; public boolean tuned; private long lastPlaybackTickAt = -1L; @@ -63,10 +63,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { private HashMap> notePredictions = new HashMap<>(); public boolean didSongReachEnd = false; public boolean loopSong = false; - private long pausePlaybackUntil = -1L; - - private boolean manualTuningRequested = false; - + private long pausePlaybackUntil = -1L; // Set after tuning, if configured public SongPlayer() { Main.TICK_LISTENERS.add(this); @@ -88,7 +85,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { }catch (Exception ex) { ex.printStackTrace(); } - MinecraftClient.getInstance().executeSync(this::tickPlayback); + tickPlayback(); } }); this.playbackThread.start(); @@ -101,7 +98,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { STOP_AFTER // 播完就停 } - public PlayMode playMode = PlayMode.STOP_AFTER; + private PlayMode playMode = PlayMode.STOP_AFTER; private boolean isRandomPlaying = false; private int randomIndex = -1; @@ -117,16 +114,17 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { } if (running) stop(); this.song = song; - //Main.LOGGER.info("Song length: " + song.length + " and tempo " + song.tempo); - //Main.TICK_LISTENERS.add(this); this.loopSong = playMode == PlayMode.SINGLE_LOOP; if(this.playbackThread == null) startPlaybackThread(); running = true; - index = 0; - tick = 0; - startTuning(); - lastPlaybackTickAt = System.currentTimeMillis(); + last100MsSpanAt = System.currentTimeMillis(); + last100MsSpanEstimatedPackets = 0; + reducePacketsUntil = -1L; + stopPacketsUntil = -1L; + lastLookSentAt = -1L; + lastSwingSentAt = -1L; + missingInstrumentBlocks.clear(); didSongReachEnd = false; isRandomPlaying = playMode == PlayMode.RANDOM; if (isRandomPlaying) { @@ -152,184 +150,108 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { notePredictions.clear(); tuned = false; tuneInitialUntunedBlocks = -1; - manualTuningRequested = false; - lastPlaybackTickAt = -1L; - // last100MsSpanAt = -1L; - // last100MsSpanEstimatedPackets = 0; - // reducePacketsUntil = -1L; - // stopPacketsUntil = -1L; - // lastLookSentAt = -1L; - // lastSwingSentAt = -1L; + last100MsSpanAt = -1L; + last100MsSpanEstimatedPackets = 0; + reducePacketsUntil = -1L; + stopPacketsUntil = -1L; + lastLookSentAt = -1L; + lastSwingSentAt = -1L; didSongReachEnd = false; // Change after running stop() if actually ended cleanly } - public synchronized void startTuning() { - if (tuned && noteBlocks != null) { - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.retuning").formatted(Formatting.YELLOW)); - } else { - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.tuning_started").formatted(Formatting.YELLOW)); - } - noteBlocks = null; - notePredictions.clear(); - tuned = false; - tuneInitialUntunedBlocks = -1; - manualTuningRequested = true; - } - - public boolean isTuningEnabled() { - return running || manualTuningRequested; - } - - - public boolean playNoteBlock(Note note) { - MinecraftClient client = MinecraftClient.getInstance(); - ClientWorld world = client.world; - ClientPlayerEntity player = client.player; - GameMode gameMode = client.interactionManager == null ? null : client.interactionManager.getCurrentGameMode(); - - if (world == null || player == null || gameMode == null || !gameMode.isSurvivalLike()) { - return false; - } - - if (noteBlocks == null || !tuned) { - return false; - } - - @Nullable BlockPos blockPos = noteBlocks.get(note.instrument()).get(note.note()); - if(blockPos == null) { - return false; - } - - if (!canInteractWith(player, blockPos)) { - return false; - } - - return sendNotePacket(blockPos, note); - } - - - private boolean sendNotePacket(BlockPos blockPos, Note note) { - MinecraftClient client = MinecraftClient.getInstance(); - ClientPlayerEntity player = client.player; - long now = System.currentTimeMillis(); - - // Update packet rate tracking - if(last100MsSpanAt != -1L && now - last100MsSpanAt >= 100) { - last100MsSpanEstimatedPackets = 0; - last100MsSpanAt = now; - }else if (last100MsSpanAt == -1L) { - last100MsSpanAt = now; - last100MsSpanEstimatedPackets = 0; - } - - if (stopPacketsUntil != -1L && stopPacketsUntil >= now) { - return false; - } - if (reducePacketsUntil != -1L && reducePacketsUntil >= now && last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter) { - return false; - } - - if(lastInteractAt != -1L) { - availableInteracts += ((System.currentTimeMillis() - lastInteractAt) / (310.0f / 8.0f)); - availableInteracts = Math.min(8f, Math.max(0f, availableInteracts)); - }else { - availableInteracts = 8f; - lastInteractAt = System.currentTimeMillis(); - } - - if (availableInteracts < 1f) { - return false; - } - - - Vec3d unit = Vec3d.ofCenter(blockPos, 0.5).subtract(player.getEyePos()).normalize(); - boolean packetsSent = false; - - if((lastLookSentAt == -1L || now - lastLookSentAt >= 50) && last100MsSpanEstimatedPackets < last100MsReducePacketsAfter) { - client.getNetworkHandler().sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(MathHelper.wrapDegrees((float) (MathHelper.atan2(unit.z, unit.x) * 57.2957763671875) - 90.0f), MathHelper.wrapDegrees((float) (-(MathHelper.atan2(unit.y, Math.sqrt(unit.x * unit.x + unit.z * unit.z)) * 57.2957763671875))), true, false)); - last100MsSpanEstimatedPackets++; - lastLookSentAt = now; - packetsSent = true; - } else if (last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter) { - reducePacketsUntil = Math.max(reducePacketsUntil, now + 500); - } - - - if(last100MsSpanEstimatedPackets < last100MsStopPacketsAfter) { - client.player.networkHandler.sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, blockPos, Direction.UP, 0)); - last100MsSpanEstimatedPackets++; - client.player.networkHandler.sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.ABORT_DESTROY_BLOCK, blockPos, Direction.UP, 0)); - last100MsSpanEstimatedPackets++; - lastInteractAt = now; - availableInteracts -= 1f; - packetsSent = true; - } else { - Main.LOGGER.info("短时间内暂停所有数据包!"); - stopPacketsUntil = Math.max(stopPacketsUntil, now + 250); - reducePacketsUntil = Math.max(reducePacketsUntil, now + 10000); - } - - - if((lastSwingSentAt == -1L || now - lastSwingSentAt >= 50) && last100MsSpanEstimatedPackets < last100MsReducePacketsAfter) { - client.executeSync(() -> client.player.swingHand(Hand.MAIN_HAND)); - lastSwingSentAt = now; - last100MsSpanEstimatedPackets++; - packetsSent = true; - } else if (last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter) { - reducePacketsUntil = Math.max(reducePacketsUntil, now + 500); - } - - return packetsSent; - } - - public synchronized void tickPlayback() { - if (!running || song == null) { + if (!running) { lastPlaybackTickAt = -1L; - // last100MsSpanAt = -1L; + last100MsSpanAt = -1L; return; } - - if (pausePlaybackUntil != -1L) { - if (System.currentTimeMillis() <= pausePlaybackUntil) { - return; - } else { - tick = 0; - index = 0; - pausePlaybackUntil = -1L; - lastPlaybackTickAt = System.currentTimeMillis(); - } + long previousPlaybackTickAt = lastPlaybackTickAt; + lastPlaybackTickAt = System.currentTimeMillis(); + if(last100MsSpanAt != -1L && System.currentTimeMillis() - last100MsSpanAt >= 100) { + last100MsSpanEstimatedPackets = 0; + last100MsSpanAt = System.currentTimeMillis(); + }else if (last100MsSpanAt == -1L) { + last100MsSpanAt = System.currentTimeMillis(); + last100MsSpanEstimatedPackets = 0; } - - long currentNow = System.currentTimeMillis(); - long elapsedMs = lastPlaybackTickAt != -1L ? currentNow - lastPlaybackTickAt : (16); - tick += song.millisecondsToTicks(elapsedMs) * speed; - lastPlaybackTickAt = currentNow; - - if(noteBlocks != null && tuned) { - while (index < song.notes.length) { + if(pausePlaybackUntil != -1L && System.currentTimeMillis() <= pausePlaybackUntil) return; + while (running) { + MinecraftClient client = MinecraftClient.getInstance(); + GameMode gameMode = client.interactionManager == null ? null : client.interactionManager.getCurrentGameMode(); + // In the best case, gameMode would only be queried in sync Ticks, no here + if (gameMode == null || !gameMode.isSurvivalLike()) { + client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.invalid_game_mode", gameMode == null ? "unknown" : gameMode.getTranslatableName()).formatted(Formatting.RED)); + stop(); + return; + } + long note = song.notes[index]; - - if ((double)(short)note <= tick) { - Note currentNote = new Note(Note.INSTRUMENTS[(byte)(note >> Note.INSTRUMENT_SHIFT)], (byte)(note >> Note.NOTE_SHIFT)); - - playNoteBlock(currentNote); + final long now = System.currentTimeMillis(); + if ((short)note <= Math.round(tick)) { + @Nullable BlockPos blockPos = noteBlocks.get(Note.INSTRUMENTS[(byte)(note >> Note.INSTRUMENT_SHIFT)]).get((byte)(note >> Note.NOTE_SHIFT)); + if(blockPos == null) { + // Instrument got likely mapped to "nothing". Skip it + index++; + continue; + } + if (!canInteractWith(client.player, blockPos)) { + stop(); + client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.to_far").formatted(Formatting.RED)); + return; + } + Vec3d unit = Vec3d.ofCenter(blockPos, 0.5).subtract(client.player.getEyePos()).normalize(); + if((lastLookSentAt == -1L || now - lastLookSentAt >= 50) && last100MsSpanEstimatedPackets < last100MsReducePacketsAfter && (reducePacketsUntil == -1L || reducePacketsUntil < now)) { + client.getNetworkHandler().sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(MathHelper.wrapDegrees((float) (MathHelper.atan2(unit.z, unit.x) * 57.2957763671875) - 90.0f), MathHelper.wrapDegrees((float) (-(MathHelper.atan2(unit.y, Math.sqrt(unit.x * unit.x + unit.z * unit.z)) * 57.2957763671875))), true, false)); + last100MsSpanEstimatedPackets++; + lastLookSentAt = now; + }else if(last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter){ + reducePacketsUntil = Math.max(reducePacketsUntil, now + 500); + } + if(last100MsSpanEstimatedPackets < last100MsStopPacketsAfter && (stopPacketsUntil == -1L || stopPacketsUntil < now)) { + // TODO: 5/30/2022 Check if the block needs tuning + //client.interactionManager.attackBlock(blockPos, Direction.UP); + client.player.networkHandler.sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, blockPos, Direction.UP, 0)); + last100MsSpanEstimatedPackets++; + }else if(last100MsSpanEstimatedPackets >= last100MsStopPacketsAfter) { + Main.LOGGER.info("Stopping all packets for a bit!"); + stopPacketsUntil = Math.max(stopPacketsUntil, now + 250); + reducePacketsUntil = Math.max(reducePacketsUntil, now + 10000); + } + if(last100MsSpanEstimatedPackets < last100MsReducePacketsAfter && (reducePacketsUntil == -1L || reducePacketsUntil < now)) { + client.player.networkHandler.sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.ABORT_DESTROY_BLOCK, blockPos, Direction.UP, 0)); + last100MsSpanEstimatedPackets++; + }else if(last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter){ + reducePacketsUntil = Math.max(reducePacketsUntil, now + 500); + } + if((lastSwingSentAt == -1L || now - lastSwingSentAt >= 50) &&last100MsSpanEstimatedPackets < last100MsReducePacketsAfter && (reducePacketsUntil == -1L || reducePacketsUntil < now)) { + client.executeSync(() -> client.player.swingHand(Hand.MAIN_HAND)); + lastSwingSentAt = now; + last100MsSpanEstimatedPackets++; + }else if(last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter){ + reducePacketsUntil = Math.max(reducePacketsUntil, now + 500); + } index++; + if (index >= song.notes.length) { + stop(); + didSongReachEnd = true; + if (playMode == PlayMode.SINGLE_LOOP) { + start(song); + } else if (playMode == PlayMode.LIST_LOOP || playMode == PlayMode.RANDOM) { + playNextSong(); + } + break; + } } else { break; } } - if (index >= song.notes.length) { - stop(); - didSongReachEnd = true; - if (playMode == PlayMode.SINGLE_LOOP) { - } else if (playMode == PlayMode.LIST_LOOP || playMode == PlayMode.RANDOM) { - playNextSong(); - } + if(running) { // Might not be running anymore (prevent small offset on song, even if that is not played anymore) + long elapsedMs = previousPlaybackTickAt != -1L && lastPlaybackTickAt != -1L ? lastPlaybackTickAt - previousPlaybackTickAt : (16); // Assume 16ms if unknown + tick += song.millisecondsToTicks(elapsedMs) * speed; } } } @@ -338,12 +260,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { if (SongLoader.currentFolder == null || SongLoader.currentFolder.songs.isEmpty()) return; int currentIndex = SongLoader.currentFolder.songs.indexOf(song); - if (currentIndex == -1) { - if (!SongLoader.currentFolder.songs.isEmpty()) { - start(SongLoader.currentFolder.songs.get(0)); - } - return; - } + if (currentIndex == -1) return; if (playMode == PlayMode.RANDOM) { int newIndex; @@ -362,30 +279,14 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { this.loopSong = mode == PlayMode.SINGLE_LOOP; } - // 我都不知道这是啥时候的,我也不删 + // this is the original author‘s comment, i dont wanna delete it // TODO: 6/2/2022 Play note blocks every song tick, instead of every tick. That way the song will sound better // 11/1/2023 Playback now done in separate thread. Not ideal but better especially when FPS are low. @Override public void onStartTick(ClientWorld world) { MinecraftClient client = MinecraftClient.getInstance(); if(world == null || client.world == null || client.player == null) return; - long now = System.currentTimeMillis(); - if(last100MsSpanAt != -1L && now - last100MsSpanAt >= 100) { - last100MsSpanEstimatedPackets = 0; - last100MsSpanAt = now; - }else if (last100MsSpanAt == -1L) { - last100MsSpanAt = now; - last100MsSpanEstimatedPackets = 0; - } - - if(lastInteractAt != -1L) { - availableInteracts += ((System.currentTimeMillis() - lastInteractAt) / (310.0f / 8.0f)); - availableInteracts = Math.min(8f, Math.max(0f, availableInteracts)); - }else { - availableInteracts = 8f; - lastInteractAt = System.currentTimeMillis(); - } - + if(song == null || !running) return; // Clear outdated note predictions ArrayList outdatedPredictions = new ArrayList<>(); @@ -395,157 +296,120 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { } for(BlockPos outdatedPrediction : outdatedPredictions) notePredictions.remove(outdatedPrediction); - if ((noteBlocks == null || !tuned) && (running || manualTuningRequested)) { - ClientPlayerEntity player = client.getInstance().player; - GameMode gameMode = client.interactionManager == null ? null : client.interactionManager.getCurrentGameMode(); - if (player == null || gameMode == null || !gameMode.isSurvivalLike()) { - noteBlocks = null; - notePredictions.clear(); - tuned = false; - tuneInitialUntunedBlocks = -1; - manualTuningRequested = false; - client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.invalid_state_tuning").formatted(Formatting.RED)); - return; + if (noteBlocks == null) { + noteBlocks = new HashMap<>(); + + ClientPlayerEntity player = client.player; + + // Create list of available noteblock positions per used instrument + HashMap> noteblocksForInstrument = new HashMap<>(); + for(NoteBlockInstrument instrument : NoteBlockInstrument.values()) + noteblocksForInstrument.put(instrument, new ArrayList<>()); + final Vec3d playerEyePos = player.getEyePos(); + + final int maxOffset; // Rough estimates, of which blocks could be in reach + if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_4_Or_Earlier) { + maxOffset = 7; + }else if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_5_Or_Later) { + maxOffset = (int) Math.ceil(player.getBlockInteractionRange() + 1.0 + 1.0); + }else if(Main.config.expectedServerVersion == ModConfig.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); } - if (noteBlocks == null) { - noteBlocks = new HashMap<>(); + for(NoteBlockInstrument instrument : noteblocksForInstrument.keySet().toArray(new NoteBlockInstrument[0])) { + for (int y : orderedOffsets) { + for (int x : orderedOffsets) { + for (int z : orderedOffsets) { + 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; + BlockState blockState = world.getBlockState(blockPos); + if (!blockState.isOf(Blocks.NOTE_BLOCK) || !world.isAir(blockPos.up())) + continue; - HashMap> noteblocksForInstrument = new HashMap<>(); - for(NoteBlockInstrument instrument : NoteBlockInstrument.values()) - noteblocksForInstrument.put(instrument, new ArrayList<>()); - final Vec3d playerEyePos = player.getEyePos(); - - final int maxOffset; - if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_4_Or_Earlier) { - maxOffset = 7; - }else if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_5_Or_Later) { - maxOffset = (int) Math.ceil(player.getBlockInteractionRange() + 1.0 + 1.0); - }else if(Main.config.expectedServerVersion == ModConfig.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 = 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; - BlockState blockState = world.getBlockState(blockPos); - if (!blockState.isOf(Blocks.NOTE_BLOCK) || !world.isAir(blockPos.up())) - continue; - - if (blockState.get(Properties.INSTRUMENT) == instrument) - noteblocksForInstrument.get(instrument).add(blockPos); - } + if (blockState.get(Properties.INSTRUMENT) == instrument) + noteblocksForInstrument.get(instrument).add(blockPos); } } } + } - if(!instrumentMap.isEmpty()) { - HashMap> newNoteblocksForInstrument = new HashMap<>(); - for(NoteBlockInstrument orig : noteblocksForInstrument.keySet()) { - NoteBlockInstrument mappedInstrument = instrumentMap.getOrDefault(orig, orig); - if(mappedInstrument == null) { - newNoteblocksForInstrument.put(orig, null); - continue; - } - - newNoteblocksForInstrument.put(orig, noteblocksForInstrument.getOrDefault(instrumentMap.getOrDefault(orig, orig), new ArrayList<>())); - } - noteblocksForInstrument = newNoteblocksForInstrument; - } - - ArrayList capturedNotes = new ArrayList<>(); - - ArrayList neededNotes = new ArrayList<>(); - if (song != null) { - neededNotes.addAll(song.uniqueNotes); - } - if (Main.keyMappingManager != null) { - for (Note mappedNote : Main.keyMappingManager.getMappings().values()) { - if (mappedNote != null && !neededNotes.contains(mappedNote)) { - neededNotes.add(mappedNote); - } - } - } - - - for(Note note : neededNotes) { - ArrayList availableBlocks = noteblocksForInstrument.get(note.instrument()); - if(availableBlocks == null) { - getNotes(note.instrument()).put(note.note(), null); + // Remap instruments for funzies + if(!instrumentMap.isEmpty()) { + 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); continue; } - BlockPos bestBlockPos = null; - int bestBlockTuningSteps = Integer.MAX_VALUE; - for(BlockPos blockPos : availableBlocks) { - boolean alreadyAssigned = false; - if (noteBlocks != null) { - for (HashMap instrumentNotes : noteBlocks.values()) { - if (instrumentNotes != null && instrumentNotes.containsValue(blockPos)) { - alreadyAssigned = true; - break; - } - } - } - if (alreadyAssigned) continue; - - int wantedNote = note.note(); - BlockState blockState = world.getBlockState(blockPos); - if (!blockState.contains(Properties.NOTE)) continue; - int currentNote = blockState.get(Properties.NOTE); - - int tuningSteps = wantedNote >= currentNote ? wantedNote - currentNote : (25 - currentNote) + wantedNote; - - if(tuningSteps < bestBlockTuningSteps) { - bestBlockPos = blockPos; - bestBlockTuningSteps = tuningSteps; - } - } - - if(bestBlockPos != null) { - capturedNotes.add(note); - availableBlocks.remove(bestBlockPos); - getNotes(note.instrument()).put(note.note(), bestBlockPos); - } + newNoteblocksForInstrument.put(orig, noteblocksForInstrument.getOrDefault(instrumentMap.getOrDefault(orig, orig), new ArrayList<>())); } - - ArrayList missingNotes = new ArrayList<>(neededNotes); - missingNotes.removeAll(capturedNotes); - if (!missingNotes.isEmpty()) { - ChatHud chatHud = MinecraftClient.getInstance().inGameHud.getChatHud(); - chatHud.addMessage(Text.translatable(Main.MOD_ID+".player.invalid_note_blocks").formatted(Formatting.RED)); - - HashMap missing = new HashMap<>(); - for (Note note : missingNotes) { - NoteBlockInstrument mappedInstrument = instrumentMap.getOrDefault(note.instrument(), note.instrument()); - if(mappedInstrument == null) continue; - Block block = Note.INSTRUMENT_BLOCKS.get(mappedInstrument); - Integer got = missing.get(block); - if (got == null) got = 0; - missing.put(block, got + 1); - } - - missingInstrumentBlocks = missing; - missing.forEach((block, integer) -> chatHud.addMessage(Text.literal(block.getName().getString()+" × "+integer).formatted(Formatting.RED))); - - } else { - missingInstrumentBlocks.clear(); - } - + noteblocksForInstrument = newNoteblocksForInstrument; } + // Find fitting noteblocks with the least amount of adjustments required (to reduce tuning time) + ArrayList capturedNotes = new ArrayList<>(); + for(Note note : song.uniqueNotes) { + ArrayList availableBlocks = noteblocksForInstrument.get(note.instrument()); + if(availableBlocks == null) { + // Note was mapped to "nothing". Pretend it got captured, but just ignore it + capturedNotes.add(note); + getNotes(note.instrument()).put(note.note(), null); + continue; + } + BlockPos bestBlockPos = null; + int bestBlockTuningSteps = Integer.MAX_VALUE; + for(BlockPos blockPos : availableBlocks) { + int wantedNote = note.note(); + int currentNote = client.world.getBlockState(blockPos).get(Properties.NOTE); + int tuningSteps = wantedNote >= currentNote ? wantedNote - currentNote : (25 - currentNote) + wantedNote; + + if(tuningSteps < bestBlockTuningSteps) { + bestBlockPos = blockPos; + bestBlockTuningSteps = tuningSteps; + } + } + + if(bestBlockPos != null) { + capturedNotes.add(note); + availableBlocks.remove(bestBlockPos); + getNotes(note.instrument()).put(note.note(), bestBlockPos); + } // else will be a missing note + } + + ArrayList missingNotes = new ArrayList<>(song.uniqueNotes); + missingNotes.removeAll(capturedNotes); + if (!missingNotes.isEmpty()) { + ChatHud chatHud = MinecraftClient.getInstance().inGameHud.getChatHud(); + chatHud.addMessage(Text.translatable(Main.MOD_ID+".player.invalid_note_blocks").formatted(Formatting.RED)); + + HashMap missing = new HashMap<>(); + for (Note note : missingNotes) { + 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); + if (got == null) got = 0; + missing.put(block, got + 1); + } + + missingInstrumentBlocks = missing; + missing.forEach((block, integer) -> chatHud.addMessage(Text.literal(block.getName().getString()+" × "+integer).formatted(Formatting.RED))); + stop(); + } + } else if (!tuned) { + //tuned = true; int ping = 0; { @@ -554,119 +418,112 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { ping = playerListEntry.getLatency(); } + if(lastInteractAt != -1L) { + // Paper allows 8 interacts per 300 ms (actually 9 it turns out, but lets keep it a bit lower anyway) + availableInteracts += ((System.currentTimeMillis() - lastInteractAt) / (310.0f / 8.0f)); + availableInteracts = Math.min(8f, Math.max(0f, availableInteracts)); + }else { + availableInteracts = 8f; + lastInteractAt = System.currentTimeMillis(); + } + int fullyTunedBlocks = 0; HashMap untunedNotes = new HashMap<>(); + for (Note note : song.uniqueNotes) { + if(noteBlocks == null || noteBlocks.get(note.instrument()) == null) + continue; + BlockPos blockPos = noteBlocks.get(note.instrument()).get(note.note()); + if(blockPos == null) continue; + BlockState blockState = world.getBlockState(blockPos); + int assumedNote = notePredictions.containsKey(blockPos) ? notePredictions.get(blockPos).getLeft() : blockState.get(Properties.NOTE); - if (noteBlocks != null) { - for (HashMap instrumentNotes : noteBlocks.values()) { - if (instrumentNotes == null) continue; - for (Map.Entry entry : instrumentNotes.entrySet()) { - BlockPos blockPos = entry.getValue(); - Byte wantedNote = entry.getKey(); - - if (blockPos == null) continue; - - BlockState blockState = world.getBlockState(blockPos); - if (!blockState.contains(Properties.NOTE)) { - Main.LOGGER.warn("注意 {} 处音符盒在调整过程中改变了状态", blockPos); - noteBlocks = null; - tuned = false; - manualTuningRequested = false; + if (blockState.contains(Properties.NOTE)) { + if(assumedNote == note.note() && blockState.get(Properties.NOTE) == note.note()) + fullyTunedBlocks++; + if (assumedNote != note.note()) { + if (!canInteractWith(client.player, blockPos)) { + stop(); + client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.to_far").formatted(Formatting.RED)); return; } - - int assumedNote = notePredictions.containsKey(blockPos) ? notePredictions.get(blockPos).getLeft() : blockState.get(Properties.NOTE); // blockState.get(Properties.NOTE) returns Integer - - byte wantedNotePrimitive = wantedNote.byteValue(); - - if(assumedNote == wantedNotePrimitive && blockState.get(Properties.NOTE).intValue() == wantedNotePrimitive) { - fullyTunedBlocks++; - } else if (assumedNote != wantedNotePrimitive) { - untunedNotes.put(blockPos, blockState.get(Properties.NOTE).intValue()); - } + untunedNotes.put(blockPos, blockState.get(Properties.NOTE)); } + } else { + noteBlocks = null; + break; } } + if(tuneInitialUntunedBlocks == -1 || tuneInitialUntunedBlocks < untunedNotes.size()) + tuneInitialUntunedBlocks = untunedNotes.size(); int existingUniqueNotesCount = 0; - if (noteBlocks != null) { - for(HashMap instrumentNotes : noteBlocks.values()) { - if (instrumentNotes != null) { - for (BlockPos pos : instrumentNotes.values()) { - if (pos != null) { - existingUniqueNotesCount++; - } - } - } - } + for(Note n : song.uniqueNotes) { + if(noteBlocks.get(n.instrument()).get(n.note()) != null) + existingUniqueNotesCount++; } - if(untunedNotes.isEmpty() && fullyTunedBlocks == existingUniqueNotesCount) { + // 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; - if (running && tick == 0 && index == 0) { - pausePlaybackUntil = System.currentTimeMillis() + (long) (Math.abs(Main.config.delayPlaybackStartBySecs) * 1000); - } else { - pausePlaybackUntil = -1L; - } + pausePlaybackUntil = System.currentTimeMillis() + (long) (Math.abs(Main.config.delayPlaybackStartBySecs) * 1000); tuneInitialUntunedBlocks = -1; - manualTuningRequested = false; - client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.tuned").formatted(Formatting.GREEN)); - } - } else { - tuned = false; - if(tuneInitialUntunedBlocks == -1 || tuneInitialUntunedBlocks < untunedNotes.size()) - tuneInitialUntunedBlocks = untunedNotes.size(); - if (availableInteracts >= 1f && !untunedNotes.isEmpty()) { - BlockPos blockPosToTune = untunedNotes.keySet().iterator().next(); - int currentNote = untunedNotes.get(blockPosToTune); - Byte targetNote = null; - if (noteBlocks != null) { - for (HashMap instrumentNotes : noteBlocks.values()) { - if (instrumentNotes != null) { - for (Map.Entry entry : instrumentNotes.entrySet()) { - if (blockPosToTune.equals(entry.getValue())) { - targetNote = entry.getKey(); - break; - } - } - } - if (targetNote != null) break; - } - } - - if (targetNote != null) { - byte targetNotePrimitive = targetNote.byteValue(); - int tuningSteps = targetNotePrimitive >= currentNote ? targetNotePrimitive - currentNote : (25 - currentNote) + targetNotePrimitive; - - if (tuningSteps > 0) { - int predictedNote = (currentNote + 1) % 25; - notePredictions.put(blockPosToTune, new Pair<>(predictedNote, System.currentTimeMillis() + ping * 2 + 100)); - client.interactionManager.interactBlock(client.player, Hand.MAIN_HAND, new BlockHitResult(Vec3d.of(blockPosToTune), Direction.UP, blockPosToTune, false)); - lastInteractAt = System.currentTimeMillis(); - availableInteracts -= 1f; - - client.player.swingHand(Hand.MAIN_HAND); - - int totalUntuned = untunedNotes.size(); - int tunedCount = existingUniqueNotesCount - totalUntuned; - - } else { - - untunedNotes.remove(blockPosToTune); - } - } else { - - untunedNotes.remove(blockPosToTune); // Remove to avoid infinite loop - } - } else if (!untunedNotes.isEmpty()) { - + // Tuning finished } } - } - if((playbackThread == null || !playbackThread.isAlive()) && running && Main.config.disableAsyncPlayback) { + + BlockPos lastBlockPos = null; + int lastTunedNote = Integer.MIN_VALUE; + float roughTuneProgress = 1 - (untunedNotes.size() / Math.max(tuneInitialUntunedBlocks + 0f, 1f)); + while(availableInteracts >= 1f && untunedNotes.size() > 0) { + BlockPos blockPos = null; + int searches = 0; + while(blockPos == null) { + searches++; + // Find higher note + for (Map.Entry entry : untunedNotes.entrySet()) { + if (entry.getValue() > lastTunedNote) { + blockPos = entry.getKey(); + break; + } + } + // Find higher note or equal + if (blockPos == null) { + for (Map.Entry entry : untunedNotes.entrySet()) { + if (entry.getValue() >= lastTunedNote) { + blockPos = entry.getKey(); + break; + } + } + } + // Not found. Reset last note + if(blockPos == null) + lastTunedNote = Integer.MIN_VALUE; + if(blockPos == null && searches > 1) { + // Something went wrong. Take any note (one should at least exist here) + blockPos = untunedNotes.keySet().toArray(new BlockPos[0])[0]; + break; + } + } + if(blockPos == null) return; // Something went very, very wrong! + + lastTunedNote = untunedNotes.get(blockPos); + untunedNotes.remove(blockPos); + int assumedNote = notePredictions.containsKey(blockPos) ? notePredictions.get(blockPos).getLeft() : client.world.getBlockState(blockPos).get(Properties.NOTE); + notePredictions.put(blockPos, new Pair<>((assumedNote + 1) % 25, System.currentTimeMillis() + ping * 2 + 100)); + client.interactionManager.interactBlock(client.player, Hand.MAIN_HAND, new BlockHitResult(Vec3d.of(blockPos), Direction.UP, blockPos, false)); + lastInteractAt = System.currentTimeMillis(); + availableInteracts -= 1f; + lastBlockPos = blockPos; + } + if(lastBlockPos != null) { + // Turn head into spinning with time and lookup up further the further tuning is progressed + //client.getNetworkHandler().sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(((float) (System.currentTimeMillis() % 2000)) * (360f/2000f), (1 - roughTuneProgress) * 180 - 90, true)); + client.player.swingHand(Hand.MAIN_HAND); + } + }else if((playbackThread == null || !playbackThread.isAlive()) && running && Main.config.disableAsyncPlayback) { + // Sync playback (off by default). Replacement for playback thread try { tickPlayback(); }catch (Exception ex) { @@ -677,16 +534,13 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick { } private HashMap getNotes(NoteBlockInstrument instrument) { - if (noteBlocks == null) { - noteBlocks = new HashMap<>(); - } return noteBlocks.computeIfAbsent(instrument, k -> new HashMap<>()); } // 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). - public boolean canInteractWith(ClientPlayerEntity player, BlockPos blockPos) { + private boolean canInteractWith(ClientPlayerEntity player, BlockPos blockPos) { final Vec3d eyePos = player.getEyePos(); if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_4_Or_Earlier) { return eyePos.squaredDistanceTo(blockPos.toCenterPos()) <= 6.0 * 6.0; diff --git a/src/main/java/semmiedev/disc_jockey_revive/gui/KeyMappingListWidget.java b/src/main/java/semmiedev/disc_jockey_revive/gui/KeyMappingListWidget.java deleted file mode 100644 index 1323062..0000000 --- a/src/main/java/semmiedev/disc_jockey_revive/gui/KeyMappingListWidget.java +++ /dev/null @@ -1,136 +0,0 @@ -package semmiedev.disc_jockey_revive.gui; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.screen.narration.NarrationPart; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.EntryListWidget; -import net.minecraft.client.util.InputUtil; -import net.minecraft.text.Text; -import semmiedev.disc_jockey_revive.KeyMappingManager; -import semmiedev.disc_jockey_revive.Main; -import semmiedev.disc_jockey_revive.Note; -import semmiedev.disc_jockey_revive.gui.screen.EditKeyMappingsScreen; - -import java.util.Comparator; -import java.util.Map; -import java.util.Objects; - -public class KeyMappingListWidget extends EntryListWidget { - - private final EditKeyMappingsScreen parentScreen; - private boolean buttonsActive = true; - - public KeyMappingListWidget(MinecraftClient client, int width, int height, int top, int itemHeight, EditKeyMappingsScreen parentScreen) { - super(client, width, height, top, itemHeight); - this.parentScreen = parentScreen; - this.centerListVertically = false; - } - public void setMappings(Map mappings) { - this.clearEntries(); - for (Map.Entry entry : mappings.entrySet()) { - - this.addEntry(new KeyMappingEntry(entry.getKey(), entry.getValue(), this.parentScreen)); - } - - this.children().sort(Comparator.comparing(entry -> entry.getKey().getTranslationKey())); - - setButtonsActive(this.buttonsActive); - } - public void setButtonsActive(boolean active) { - this.buttonsActive = active; - - for (KeyMappingEntry entry : children()) { - entry.setButtonsActive(active); - } - } - @Override - public int getRowWidth() { - return this.width - 40; - } - - @Override - protected int getScrollbarX() { - return this.width - 10; - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - } - - public class KeyMappingEntry extends EntryListWidget.Entry { - private final InputUtil.Key key; - private final Note note; - private final EditKeyMappingsScreen screen; - - private ButtonWidget changeButton; - private ButtonWidget removeButton; - - public KeyMappingEntry(InputUtil.Key key, Note note, EditKeyMappingsScreen screen) { - this.key = key; - this.note = note; - this.screen = screen; - } - - public InputUtil.Key getKey() { - return key; - } - - public Note getNote() { - return note; - } - - public void setButtonsActive(boolean active) { - if (changeButton != null) changeButton.active = active; - if (removeButton != null) removeButton.active = active; - } - @Override - public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - MinecraftClient client = MinecraftClient.getInstance(); - int textY = y + (entryHeight - client.textRenderer.fontHeight) / 2; - Text keyText = Text.translatable(key.getTranslationKey()); - context.drawTextWithShadow(client.textRenderer, keyText, x + 5, textY, 0xFFFFFF); - String noteDisplayName = KeyMappingManager.getNoteDisplayName(note); - context.drawTextWithShadow(client.textRenderer, noteDisplayName, x + 100, textY, 0xAAAAAA); - int buttonWidth = 50; - int buttonHeight = 18; - int buttonY = y + (entryHeight - buttonHeight) / 2; - int buttonX = x + entryWidth - buttonWidth - 5; - changeButton = ButtonWidget.builder(Text.translatable(Main.MOD_ID + ".screen.edit_mappings.change"), button -> { - screen.startWaitingForKeyPress(this); - }).dimensions(buttonX - buttonWidth - 5, buttonY, buttonWidth, buttonHeight).build(); - changeButton.render(context, mouseX, mouseY, tickDelta); - changeButton.active = buttonsActive; - removeButton = ButtonWidget.builder(Text.translatable(Main.MOD_ID + ".screen.edit_mappings.remove"), button -> { - screen.removeMapping(key); - }).dimensions(buttonX, buttonY, buttonWidth, buttonHeight).build(); - removeButton.render(context, mouseX, mouseY, tickDelta); - removeButton.active = buttonsActive; - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - - if (changeButton.mouseClicked(mouseX, mouseY, button)) { - return true; - } - if (removeButton.mouseClicked(mouseX, mouseY, button)) { - return true; - } - return false; - } - - @Override - public boolean isMouseOver(double mouseX, double mouseY) { - return super.isMouseOver(mouseX, mouseY) || - (changeButton != null && changeButton.isMouseOver(mouseX, mouseY)) || - (removeButton != null && removeButton.isMouseOver(mouseX, mouseY)); - } - public void appendClickableNarrations(NarrationMessageBuilder builder) { - builder.put(NarrationPart.TITLE, Text.translatable(key.getTranslationKey()).append(" -> ").append(KeyMappingManager.getNoteDisplayName(note))); - if (changeButton != null) changeButton.appendNarrations(builder); - if (removeButton != null) removeButton.appendNarrations(builder); - } - } -} diff --git a/src/main/java/semmiedev/disc_jockey_revive/gui/screen/DiscJockeyScreen.java b/src/main/java/semmiedev/disc_jockey_revive/gui/screen/DiscJockeyScreen.java index eca3e73..33a4127 100644 --- a/src/main/java/semmiedev/disc_jockey_revive/gui/screen/DiscJockeyScreen.java +++ b/src/main/java/semmiedev/disc_jockey_revive/gui/screen/DiscJockeyScreen.java @@ -9,14 +9,12 @@ import net.minecraft.item.ItemStack; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; -import org.lwjgl.glfw.GLFW; import semmiedev.disc_jockey_revive.Main; import semmiedev.disc_jockey_revive.Note; import semmiedev.disc_jockey_revive.Song; import semmiedev.disc_jockey_revive.SongLoader; import semmiedev.disc_jockey_revive.gui.SongListWidget; import semmiedev.disc_jockey_revive.gui.hud.BlocksOverlay; -import semmiedev.disc_jockey_revive.gui.widget.ProgressBarWidget; import java.io.File; import java.io.IOException; @@ -28,7 +26,6 @@ import java.util.stream.Collectors; import semmiedev.disc_jockey_revive.SongLoader.SongFolder; import semmiedev.disc_jockey_revive.SongPlayer.PlayMode; -import org.jetbrains.annotations.Nullable; public class DiscJockeyScreen extends Screen { private static final MutableText @@ -38,14 +35,12 @@ public class DiscJockeyScreen extends Screen { PREVIEW = Text.translatable(Main.MOD_ID+".screen.preview"), PREVIEW_STOP = Text.translatable(Main.MOD_ID+".screen.preview.stop"), DROP_HINT = Text.translatable(Main.MOD_ID+".screen.drop_hint").formatted(Formatting.GRAY) - ; + ; private SongListWidget songListWidget; - private ButtonWidget playButton, previewButton, prevButton, nextButton; + private ButtonWidget playButton, previewButton; public boolean shouldFilter; private String query = ""; - private ProgressBarWidget progressBar; - private TextFieldWidget searchBar; private static final MutableText FOLDER_UP = Text.literal("↑"), @@ -71,137 +66,39 @@ public class DiscJockeyScreen extends Screen { @Override protected void init() { shouldFilter = true; + songListWidget = new SongListWidget(client, width, height - 64 - 32, 32, 20, this); + // 恢复播放模式 currentPlayMode = Main.config.playMode; + // 恢复文件夹状态 if (!Main.config.currentFolderPath.isEmpty()) { currentFolder = findFolderByPath(Main.config.currentFolderPath); SongLoader.currentFolder = currentFolder; } - - int listTop = 40; - int listBottom = height - 100; - int listHeight = listBottom - listTop; - songListWidget = new SongListWidget(client, width, height - 100, 40, 20, this); addDrawableChild(songListWidget); + for (int i = 0; i < SongLoader.SONGS.size(); i++) { + Song song = SongLoader.SONGS.get(i); + song.entry.songListWidget = songListWidget; + if (song.entry.selected) songListWidget.setSelected(song.entry); - - int playbackButtonHeight = 20; - int playbackButtonY = listBottom +50; - int centerX = width / 2; - - // 上一首 - prevButton = ButtonWidget.builder(Text.literal("⏮"), button -> playPreviousSong()) - .dimensions(centerX - 80, playbackButtonY, 40, playbackButtonHeight).build(); - addDrawableChild(prevButton); - - // 播放 - playButton = ButtonWidget.builder(PLAY, button -> { - if (Main.SONG_PLAYER.running) { - Main.SONG_PLAYER.stop(); - } else { - SongListWidget.SongEntry entry = songListWidget.getSelectedSongOrNull(); - if (entry != null) { - Main.SONG_PLAYER.start(entry.song); - } + // 添加文件夹条目 + if (song.folder != null && !songListWidget.children().contains(song.folder.entry)) { + song.folder.entry = new SongListWidget.FolderEntry(song.folder, songListWidget); + songListWidget.children().add(song.folder.entry); } - }).dimensions(centerX - 30, playbackButtonY, 60, playbackButtonHeight).build(); - addDrawableChild(playButton); - - // 下一首 - nextButton = ButtonWidget.builder(Text.literal("⏭"), button -> playNextSong()) - .dimensions(centerX + 40, playbackButtonY, 40, playbackButtonHeight).build(); - addDrawableChild(nextButton); - - // 预览 - previewButton = ButtonWidget.builder(PREVIEW, button -> { - if (Main.PREVIEWER.running) { - Main.PREVIEWER.stop(); - } else { - SongListWidget.SongEntry entry = songListWidget.getSelectedSongOrNull(); - if (entry != null) Main.PREVIEWER.start(entry.song); - } - }).dimensions(centerX + 90, playbackButtonY, 60, playbackButtonHeight).build(); - addDrawableChild(previewButton); - - // 进度条 - int progressBarHeight = 10; - int progressBarY = playbackButtonY + playbackButtonHeight + 5; - progressBar = new ProgressBarWidget(centerX - 100, progressBarY, 200, progressBarHeight, - Text.translatable(Main.MOD_ID + ".progress"), 0, 100) { - @Override - protected void onProgressChanged(double progress) { - if (Main.SONG_PLAYER.running && Main.SONG_PLAYER.song != null) { - double targetTick = progress * Main.SONG_PLAYER.song.length / 100.0; - Main.SONG_PLAYER.tick = targetTick; - updateNoteIndexFromTick(); - } - } - }; - addDrawableChild(progressBar); - - // 搜索框 - int searchBarWidth = 150; - int searchBarHeight = 20; - int searchBarX = 10; - int searchBarY = height - searchBarHeight - 10; - searchBar = new TextFieldWidget(textRenderer, searchBarX, searchBarY, searchBarWidth, searchBarHeight, - Text.translatable(Main.MOD_ID+".screen.search")); - searchBar.setChangedListener(query -> { - query = query.toLowerCase().replaceAll("\\s", ""); - if (this.query.equals(query)) return; - this.query = query; - shouldFilter = true; - }); - addDrawableChild(searchBar); - int otherButtonWidth = 100; - int otherButtonHeight = 20; - int otherButtonMargin = 10; - - // Blocks - int blocksButtonX = width - otherButtonWidth - otherButtonMargin; - int blocksButtonY = height - otherButtonHeight - otherButtonMargin - otherButtonHeight - otherButtonMargin; - addDrawableChild(ButtonWidget.builder(Text.translatable(Main.MOD_ID+".screen.blocks"), button -> { - if (BlocksOverlay.itemStacks == null) { - SongListWidget.SongEntry entry = songListWidget.getSelectedSongOrNull(); - if (entry != null) { - client.setScreen(null); - updateBlocksOverlay(entry.song); - } - } else { - BlocksOverlay.itemStacks = null; - client.setScreen(null); - } - }).dimensions(blocksButtonX, blocksButtonY, otherButtonWidth, otherButtonHeight).build()); - - // 打开文件夹 - int openFolderButtonX = width - otherButtonWidth - otherButtonMargin; - int openFolderButtonY = height - otherButtonHeight - otherButtonMargin - otherButtonHeight - otherButtonMargin + otherButtonHeight + otherButtonMargin; // 在 Blocks 按钮下方一行 - addDrawableChild(ButtonWidget.builder(OPEN_FOLDER, button -> openSongsFolder()) - .dimensions(openFolderButtonX, openFolderButtonY, otherButtonWidth, otherButtonHeight).build()); - - // 重新加载 - int reloadButtonX = width - otherButtonWidth - otherButtonMargin - otherButtonWidth - otherButtonMargin; - int reloadButtonY = openFolderButtonY; - addDrawableChild(ButtonWidget.builder(RELOAD, button -> { - SongLoader.loadSongs(); - client.setScreen(null); - }).dimensions(reloadButtonX, reloadButtonY, otherButtonWidth, otherButtonHeight).build()); - - + } folderUpButton = ButtonWidget.builder(FOLDER_UP, button -> { if (currentFolder != null) { - SongFolder parent = findParentFolder(currentFolder); - currentFolder = parent; - SongLoader.currentFolder = parent; + currentFolder = null; + SongLoader.currentFolder = null; shouldFilter = true; } }).dimensions(10, 10, 20, 20).build(); addDrawableChild(folderUpButton); - playModeButton = ButtonWidget.builder(getPlayModeText(), button -> { switch (currentPlayMode) { case SINGLE_LOOP -> currentPlayMode = PlayMode.LIST_LOOP; @@ -214,200 +111,131 @@ public class DiscJockeyScreen extends Screen { }).dimensions(width - 120, 10, 100, 20).build(); addDrawableChild(playModeButton); - for (int i = 0; i < SongLoader.SONGS.size(); i++) { - Song song = SongLoader.SONGS.get(i); - song.entry.songListWidget = songListWidget; - if (song.entry.selected) songListWidget.setSelected(song.entry); + playButton = ButtonWidget.builder(PLAY, button -> { + if (Main.SONG_PLAYER.running) { + Main.SONG_PLAYER.stop(); + } else { + SongListWidget.SongEntry entry = songListWidget.getSelectedSongOrNull(); + if (entry != null) { + Main.SONG_PLAYER.start(entry.song); + client.setScreen(null); + } + } + }).dimensions(width / 2 - 160, height - 61, 100, 20).build(); + addDrawableChild(playButton); - if (song.folder != null) { - boolean folderEntryExists = SongLoader.FOLDERS.stream() - .anyMatch(f -> f == song.folder || findFolderByPathInSubfolders(f, song.folder.path) != null); - if (!folderEntryExists) { - } else { - if (song.folder.entry == null) { - song.folder.entry = new SongListWidget.FolderEntry(song.folder, songListWidget); + previewButton = ButtonWidget.builder(PREVIEW, button -> { + if (Main.PREVIEWER.running) { + Main.PREVIEWER.stop(); + } else { + SongListWidget.SongEntry entry = songListWidget.getSelectedSongOrNull(); + if (entry != null) Main.PREVIEWER.start(entry.song); + } + }).dimensions(width / 2 - 50, height - 61, 100, 20).build(); + addDrawableChild(previewButton); + + addDrawableChild(ButtonWidget.builder(Text.translatable(Main.MOD_ID+".screen.blocks"), button -> { + // TODO: 6/2/2022 Add an auto build mode + if (BlocksOverlay.itemStacks == null) { + SongListWidget.SongEntry entry = songListWidget.getSelectedSongOrNull(); + if (entry != null) { + client.setScreen(null); + + BlocksOverlay.itemStacks = new ItemStack[0]; + BlocksOverlay.amounts = new int[0]; + BlocksOverlay.amountOfNoteBlocks = entry.song.uniqueNotes.size(); + + for (Note note : entry.song.uniqueNotes) { + ItemStack itemStack = Note.INSTRUMENT_BLOCKS.get(note.instrument()).asItem().getDefaultStack(); + int index = -1; + + for (int i = 0; i < BlocksOverlay.itemStacks.length; i++) { + if (BlocksOverlay.itemStacks[i].getItem() == itemStack.getItem()) { + index = i; + break; + } + } + + if (index == -1) { + BlocksOverlay.itemStacks = Arrays.copyOf(BlocksOverlay.itemStacks, BlocksOverlay.itemStacks.length + 1); + BlocksOverlay.amounts = Arrays.copyOf(BlocksOverlay.amounts, BlocksOverlay.amounts.length + 1); + + BlocksOverlay.itemStacks[BlocksOverlay.itemStacks.length - 1] = itemStack; + BlocksOverlay.amounts[BlocksOverlay.amounts.length - 1] = 1; + } else { + BlocksOverlay.amounts[index] = BlocksOverlay.amounts[index] + 1; + } } } - } - } - } - - // 播放进度 - private void updateNoteIndexFromTick() { - if (Main.SONG_PLAYER.song == null) return; - - double targetTick = Main.SONG_PLAYER.tick; - int newIndex = 0; - for (int i = 0; i < Main.SONG_PLAYER.song.notes.length; i++) { - long note = Main.SONG_PLAYER.song.notes[i]; - if ((short)note > targetTick) { - newIndex = i; - break; - } - newIndex = i; - } - Main.SONG_PLAYER.index = newIndex; - } - - private void updateBlocksOverlay(Song song) { - BlocksOverlay.itemStacks = new ItemStack[0]; - BlocksOverlay.amounts = new int[0]; - BlocksOverlay.amountOfNoteBlocks = song.uniqueNotes.size(); - - for (Note note : song.uniqueNotes) { - ItemStack itemStack = Note.INSTRUMENT_BLOCKS.get(note.instrument()).asItem().getDefaultStack(); - int index = -1; - - for (int i = 0; i < BlocksOverlay.itemStacks.length; i++) { - if (BlocksOverlay.itemStacks[i].getItem() == itemStack.getItem()) { - index = i; - break; - } - } - - if (index == -1) { - BlocksOverlay.itemStacks = Arrays.copyOf(BlocksOverlay.itemStacks, BlocksOverlay.itemStacks.length + 1); - BlocksOverlay.amounts = Arrays.copyOf(BlocksOverlay.amounts, BlocksOverlay.amounts.length + 1); - - BlocksOverlay.itemStacks[BlocksOverlay.itemStacks.length - 1] = itemStack; - BlocksOverlay.amounts[BlocksOverlay.amounts.length - 1] = 1; } else { - BlocksOverlay.amounts[index] = BlocksOverlay.amounts[index] + 1; + BlocksOverlay.itemStacks = null; + client.setScreen(null); } - } - } + }).dimensions(width / 2 + 60, height - 61, 100, 20).build()); - // 打开文件夹 - private void openSongsFolder() { - try { - String folderPath = currentFolder != null ? - currentFolder.path : Main.songsFolder.getAbsolutePath(); + // 打开文件夹 + addDrawableChild(ButtonWidget.builder(OPEN_FOLDER, button -> { + try { + String folderPath = currentFolder != null ? + currentFolder.path : + Main.songsFolder.getAbsolutePath(); // 使用绝对路径 - File target = new File(folderPath); - if (!target.exists()) { + File target = new File(folderPath); + if (!target.exists()) { + client.inGameHud.getChatHud().addMessage( + Text.translatable(Main.MOD_ID+".screen.folder_not_exist", folderPath) + .formatted(Formatting.RED)); + return; + } + + // Windows 专用命令,其他的不会 + if (System.getProperty("os.name").toLowerCase().contains("win")) { + new ProcessBuilder("explorer.exe", "/select,", target.getAbsolutePath()).start(); + } else { + java.awt.Desktop.getDesktop().open(target); + } + } catch (Exception e) { + Main.LOGGER.error("打开文件夹失败", e); client.inGameHud.getChatHud().addMessage( - Text.translatable(Main.MOD_ID+".screen.folder_not_exist", folderPath) + Text.translatable(Main.MOD_ID+".screen.open_folder_failed") .formatted(Formatting.RED)); - return; } + }).dimensions(width / 2 - 160, height - 31, 100, 20).build()); - if (System.getProperty("os.name").toLowerCase().contains("win")) { - new ProcessBuilder("explorer.exe", "/select,", target.getAbsolutePath()).start(); - } else { - java.awt.Desktop.getDesktop().open(target); - } - } catch (Exception e) { - Main.LOGGER.error("打开文件夹失败", e); - client.inGameHud.getChatHud().addMessage( - Text.translatable(Main.MOD_ID+".screen.open_folder_failed") - .formatted(Formatting.RED)); - } + + // 重新加载 + addDrawableChild(ButtonWidget.builder(RELOAD, button -> { + SongLoader.loadSongs(); + client.setScreen(null); + }).dimensions(width / 2 + 60, height - 31, 100, 20).build()); + + TextFieldWidget searchBar = new TextFieldWidget(textRenderer, width / 2 - 50, height - 31, 100, 20, Text.translatable(Main.MOD_ID+".screen.search")); + searchBar.setChangedListener(query -> { + query = query.toLowerCase().replaceAll("\\s", ""); + if (this.query.equals(query)) return; + this.query = query; + shouldFilter = true; + }); + addDrawableChild(searchBar); + + // TODO: 6/2/2022 Add a reload button } - // 预览 - private void playPreviousSong() { - if (SongLoader.currentFolder == null || SongLoader.currentFolder.songs.isEmpty()) return; - - int currentIndex = SongLoader.currentFolder.songs.indexOf(Main.SONG_PLAYER.song); - if (currentIndex == -1) { - if (!SongLoader.currentFolder.songs.isEmpty()) { - startSong(SongLoader.currentFolder.songs.get(0)); - } - return; - } - - int prevIndex = (currentIndex - 1 + SongLoader.currentFolder.songs.size()) % SongLoader.currentFolder.songs.size(); - startSong(SongLoader.currentFolder.songs.get(prevIndex)); - } - - // 下一首 - private void playNextSong() { - if (SongLoader.currentFolder == null || SongLoader.currentFolder.songs.isEmpty()) return; - - if (Main.SONG_PLAYER.playMode == PlayMode.RANDOM) { - int newIndex; - int currentIndex = SongLoader.currentFolder.songs.indexOf(Main.SONG_PLAYER.song); - do { - newIndex = (int)(Math.random() * SongLoader.currentFolder.songs.size()); - } while (newIndex == currentIndex && - SongLoader.currentFolder.songs.size() > 1); - startSong(SongLoader.currentFolder.songs.get(newIndex)); - } else { - int currentIndex = SongLoader.currentFolder.songs.indexOf(Main.SONG_PLAYER.song); - if (currentIndex == -1) { - if (!SongLoader.currentFolder.songs.isEmpty()) { - startSong(SongLoader.currentFolder.songs.get(0)); - } - return; - } - - int nextIndex = (currentIndex + 1) % SongLoader.currentFolder.songs.size(); - startSong(SongLoader.currentFolder.songs.get(nextIndex)); - } - } - - private void startSong(Song song) { - SongListWidget.SongEntry entry = findEntryForSong(song); - if (entry != null) { - songListWidget.setSelected(entry); - Main.SONG_PLAYER.start(song); - } - } - - private SongListWidget.SongEntry findEntryForSong(Song song) { - for (SongListWidget.Entry entry : songListWidget.children()) { - if (entry instanceof SongListWidget.SongEntry songEntry && songEntry.song == song) { - return songEntry; - } - } - return null; - } - - @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackground(context, mouseX, mouseY, delta); super.render(context, mouseX, mouseY, delta); - // 标题 context.drawCenteredTextWithShadow(textRenderer, DROP_HINT, width / 2, 5, 0xFFFFFF); context.drawCenteredTextWithShadow(textRenderer, SELECT_SONG, width / 2, 20, 0xFFFFFF); - // 文件夹 播放模式 + // 显示当前文件夹和播放模式 String folderName = currentFolder == null ? "/" : currentFolder.name; context.drawTextWithShadow(textRenderer, CURRENT_FOLDER.getString() + ": " + folderName, 35, 15, 0xFFFFFF); context.drawTextWithShadow(textRenderer, PLAY_MODE.getString() + ":", width - 220, 15, 0xFFFFFF); - - // 进度条 - if (Main.SONG_PLAYER.running && Main.SONG_PLAYER.song != null) { - double progress = (Main.SONG_PLAYER.tick / Main.SONG_PLAYER.song.length) * 100; - if (!progressBar.isDragging()) { - progressBar.setProgress(progress); - } - String timeText = formatTime(Main.SONG_PLAYER.getSongElapsedSeconds()) + " / " + - formatTime(Main.SONG_PLAYER.song.getLengthInSeconds()); - int timeTextY = progressBar.getY() + progressBar.getHeight() + 2; - context.drawTextWithShadow(textRenderer, timeText, width / 2 - textRenderer.getWidth(timeText) / 2, timeTextY, 0xFFFFFF); - } else { - progressBar.setProgress(0); - String timeText = formatTime(0) + " / " + formatTime(0); - int timeTextY = progressBar.getY() + progressBar.getHeight() + 2; - context.drawTextWithShadow(textRenderer, timeText, width / 2 - textRenderer.getWidth(timeText) / 2, timeTextY, 0xFFFFFF); - } - } - - // 时间格式hua - private String formatTime(double seconds) { - int totalSeconds = (int) seconds; - int minutes = totalSeconds / 60; - int secs = totalSeconds % 60; - return String.format("%02d:%02d", minutes, secs); } @Override public void tick() { - super.tick(); - previewButton.setMessage(Main.PREVIEWER.running ? PREVIEW_STOP : PREVIEW); playButton.setMessage(Main.SONG_PLAYER.running ? PLAY_STOP : PLAY); @@ -417,7 +245,7 @@ public class DiscJockeyScreen extends Screen { boolean empty = query.isEmpty(); boolean isInSongsOrSubfolder = currentFolder == null || - (Main.songsFolder != null && currentFolder.path.startsWith(Main.songsFolder.getPath())); + currentFolder.path.startsWith(Main.songsFolder.getPath()); if (currentFolder == null) { for (SongFolder folder : SongLoader.FOLDERS) { @@ -429,10 +257,12 @@ public class DiscJockeyScreen extends Screen { } } } else { + // 返回上级 SongListWidget.FolderEntry parentEntry = new SongListWidget.FolderEntry(null, songListWidget); parentEntry.displayName = ".."; songListWidget.children().add(parentEntry); + // 子文件夹 for (SongFolder subFolder : currentFolder.subFolders) { if (empty || subFolder.name.toLowerCase().contains(query)) { if (subFolder.entry == null) { @@ -443,7 +273,9 @@ public class DiscJockeyScreen extends Screen { } } + // 只有在songs目录或其子目录中才显示歌曲(原作者的💩跑我这了) if (isInSongsOrSubfolder) { + // 歌曲条目 List songsToShow = currentFolder == null ? SongLoader.SONGS.stream() .filter(song -> song.folder == null) @@ -452,14 +284,14 @@ public class DiscJockeyScreen extends Screen { .filter(song -> song.folder == currentFolder) .collect(Collectors.toList()); - // 已收藏 + // 已收藏歌曲 for (Song song : songsToShow) { if (song.entry.favorite && (empty || song.searchableFileName.contains(query) || song.searchableName.contains(query))) { songListWidget.children().add(song.entry); } } - // 未收藏 + // 未收藏歌曲 for (Song song : songsToShow) { if (!song.entry.favorite && (empty || song.searchableFileName.contains(query) || song.searchableName.contains(query))) { songListWidget.children().add(song.entry); @@ -469,7 +301,40 @@ public class DiscJockeyScreen extends Screen { } } - // 拖文件 + + + public SongFolder findParentFolder(SongFolder current) { + if (current == null) return null; + + if (SongLoader.FOLDERS.contains(current)) { + return null; + } + + for (SongFolder folder : SongLoader.FOLDERS) { + if (folder.subFolders.contains(current)) { + return folder; + } + SongFolder found = findParentInSubfolders(folder, current); + if (found != null) { + return found; + } + } + return null; + } + + private SongFolder findParentInSubfolders(SongFolder parent, SongFolder target) { + for (SongFolder subFolder : parent.subFolders) { + if (subFolder == target) { + return parent; + } + SongFolder found = findParentInSubfolders(subFolder, target); + if (found != null) { + return found; + } + } + return null; + } + @Override public void onFilesDropped(List paths) { String string = paths.stream().map(Path::getFileName).map(Path::toString).collect(Collectors.joining(", ")); @@ -485,14 +350,15 @@ public class DiscJockeyScreen extends Screen { Song song = SongLoader.loadSong(file); if (song != null) { - File targetFolder = currentFolder != null ? new File(currentFolder.path) : Main.songsFolder; - Files.copy(path, targetFolder.toPath().resolve(file.getName())); - SongLoader.loadSongs(); + Files.copy(path, Main.songsFolder.toPath().resolve(file.getName())); + SongLoader.SONGS.add(song); } } catch (IOException exception) { - Main.LOGGER.warn("无法将歌曲文件从 {} 复制到 {} ", path, Main.songsFolder.toPath(), exception); + Main.LOGGER.warn("Failed to copy song file from {} to {}", path, Main.songsFolder.toPath(), exception); } }); + + SongLoader.sort(); } client.setScreen(this); }, Text.translatable(Main.MOD_ID+".screen.drop_confirm"), Text.literal(string))); @@ -506,11 +372,27 @@ public class DiscJockeyScreen extends Screen { @Override public void close() { super.close(); + // 保存当前文件夹 Main.config.currentFolderPath = currentFolder != null ? currentFolder.path : ""; + // 保存播放模式 Main.config.playMode = currentPlayMode; + // 异步保存配置 new Thread(() -> Main.configHolder.save()).start(); } + private SongFolder findInSubFolders(SongFolder parent, SongFolder target) { + for (SongFolder subFolder : parent.subFolders) { + if (subFolder == target) { + return parent; + } + SongFolder found = findInSubFolders(subFolder, target); + if (found != null) { + return found; + } + } + return null; + } + private SongFolder findFolderByPath(String path) { for (SongFolder folder : SongLoader.FOLDERS) { if (folder.path.equals(path)) { @@ -527,7 +409,6 @@ public class DiscJockeyScreen extends Screen { return null; } - @Nullable private SongFolder findFolderByPathInSubfolders(SongFolder parent, String targetPath) { for (SongFolder subFolder : parent.subFolders) { if (subFolder.path.equals(targetPath)) { @@ -541,38 +422,6 @@ public class DiscJockeyScreen extends Screen { return null; } - @Nullable - public SongFolder findParentFolder(@Nullable SongFolder targetFolder) { - if (targetFolder == null) { - return null; - } - for (SongFolder folder : SongLoader.FOLDERS) { - if (folder.subFolders.contains(targetFolder)) { - return folder; - } - SongFolder parentInSub = findParentFolderInSubfoldersForParent(folder, targetFolder); - if (parentInSub != null) { - return parentInSub; - } - } - return null; - } - - @Nullable - private SongFolder findParentFolderInSubfoldersForParent(SongFolder current, SongFolder targetFolder) { - for (SongFolder subFolder : current.subFolders) { - if (subFolder.subFolders.contains(targetFolder)) { - return subFolder; - } - SongFolder parentInSub = findParentFolderInSubfoldersForParent(subFolder, targetFolder); - if (parentInSub != null) { - return parentInSub; - } - } - return null; - } - - private Text getPlayModeText() { return switch (currentPlayMode) { case SINGLE_LOOP -> MODE_SINGLE; diff --git a/src/main/java/semmiedev/disc_jockey_revive/gui/screen/EditKeyMappingsScreen.java b/src/main/java/semmiedev/disc_jockey_revive/gui/screen/EditKeyMappingsScreen.java deleted file mode 100644 index ae6e656..0000000 --- a/src/main/java/semmiedev/disc_jockey_revive/gui/screen/EditKeyMappingsScreen.java +++ /dev/null @@ -1,150 +0,0 @@ -package semmiedev.disc_jockey_revive.gui.screen; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.util.InputUtil; -import net.minecraft.text.Text; -import org.lwjgl.glfw.GLFW; -import semmiedev.disc_jockey_revive.Main; -import semmiedev.disc_jockey_revive.Note; -import semmiedev.disc_jockey_revive.gui.KeyMappingListWidget; - -import java.util.Map; - -public class EditKeyMappingsScreen extends Screen { - - private static final Text TITLE = Text.translatable(Main.MOD_ID + ".screen.edit_mappings.title"); - private static final Text ADD_MAPPING_BUTTON_TEXT = Text.translatable(Main.MOD_ID + ".screen.edit_mappings.add_mapping"); - private static final Text DONE_BUTTON_TEXT = Text.translatable("gui.done"); - private static final Text PRESS_KEY_INSTRUCTION = Text.translatable(Main.MOD_ID + ".screen.edit_mappings.press_key"); - - private final Screen parent; - private KeyMappingListWidget mappingListWidget; - private ButtonWidget addMappingButton; - private ButtonWidget doneButton; - - private boolean waitingForKeyPress = false; - private KeyMappingListWidget.KeyMappingEntry entryToEdit = null; - - public EditKeyMappingsScreen(Screen parent) { - super(TITLE); - this.parent = parent; - } - - @Override - protected void init() { - super.init(); - - int listTop = 40; - int listBottom = this.height - 50; - int listHeight = listBottom - listTop; - - mappingListWidget = new KeyMappingListWidget(this.client, this.width, listHeight, listTop, 20, this); - addDrawableChild(mappingListWidget); - refreshMappingList(); - int buttonWidth = 100; - int buttonHeight = 20; - int buttonY = this.height - 30; - int buttonX = this.width / 2 - buttonWidth - 5; - - addMappingButton = ButtonWidget.builder(ADD_MAPPING_BUTTON_TEXT, button -> { - startWaitingForKeyPress(null); - }).dimensions(buttonX, buttonY, buttonWidth, buttonHeight).build(); - addDrawableChild(addMappingButton); - buttonX = this.width / 2 + 5; - doneButton = ButtonWidget.builder(DONE_BUTTON_TEXT, button -> { - Main.keyMappingManager.saveMappings(); - this.client.setScreen(this.parent); - }).dimensions(buttonX, buttonY, buttonWidth, buttonHeight).build(); - addDrawableChild(doneButton); - } - - private void refreshMappingList() { - - mappingListWidget.setMappings(Main.keyMappingManager.getMappings()); - } - public void startWaitingForKeyPress(KeyMappingListWidget.KeyMappingEntry entry) { - this.waitingForKeyPress = true; - this.entryToEdit = entry; - - addMappingButton.active = false; - doneButton.active = false; - mappingListWidget.setButtonsActive(false); - } - public void addNewMapping(InputUtil.Key key, Note note) { - Main.keyMappingManager.setMapping(key, note); - refreshMappingList(); - - } - public void removeMapping(InputUtil.Key key) { - Main.keyMappingManager.removeMapping(key); - refreshMappingList(); - } - public void stopWaitingForKeyPress() { - this.waitingForKeyPress = false; - this.entryToEdit = null; - - addMappingButton.active = true; - doneButton.active = true; - mappingListWidget.setButtonsActive(true); - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (waitingForKeyPress) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - - stopWaitingForKeyPress(); - return true; - } - - InputUtil.Key pressedKey = InputUtil.fromKeyCode(keyCode, scanCode); - - if (entryToEdit != null) { - - Note note = entryToEdit.getNote(); - - Main.keyMappingManager.removeMapping(entryToEdit.getKey()); - - Main.keyMappingManager.setMapping(pressedKey, note); - refreshMappingList(); - stopWaitingForKeyPress(); - } else { - this.client.setScreen(new SelectNoteScreen(this, pressedKey)); - - } - return true; - } - if (mappingListWidget.keyPressed(keyCode, scanCode, modifiers)) { - return true; - } - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - Main.keyMappingManager.saveMappings(); - this.client.setScreen(this.parent); - return true; - } - - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - context.drawCenteredTextWithShadow(textRenderer, TITLE, this.width / 2, 10, 0xFFFFFF); - if (waitingForKeyPress) { - - context.fill(0, 0, this.width, this.height, 0x80000000); - context.drawCenteredTextWithShadow(textRenderer, PRESS_KEY_INSTRUCTION, this.width / 2, this.height / 2 - 10, 0xFFFFFF); - } - mappingListWidget.render(context, mouseX, mouseY, delta); - } - - @Override - public boolean shouldPause() { - - return true; - } -} diff --git a/src/main/java/semmiedev/disc_jockey_revive/gui/screen/LiveDjScreen.java b/src/main/java/semmiedev/disc_jockey_revive/gui/screen/LiveDjScreen.java deleted file mode 100644 index a8a9ef7..0000000 --- a/src/main/java/semmiedev/disc_jockey_revive/gui/screen/LiveDjScreen.java +++ /dev/null @@ -1,175 +0,0 @@ -package semmiedev.disc_jockey_revive.gui.screen; - -import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.annotation.Nullable; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.util.InputUtil; -import net.minecraft.sound.SoundCategory; -import net.minecraft.text.Text; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; -import semmiedev.disc_jockey_revive.Main; -import semmiedev.disc_jockey_revive.Note; -import semmiedev.disc_jockey_revive.KeyMappingManager; -import org.lwjgl.glfw.GLFW; - -import java.util.HashMap; -import java.util.Map; - -public class LiveDjScreen extends Screen { - - private static final Text TITLE = Text.translatable(Main.MOD_ID + ".screen.live_dj.title"); - private static final Text INSTRUCTIONS = Text.translatable(Main.MOD_ID + ".screen.live_dj.instructions"); - private static final Text EDIT_MAPPINGS_BUTTON_TEXT = Text.translatable(Main.MOD_ID + ".screen.live_dj.edit_mappings"); - private static final Text START_TUNING_BUTTON_TEXT = Text.translatable(Main.MOD_ID + ".screen.live_dj.start_tuning"); - private static final Text NOT_TUNED_MESSAGE = Text.translatable(Main.MOD_ID + ".player.not_tuned").formatted(net.minecraft.util.Formatting.RED); - private static final Text NOTE_BLOCK_MISSING_MESSAGE = Text.translatable(Main.MOD_ID + ".player.note_block_missing_live").formatted(net.minecraft.util.Formatting.RED); - private static final Text TOO_FAR_MESSAGE = Text.translatable(Main.MOD_ID + ".player.to_far_live").formatted(net.minecraft.util.Formatting.RED); - private static final Text RATE_LIMITED_MESSAGE = Text.translatable(Main.MOD_ID + ".player.rate_limited_live").formatted(net.minecraft.util.Formatting.YELLOW); - private ButtonWidget startTuningButton; - - public LiveDjScreen() { - super(TITLE); - } - - @Override - protected void init() { - super.init(); - - int centerX = this.width / 2; - int buttonWidth = 150; - int buttonHeight = 20; - int buttonY = this.height - 30; - int margin = 5; - addDrawableChild(ButtonWidget.builder(EDIT_MAPPINGS_BUTTON_TEXT, button -> { - MinecraftClient.getInstance().setScreen(new EditKeyMappingsScreen(this)); - }).dimensions(centerX - buttonWidth - margin, buttonY, buttonWidth, buttonHeight).build()); - startTuningButton = ButtonWidget.builder(START_TUNING_BUTTON_TEXT, button -> { - Main.SONG_PLAYER.startTuning(); - }).dimensions(centerX + margin, buttonY, buttonWidth, buttonHeight).build(); - addDrawableChild(startTuningButton); - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - context.drawCenteredTextWithShadow(textRenderer, TITLE, this.width / 2, 10, 0xFFFFFF); - context.drawCenteredTextWithShadow(textRenderer, INSTRUCTIONS, this.width / 2, 30, 0xFFFFFF); - Text tuningStatusText; - if (Main.SONG_PLAYER.noteBlocks == null) { - tuningStatusText = Text.translatable(Main.MOD_ID + ".player.discovering").formatted(net.minecraft.util.Formatting.YELLOW); - startTuningButton.active = true; - startTuningButton.visible = true; - } else if (!Main.SONG_PLAYER.tuned) { - - int totalNeeded = 0; - if (Main.SONG_PLAYER.noteBlocks != null) { - for (HashMap instrumentNotes : Main.SONG_PLAYER.noteBlocks.values()) { - if (instrumentNotes != null) { - for (net.minecraft.util.math.BlockPos pos : instrumentNotes.values()) { - if (pos != null) { - totalNeeded++; - } - } - } - } - } - int tunedCount = 0; - - MinecraftClient client = MinecraftClient.getInstance(); - if (client.world != null && Main.SONG_PLAYER.noteBlocks != null) { - for (HashMap instrumentNotes : Main.SONG_PLAYER.noteBlocks.values()) { - if (instrumentNotes != null) { - for (Map.Entry entry : instrumentNotes.entrySet()) { - net.minecraft.util.math.BlockPos pos = entry.getValue(); - Byte wantedNote = entry.getKey(); - if (pos != null) { - - if (client.world.getBlockState(pos).contains(net.minecraft.state.property.Properties.NOTE)) { - int currentNote = client.world.getBlockState(pos).get(net.minecraft.state.property.Properties.NOTE); - if (currentNote == wantedNote.byteValue()) { - tunedCount++; - } - } - } - } - } - } - } - if (totalNeeded > 0) { - tuningStatusText = Text.translatable(Main.MOD_ID + ".player.tuning_progress", tunedCount, totalNeeded).formatted(net.minecraft.util.Formatting.YELLOW); - } else { - - tuningStatusText = Text.translatable(Main.MOD_ID + ".player.finding_blocks").formatted(net.minecraft.util.Formatting.YELLOW); - } - startTuningButton.active = true; - startTuningButton.visible = true; - - } else { - tuningStatusText = Text.translatable(Main.MOD_ID + ".player.tuned").formatted(net.minecraft.util.Formatting.GREEN); - startTuningButton.active = false; - startTuningButton.visible = false; - } - context.drawCenteredTextWithShadow(textRenderer, tuningStatusText, this.width / 2, 50, 0xFFFFFF); - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - - InputUtil.Key key = InputUtil.fromKeyCode(keyCode, scanCode); - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - this.close(); - return true; - } - Note note = Main.keyMappingManager.getNoteForKey(key); - - if (note != null) { - - if (!Main.SONG_PLAYER.tuned) { - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(NOT_TUNED_MESSAGE); - return true; - } - boolean played = Main.SONG_PLAYER.playNoteBlock(note); - if (!played) { - @Nullable net.minecraft.util.math.BlockPos blockPos = null; - if (Main.SONG_PLAYER.noteBlocks != null && Main.SONG_PLAYER.noteBlocks.containsKey(note.instrument())) { - blockPos = Main.SONG_PLAYER.noteBlocks.get(note.instrument()).get(note.note()); - } - - if (blockPos == null) { - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(NOTE_BLOCK_MISSING_MESSAGE.copy().append(" (" + KeyMappingManager.getNoteDisplayName(note) + ")")); - } else if (!Main.SONG_PLAYER.canInteractWith(MinecraftClient.getInstance().player, blockPos)) { - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(TOO_FAR_MESSAGE.copy().append(" (" + KeyMappingManager.getNoteDisplayName(note) + ")")); - } else { - - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(RATE_LIMITED_MESSAGE); - } - } - - return true; - } - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public void tick() { - super.tick(); - - if (startTuningButton != null) { - startTuningButton.visible = !Main.SONG_PLAYER.tuned; - startTuningButton.active = !Main.SONG_PLAYER.isTuningEnabled(); - } - } - @Override - public boolean shouldPause() { - return false; - } - - @Override - public void close() { - - super.close(); - } -} diff --git a/src/main/java/semmiedev/disc_jockey_revive/gui/screen/SelectNoteScreen.java b/src/main/java/semmiedev/disc_jockey_revive/gui/screen/SelectNoteScreen.java deleted file mode 100644 index eedfc81..0000000 --- a/src/main/java/semmiedev/disc_jockey_revive/gui/screen/SelectNoteScreen.java +++ /dev/null @@ -1,173 +0,0 @@ -package semmiedev.disc_jockey_revive.gui.screen; - -import net.minecraft.block.enums.NoteBlockInstrument; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.CyclingButtonWidget; -import net.minecraft.client.gui.widget.SliderWidget; -import net.minecraft.client.util.InputUtil; -import net.minecraft.sound.SoundCategory; -import net.minecraft.sound.SoundEvent; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; -import org.lwjgl.glfw.GLFW; -import semmiedev.disc_jockey_revive.Main; -import semmiedev.disc_jockey_revive.Note; -import semmiedev.disc_jockey_revive.KeyMappingManager; - -public class SelectNoteScreen extends Screen { - - private static final Text TITLE = Text.translatable(Main.MOD_ID + ".screen.select_note.title"); - private static final Text INSTRUMENT_TEXT = Text.translatable(Main.MOD_ID + ".screen.select_note.instrument"); - private static final Text PITCH_TEXT = Text.translatable(Main.MOD_ID + ".screen.select_note.pitch"); - private static final Text DONE_BUTTON_TEXT = Text.translatable("gui.done"); - private static final Text CANCEL_BUTTON_TEXT = Text.translatable("gui.cancel"); - private static final Text PREVIEW_BUTTON_TEXT = Text.translatable(Main.MOD_ID + ".screen.select_note.preview"); - private final EditKeyMappingsScreen parent; - private final InputUtil.Key keyToMap; - - private NoteBlockInstrument selectedInstrument = NoteBlockInstrument.HARP; - private int selectedPitch = 12; - private CustomPitchSlider pitchSlider; - - private ButtonWidget previewButton; - - public SelectNoteScreen(EditKeyMappingsScreen parent, InputUtil.Key keyToMap) { - super(TITLE); - this.parent = parent; - this.keyToMap = keyToMap; - } - - @Override - protected void init() { - super.init(); - - int centerX = this.width / 2; - int startY = this.height / 2 - 50; - int widgetWidth = 200; - int widgetHeight = 20; - int margin = 5; - CyclingButtonWidget instrumentButton = CyclingButtonWidget.builder((NoteBlockInstrument instrument) -> Text.translatable("block.minecraft.note_block.instrument." + instrument.asString())) - .values(NoteBlockInstrument.values()) - .initially(selectedInstrument) - - .build(centerX - widgetWidth / 2, startY, widgetWidth, widgetHeight, INSTRUMENT_TEXT, (button, instrument) -> { - this.selectedInstrument = instrument; - updatePitchSliderDisplay(); - updatePreviewButton(); - }); - addDrawableChild(instrumentButton); - pitchSlider = new CustomPitchSlider(centerX - widgetWidth / 2, startY + widgetHeight + margin, widgetWidth, widgetHeight, PITCH_TEXT, (selectedPitch / 24.0)); - updatePitchSliderDisplay(); - addDrawableChild(pitchSlider); - previewButton = ButtonWidget.builder(PREVIEW_BUTTON_TEXT, button -> { - playPreviewNote(); - }).dimensions(centerX - widgetWidth / 2, startY + (widgetHeight + margin) * 2, widgetWidth, widgetHeight).build(); - addDrawableChild(previewButton); - int buttonWidth = 100; - int buttonY = this.height - 30; - int doneButtonX = centerX - buttonWidth - margin; - addDrawableChild(ButtonWidget.builder(DONE_BUTTON_TEXT, button -> { - Note selectedNote = new Note(selectedInstrument, (byte) selectedPitch); - parent.addNewMapping(keyToMap, selectedNote); - this.client.setScreen(this.parent); - parent.stopWaitingForKeyPress(); - }).dimensions(doneButtonX, buttonY, buttonWidth, widgetHeight).build()); - int cancelButtonX = centerX + margin; - addDrawableChild(ButtonWidget.builder(CANCEL_BUTTON_TEXT, button -> { - this.client.setScreen(this.parent); - parent.stopWaitingForKeyPress(); - }).dimensions(cancelButtonX, buttonY, buttonWidth, widgetHeight).build()); - } - private class CustomPitchSlider extends SliderWidget { - public CustomPitchSlider(int x, int y, int width, int height, Text text, double value) { - super(x, y, width, height, text, value); - } - - @Override - protected void updateMessage() { - - this.setMessage(PITCH_TEXT.copy().append(": " + KeyMappingManager.getNoteDisplayName(new Note(selectedInstrument, (byte) selectedPitch)))); - } - - @Override - protected void applyValue() { - selectedPitch = (int) Math.round(MathHelper.lerp(this.value, 0.0, 24.0)); - updateMessage(); - updatePreviewButton(); - } - public void forceUpdateDisplay() { - this.updateMessage(); - } - } - private void updatePitchSliderDisplay() { - if (pitchSlider != null) { - - pitchSlider.forceUpdateDisplay(); - } - } - - private void updatePreviewButton() { - - } - - private void playPreviewNote() { - MinecraftClient client = MinecraftClient.getInstance(); - if (client.world != null && client.gameRenderer != null) { - Vec3d pos = client.gameRenderer.getCamera().getPos(); - try { - Note note = new Note(selectedInstrument, (byte) selectedPitch); - - if (note.instrument().canBePitched()) { - Identifier soundId = note.instrument().getSound().value().id(); - float pitchMultiplier = (float) Math.pow(2.0, (note.note() - 12) / 12.0); - client.world.playSound(pos.x, pos.y, pos.z, SoundEvent.of(soundId), SoundCategory.RECORDS, 3.0f, pitchMultiplier, false); - } else { - - Identifier soundId = note.instrument().getSound().value().id(); - client.world.playSound(pos.x, pos.y, pos.z, SoundEvent.of(soundId), SoundCategory.RECORDS, 3.0f, 1.0f, false); - } - - } catch (Exception e) { - Main.LOGGER.error("无法播放预览声音。", e); - } - } - } - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - - super.render(context, mouseX, mouseY, delta); - - context.drawCenteredTextWithShadow(textRenderer, TITLE, this.width / 2, 10, 0xFFFFFF); - - context.drawCenteredTextWithShadow(textRenderer, Text.translatable(Main.MOD_ID + ".screen.select_note.mapping_key", Text.translatable(keyToMap.getTranslationKey())), this.width / 2, 30, 0xFFFFFF); - } - - @Override - public boolean shouldPause() { - return true; - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { - this.client.setScreen(this.parent); - parent.stopWaitingForKeyPress(); - return true; - } - - if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { - Note selectedNote = new Note(selectedInstrument, (byte) selectedPitch); - parent.addNewMapping(keyToMap, selectedNote); - this.client.setScreen(this.parent); - parent.stopWaitingForKeyPress(); - return true; - } - return super.keyPressed(keyCode, scanCode, modifiers); - } -} diff --git a/src/main/java/semmiedev/disc_jockey_revive/gui/widget/ProgressBarWidget.java b/src/main/java/semmiedev/disc_jockey_revive/gui/widget/ProgressBarWidget.java deleted file mode 100644 index ac5275b..0000000 --- a/src/main/java/semmiedev/disc_jockey_revive/gui/widget/ProgressBarWidget.java +++ /dev/null @@ -1,91 +0,0 @@ -package semmiedev.disc_jockey_revive.gui.widget; - -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.widget.ClickableWidget; -import net.minecraft.text.Text; -import net.minecraft.util.math.MathHelper; - -public class ProgressBarWidget extends ClickableWidget { - private double progress; - private final int minValue; - private final int maxValue; - private boolean dragging; - - public ProgressBarWidget(int x, int y, int width, int height, Text message, int minValue, int maxValue) { - super(x, y, width, height, message); - this.minValue = minValue; - this.maxValue = maxValue; - this.progress = minValue; - } - - @Override - public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { - // 背景 - context.fill(getX(), getY(), getX() + width, getY() + height, 0xFF555555); - - // 进度条 - int progressWidth = (int)(width * (progress - minValue) / (maxValue - minValue)); - context.fill(getX(), getY(), getX() + progressWidth, getY() + height, 0xFF00FF00); - - // 滑块 - int sliderX = getX() + progressWidth - 3; - context.fill(sliderX, getY() - 2, sliderX + 6, getY() + height + 2, 0xFFFFFFFF); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == 0 && isMouseOver(mouseX, mouseY)) { - setProgressFromMouse(mouseX); - dragging = true; - return true; - } - return false; - } - - @Override - public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - if (dragging) { - setProgressFromMouse(mouseX); - return true; - } - return false; - } - - @Override - public boolean mouseReleased(double mouseX, double mouseY, int button) { - if (button == 0) { - dragging = false; - return true; - } - return false; - } - - private void setProgressFromMouse(double mouseX) { - double relativeX = MathHelper.clamp(mouseX - getX(), 0, width); - double newProgress = (relativeX / width) * (maxValue - minValue) + minValue; - setProgress(newProgress); - } - - public void setProgress(double progress) { - this.progress = MathHelper.clamp(progress, minValue, maxValue); - onProgressChanged(this.progress); - } - - public double getProgress() { - return progress; - } - - public boolean isDragging() { - return this.dragging; - } - - protected void onProgressChanged(double progress) { - - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - appendDefaultNarrations(builder); - } -} diff --git a/src/main/resources/assets/disc_jockey/icon1.png b/src/main/resources/assets/disc_jockey/icon1.png new file mode 100644 index 0000000000000000000000000000000000000000..38824636a02aa74fa75b49786d4a24bcc0f5998e GIT binary patch literal 117871 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

$VQ^5pi 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 J25uJezW^kpM&|$k literal 0 HcmV?d00001 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 2539473..c5bb33f 100644 --- a/src/main/resources/assets/disc_jockey/lang/en_us.json +++ b/src/main/resources/assets/disc_jockey/lang/en_us.json @@ -61,54 +61,5 @@ "disc_jockey_revive.screen.open_folder": "Open Folder", "disc_jockey_revive.screen.open_folder_failed": "Failed to open folder", "disc_jockey_revive.screen.reload": "Reload Songs", - "disc_jockey_revive.screen.reloading": "Reloading songs...", - - "key.disc_jockey_revive.open_screen": "Open Disc Jockey Screen", - "key.disc_jockey_revive.open_live_dj_screen": "Open Live DJ Screen", - - "disc_jockey_revive.screen.live_dj.title": "Live DJ Mode", - "disc_jockey_revive.screen.live_dj.instructions": "Press mapped keys to play notes. Press ESC to exit. Default key mapping reference FL Studio", - "disc_jockey_revive.screen.live_dj.edit_mappings": "Edit Mappings", - - "disc_jockey_revive.screen.edit_mappings.title": "Edit Key Mappings", - "disc_jockey_revive.screen.edit_mappings.add_mapping": "Add Mapping", - "disc_jockey_revive.screen.edit_mappings.change": "Change Key", - "disc_jockey_revive.screen.edit_mappings.remove": "Remove", - "disc_jockey_revive.screen.edit_mappings.press_key": "Press any key to map...", - - "disc_jockey_revive.screen.select_note.title": "Select Note", - "disc_jockey_revive.screen.select_note.mapping_key": "Mapping Key: %s", - "disc_jockey_revive.screen.select_note.instrument": "Instrument", - "disc_jockey_revive.screen.select_note.pitch": "Pitch", - "disc_jockey_revive.screen.select_note.preview": "Preview Note", - - "block.minecraft.note_block.instrument.harp": "Air", - "block.minecraft.note_block.instrument.basedrum": "Stone", - "block.minecraft.note_block.instrument.snare": "Sand", - "block.minecraft.note_block.instrument.hat": "Glass", - "block.minecraft.note_block.instrument.bass": "Wood", - "block.minecraft.note_block.instrument.flute": "Clay", - "block.minecraft.note_block.instrument.bell": "Gold Block", - "block.minecraft.note_block.instrument.guitar": "Wool", - "block.minecraft.note_block.instrument.chime": "Packed Ice", - "block.minecraft.note_block.instrument.xylophone": "Bone Block", - "block.minecraft.note_block.instrument.iron_xylophone": "Iron Block", - "block.minecraft.note_block.instrument.cow_bell": "Soul Sand", - "block.minecraft.note_block.instrument.didgeridoo": "Pumpkin", - "block.minecraft.note_block.instrument.bit": "Emerald Block", - "block.minecraft.note_block.instrument.banjo": "Hay Bale", - "block.minecraft.note_block.instrument.pling": "Glowstone", - - "disc_jockey_revive.player.not_tuned": "Note blocks are not tuned yet!", - "disc_jockey_revive.player.discovering": "Discovering note blocks...", - "disc_jockey_revive.player.finding_blocks": "Finding note blocks...", - "disc_jockey_revive.player.tuning_progress": "Tuning note blocks: %s/%s", - "disc_jockey_revive.player.tuned": "Note blocks are tuned!", - "disc_jockey_revive.player.note_block_missing_live": "Note block missing for this note!", - "disc_jockey_revive.player.to_far_live": "Too far from note block for this note!", - "disc_jockey_revive.player.rate_limited_live": "Rate limited. Cannot play note right now.", - - "disc_jockey_revive.screen.live_dj.start_tuning": "Start Tuning", - "disc_jockey_revive.player.tuning_started": "Tuning started...", - "disc_jockey_revive.player.retuning": "Retuning note blocks..." + "disc_jockey_revive.screen.reloading": "Reloading songs..." } \ No newline at end of file diff --git a/src/main/resources/assets/disc_jockey/lang/zh_cn.json b/src/main/resources/assets/disc_jockey/lang/zh_cn.json index d1cec9f..0a5cfb8 100644 --- a/src/main/resources/assets/disc_jockey/lang/zh_cn.json +++ b/src/main/resources/assets/disc_jockey/lang/zh_cn.json @@ -60,54 +60,5 @@ "disc_jockey_revive.screen.mode_stop": "播完停止","disc_jockey_revive.screen.open_folder": "打开文件夹", "disc_jockey_revive.screen.open_folder_failed": "无法打开文件夹", "disc_jockey_revive.screen.reload": "重新加载", - "disc_jockey_revive.screen.reloading": "正在重新加载...", - - "key.disc_jockey_revive.open_screen": "打开Disc Jockey界面", - "key.disc_jockey_revive.open_live_dj_screen": "打开现场演奏界面", - - "disc_jockey_revive.screen.live_dj.title": "现场演奏模式", - "disc_jockey_revive.screen.live_dj.instructions": "按下已映射的按键来弹奏音符。按 ESC 退出。默认按键映射参考FL Studio", - "disc_jockey_revive.screen.live_dj.edit_mappings": "编辑按键映射", - - "disc_jockey_revive.screen.edit_mappings.title": "编辑按键映射", - "disc_jockey_revive.screen.edit_mappings.add_mapping": "添加映射", - "disc_jockey_revive.screen.edit_mappings.change": "更改按键", - "disc_jockey_revive.screen.edit_mappings.remove": "移除", - "disc_jockey_revive.screen.edit_mappings.press_key": "按下任意按键进行映射...", - - "disc_jockey_revive.screen.select_note.title": "选择音符", - "disc_jockey_revive.screen.select_note.mapping_key": "映射按键:%s", - "disc_jockey_revive.screen.select_note.instrument": "乐器", - "disc_jockey_revive.screen.select_note.pitch": "音高", - "disc_jockey_revive.screen.select_note.preview": "试听音符", - - "block.minecraft.note_block.instrument.harp": "空气", - "block.minecraft.note_block.instrument.basedrum": "石头", - "block.minecraft.note_block.instrument.snare": "沙子", - "block.minecraft.note_block.instrument.hat": "玻璃", - "block.minecraft.note_block.instrument.bass": "木头", - "block.minecraft.note_block.instrument.flute": "粘土", - "block.minecraft.note_block.instrument.bell": "金块", - "block.minecraft.note_block.instrument.guitar": "羊毛", - "block.minecraft.note_block.instrument.chime": "浮冰", - "block.minecraft.note_block.instrument.xylophone": "骨块", - "block.minecraft.note_block.instrument.iron_xylophone": "铁块", - "block.minecraft.note_block.instrument.cow_bell": "灵魂沙", - "block.minecraft.note_block.instrument.didgeridoo": "南瓜", - "block.minecraft.note_block.instrument.bit": "绿宝石", - "block.minecraft.note_block.instrument.banjo": "甘草快", - "block.minecraft.note_block.instrument.pling": "荧石", - - "disc_jockey_revive.player.not_tuned": "音符盒尚未调音!", - "disc_jockey_revive.player.discovering": "正在发现音符盒...", - "disc_jockey_revive.player.finding_blocks": "正在查找音符盒...", - "disc_jockey_revive.player.tuning_progress": "正在调音音符盒:%s/%s", - "disc_jockey_revive.player.tuned": "音符盒已调音!", - "disc_jockey_revive.player.note_block_missing_live": "此音符的音符盒缺失!", - "disc_jockey_revive.player.to_far_live": "离此音符的音符盒太远!", - "disc_jockey_revive.player.rate_limited_live": "速率受限。暂时无法弹奏音符。", - - "disc_jockey_revive.screen.live_dj.start_tuning": "开始调音", - "disc_jockey_revive.player.tuning_started": "调音已开始...", - "disc_jockey_revive.player.retuning": "正在重新调音音符盒..." + "disc_jockey_revive.screen.reloading": "正在重新加载..." } \ No newline at end of file