Added a drag and drop feature for songs
This commit is contained in:
parent
b87a6b3586
commit
0e28e33bf9
@ -16,6 +16,7 @@ import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.text.MutableText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
@ -62,7 +63,7 @@ public class Main implements ClientModInitializer {
|
||||
|
||||
if (openScreenKeyBind.wasPressed()) {
|
||||
if (SongLoader.loadingSongs) {
|
||||
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".still_loading"));
|
||||
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".still_loading").formatted(Formatting.RED));
|
||||
} else {
|
||||
client.setScreen(new DiscJockeyScreen());
|
||||
}
|
||||
|
@ -6,9 +6,11 @@ import net.minecraft.text.Text;
|
||||
import semmiedev.disc_jockey.gui.SongListWidget;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class SongLoader {
|
||||
public static final ArrayList<Song> SONGS = new ArrayList<>();
|
||||
@ -23,93 +25,104 @@ public class SongLoader {
|
||||
SONG_SUGGESTIONS.clear();
|
||||
SONG_SUGGESTIONS.add("Songs are loading, please wait");
|
||||
for (File file : Main.songsFolder.listFiles()) {
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
BinaryReader reader = new BinaryReader(Files.newInputStream(file.toPath()));
|
||||
Song song = new Song();
|
||||
|
||||
song.fileName = file.getName().replaceAll("[\\n\\r]", "");
|
||||
|
||||
song.length = reader.readShort();
|
||||
|
||||
boolean newFormat = song.length == 0;
|
||||
if (newFormat) {
|
||||
song.formatVersion = reader.readByte();
|
||||
song.vanillaInstrumentCount = reader.readByte();
|
||||
song.length = reader.readShort();
|
||||
}
|
||||
|
||||
song.height = reader.readShort();
|
||||
song.name = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
song.author = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
song.originalAuthor = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
song.description = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
song.tempo = reader.readShort();
|
||||
song.autoSaving = reader.readByte();
|
||||
song.autoSavingDuration = reader.readByte();
|
||||
song.timeSignature = reader.readByte();
|
||||
song.minutesSpent = reader.readInt();
|
||||
song.leftClicks = reader.readInt();
|
||||
song.rightClicks = reader.readInt();
|
||||
song.blocksAdded = reader.readInt();
|
||||
song.blocksRemoved = reader.readInt();
|
||||
song.importFileName = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
|
||||
if (newFormat) {
|
||||
song.loop = reader.readByte();
|
||||
song.maxLoopCount = reader.readByte();
|
||||
song.loopStartTick = reader.readShort();
|
||||
}
|
||||
|
||||
song.displayName = song.name.replaceAll("\\s", "").isEmpty() ? song.fileName : song.name+" ("+song.fileName+")";
|
||||
song.entry = new SongListWidget.SongEntry(song, SONGS.size());
|
||||
song.entry.favorite = Main.config.favorites.contains(song.fileName);
|
||||
song.searchableFileName = song.fileName.toLowerCase().replaceAll("\\s", "");
|
||||
song.searchableName = song.name.toLowerCase().replaceAll("\\s", "");
|
||||
|
||||
short tick = -1;
|
||||
short jumps;
|
||||
while ((jumps = reader.readShort()) != 0) {
|
||||
tick += jumps;
|
||||
short layer = -1;
|
||||
while ((jumps = reader.readShort()) != 0) {
|
||||
layer += jumps;
|
||||
|
||||
byte instrumentId = reader.readByte();
|
||||
byte noteId = (byte)(reader.readByte() - 33);
|
||||
|
||||
if (newFormat) {
|
||||
// Data that is not needed as it only works with commands
|
||||
reader.readByte(); // Velocity
|
||||
reader.readByte(); // Panning
|
||||
reader.readShort(); // Pitch
|
||||
}
|
||||
|
||||
if (noteId < 0) {
|
||||
noteId = 0;
|
||||
} else if (noteId > 24) {
|
||||
noteId = 24;
|
||||
}
|
||||
|
||||
Note note = new Note(Note.INSTRUMENTS[instrumentId], noteId);
|
||||
if (!song.uniqueNotes.contains(note)) song.uniqueNotes.add(note);
|
||||
|
||||
song.notes = Arrays.copyOf(song.notes, song.notes.length + 1);
|
||||
song.notes[song.notes.length - 1] = tick | layer << Note.LAYER_SHIFT | (long)instrumentId << Note.INSTRUMENT_SHIFT | (long)noteId << Note.NOTE_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
SONGS.add(song);
|
||||
} catch (Throwable exception) {
|
||||
Main.LOGGER.error("Unable to read song "+file.getName(), exception);
|
||||
}
|
||||
Song song = null;
|
||||
try {
|
||||
song = loadSong(file);
|
||||
} catch (IOException exception) {
|
||||
Main.LOGGER.error("Unable to read song "+file.getName(), exception);
|
||||
}
|
||||
if (song != null) SONGS.add(song);
|
||||
}
|
||||
for (Song song : SONGS) SONG_SUGGESTIONS.add(song.displayName);
|
||||
Main.config.favorites.removeIf(favorite -> SongLoader.SONGS.stream().map(song -> song.fileName).noneMatch(favorite::equals));
|
||||
|
||||
SystemToast.add(MinecraftClient.getInstance().getToastManager(), SystemToast.Type.PACK_LOAD_FAILURE, Main.NAME, Text.translatable(Main.MOD_ID+".loading_done"));
|
||||
if (MinecraftClient.getInstance().textRenderer != null) SystemToast.add(MinecraftClient.getInstance().getToastManager(), SystemToast.Type.PACK_LOAD_FAILURE, Main.NAME, Text.translatable(Main.MOD_ID+".loading_done"));
|
||||
loadingSongs = false;
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static Song loadSong(File file) throws IOException {
|
||||
if (file.isFile()) {
|
||||
BinaryReader reader = new BinaryReader(Files.newInputStream(file.toPath()));
|
||||
Song song = new Song();
|
||||
|
||||
song.fileName = file.getName().replaceAll("[\\n\\r]", "");
|
||||
|
||||
song.length = reader.readShort();
|
||||
|
||||
boolean newFormat = song.length == 0;
|
||||
if (newFormat) {
|
||||
song.formatVersion = reader.readByte();
|
||||
song.vanillaInstrumentCount = reader.readByte();
|
||||
song.length = reader.readShort();
|
||||
}
|
||||
|
||||
song.height = reader.readShort();
|
||||
song.name = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
song.author = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
song.originalAuthor = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
song.description = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
song.tempo = reader.readShort();
|
||||
song.autoSaving = reader.readByte();
|
||||
song.autoSavingDuration = reader.readByte();
|
||||
song.timeSignature = reader.readByte();
|
||||
song.minutesSpent = reader.readInt();
|
||||
song.leftClicks = reader.readInt();
|
||||
song.rightClicks = reader.readInt();
|
||||
song.blocksAdded = reader.readInt();
|
||||
song.blocksRemoved = reader.readInt();
|
||||
song.importFileName = reader.readString().replaceAll("[\\n\\r]", "");
|
||||
|
||||
if (newFormat) {
|
||||
song.loop = reader.readByte();
|
||||
song.maxLoopCount = reader.readByte();
|
||||
song.loopStartTick = reader.readShort();
|
||||
}
|
||||
|
||||
song.displayName = song.name.replaceAll("\\s", "").isEmpty() ? song.fileName : song.name+" ("+song.fileName+")";
|
||||
song.entry = new SongListWidget.SongEntry(song, SONGS.size());
|
||||
song.entry.favorite = Main.config.favorites.contains(song.fileName);
|
||||
song.searchableFileName = song.fileName.toLowerCase().replaceAll("\\s", "");
|
||||
song.searchableName = song.name.toLowerCase().replaceAll("\\s", "");
|
||||
|
||||
short tick = -1;
|
||||
short jumps;
|
||||
while ((jumps = reader.readShort()) != 0) {
|
||||
tick += jumps;
|
||||
short layer = -1;
|
||||
while ((jumps = reader.readShort()) != 0) {
|
||||
layer += jumps;
|
||||
|
||||
byte instrumentId = reader.readByte();
|
||||
byte noteId = (byte)(reader.readByte() - 33);
|
||||
|
||||
if (newFormat) {
|
||||
// Data that is not needed as it only works with commands
|
||||
reader.readByte(); // Velocity
|
||||
reader.readByte(); // Panning
|
||||
reader.readShort(); // Pitch
|
||||
}
|
||||
|
||||
if (noteId < 0) {
|
||||
noteId = 0;
|
||||
} else if (noteId > 24) {
|
||||
noteId = 24;
|
||||
}
|
||||
|
||||
Note note = new Note(Note.INSTRUMENTS[instrumentId], noteId);
|
||||
if (!song.uniqueNotes.contains(note)) song.uniqueNotes.add(note);
|
||||
|
||||
song.notes = Arrays.copyOf(song.notes, song.notes.length + 1);
|
||||
song.notes[song.notes.length - 1] = tick | layer << Note.LAYER_SHIFT | (long)instrumentId << Note.INSTRUMENT_SHIFT | (long)noteId << Note.NOTE_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
return song;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void sort() {
|
||||
SONGS.sort(Comparator.comparing(song -> song.displayName));
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ public class SongListWidget extends EntryListWidget<SongListWidget.SongEntry> {
|
||||
super.setSelected(entry);
|
||||
}
|
||||
|
||||
// TODO: 6/2/2022 Add a delete icon
|
||||
public static class SongEntry extends Entry<SongEntry> {
|
||||
private static final Identifier ICONS = new Identifier(Main.MOD_ID, "textures/gui/icons.png");
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package semmiedev.disc_jockey.gui.screen;
|
||||
|
||||
import net.minecraft.client.gui.screen.ConfirmScreen;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
@ -7,6 +8,7 @@ import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.text.MutableText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import semmiedev.disc_jockey.Main;
|
||||
import semmiedev.disc_jockey.Note;
|
||||
import semmiedev.disc_jockey.Song;
|
||||
@ -14,16 +16,22 @@ import semmiedev.disc_jockey.SongLoader;
|
||||
import semmiedev.disc_jockey.gui.SongListWidget;
|
||||
import semmiedev.disc_jockey.gui.hud.BlocksOverlay;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// TODO: 6/1/2022 Add a drag and drop action for songs
|
||||
public class DiscJockeyScreen extends Screen {
|
||||
private static final MutableText
|
||||
SELECT_SONG = Text.translatable(Main.MOD_ID+".screen.select_song"),
|
||||
PLAY = Text.translatable(Main.MOD_ID+".screen.play"),
|
||||
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")
|
||||
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;
|
||||
@ -70,6 +78,7 @@ public class DiscJockeyScreen extends Screen {
|
||||
addDrawableChild(previewButton);
|
||||
|
||||
addDrawableChild(new ButtonWidget(width / 2 + 60, height - 61, 100, 20, 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.getSelectedOrNull();
|
||||
if (entry != null) {
|
||||
@ -115,12 +124,15 @@ public class DiscJockeyScreen extends Screen {
|
||||
shouldFilter = true;
|
||||
});
|
||||
addDrawableChild(searchBar);
|
||||
|
||||
// TODO: 6/2/2022 Add a reload button
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
|
||||
super.render(matrices, mouseX, mouseY, delta);
|
||||
|
||||
drawCenteredText(matrices, textRenderer, DROP_HINT, width / 2, 5, 0xFFFFFF);
|
||||
drawCenteredText(matrices, textRenderer, SELECT_SONG, width / 2, 20, 0xFFFFFF);
|
||||
}
|
||||
|
||||
@ -147,6 +159,35 @@ public class DiscJockeyScreen extends Screen {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filesDragged(List<Path> paths) {
|
||||
String string = paths.stream().map(Path::getFileName).map(Path::toString).collect(Collectors.joining(", "));
|
||||
if (string.length() > 300) string = string.substring(0, 300)+"...";
|
||||
|
||||
client.setScreen(new ConfirmScreen(confirmed -> {
|
||||
if (confirmed) {
|
||||
paths.forEach(path -> {
|
||||
try {
|
||||
File file = path.toFile();
|
||||
|
||||
if (SongLoader.SONGS.stream().anyMatch(input -> input.fileName.equalsIgnoreCase(file.getName()))) return;
|
||||
|
||||
Song song = SongLoader.loadSong(file);
|
||||
if (song != null) {
|
||||
Files.copy(path, Main.songsFolder.toPath().resolve(file.getName()));
|
||||
SongLoader.SONGS.add(song);
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
Main.LOGGER.warn("Failed to copy song file from {} to {}", path, Main.songsFolder.toPath(), exception);
|
||||
}
|
||||
});
|
||||
|
||||
SongLoader.sort();
|
||||
}
|
||||
client.setScreen(this);
|
||||
}, Text.translatable("disc_jockey.drop_confirm"), Text.literal(string)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPause() {
|
||||
return false;
|
||||
|
@ -7,6 +7,8 @@
|
||||
"disc_jockey.screen.blocks.title": "Blocks",
|
||||
"disc_jockey.screen.blocks": "Blocks",
|
||||
"disc_jockey.screen.search": "Search For Songs",
|
||||
"disc_jockey.screen.drop_hint": "Drag and drop song files into this window to add them",
|
||||
"disc_jockey.screen.drop_confirm": "Do you want to add the following songs to Disc Jockey?",
|
||||
"disc_jockey.player.invalid_note_blocks": "The Note Blocks near you are not in the correct configuration. Missing:",
|
||||
"disc_jockey.player.invalid_game_mode": "You can't play in %s",
|
||||
"disc_jockey.player.to_far": "You went to far away",
|
||||
|
Loading…
x
Reference in New Issue
Block a user