Compare commits

..

2 Commits

Author SHA1 Message Date
BRanulf
f3586dce44 又一点点小更新 2025-04-25 22:17:30 +08:00
BRanulf
dfa80c8e47 一点点小更新 2025-04-25 22:02:08 +08:00
7 changed files with 211 additions and 32 deletions

View File

@ -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.021
mod_version=1.14.514.022
maven_group=semmiedev
archives_base_name=disc_jockey_revive
# Dependencies

View File

@ -20,6 +20,7 @@ import net.minecraft.util.Formatting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
import semmiedev.disc_jockey_revive.gui.SongListWidget;
import semmiedev.disc_jockey_revive.gui.hud.BlocksOverlay;
import semmiedev.disc_jockey_revive.gui.screen.DiscJockeyScreen;
@ -38,6 +39,15 @@ public class Main implements ClientModInitializer {
public static ModConfig config;
public static ConfigHolder<ModConfig> configHolder;
public static KeyBinding nextSongKeyBind;
// public static KeyBinding prevSongKeyBind;
static KeyBinding playStopKeyBind;
public static KeyBinding reloadKeyBind;
private static long lastNextSongPress = 0;
public MinecraftClient client;
@Override
public void onInitializeClient() {
configHolder = AutoConfig.register(ModConfig.class, JanksonConfigSerializer::new);
@ -48,8 +58,40 @@ public class Main implements ClientModInitializer {
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));
// 下一首和上一首快捷键
nextSongKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(
MOD_ID+".key_bind.next_song",
InputUtil.Type.KEYSYM,
InputUtil.UNKNOWN_KEY.getCode(), // Default to unbound
"key.category."+MOD_ID
));
// 上一首不要了没啥用
// prevSongKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(
// MOD_ID+".key_bind.prev_song",
// InputUtil.Type.KEYSYM,
// InputUtil.UNKNOWN_KEY.getCode(), // Default to unbound
// "key.category."+MOD_ID
// ));
// 停止
playStopKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(
MOD_ID+".key_bind.play_stop",
InputUtil.Type.KEYSYM,
InputUtil.UNKNOWN_KEY.getCode(), // Default unbound
"key.category."+MOD_ID
));
// 重载
reloadKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(
MOD_ID+".key_bind.reload",
InputUtil.Type.KEYSYM,
InputUtil.UNKNOWN_KEY.getCode(), // Default unbound
"key.category."+MOD_ID
));
ClientTickEvents.START_CLIENT_TICK.register(new ClientTickEvents.StartTick() {
private ClientWorld prevWorld;
@ -69,9 +111,31 @@ public class Main implements ClientModInitializer {
client.setScreen(new DiscJockeyScreen());
}
}
if (nextSongKeyBind.wasPressed()) {
if (SONG_PLAYER.running) {
SONG_PLAYER.playNextSong();
}
}
// if (prevSongKeyBind.wasPressed()) {
// SONG_PLAYER.playPreviousSong();
// }
if (playStopKeyBind.wasPressed()) {
if (SONG_PLAYER.running) {
SONG_PLAYER.stop();
}
}
if (reloadKeyBind.wasPressed()) {
SongLoader.loadSongs();
if (client.currentScreen instanceof DiscJockeyScreen) {
client.setScreen(new DiscJockeyScreen());
}
}
}
});
ClientTickEvents.START_WORLD_TICK.register(world -> {
for (ClientTickEvents.StartWorldTick listener : TICK_LISTENERS) listener.onStartTick(world);
});

View File

@ -2,6 +2,7 @@ package semmiedev.disc_jockey_revive;
import me.shedaniel.autoconfig.ConfigData;
import me.shedaniel.autoconfig.annotation.ConfigEntry;
import net.minecraft.text.Text;
import java.util.ArrayList;
@ -37,6 +38,29 @@ public class ModConfig implements ConfigData {
}
}
public enum ErrorHandlingMode {
STOP_PLAYBACK(Text.translatable(Main.MOD_ID+".config.error_handling.stop")),
PLAY_NEXT(Text.translatable(Main.MOD_ID+".config.error_handling.next"));
private final Text displayText;
ErrorHandlingMode(Text displayText) {
this.displayText = displayText;
}
public Text getDisplayText() {
return displayText;
}
@Override
public String toString() {
return displayText.getString();
}
}
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
public ErrorHandlingMode errorHandlingMode = ErrorHandlingMode.STOP_PLAYBACK;
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
@ConfigEntry.Gui.Tooltip(count = 4)
public ExpectedServerVersion expectedServerVersion = ExpectedServerVersion.All;

View File

@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SongPlayer implements ClientTickEvents.StartWorldTick {
@ -98,7 +99,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
STOP_AFTER // 播完就停
}
private PlayMode playMode = PlayMode.STOP_AFTER;
public PlayMode playMode = PlayMode.STOP_AFTER;
private boolean isRandomPlaying = false;
private int randomIndex = -1;
@ -160,6 +161,27 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
didSongReachEnd = false; // Change after running stop() if actually ended cleanly
}
public synchronized void playPreviousSong() {
if (!running || song == null) return;
if (SongLoader.currentFolder == null || SongLoader.currentFolder.songs.isEmpty()) return;
int currentIndex = SongLoader.currentFolder.songs.indexOf(song);
if (currentIndex == -1) return;
if (playMode == PlayMode.RANDOM) {
int newIndex;
do {
newIndex = (int) (Math.random() * SongLoader.currentFolder.songs.size());
} while (newIndex == currentIndex && SongLoader.currentFolder.songs.size() > 1);
start(SongLoader.currentFolder.songs.get(newIndex));
} else {
int prevIndex = (currentIndex - 1 + SongLoader.currentFolder.songs.size()) % SongLoader.currentFolder.songs.size();
start(SongLoader.currentFolder.songs.get(prevIndex));
}
}
public synchronized void tickPlayback() {
if (!running) {
lastPlaybackTickAt = -1L;
@ -197,8 +219,14 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
continue;
}
if (!canInteractWith(client.player, blockPos)) {
stop();
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.to_far").formatted(Formatting.RED));
if (Main.config.errorHandlingMode == ModConfig.ErrorHandlingMode.PLAY_NEXT) {
playNextSong();
} else {
stop();
if (client.player != null) {
client.player.sendMessage(Text.translatable(Main.MOD_ID+".player.to_far").formatted(Formatting.RED), false);
}
}
return;
}
Vec3d unit = Vec3d.ofCenter(blockPos, 0.5).subtract(client.player.getEyePos()).normalize();
@ -256,24 +284,39 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
}
}
private void playNextSong() {
if (SongLoader.currentFolder == null || SongLoader.currentFolder.songs.isEmpty()) return;
public synchronized void playNextSong() {
if (!running || song == null) return;
int currentIndex = SongLoader.currentFolder.songs.indexOf(song);
if (currentIndex == -1) return;
if (SongLoader.currentFolder == null ||
(SongLoader.currentFolder.songs.isEmpty() && SongLoader.SONGS.isEmpty())) {
return;
}
List<Song> availableSongs = SongLoader.currentFolder != null ?
SongLoader.currentFolder.songs : SongLoader.SONGS;
if (availableSongs.isEmpty()) return;
if (playMode == PlayMode.RANDOM) {
// 随机播放
int newIndex;
do {
newIndex = (int) (Math.random() * SongLoader.currentFolder.songs.size());
} while (newIndex == currentIndex && SongLoader.currentFolder.songs.size() > 1);
start(SongLoader.currentFolder.songs.get(newIndex));
} else if (playMode == PlayMode.LIST_LOOP) {
int nextIndex = (currentIndex + 1) % SongLoader.currentFolder.songs.size();
start(SongLoader.currentFolder.songs.get(nextIndex));
newIndex = (int)(Math.random() * availableSongs.size());
} while (availableSongs.size() > 1 &&
availableSongs.get(newIndex).fileName.equals(song.fileName));
start(availableSongs.get(newIndex));
}
else { // 列表循环和单曲循环
int currentIndex = availableSongs.indexOf(song);
if (currentIndex == -1) return;
int nextIndex = (currentIndex + 1) % availableSongs.size();
start(availableSongs.get(nextIndex));
}
}
public synchronized void setPlayMode(PlayMode mode) {
this.playMode = mode;
this.loopSong = mode == PlayMode.SINGLE_LOOP;

View File

@ -9,10 +9,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
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.*;
import semmiedev.disc_jockey_revive.gui.SongListWidget;
import semmiedev.disc_jockey_revive.gui.hud.BlocksOverlay;
@ -34,26 +31,25 @@ public class DiscJockeyScreen extends Screen {
PLAY_STOP = Text.translatable(Main.MOD_ID+".screen.play.stop"),
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)
;
DROP_HINT = Text.translatable(Main.MOD_ID+".screen.drop_hint").formatted(Formatting.GRAY),
private SongListWidget songListWidget;
private ButtonWidget playButton, previewButton;
public boolean shouldFilter;
private String query = "";
private static final MutableText
FOLDER_UP = Text.literal(""),
CURRENT_FOLDER = Text.translatable(Main.MOD_ID+".screen.current_folder"),
PLAY_MODE = Text.translatable(Main.MOD_ID+".screen.play_mode"),
MODE_SINGLE = Text.translatable(Main.MOD_ID+".screen.mode_single"),
MODE_LIST = Text.translatable(Main.MOD_ID+".screen.mode_list"),
MODE_RANDOM = Text.translatable(Main.MOD_ID+".screen.mode_random"),
MODE_STOP = Text.translatable(Main.MOD_ID+".screen.mode_stop");
private static final MutableText
MODE_STOP = Text.translatable(Main.MOD_ID+".screen.mode_stop"),
OPEN_FOLDER = Text.translatable(Main.MOD_ID+".screen.open_folder"),
RELOAD = Text.translatable(Main.MOD_ID+".screen.reload");
RELOAD = Text.translatable(Main.MOD_ID+".screen.reload"),
NEXT_SONG = Text.translatable(Main.MOD_ID+".screen.next_song"),
PREV_SONG = Text.translatable(Main.MOD_ID+".screen.prev_song");
public SongListWidget songListWidget;
private ButtonWidget playButton, previewButton;
public boolean shouldFilter;
private String query = "";
private ButtonWidget folderUpButton, playModeButton;
public SongFolder currentFolder;
@ -209,6 +205,38 @@ public class DiscJockeyScreen extends Screen {
client.setScreen(null);
}).dimensions(width / 2 + 60, height - 31, 100, 20).build());
// 下一首
addDrawableChild(ButtonWidget.builder(NEXT_SONG, button -> {
SongPlayer player = Main.SONG_PLAYER;
if (player.running) {
player.playNextSong();
} else {
SongListWidget.SongEntry entry = songListWidget.getSelectedSongOrNull();
if (entry != null) {
List<Song> availableSongs = SongLoader.currentFolder != null ?
SongLoader.currentFolder.songs : SongLoader.SONGS;
int currentIndex = availableSongs.indexOf(entry.song);
if (currentIndex != -1) {
int nextIndex;
if (player.playMode == PlayMode.RANDOM) {
// Get random song in menu too
do {
nextIndex = (int)(Math.random() * availableSongs.size());
} while (availableSongs.size() > 1 && nextIndex == currentIndex);
} else {
nextIndex = (currentIndex + 1) % availableSongs.size();
}
songListWidget.setSelected(availableSongs.get(nextIndex).entry);
}
}
}
}).dimensions(width / 2 + 60 + 110 + 5, height - 61, 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", "");

View File

@ -61,5 +61,15 @@
"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..."
"disc_jockey_revive.screen.reloading": "Reloading songs...",
"disc_jockey_revive.screen.next_song": "Next Song",
"disc_jockey_revive.screen.prev_song": "Previous Song",
"disc_jockey_revive.key_bind.next_song": "Next Song",
"disc_jockey_revive.key_bind.prev_song": "Previous Song",
"text.autoconfig.disc_jockey_revive.option.errorHandlingMode": "Error Handling",
"text.autoconfig.disc_jockey_revive.option.errorHandlingMode.@Tooltip": "What to do when errors occur",
"disc_jockey_revive.config.error_handling.stop": "Stop Playback",
"disc_jockey_revive.config.error_handling.next": "Play Next Song",
"disc_jockey_revive.key_bind.play_stop": "Play/Stop",
"disc_jockey_revive.key_bind.reload": "Reload"
}

View File

@ -60,5 +60,15 @@
"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": "正在重新加载..."
"disc_jockey_revive.screen.reloading": "正在重新加载...",
"disc_jockey_revive.screen.next_song": "下一首",
"disc_jockey_revive.screen.prev_song": "上一首",
"disc_jockey_revive.key_bind.next_song": "下一首",
"disc_jockey_revive.key_bind.prev_song": "上一首",
"text.autoconfig.disc_jockey_revive.option.errorHandlingMode": "错误处理",
"text.autoconfig.disc_jockey_revive.option.errorHandlingMode.@Tooltip": "播放错误时的处理方式",
"disc_jockey_revive.config.error_handling.stop": "停止播放",
"disc_jockey_revive.config.error_handling.next": "播放下一首",
"disc_jockey_revive.key_bind.play_stop": "播放/停止",
"disc_jockey_revive.key_bind.reload": "重新加载"
}