Switched Java version to 8 so the mod works on more Minecraft versions

Made the loading of songs multithreaded, decreasing startup time of the client
Fixed a bug where incorrect instruments were getting played
Fixed a bug where the blocks overlay was unable to be closed
Fixed a bug where the mod would continue playing the song after switching worlds
Fixed a bug where note blocks with a block above them were seen as a valid note block
This commit is contained in:
Semmieboy YT 2022-03-06 03:10:35 +01:00
parent 25aeb857cf
commit afe3e78b0f
10 changed files with 210 additions and 139 deletions

View File

@ -33,7 +33,7 @@ processResources {
}
}
def targetJavaVersion = 17
def targetJavaVersion = 8
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly

View File

@ -6,7 +6,7 @@ minecraft_version=1.18.1
yarn_mappings=1.18.1+build.22
loader_version=0.13.2
# Mod Properties
mod_version=1.0.0
mod_version=1.0.1
maven_group=semmieboy_yt
archives_base_name=disc_jockey
# Dependencies

View File

@ -15,7 +15,7 @@ public class BinaryReader {
}
public int readInt() throws IOException {
return buffer.clear().put(readBytes(Integer.BYTES)).rewind().getInt();
return ((ByteBuffer)((ByteBuffer)buffer.clear()).put(readBytes(Integer.BYTES)).rewind()).getInt();
}
public long readUInt() throws IOException {
@ -27,7 +27,7 @@ public class BinaryReader {
}
public short readShort() throws IOException {
return buffer.clear().put(readBytes(2)).rewind().getShort();
return ((ByteBuffer)((ByteBuffer)buffer.clear()).put(readBytes(Short.BYTES)).rewind()).getShort();
}
public String readString() throws IOException {
@ -35,7 +35,7 @@ public class BinaryReader {
}
public float readFloat() throws IOException {
return buffer.clear().put(readBytes(4)).rewind().getFloat();
return ((ByteBuffer)((ByteBuffer)buffer.clear()).put(readBytes(Float.BYTES)).rewind()).getFloat();
}
/*private int getStringLength() throws IOException {
@ -60,6 +60,8 @@ public class BinaryReader {
}
public byte[] readBytes(int length) throws IOException {
return in.readNBytes(length);
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++) bytes[i] = readByte();
return bytes;
}
}

View File

@ -1,7 +1,9 @@
package semmieboy_yt.disc_jockey;
import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.TranslatableText;
import semmieboy_yt.disc_jockey.gui.screen.DiscJockeyScreen;
import static net.fabricmc.fabric.api.client.command.v1.ClientCommandManager.literal;
@ -11,13 +13,22 @@ public class DiscjockeyCommand {
ClientCommandManager.DISPATCHER.register(
literal("discjockey")
.executes(context -> {
MinecraftClient client = context.getSource().getClient();
client.send(() -> client.setScreen(new DiscJockeyScreen()));
FabricClientCommandSource source = context.getSource();
if (SongLoader.loadingSongs) {
source.sendError(new TranslatableText(Main.MOD_ID+".still_loading"));
} else {
MinecraftClient client = source.getClient();
client.send(() -> client.setScreen(new DiscJockeyScreen()));
}
return 1;
})
.then(literal("reload")
.executes(context -> {
SongLoader.loadSongs();
if (SongLoader.loadingSongs) {
context.getSource().sendError(new TranslatableText(Main.MOD_ID+".still_loading"));
} else {
SongLoader.loadSongs();
}
return 1;
})
)

View File

@ -5,6 +5,8 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import semmieboy_yt.disc_jockey.gui.hud.BlocksOverlay;
@ -30,6 +32,18 @@ public class Main implements ClientModInitializer {
SongLoader.loadSongs();
ClientTickEvents.START_CLIENT_TICK.register(new ClientTickEvents.StartTick() {
private ClientWorld prevWorld;
@Override
public void onStartTick(MinecraftClient client) {
if (prevWorld != client.world) {
PREVIEWER.stop();
SONG_PLAYER.stop();
}
prevWorld = client.world;
}
});
ClientTickEvents.START_WORLD_TICK.register(world -> {
for (ClientTickEvents.StartWorldTick listener : TICK_LISTENERS) listener.onStartTick(world);
});

View File

@ -4,31 +4,67 @@ import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.enums.Instrument;
import java.util.Map;
import java.util.HashMap;
public record Note(Instrument instrument, byte note) {
public static final Map<Instrument, Block> INSTRUMENT_BLOCKS = Map.ofEntries(
Map.entry(Instrument.HARP, Blocks.AIR),
Map.entry(Instrument.BASEDRUM, Blocks.STONE),
Map.entry(Instrument.SNARE, Blocks.SAND),
Map.entry(Instrument.HAT, Blocks.GLASS),
Map.entry(Instrument.BASS, Blocks.OAK_PLANKS),
Map.entry(Instrument.FLUTE, Blocks.CLAY),
Map.entry(Instrument.BELL, Blocks.GOLD_BLOCK),
Map.entry(Instrument.GUITAR, Blocks.WHITE_WOOL),
Map.entry(Instrument.CHIME, Blocks.PACKED_ICE),
Map.entry(Instrument.XYLOPHONE, Blocks.BONE_BLOCK),
Map.entry(Instrument.IRON_XYLOPHONE, Blocks.IRON_BLOCK),
Map.entry(Instrument.COW_BELL, Blocks.SOUL_SAND),
Map.entry(Instrument.DIDGERIDOO, Blocks.PUMPKIN),
Map.entry(Instrument.BIT, Blocks.EMERALD_BLOCK),
Map.entry(Instrument.BANJO, Blocks.HAY_BLOCK),
Map.entry(Instrument.PLING, Blocks.GLOWSTONE)
);
public class Note {
public static final HashMap<Instrument, Block> INSTRUMENT_BLOCKS = new HashMap<>();
public static final byte LAYER_SHIFT = Short.SIZE;
public static final byte INSTRUMENT_SHIFT = Short.SIZE * 2;
public static final byte NOTE_SHIFT = Short.SIZE * 2 + Byte.SIZE;
public static final Instrument[] INSTRUMENTS = Instrument.values();
public static final Instrument[] INSTRUMENTS = new Instrument[] {
Instrument.HARP,
Instrument.BASS,
Instrument.BASEDRUM,
Instrument.SNARE,
Instrument.HAT,
Instrument.GUITAR,
Instrument.FLUTE,
Instrument.BELL,
Instrument.CHIME,
Instrument.XYLOPHONE,
Instrument.IRON_XYLOPHONE,
Instrument.COW_BELL,
Instrument.DIDGERIDOO,
Instrument.BIT,
Instrument.BANJO,
Instrument.PLING
};
static {
INSTRUMENT_BLOCKS.put(Instrument.HARP, Blocks.AIR);
INSTRUMENT_BLOCKS.put(Instrument.BASEDRUM, Blocks.STONE);
INSTRUMENT_BLOCKS.put(Instrument.SNARE, Blocks.SAND);
INSTRUMENT_BLOCKS.put(Instrument.HAT, Blocks.GLASS);
INSTRUMENT_BLOCKS.put(Instrument.BASS, Blocks.OAK_PLANKS);
INSTRUMENT_BLOCKS.put(Instrument.FLUTE, Blocks.CLAY);
INSTRUMENT_BLOCKS.put(Instrument.BELL, Blocks.GOLD_BLOCK);
INSTRUMENT_BLOCKS.put(Instrument.GUITAR, Blocks.WHITE_WOOL);
INSTRUMENT_BLOCKS.put(Instrument.CHIME, Blocks.PACKED_ICE);
INSTRUMENT_BLOCKS.put(Instrument.XYLOPHONE, Blocks.BONE_BLOCK);
INSTRUMENT_BLOCKS.put(Instrument.IRON_XYLOPHONE, Blocks.IRON_BLOCK);
INSTRUMENT_BLOCKS.put(Instrument.COW_BELL, Blocks.SOUL_SAND);
INSTRUMENT_BLOCKS.put(Instrument.DIDGERIDOO, Blocks.PUMPKIN);
INSTRUMENT_BLOCKS.put(Instrument.BIT, Blocks.EMERALD_BLOCK);
INSTRUMENT_BLOCKS.put(Instrument.BANJO, Blocks.HAY_BLOCK);
INSTRUMENT_BLOCKS.put(Instrument.PLING, Blocks.GLOWSTONE);
}
public final Instrument instrument;
public final byte note;
public Note(Instrument instrument, byte note) {
this.instrument = instrument;
this.note = note;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Note) {
Note note = (Note)obj;
return note.note == this.note && note.instrument == instrument;
}
return false;
}
}

View File

@ -4,98 +4,100 @@ import semmieboy_yt.disc_jockey.gui.SongListWidget;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
public class SongLoader {
public static final ArrayList<Song> SONGS = new ArrayList<>();
public static volatile boolean loadingSongs;
public static void loadSongs() {
SONGS.clear();
for (File file : Main.songsFolder.listFiles()) {
if (file.isFile()) {
try {
BinaryReader reader = new BinaryReader(new FileInputStream(file));
Song song = new Song();
if (loadingSongs) return;
new Thread(() -> {
loadingSongs = true;
SONGS.clear();
for (File file : Main.songsFolder.listFiles()) {
if (file.isFile()) {
try {
BinaryReader reader = new BinaryReader(new FileInputStream(file));
Song song = new Song();
song.fileName = file.getName();
song.fileName = file.getName();
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();
song.author = reader.readString();
song.originalAuthor = reader.readString();
song.description = reader.readString();
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();
if (newFormat) {
song.loop = reader.readByte();
song.maxLoopCount = reader.readByte();
song.loopStartTick = reader.readShort();
}
song.entry = new SongListWidget.SongEntry(song.name.isBlank() ? song.fileName : song.name+" ("+song.fileName+")", SONGS.size());
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;
boolean newFormat = song.length == 0;
if (newFormat) {
song.formatVersion = reader.readByte();
song.vanillaInstrumentCount = reader.readByte();
song.length = reader.readShort();
}
}
SONGS.add(song);
} catch (FileNotFoundException ignored) {
// Won't be thrown
} catch (IOException exception) {
Main.LOGGER.error("Unable to read song "+file.getName(), exception);
song.height = reader.readShort();
song.name = reader.readString();
song.author = reader.readString();
song.originalAuthor = reader.readString();
song.description = reader.readString();
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();
if (newFormat) {
song.loop = reader.readByte();
song.maxLoopCount = reader.readByte();
song.loopStartTick = reader.readShort();
}
song.entry = new SongListWidget.SongEntry(song.name.replaceAll("\\s", "").isEmpty() ? song.fileName : song.name+" ("+song.fileName+")", SONGS.size());
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);
}
}
}
}
loadingSongs = false;
}).start();
}
}

View File

@ -67,10 +67,10 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
BlockPos blockPos = new BlockPos(pos);
if (playerPos.squaredDistanceTo(pos) < 4.5 * 4.5) {
BlockState blockState = world.getBlockState(blockPos);
if (blockState.isOf(Blocks.NOTE_BLOCK)) {
if (blockState.isOf(Blocks.NOTE_BLOCK) && world.isAir(blockPos.up())) {
for (Note note : song.uniqueNotes) {
if (!capturedNotes.contains(note) && blockState.get(Properties.INSTRUMENT) == note.instrument()) {
getNotes(note.instrument()).put(note.note(), blockPos);
if (!capturedNotes.contains(note) && blockState.get(Properties.INSTRUMENT) == note.instrument) {
getNotes(note.instrument).put(note.note, blockPos);
capturedNotes.add(note);
break;
}
@ -89,7 +89,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
HashMap<Block, Integer> missing = new HashMap<>();
for (Note note : missingNotes) {
Block block = Note.INSTRUMENT_BLOCKS.get(note.instrument());
Block block = Note.INSTRUMENT_BLOCKS.get(note.instrument);
Integer got = missing.get(block);
if (got == null) got = 0;
missing.put(block, got + 1);
@ -107,11 +107,11 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
MinecraftClient client = MinecraftClient.getInstance();
int tuneAmount = 0;
for (Note note : song.uniqueNotes) {
BlockPos blockPos = noteBlocks.get(note.instrument()).get(note.note());
BlockPos blockPos = noteBlocks.get(note.instrument).get(note.note);
BlockState blockState = world.getBlockState(blockPos);
if (blockState.contains(Properties.NOTE)) {
if (blockState.get(Properties.NOTE) != note.note()) {
if (blockState.get(Properties.NOTE) != note.note) {
if (client.player.getEyePos().squaredDistanceTo(Vec3d.ofCenter(blockPos, 0.5)) >= 4.5 * 4.5) {
stop();
client.inGameHud.getChatHud().addMessage(new TranslatableText(Main.MOD_ID+".player.to_far").formatted(Formatting.RED));

View File

@ -68,36 +68,41 @@ public class DiscJockeyScreen extends Screen {
addDrawableChild(previewButton);
addDrawableChild(new ButtonWidget(width / 2 + 60, height - 61, 100, 20, new TranslatableText(Main.MOD_ID+".screen.blocks"), button -> {
SongListWidget.SongEntry entry = songListWidget.getSelectedOrNull();
if (entry != null) {
client.setScreen(null);
Song song = SongLoader.SONGS.get(entry.index);
if (BlocksOverlay.itemStacks == null) {
SongListWidget.SongEntry entry = songListWidget.getSelectedOrNull();
if (entry != null) {
client.setScreen(null);
Song song = SongLoader.SONGS.get(entry.index);
BlocksOverlay.itemStacks = new ItemStack[0];
BlocksOverlay.amounts = new int[0];
BlocksOverlay.amountOfNoteBlocks = song.uniqueNotes.size();
BlocksOverlay.itemStacks = new ItemStack[0];
BlocksOverlay.amounts = new int[0];
BlocksOverlay.amountOfNoteBlocks = song.uniqueNotes.size();
for (Note note : song.uniqueNotes) {
ItemStack itemStack = Note.INSTRUMENT_BLOCKS.get(note.instrument()).asItem().getDefaultStack();
int index = -1;
for (Note note : song.uniqueNotes) {
ItemStack itemStack = Note.INSTRUMENT_BLOCKS.get(note.instrument).asItem().getDefaultStack();
int index = -1;
for (int i = 0; i < BlocksOverlay.itemStacks.length; i++) {
if (BlocksOverlay.itemStacks[i].getItem() == itemStack.getItem()) {
index = i;
break;
for (int i = 0; i < BlocksOverlay.itemStacks.length; i++) {
if (BlocksOverlay.itemStacks[i].getItem() == itemStack.getItem()) {
index = i;
break;
}
}
if (index == -1) {
BlocksOverlay.itemStacks = Arrays.copyOf(BlocksOverlay.itemStacks, BlocksOverlay.itemStacks.length + 1);
BlocksOverlay.amounts = Arrays.copyOf(BlocksOverlay.amounts, BlocksOverlay.amounts.length + 1);
BlocksOverlay.itemStacks[BlocksOverlay.itemStacks.length - 1] = itemStack;
BlocksOverlay.amounts[BlocksOverlay.amounts.length - 1] = 1;
} else {
BlocksOverlay.amounts[index] = BlocksOverlay.amounts[index] + 1;
}
}
if (index == -1) {
BlocksOverlay.itemStacks = Arrays.copyOf(BlocksOverlay.itemStacks, BlocksOverlay.itemStacks.length + 1);
BlocksOverlay.amounts = Arrays.copyOf(BlocksOverlay.amounts, BlocksOverlay.amounts.length + 1);
BlocksOverlay.itemStacks[BlocksOverlay.itemStacks.length - 1] = itemStack;
BlocksOverlay.amounts[BlocksOverlay.amounts.length - 1] = 1;
} else {
BlocksOverlay.amounts[index] = BlocksOverlay.amounts[index] + 1;
}
}
} else {
BlocksOverlay.itemStacks = null;
client.setScreen(null);
}
}));

View File

@ -10,5 +10,6 @@
"disc_jockey.screen.search": "Search For Songs",
"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"
"disc_jockey.player.to_far": "You went to far away",
"disc_jockey.still_loading": "The songs are still loading"
}