Added a drag and drop feature for songs

This commit is contained in:
Semmieboy YT 2022-06-02 20:02:05 +02:00
parent b87a6b3586
commit 0e28e33bf9
5 changed files with 142 additions and 84 deletions

View File

@ -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());
}

View File

@ -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));
}
}

View File

@ -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");

View File

@ -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;

View File

@ -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",