Impelmented playlists
This commit is contained in:
parent
b37a50d365
commit
6c225c8bd3
9 changed files with 690 additions and 96 deletions
|
@ -2,6 +2,7 @@ package com.github.hhhzzzsss.songplayer;
|
|||
|
||||
import com.github.hhhzzzsss.songplayer.playing.SongHandler;
|
||||
import com.github.hhhzzzsss.songplayer.song.Note;
|
||||
import com.github.hhhzzzsss.songplayer.song.Playlist;
|
||||
import com.github.hhhzzzsss.songplayer.song.Song;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
@ -33,6 +34,7 @@ public class CommandProcessor {
|
|||
commands.add(new statusCommand());
|
||||
commands.add(new queueCommand());
|
||||
commands.add(new songsCommand());
|
||||
commands.add(new playlistCommand());
|
||||
commands.add(new setCreativeCommandCommand());
|
||||
commands.add(new setSurvivalCommandCommand());
|
||||
commands.add(new useEssentialsCommandsCommand());
|
||||
|
@ -62,7 +64,18 @@ public class CommandProcessor {
|
|||
} else {
|
||||
boolean success = c.processCommand(args);
|
||||
if (!success) {
|
||||
SongPlayer.addChatMessage("§cSyntax - " + c.getSyntax());
|
||||
if (c.getSyntax().length == 0) {
|
||||
SongPlayer.addChatMessage("§cSyntax: " + Config.getConfig().prefix + c.getName());
|
||||
}
|
||||
else if (c.getSyntax().length == 1) {
|
||||
SongPlayer.addChatMessage("§cSyntax: " + Config.getConfig().prefix + c.getName() + " " + c.getSyntax()[0]);
|
||||
}
|
||||
else {
|
||||
SongPlayer.addChatMessage("§cSyntax:");
|
||||
for (String syntax : c.getSyntax()) {
|
||||
SongPlayer.addChatMessage("§c " + Config.getConfig().prefix + c.getName() + " " + syntax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -73,7 +86,7 @@ public class CommandProcessor {
|
|||
|
||||
private static abstract class Command {
|
||||
public abstract String getName();
|
||||
public abstract String getSyntax();
|
||||
public abstract String[] getSyntax();
|
||||
public abstract String getDescription();
|
||||
public abstract boolean processCommand(String args);
|
||||
public String[] getAliases() {
|
||||
|
@ -88,8 +101,8 @@ public class CommandProcessor {
|
|||
public String getName() {
|
||||
return "help";
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "help [command]";
|
||||
public String[] getSyntax() {
|
||||
return new String[]{"[command]"};
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Lists commands or explains command";
|
||||
|
@ -108,7 +121,17 @@ public class CommandProcessor {
|
|||
SongPlayer.addChatMessage("§6------------------------------");
|
||||
SongPlayer.addChatMessage("§6Help: §3" + c.getName());
|
||||
SongPlayer.addChatMessage("§6Description: §3" + c.getDescription());
|
||||
SongPlayer.addChatMessage("§6Usage: §3" + Config.getConfig().prefix + c.getSyntax());
|
||||
if (c.getSyntax().length == 0) {
|
||||
SongPlayer.addChatMessage("§6Usage: §3" + Config.getConfig().prefix + c.getName());
|
||||
}
|
||||
else if (c.getSyntax().length == 1) {
|
||||
SongPlayer.addChatMessage("§6Usage: §3" + Config.getConfig().prefix + c.getName() + " " + c.getSyntax()[0]);
|
||||
} else {
|
||||
SongPlayer.addChatMessage("§6Usage:");
|
||||
for (String syntax : c.getSyntax()) {
|
||||
SongPlayer.addChatMessage(" §3" + Config.getConfig().prefix + c.getName() + " " + syntax);
|
||||
}
|
||||
}
|
||||
if (c.getAliases().length > 0) {
|
||||
SongPlayer.addChatMessage("§6Aliases: §3" + String.join(", ", c.getAliases()));
|
||||
}
|
||||
|
@ -131,8 +154,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"prefix"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "setPrefix <prefix>";
|
||||
public String[] getSyntax() {
|
||||
return new String[] {"<prefix>"};
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Sets the command prefix used by SongPlayer";
|
||||
|
@ -158,8 +181,8 @@ public class CommandProcessor {
|
|||
public String getName() {
|
||||
return "play";
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "play <song or url>";
|
||||
public String[] getSyntax() {
|
||||
return new String[] {"<song or url>"};
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Plays a song";
|
||||
|
@ -174,26 +197,12 @@ public class CommandProcessor {
|
|||
}
|
||||
}
|
||||
public CompletableFuture<Suggestions> getSuggestions(String args, SuggestionsBuilder suggestionsBuilder) {
|
||||
int lastSlash = args.lastIndexOf("/");
|
||||
String dirString = "";
|
||||
File dir = SongPlayer.SONG_DIR;
|
||||
if (lastSlash >= 0) {
|
||||
dirString = args.substring(0, lastSlash+1);
|
||||
dir = new File(dir, dirString);
|
||||
if (!args.contains(" ")) {
|
||||
return Util.giveSongSuggestions(args, suggestionsBuilder);
|
||||
}
|
||||
|
||||
if (!dir.exists()) return null;
|
||||
|
||||
ArrayList<String> suggestions = new ArrayList<>();
|
||||
for (File file : dir.listFiles()) {
|
||||
if (file.isFile()) {
|
||||
suggestions.add(dirString + file.getName());
|
||||
}
|
||||
else if (file.isDirectory()) {
|
||||
suggestions.add(dirString + file.getName() + "/");
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return CommandSource.suggestMatching(suggestions, suggestionsBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,8 +210,8 @@ public class CommandProcessor {
|
|||
public String getName() {
|
||||
return "stop";
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "stop";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Stops playing";
|
||||
|
@ -230,8 +239,8 @@ public class CommandProcessor {
|
|||
public String getName() {
|
||||
return "skip";
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "skip";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Skips current song";
|
||||
|
@ -255,8 +264,8 @@ public class CommandProcessor {
|
|||
public String getName() {
|
||||
return "goto";
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "goto <mm:ss>";
|
||||
public String[] getSyntax() {
|
||||
return new String[] {"<mm:ss>"};
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Goes to a specific time in the song";
|
||||
|
@ -288,8 +297,8 @@ public class CommandProcessor {
|
|||
public String getName() {
|
||||
return "loop";
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "loop";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Toggles song looping";
|
||||
|
@ -319,8 +328,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"current"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "status";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Gets the status of the song that is currently playing";
|
||||
|
@ -350,8 +359,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"showQueue"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "queue";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Shows the current song queue";
|
||||
|
@ -388,8 +397,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"list"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "songs";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Lists available songs";
|
||||
|
@ -417,6 +426,187 @@ public class CommandProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
private static class playlistCommand extends Command {
|
||||
public String getName() {
|
||||
return "playlist";
|
||||
}
|
||||
public String[] getSyntax() {
|
||||
return new String[] {
|
||||
"play <playlist>",
|
||||
"create <playlist>",
|
||||
"list [playlist]",
|
||||
"delete <playlist> <song>",
|
||||
"addSong <playlist> <song>",
|
||||
"removeSong <playlist> <song>",
|
||||
"renameSong <playlist> <old name> <new name>",
|
||||
"loop",
|
||||
"shuffle",
|
||||
};
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Configures playlists";
|
||||
}
|
||||
public boolean processCommand(String args) {
|
||||
String[] split = args.split(" ");
|
||||
|
||||
if (split.length < 1) return false;
|
||||
|
||||
try {
|
||||
File playlistDir = null;
|
||||
if (split.length >= 2) {
|
||||
playlistDir = new File(SongPlayer.PLAYLISTS_DIR, split[1]);
|
||||
}
|
||||
switch (split[0].toLowerCase()) {
|
||||
case "play":
|
||||
if (split.length != 2) return false;
|
||||
if (!playlistDir.exists()) {
|
||||
SongPlayer.addChatMessage("§cPlaylist does not exist");
|
||||
return true;
|
||||
}
|
||||
SongHandler.getInstance().setPlaylist(playlistDir);
|
||||
return true;
|
||||
case "create":
|
||||
if (split.length > 2) {
|
||||
SongPlayer.addChatMessage("§cCannot have spaces in playlist name");
|
||||
return true;
|
||||
}
|
||||
if (split.length != 2) return false;
|
||||
Playlist.createPlaylist(split[1]);
|
||||
SongPlayer.addChatMessage(String.format("§6Created playlist §3%s", split[1]));
|
||||
return true;
|
||||
case "delete":
|
||||
if (split.length != 2) return false;
|
||||
Playlist.deletePlaylist(playlistDir);
|
||||
SongPlayer.addChatMessage(String.format("§6Deleted playlist §3%s", split[1]));
|
||||
return true;
|
||||
case "list":
|
||||
if (split.length == 1) {
|
||||
if (!SongPlayer.PLAYLISTS_DIR.exists()) return true;
|
||||
List<String> playlists = Arrays.stream(SongPlayer.PLAYLISTS_DIR.listFiles())
|
||||
.filter(File::isDirectory)
|
||||
.map(File::getName)
|
||||
.collect(Collectors.toList());
|
||||
if (playlists.size() == 0) {
|
||||
SongPlayer.addChatMessage("§6No playlists found");
|
||||
} else {
|
||||
SongPlayer.addChatMessage("§6Playlists: §3" + String.join(", ", playlists));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
List<String> playlistIndex = Playlist.listSongs(playlistDir);
|
||||
SongPlayer.addChatMessage("§6------------------------------");
|
||||
int index = 0;
|
||||
for (String songName : playlistIndex) {
|
||||
index++;
|
||||
SongPlayer.addChatMessage(String.format("§6%d. §3%s", index, songName));
|
||||
}
|
||||
SongPlayer.addChatMessage("§6------------------------------");
|
||||
return true;
|
||||
case "addsong":
|
||||
if (split.length != 3) return false;
|
||||
Playlist.addSong(playlistDir, new File(SongPlayer.SONG_DIR, split[2]));
|
||||
SongPlayer.addChatMessage(String.format("§6Added §3%s §6to §3%s", split[2], split[1]));
|
||||
return true;
|
||||
case "removesong":
|
||||
if (split.length != 3) return false;
|
||||
Playlist.removeSong(playlistDir, split[2]);
|
||||
SongPlayer.addChatMessage(String.format("§6Removed §3%s §6from §3%s", split[2], split[1]));
|
||||
return true;
|
||||
case "renamesong":
|
||||
if (split.length != 4) return false;
|
||||
Playlist.renameSong(playlistDir, split[2], split[3]);
|
||||
SongPlayer.addChatMessage(String.format("§6Renamed song from §3%s §6from to §3%s", split[2], split[3]));
|
||||
return true;
|
||||
case "loop":
|
||||
if (split.length != 1) return false;
|
||||
Config.getConfig().loopPlaylists = !Config.getConfig().loopPlaylists;
|
||||
SongHandler.getInstance().setPlaylistLoop(Config.getConfig().loopPlaylists);
|
||||
if (Config.getConfig().loopPlaylists) {
|
||||
SongPlayer.addChatMessage("§6Enabled playlist looping");
|
||||
}
|
||||
else {
|
||||
SongPlayer.addChatMessage("§6Disabled playlist looping");
|
||||
}
|
||||
Config.saveConfigWithErrorHandling();
|
||||
return true;
|
||||
case "shuffle":
|
||||
if (split.length != 1) return false;
|
||||
Config.getConfig().shufflePlaylists = !Config.getConfig().shufflePlaylists;
|
||||
SongHandler.getInstance().setPlaylistShuffle(Config.getConfig().shufflePlaylists);
|
||||
if (Config.getConfig().loopPlaylists) {
|
||||
SongPlayer.addChatMessage("§6Enabled playlist shuffling");
|
||||
}
|
||||
else {
|
||||
SongPlayer.addChatMessage("§6Disabled playlist shuffling");
|
||||
}
|
||||
Config.saveConfigWithErrorHandling();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
SongPlayer.addChatMessage("§c" + e.getMessage());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public CompletableFuture<Suggestions> getSuggestions(String args, SuggestionsBuilder suggestionsBuilder) {
|
||||
String[] split = args.split(" ", -1);
|
||||
if (split.length <= 1) {
|
||||
return CommandSource.suggestMatching(new String[] {
|
||||
"play",
|
||||
"create",
|
||||
"delete",
|
||||
"list",
|
||||
"addSong",
|
||||
"removeSong",
|
||||
"renameSong",
|
||||
"loop",
|
||||
"shuffle",
|
||||
}, suggestionsBuilder);
|
||||
}
|
||||
switch (split[0].toLowerCase()) {
|
||||
case "create":
|
||||
case "loop":
|
||||
case "shuffle":
|
||||
default:
|
||||
return null;
|
||||
case "play":
|
||||
case "list":
|
||||
case "delete":
|
||||
if (split.length == 2) {
|
||||
return Util.givePlaylistSuggestions(suggestionsBuilder);
|
||||
}
|
||||
return null;
|
||||
case "addsong":
|
||||
if (split.length == 2) {
|
||||
return Util.givePlaylistSuggestions(suggestionsBuilder);
|
||||
}
|
||||
else if (split.length == 3) {
|
||||
return Util.giveSongSuggestions(split[2], suggestionsBuilder);
|
||||
}
|
||||
return null;
|
||||
case "removesong":
|
||||
case "renamesong":
|
||||
if (split.length == 2) {
|
||||
return Util.givePlaylistSuggestions(suggestionsBuilder);
|
||||
}
|
||||
else if (split.length == 3) {
|
||||
File playlistDir = new File(SongPlayer.PLAYLISTS_DIR, split[1]);
|
||||
List<File> playlistFiles = Playlist.getSongFiles(playlistDir);
|
||||
if (playlistFiles == null) {
|
||||
return null;
|
||||
}
|
||||
return CommandSource.suggestMatching(
|
||||
playlistFiles.stream()
|
||||
.map(File::getName),
|
||||
suggestionsBuilder);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class setCreativeCommandCommand extends Command {
|
||||
public String getName() {
|
||||
return "setCreativeCommand";
|
||||
|
@ -424,8 +614,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"sc"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "setCreativeCommand <command>";
|
||||
public String[] getSyntax() {
|
||||
return new String[] {"<command>"};
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Sets the command used to go into creative mode";
|
||||
|
@ -454,8 +644,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"ss"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "setSurvivalCommand <command>";
|
||||
public String[] getSyntax() {
|
||||
return new String[] {"<command>"};
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Sets the command used to go into survival mode";
|
||||
|
@ -484,8 +674,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"essentials", "useEssentials", "essentialsCommands"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "useEssentialsCommands";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Switches to using essentials gamemode commands";
|
||||
|
@ -511,8 +701,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"vanilla", "useVanilla", "vanillaCommands"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "useVanillaCommands";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Switches to using vanilla gamemode commands";
|
||||
|
@ -538,8 +728,8 @@ public class CommandProcessor {
|
|||
public String[] getAliases() {
|
||||
return new String[]{"fakePlayer", "fp"};
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "toggleFakePlayer";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Shows a fake player representing your true position when playing songs";
|
||||
|
@ -566,8 +756,8 @@ public class CommandProcessor {
|
|||
public String getName() {
|
||||
return "testSong";
|
||||
}
|
||||
public String getSyntax() {
|
||||
return "testSong";
|
||||
public String[] getSyntax() {
|
||||
return new String[0];
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Creates a song for testing";
|
||||
|
@ -596,7 +786,7 @@ public class CommandProcessor {
|
|||
.collect(Collectors.toList());
|
||||
return CommandSource.suggestMatching(names, suggestionsBuilder);
|
||||
} else {
|
||||
String[] split = text.split(" ");
|
||||
String[] split = text.split(" ", 2);
|
||||
if (split[0].startsWith(Config.getConfig().prefix)) {
|
||||
String commandName = split[0].substring(1).toLowerCase();
|
||||
if (commandMap.containsKey(commandName)) {
|
||||
|
|
|
@ -15,6 +15,8 @@ public class Config {
|
|||
public String creativeCommand = "gmc";
|
||||
public String survivalCommand = "gms";
|
||||
public boolean showFakePlayer = false;
|
||||
public boolean loopPlaylists;
|
||||
public boolean shufflePlaylists;
|
||||
|
||||
public static Config getConfig() {
|
||||
if (config == null) {
|
||||
|
|
|
@ -16,15 +16,19 @@ public class SongPlayer implements ModInitializer {
|
|||
|
||||
public static final File SONG_DIR = new File("songs");
|
||||
public static final File SONGPLAYER_DIR = new File("SongPlayer");
|
||||
public static final File PLAYLISTS_DIR = new File("SongPlayer/playlists");
|
||||
public static FakePlayerEntity fakePlayer;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
if (!SONG_DIR.exists()) {
|
||||
SONG_DIR.mkdir();
|
||||
SONG_DIR.mkdirs();
|
||||
}
|
||||
if (!SONGPLAYER_DIR.exists()) {
|
||||
SONGPLAYER_DIR.mkdir();
|
||||
SONGPLAYER_DIR.mkdirs();
|
||||
}
|
||||
if (!PLAYLISTS_DIR.exists()) {
|
||||
PLAYLISTS_DIR.mkdirs();
|
||||
}
|
||||
|
||||
CommandProcessor.initCommands();
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
package com.github.hhhzzzsss.songplayer;
|
||||
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.command.CommandSource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -45,4 +53,36 @@ public class Util {
|
|||
throw new IOException("Invalid time pattern");
|
||||
}
|
||||
}
|
||||
|
||||
public static CompletableFuture<Suggestions> giveSongSuggestions(String arg, SuggestionsBuilder suggestionsBuilder) {
|
||||
int lastSlash = arg.lastIndexOf("/");
|
||||
String dirString = "";
|
||||
File dir = SongPlayer.SONG_DIR;
|
||||
if (lastSlash >= 0) {
|
||||
dirString = arg.substring(0, lastSlash+1);
|
||||
dir = new File(dir, dirString);
|
||||
}
|
||||
|
||||
if (!dir.exists()) return null;
|
||||
|
||||
ArrayList<String> suggestions = new ArrayList<>();
|
||||
for (File file : dir.listFiles()) {
|
||||
if (file.isFile()) {
|
||||
suggestions.add(dirString + file.getName());
|
||||
}
|
||||
else if (file.isDirectory()) {
|
||||
suggestions.add(dirString + file.getName() + "/");
|
||||
}
|
||||
}
|
||||
return CommandSource.suggestMatching(suggestions, suggestionsBuilder);
|
||||
}
|
||||
|
||||
public static CompletableFuture<Suggestions> givePlaylistSuggestions(SuggestionsBuilder suggestionsBuilder) {
|
||||
if (!SongPlayer.PLAYLISTS_DIR.exists()) return null;
|
||||
return CommandSource.suggestMatching(
|
||||
Arrays.stream(SongPlayer.PLAYLISTS_DIR.listFiles())
|
||||
.filter(File::isDirectory)
|
||||
.map(File::getName),
|
||||
suggestionsBuilder);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,13 @@ public class ProgressDisplay {
|
|||
}
|
||||
private ProgressDisplay() {}
|
||||
|
||||
public MutableText displayText = Text.empty();
|
||||
public MutableText topText = Text.empty();
|
||||
public MutableText bottomText = Text.empty();
|
||||
public int fade = 0;
|
||||
|
||||
public void setText(MutableText text) {
|
||||
displayText = text;
|
||||
public void setText(MutableText bottomText, MutableText topText) {
|
||||
this.bottomText = bottomText;
|
||||
this.topText = topText;
|
||||
fade = 100;
|
||||
}
|
||||
|
||||
|
@ -31,15 +33,18 @@ public class ProgressDisplay {
|
|||
return;
|
||||
}
|
||||
|
||||
int textWidth = SongPlayer.MC.textRenderer.getWidth(displayText);
|
||||
int textX = (scaledWidth - textWidth) / 2;
|
||||
int textY = scaledHeight - 59;
|
||||
int bottomTextWidth = SongPlayer.MC.textRenderer.getWidth(bottomText);
|
||||
int topTextWidth = SongPlayer.MC.textRenderer.getWidth(topText);
|
||||
int bottomTextX = (scaledWidth - bottomTextWidth) / 2;
|
||||
int topTextX = (scaledWidth - topTextWidth) / 2;
|
||||
int bottomTextY = scaledHeight - 59;
|
||||
if (!SongPlayer.MC.interactionManager.hasStatusBars()) {
|
||||
textY += 14;
|
||||
bottomTextY += 14;
|
||||
}
|
||||
if (heldItemTooltipFade > 0) {
|
||||
textY -= 12;
|
||||
bottomTextY -= 12;
|
||||
}
|
||||
int topTextY = bottomTextY - 12;
|
||||
|
||||
int opacity = (int)((float)this.fade * 256.0F / 10.0F);
|
||||
if (opacity > 255) {
|
||||
|
@ -49,7 +54,8 @@ public class ProgressDisplay {
|
|||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
Objects.requireNonNull(SongPlayer.MC.textRenderer);
|
||||
SongPlayer.MC.textRenderer.drawWithShadow(matrixStack, displayText, (float)textX, (float)textY, 16777215 + (opacity << 24));
|
||||
SongPlayer.MC.textRenderer.drawWithShadow(matrixStack, bottomText, (float)bottomTextX, (float)bottomTextY, 16777215 + (opacity << 24));
|
||||
SongPlayer.MC.textRenderer.drawWithShadow(matrixStack, topText, (float)topTextX, (float)topTextY, 16777215 + (opacity << 24));
|
||||
RenderSystem.disableBlend();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import net.minecraft.util.math.Direction;
|
|||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.GameMode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
|
||||
|
@ -36,13 +37,42 @@ public class SongHandler {
|
|||
public SongLoaderThread loaderThread = null;
|
||||
public LinkedList<Song> songQueue = new LinkedList<>();
|
||||
public Song currentSong = null;
|
||||
public Playlist currentPlaylist = null;
|
||||
public Stage stage = null;
|
||||
public boolean building = false;
|
||||
|
||||
boolean playlistChecked = false;
|
||||
|
||||
public void onUpdate(boolean tick) {
|
||||
if (currentSong == null && songQueue.size() > 0) {
|
||||
// Check current playlist and load song from it if necessary
|
||||
if (currentSong == null && currentPlaylist != null && currentPlaylist.loaded) {
|
||||
if (!playlistChecked) {
|
||||
playlistChecked = true;
|
||||
if (currentPlaylist.songsFailedToLoad.size() > 0) {
|
||||
SongPlayer.addChatMessage("§cFailed to load the following songs from the playlist: §4" + String.join(" ", currentPlaylist.songsFailedToLoad));
|
||||
}
|
||||
}
|
||||
Song nextSong = currentPlaylist.getNext();
|
||||
if (currentPlaylist.songs.size() == 0) {
|
||||
SongPlayer.addChatMessage("§cPlaylist has no playable songs");
|
||||
currentPlaylist = null;
|
||||
}
|
||||
else if (nextSong == null) {
|
||||
SongPlayer.addChatMessage("§6Playlist has finished playing");
|
||||
currentPlaylist = null;
|
||||
}
|
||||
else {
|
||||
nextSong.reset();
|
||||
setSong(nextSong);
|
||||
}
|
||||
}
|
||||
|
||||
// Check queue and load song from it if necessary
|
||||
if (currentSong == null && currentPlaylist == null && songQueue.size() > 0) {
|
||||
setSong(songQueue.poll());
|
||||
}
|
||||
|
||||
// Check if loader thread is finished and handle accordingly
|
||||
if (loaderThread != null && !loaderThread.isAlive()) {
|
||||
if (loaderThread.exception != null) {
|
||||
SongPlayer.addChatMessage("§cFailed to load song: §4" + loaderThread.exception.getMessage());
|
||||
|
@ -56,6 +86,7 @@ public class SongHandler {
|
|||
loaderThread = null;
|
||||
}
|
||||
|
||||
// Check if no song is playing and, if necessary, handle cleanup
|
||||
if (currentSong == null) {
|
||||
if (stage != null || SongPlayer.fakePlayer != null) {
|
||||
if (stage != null) {
|
||||
|
@ -63,37 +94,42 @@ public class SongHandler {
|
|||
}
|
||||
cleanup();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (stage == null) {
|
||||
stage = new Stage();
|
||||
stage.movePlayerToStagePosition();
|
||||
}
|
||||
if (Config.getConfig().showFakePlayer && SongPlayer.fakePlayer == null) {
|
||||
SongPlayer.fakePlayer = new FakePlayerEntity();
|
||||
SongPlayer.fakePlayer.copyStagePosAndPlayerLook();
|
||||
}
|
||||
if (!Config.getConfig().showFakePlayer && SongPlayer.fakePlayer != null) {
|
||||
SongPlayer.removeFakePlayer();
|
||||
}
|
||||
|
||||
checkCommandCache();
|
||||
|
||||
SongPlayer.MC.player.getAbilities().allowFlying = true;
|
||||
if (building) {
|
||||
if (tick) {
|
||||
handleBuilding();
|
||||
// Otherwise, handle song playing
|
||||
else {
|
||||
if (stage == null) {
|
||||
stage = new Stage();
|
||||
stage.movePlayerToStagePosition();
|
||||
}
|
||||
if (Config.getConfig().showFakePlayer && SongPlayer.fakePlayer == null) {
|
||||
SongPlayer.fakePlayer = new FakePlayerEntity();
|
||||
SongPlayer.fakePlayer.copyStagePosAndPlayerLook();
|
||||
}
|
||||
if (!Config.getConfig().showFakePlayer && SongPlayer.fakePlayer != null) {
|
||||
SongPlayer.removeFakePlayer();
|
||||
}
|
||||
|
||||
checkCommandCache();
|
||||
|
||||
SongPlayer.MC.player.getAbilities().allowFlying = true;
|
||||
if (building) {
|
||||
if (tick) {
|
||||
handleBuilding();
|
||||
}
|
||||
} else {
|
||||
handlePlaying(tick);
|
||||
}
|
||||
} else {
|
||||
handlePlaying(tick);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadSong(String location) {
|
||||
if (loaderThread != null) {
|
||||
SongPlayer.addChatMessage("§cAlready loading a song, cannot load another");
|
||||
} else {
|
||||
}
|
||||
else if (currentPlaylist != null) {
|
||||
SongPlayer.addChatMessage("§cCannot load a song while a playlist is playing");
|
||||
}
|
||||
else {
|
||||
try {
|
||||
loaderThread = new SongLoaderThread(location);
|
||||
SongPlayer.addChatMessage("§6Loading §3" + location + "");
|
||||
|
@ -119,6 +155,28 @@ public class SongHandler {
|
|||
SongPlayer.addChatMessage("§6Added song to queue: §3" + song.name);
|
||||
}
|
||||
|
||||
public void setPlaylist(File playlist) {
|
||||
if (loaderThread != null || currentSong != null || !songQueue.isEmpty()) {
|
||||
SongPlayer.addChatMessage("§cCannot start playing a playlist while something else is playing");
|
||||
}
|
||||
else {
|
||||
currentPlaylist = new Playlist(playlist, Config.getConfig().loopPlaylists, Config.getConfig().shufflePlaylists);
|
||||
playlistChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlaylistLoop(boolean loop) {
|
||||
if (currentPlaylist != null) {
|
||||
currentPlaylist.setLoop(loop);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlaylistShuffle(boolean shuffle) {
|
||||
if (currentPlaylist != null) {
|
||||
currentPlaylist.setShuffle(shuffle);
|
||||
}
|
||||
}
|
||||
|
||||
// Runs every tick
|
||||
private int buildStartDelay = 0;
|
||||
private int buildEndDelay = 0;
|
||||
|
@ -181,10 +239,23 @@ public class SongHandler {
|
|||
}
|
||||
}
|
||||
private void setBuildProgressDisplay() {
|
||||
MutableText text = Text.empty()
|
||||
MutableText buildText = Text.empty()
|
||||
.append(Text.literal("Building noteblocks | " ).formatted(Formatting.GOLD))
|
||||
.append(Text.literal((stage.totalMissingNotes - stage.missingNotes.size()) + "/" + stage.totalMissingNotes).formatted(Formatting.DARK_AQUA));
|
||||
ProgressDisplay.getInstance().setText(text);
|
||||
MutableText playlistText = Text.empty();
|
||||
if (currentPlaylist != null && currentPlaylist.loaded) {
|
||||
playlistText = playlistText.append(Text.literal("Playlist: ").formatted(Formatting.GOLD))
|
||||
.append(Text.literal(currentPlaylist.name).formatted(Formatting.BLUE))
|
||||
.append(Text.literal(" | ").formatted(Formatting.GOLD))
|
||||
.append(Text.literal(String.format(" (%s/%s)", currentPlaylist.songNumber, currentPlaylist.songs.size())).formatted(Formatting.DARK_AQUA));
|
||||
if (currentPlaylist.loop) {
|
||||
playlistText.append(Text.literal(" | Looping").formatted(Formatting.GOLD));
|
||||
}
|
||||
if (currentPlaylist.shuffle) {
|
||||
playlistText.append(Text.literal(" | Shuffled").formatted(Formatting.GOLD));
|
||||
}
|
||||
}
|
||||
ProgressDisplay.getInstance().setText(buildText, playlistText);
|
||||
}
|
||||
|
||||
// Runs every frame
|
||||
|
@ -244,23 +315,37 @@ public class SongHandler {
|
|||
public void setPlayProgressDisplay() {
|
||||
long currentTime = Math.min(currentSong.time, currentSong.length);
|
||||
long totalTime = currentSong.length;
|
||||
MutableText text = Text.empty()
|
||||
.append(Text.literal("Now playing ").formatted(Formatting.GOLD))
|
||||
MutableText songText = Text.empty()
|
||||
.append(Text.literal("Now playing: ").formatted(Formatting.GOLD))
|
||||
.append(Text.literal(currentSong.name).formatted(Formatting.BLUE))
|
||||
.append(Text.literal(" | ").formatted(Formatting.GOLD))
|
||||
.append(Text.literal(String.format("%s/%s", Util.formatTime(currentTime), Util.formatTime(totalTime))).formatted(Formatting.DARK_AQUA));
|
||||
if (currentSong.looping) {
|
||||
if (currentSong.loopCount > 0) {
|
||||
text.append(Text.literal(String.format(" | Loop (%d/%d)", currentSong.currentLoop, currentSong.loopCount)).formatted(Formatting.GOLD));
|
||||
songText.append(Text.literal(String.format(" | Loop (%d/%d)", currentSong.currentLoop, currentSong.loopCount)).formatted(Formatting.GOLD));
|
||||
} else {
|
||||
text.append(Text.literal(" | Looping enabled").formatted(Formatting.GOLD));
|
||||
songText.append(Text.literal(" | Looping enabled").formatted(Formatting.GOLD));
|
||||
}
|
||||
}
|
||||
ProgressDisplay.getInstance().setText(text);
|
||||
MutableText playlistText = Text.empty();
|
||||
if (currentPlaylist != null && currentPlaylist.loaded) {
|
||||
playlistText = playlistText.append(Text.literal("Playlist: ").formatted(Formatting.GOLD))
|
||||
.append(Text.literal(currentPlaylist.name).formatted(Formatting.BLUE))
|
||||
.append(Text.literal(" | ").formatted(Formatting.GOLD))
|
||||
.append(Text.literal(String.format(" (%s/%s)", currentPlaylist.songNumber, currentPlaylist.songs.size())).formatted(Formatting.DARK_AQUA));
|
||||
if (currentPlaylist.loop) {
|
||||
playlistText.append(Text.literal(" | Looping").formatted(Formatting.GOLD));
|
||||
}
|
||||
if (currentPlaylist.shuffle) {
|
||||
playlistText.append(Text.literal(" | Shuffled").formatted(Formatting.GOLD));
|
||||
}
|
||||
}
|
||||
ProgressDisplay.getInstance().setText(songText, playlistText);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
currentSong = null;
|
||||
currentPlaylist = null;
|
||||
songQueue.clear();
|
||||
stage = null;
|
||||
SongPlayer.removeFakePlayer();
|
||||
|
@ -268,6 +353,7 @@ public class SongHandler {
|
|||
|
||||
public void onNotIngame() {
|
||||
currentSong = null;
|
||||
currentPlaylist = null;
|
||||
songQueue.clear();
|
||||
}
|
||||
|
||||
|
|
256
src/main/java/com/github/hhhzzzsss/songplayer/song/Playlist.java
Normal file
256
src/main/java/com/github/hhhzzzsss/songplayer/song/Playlist.java
Normal file
|
@ -0,0 +1,256 @@
|
|||
package com.github.hhhzzzsss.songplayer.song;
|
||||
|
||||
import com.github.hhhzzzsss.songplayer.SongPlayer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Playlist {
|
||||
public static final String INDEX_FILE_NAME = "index.json";
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
public String name;
|
||||
public boolean loop = false;
|
||||
public boolean shuffle = false;
|
||||
|
||||
public List<String> index;
|
||||
public List<File> songFiles;
|
||||
public List<Song> songs = new ArrayList<>();
|
||||
public List<Integer> ordering = null;
|
||||
public int songNumber = 0;
|
||||
public boolean loaded = false;
|
||||
public ArrayList<String> songsFailedToLoad = new ArrayList<>();
|
||||
|
||||
public Playlist(File directory, boolean loop, boolean shuffle) {
|
||||
this.name = directory.getName();
|
||||
this.loop = loop;
|
||||
this.shuffle = shuffle;
|
||||
this.setShuffle(this.shuffle);
|
||||
if (directory.isDirectory()) {
|
||||
index = validateAndLoadIndex(directory);
|
||||
songFiles = index.stream()
|
||||
.map(name -> new File(directory, name))
|
||||
.collect(Collectors.toList());
|
||||
(new PlaylistLoaderThread()).start();
|
||||
} else {
|
||||
ordering = new ArrayList<>();
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class PlaylistLoaderThread extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
for (File file : songFiles) {
|
||||
SongLoaderThread slt = new SongLoaderThread(file);
|
||||
slt.run();
|
||||
if (slt.exception != null) {
|
||||
songsFailedToLoad.add(file.getName());
|
||||
} else {
|
||||
songs.add(slt.song);
|
||||
}
|
||||
}
|
||||
// Don't run loadOrdering asynchronously to avoid concurrency issues
|
||||
SongPlayer.MC.execute(() -> {
|
||||
loadOrdering(shuffle);
|
||||
loaded = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setLoop(boolean loop) {
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
public void setShuffle(boolean shuffle) {
|
||||
if (this.shuffle != shuffle && loaded) {
|
||||
loadOrdering(shuffle);
|
||||
}
|
||||
this.shuffle = shuffle;
|
||||
}
|
||||
|
||||
public void loadOrdering(boolean shouldShuffle) {
|
||||
songNumber = 0;
|
||||
ordering = new ArrayList<>();
|
||||
for (int i = 0; i < songs.size(); i++) {
|
||||
ordering.add(i);
|
||||
}
|
||||
if (shouldShuffle) {
|
||||
Collections.shuffle(ordering);
|
||||
}
|
||||
}
|
||||
|
||||
public Song getNext() {
|
||||
if (songs.isEmpty()) return null;
|
||||
|
||||
if (songNumber >= songs.size()) {
|
||||
if (loop) {
|
||||
loadOrdering(shuffle);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return songs.get(ordering.get(songNumber++));
|
||||
}
|
||||
|
||||
private static List<String> validateAndLoadIndex(File directory) {
|
||||
List<String> songNames = getSongFiles(directory).stream()
|
||||
.map(File::getName)
|
||||
.collect(Collectors.toList());
|
||||
if (!getIndexFile(directory).exists()) {
|
||||
saveIndexSilently(directory, songNames);
|
||||
return songNames;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
List<String> index = loadIndex(directory);
|
||||
boolean modified = false;
|
||||
|
||||
// Remove nonexistent songs from index
|
||||
HashSet<String> songSet = new HashSet<>(songNames);
|
||||
ListIterator<String> indexItr = index.listIterator();
|
||||
while (indexItr.hasNext()) {
|
||||
if (!songSet.contains(indexItr.next())) {
|
||||
indexItr.remove();
|
||||
modified=true;
|
||||
}
|
||||
}
|
||||
|
||||
// Add songs that aren't in the index to the index
|
||||
HashSet<String> indexSet = new HashSet<>(index);
|
||||
for (String name : songNames) {
|
||||
if (!indexSet.contains(name)) {
|
||||
index.add(name);
|
||||
indexSet.add(name);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
saveIndexSilently(directory, index);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
catch (Exception e) {
|
||||
return songNames;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static File getIndexFile(File directory) {
|
||||
return new File(directory, INDEX_FILE_NAME);
|
||||
}
|
||||
|
||||
public static List<File> getSongFiles(File directory) {
|
||||
File[] files = directory.listFiles();
|
||||
if (files == null) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.stream(files)
|
||||
.filter(file -> !file.getName().equals(INDEX_FILE_NAME))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<String> loadIndex(File directory) throws IOException {
|
||||
File indexFile = getIndexFile(directory);
|
||||
FileInputStream fis = new FileInputStream(indexFile);
|
||||
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
|
||||
BufferedReader reader = new BufferedReader(isr);
|
||||
Type type = new TypeToken<ArrayList<String>>(){}.getType();
|
||||
List<String> index = gson.fromJson(reader, type);
|
||||
reader.close();
|
||||
return index;
|
||||
}
|
||||
|
||||
private static void saveIndex(File directory, List<String> index) throws IOException {
|
||||
File indexFile = getIndexFile(directory);
|
||||
FileOutputStream fos = new FileOutputStream(indexFile);
|
||||
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
|
||||
BufferedWriter writer = new BufferedWriter(osw);
|
||||
writer.write(gson.toJson(index));
|
||||
writer.close();
|
||||
}
|
||||
|
||||
private static void saveIndexSilently(File directory, List<String> index) {
|
||||
try {
|
||||
saveIndex(directory, index);
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> listSongs(File directory) throws IOException {
|
||||
if (!directory.exists()) {
|
||||
throw new IOException("Playlist does not exist");
|
||||
}
|
||||
return validateAndLoadIndex(directory);
|
||||
}
|
||||
|
||||
public static void createPlaylist(String playlist) {
|
||||
File playlistDir = new File(SongPlayer.PLAYLISTS_DIR, playlist);
|
||||
playlistDir.mkdir();
|
||||
}
|
||||
|
||||
public static void addSong(File directory, File songFile) throws IOException {
|
||||
if (!directory.exists()) {
|
||||
throw new IOException("Playlist does not exist");
|
||||
}
|
||||
if (!songFile.exists()) {
|
||||
throw new IOException("Could not find specified song");
|
||||
}
|
||||
|
||||
List<String> index = validateAndLoadIndex(directory);
|
||||
if (index.contains(songFile.getName())) {
|
||||
throw new IOException("Playlist already contains a song by this name");
|
||||
}
|
||||
Files.copy( songFile.toPath(), (new File(directory,songFile.getName())).toPath() );
|
||||
index.add(songFile.getName());
|
||||
saveIndex(directory, index);
|
||||
}
|
||||
|
||||
public static void removeSong(File directory, String songName) throws IOException {
|
||||
if (!directory.exists()) {
|
||||
throw new IOException("Playlist does not exist");
|
||||
}
|
||||
File songFile = new File(directory, songName);
|
||||
if (!songFile.exists()) {
|
||||
throw new IOException("Playlist does not contain a song by this name");
|
||||
}
|
||||
|
||||
List<String> index = validateAndLoadIndex(directory);
|
||||
songFile.delete();
|
||||
index.remove(songName);
|
||||
saveIndex(directory, index);
|
||||
}
|
||||
|
||||
public static void deletePlaylist(File directory) throws IOException {
|
||||
if (!directory.exists()) {
|
||||
throw new IOException("Playlist does not exist");
|
||||
}
|
||||
Files.walk(directory.toPath())
|
||||
.map(Path::toFile)
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.forEach(File::delete);
|
||||
}
|
||||
|
||||
public static void renameSong(File directory, String oldName, String newName) throws IOException {
|
||||
List<String> index = validateAndLoadIndex(directory);
|
||||
int pos = index.indexOf(oldName);
|
||||
if (pos < 0) {
|
||||
throw new IOException("Song not found in playlist");
|
||||
}
|
||||
(new File(directory, oldName)).renameTo(new File(directory, newName));
|
||||
index.set(pos, newName);
|
||||
saveIndex(directory, index);
|
||||
}
|
||||
}
|
|
@ -55,6 +55,12 @@ public class Song {
|
|||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
paused = true;
|
||||
setTime(0);
|
||||
loopCount = 0;
|
||||
}
|
||||
|
||||
public void setTime(long t) {
|
||||
time = t;
|
||||
startTime = System.currentTimeMillis() - time;
|
||||
|
|
|
@ -40,6 +40,10 @@ public class SongLoaderThread extends Thread{
|
|||
throw new IOException("Could not find song: " + location);
|
||||
}
|
||||
}
|
||||
|
||||
public SongLoaderThread(File file) {
|
||||
this.songPath = file;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
|
|
Loading…
Reference in a new issue