songplayer music

This commit is contained in:
Chayapak 2023-10-14 09:49:35 +07:00
parent b35218fef9
commit 364afb0879
3 changed files with 189 additions and 0 deletions

View file

@ -79,6 +79,7 @@ public class MusicCommand extends Command {
return switch (action) {
case "play", "playurl", "playnbs", "playnbsurl" -> play(context);
case "playfromitem", "playitem" -> playFromItem(context);
case "playsongplayer" -> playSongPlayer(context);
case "stop" -> stop(context);
case "loop" -> loop(context);
case "list" -> list(context);
@ -256,6 +257,67 @@ public class MusicCommand extends Command {
return null;
}
public Component playSongPlayer (CommandContext context) throws CommandException {
// dupe codes ??
final Bot bot = context.bot;
final CompletableFuture<CompoundTag> future = bot.core.runTracked(
"minecraft:data get entity " +
UUIDUtilities.selector(context.sender.profile.getId()) +
" SelectedItem.tag.SongItemData.SongData"
);
if (future == null) {
throw new CommandException(Component.text("There was an error while getting your data"));
}
future.thenApply(tags -> {
if (!tags.contains("LastOutput") || !(tags.get("LastOutput") instanceof StringTag)) return tags;
final StringTag lastOutput = tags.get("LastOutput");
final Component output = GsonComponentSerializer.gson().deserialize(lastOutput.getValue());
final List<Component> children = output.children();
if (
!children.isEmpty() &&
!children.get(0).children().isEmpty() &&
((TranslatableComponent) children.get(0).children().get(0))
.key()
.equals("arguments.nbtpath.nothing_found")
) {
context.sendOutput(Component.text("Player has no SongItemData -> SongData NBT tag in the selected item").color(NamedTextColor.RED));
return tags;
}
final String value = ComponentUtilities.stringify(((TranslatableComponent) children.get(0)).args().get(1));
if (!value.startsWith("\"") && !value.endsWith("\"") && !value.startsWith("'") && !value.endsWith("'")) {
context.sendOutput(Component.text("NBT is not a string").color(NamedTextColor.RED));
return tags;
}
try {
bot.music.loadSong(
Base64.getDecoder().decode(
value
.substring(1)
.substring(0, value.length() - 2)
),
context.sender
);
} catch (IllegalArgumentException e) {
context.sendOutput(Component.text("Invalid song data in the selected item").color(NamedTextColor.RED));
}
return tags;
});
return null;
}
public Component stop (CommandContext context) {
final Bot bot = context.bot;
bot.music.stopPlaying();

View file

@ -25,6 +25,7 @@ public class SongLoaderThread extends Thread {
converters.add(new MidiConverter());
converters.add(new NBSConverter());
converters.add(new TextFileConverter());
converters.add(new SongPlayerConverter());
}
public final String fileName;

View file

@ -0,0 +1,126 @@
package land.chipmunk.chayapak.chomens_bot.song;
import land.chipmunk.chayapak.chomens_bot.Bot;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;
// Author: hhhzzzsss (i ported it from songplayer)
public class SongPlayerConverter implements Converter {
public static final byte[] FILE_TYPE_SIGNATURE = {-53, 123, -51, -124, -122, -46, -35, 38};
public static final long MAX_UNCOMPRESSED_SIZE = 50 * 1024 * 1024;
@Override
public Song getSongFromBytes(byte[] bytes, String fileName, Bot bot) throws Exception {
InputStream is = new LimitedSizeInputStream(new GZIPInputStream(new ByteArrayInputStream(bytes)), MAX_UNCOMPRESSED_SIZE);
bytes = is.readAllBytes();
is.close();
ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.order(ByteOrder.LITTLE_ENDIAN);
for (byte b : FILE_TYPE_SIGNATURE) {
if (b != buffer.get()) {
throw new IOException("Invalid file type signature");
}
}
byte version = buffer.get();
// Currently on format version 1
if (version != 1) {
throw new IOException("Unsupported format version!");
}
long songLength = buffer.getLong();
String songName = getString(buffer, bytes.length);
int loop = buffer.get() & 0xFF;
int loopCount = buffer.get() & 0xFF;
long loopPosition = buffer.getLong();
Song song = new Song(fileName, bot, !songName.trim().isEmpty() ? songName : null, null, null, null, false);
song.length = songLength;
// song.looping = loop > 0;
// song.loopCount = loopCount;
song.loopPosition = loopPosition == 0 ? 200 : loopPosition;
long time = 0;
while (true) {
int noteId = buffer.getShort();
if (noteId >= 0 && noteId < 400) {
time += getVarLong(buffer);
song.add(new Note(Instrument.fromId(noteId / 25), noteId % 25, noteId % 25, 1, time, -1, 100));
}
else if ((noteId & 0xFFFF) == 0xFFFF) {
break;
}
else {
throw new IOException("Song contains invalid note id of " + noteId);
}
}
return song;
}
private static String getString(ByteBuffer buffer, int maxSize) throws IOException {
int length = buffer.getInt();
if (length > maxSize) {
throw new IOException("String is too large");
}
byte[] arr = new byte[length];
buffer.get(arr, 0, length);
return new String(arr, StandardCharsets.UTF_8);
}
private static long getVarLong(ByteBuffer buffer) {
long val = 0;
long mult = 1;
int flag = 1;
while (flag != 0) {
int b = buffer.get() & 0xFF;
val += (b & 0x7F) * mult;
mult <<= 7;
flag = b >>> 7;
}
return val;
}
private static class LimitedSizeInputStream extends InputStream {
private final InputStream original;
private final long maxSize;
private long total;
public LimitedSizeInputStream(InputStream original, long maxSize) {
this.original = original;
this.maxSize = maxSize;
}
@Override
public int read() throws IOException {
int i = original.read();
if (i>=0) incrementCounter(1);
return i;
}
@Override
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte b[], int off, int len) throws IOException {
int i = original.read(b, off, len);
if (i>=0) incrementCounter(i);
return i;
}
private void incrementCounter(int size) throws IOException {
total += size;
if (total>maxSize) throw new IOException("Input stream exceeded maximum size of " + maxSize + " bytes");
}
}
}