Merge pull request #21 from EnderKill98/feature/bugfixes-and-improvements
Bugfixes and improvements
This commit is contained in:
commit
88800c055f
@ -9,6 +9,7 @@ import java.util.ArrayList;
|
||||
@me.shedaniel.autoconfig.annotation.Config.Gui.Background("textures/block/note_block.png")
|
||||
public class Config implements ConfigData {
|
||||
public boolean hideWarning;
|
||||
@ConfigEntry.Gui.Tooltip(count = 2) public boolean disableAsyncPlayback;
|
||||
@ConfigEntry.Gui.Excluded @ConfigEntry.Gui.Tooltip(count = 2) public boolean monoNoteBlocks;
|
||||
|
||||
@ConfigEntry.Gui.Excluded
|
||||
|
@ -5,19 +5,35 @@ import com.mojang.brigadier.arguments.FloatArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.block.enums.Instrument;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.command.CommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import semmiedev.disc_jockey.gui.screen.DiscJockeyScreen;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
|
||||
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
|
||||
|
||||
public class DiscjockeyCommand {
|
||||
|
||||
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> commandDispatcher) {
|
||||
final ArrayList<String> instrumentNames = new ArrayList<>();
|
||||
for (Instrument instrument : Instrument.values()) {
|
||||
instrumentNames.add(instrument.toString().toLowerCase());
|
||||
}
|
||||
final ArrayList<String> instrumentNamesAndAll = new ArrayList<>(instrumentNames);
|
||||
instrumentNamesAndAll.add("all");
|
||||
final ArrayList<String> instrumentNamesAndNothing = new ArrayList<>(instrumentNames);
|
||||
instrumentNamesAndNothing.add("nothing");
|
||||
|
||||
commandDispatcher.register(
|
||||
literal("discjockey")
|
||||
.executes(context -> {
|
||||
@ -98,7 +114,130 @@ public class DiscjockeyCommand {
|
||||
}
|
||||
})
|
||||
)
|
||||
.then(literal("remapInstruments")
|
||||
.executes(context -> {
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".instrument_info"));
|
||||
return 0;
|
||||
})
|
||||
.then(literal("map")
|
||||
.then(argument("originalInstrument", StringArgumentType.word())
|
||||
.suggests((context, builder) -> CommandSource.suggestMatching(instrumentNamesAndAll, builder))
|
||||
.then(argument("newInstrument", StringArgumentType.word())
|
||||
.suggests((context, builder) -> CommandSource.suggestMatching(instrumentNamesAndNothing, builder))
|
||||
.executes(context -> {
|
||||
String originalInstrumentStr = StringArgumentType.getString(context, "originalInstrument");
|
||||
String newInstrumentStr = StringArgumentType.getString(context, "newInstrument");
|
||||
@Nullable Instrument originalInstrument = null, newInstrument = null;
|
||||
for(Instrument maybeInstrument : Instrument.values()) {
|
||||
if(maybeInstrument.toString().equalsIgnoreCase(originalInstrumentStr)) {
|
||||
originalInstrument = maybeInstrument;
|
||||
}
|
||||
if(maybeInstrument.toString().equalsIgnoreCase(newInstrumentStr)) {
|
||||
newInstrument = maybeInstrument;
|
||||
}
|
||||
}
|
||||
|
||||
if(originalInstrument == null && !originalInstrumentStr.equalsIgnoreCase("all")) {
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".invalid_instrument", originalInstrumentStr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(newInstrument == null && !newInstrumentStr.equalsIgnoreCase("nothing")) {
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".invalid_instrument", newInstrumentStr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// (originalInstrument == null) means: all instruments
|
||||
// (newInstrument == null) means: nothing (represented by null in hashmap, so no special handling below)
|
||||
|
||||
if(originalInstrument == null) {
|
||||
// All instruments
|
||||
for(Instrument instrument : Instrument.values()) {
|
||||
Main.SONG_PLAYER.instrumentMap.put(instrument, newInstrument);
|
||||
}
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".instrument_mapped_all", newInstrumentStr.toLowerCase()));
|
||||
}else {
|
||||
Main.SONG_PLAYER.instrumentMap.put(originalInstrument, newInstrument);
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".instrument_mapped", originalInstrumentStr.toLowerCase(), newInstrumentStr.toLowerCase()));
|
||||
}
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(literal("unmap")
|
||||
.then(argument("instrument", StringArgumentType.word())
|
||||
.suggests((context, builder) -> CommandSource.suggestMatching(instrumentNames, builder))
|
||||
.executes(context -> {
|
||||
String instrumentStr = StringArgumentType.getString(context, "instrument");
|
||||
|
||||
Instrument instrument = null;
|
||||
for(Instrument maybeInstrument : Instrument.values()) {
|
||||
if(maybeInstrument.toString().equalsIgnoreCase(instrumentStr)) {
|
||||
instrument = maybeInstrument;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(instrument == null) {
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".invalid_instrument", instrumentStr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Main.SONG_PLAYER.instrumentMap.remove(instrument);
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".instrument_unmapped", instrumentStr.toLowerCase()));
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(literal("show")
|
||||
.executes(context -> {
|
||||
if(Main.SONG_PLAYER.instrumentMap.isEmpty()) {
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".no_mapped_instruments"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
StringBuilder maps = new StringBuilder();
|
||||
for(Map.Entry<Instrument, Instrument> entry : Main.SONG_PLAYER.instrumentMap.entrySet()) {
|
||||
if(maps.length() > 0) {
|
||||
maps.append(", ");
|
||||
}
|
||||
maps
|
||||
.append(entry.getKey().toString().toLowerCase())
|
||||
.append("->")
|
||||
.append(entry.getValue() == null ? "nothing" : entry.getValue().toString().toLowerCase());
|
||||
}
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".mapped_instruments", maps.toString()));
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
.then(literal("clear")
|
||||
.executes(context -> {
|
||||
Main.SONG_PLAYER.instrumentMap.clear();
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".instrument_maps_cleared"));
|
||||
return 1;
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
.then(literal("loop")
|
||||
.executes(context -> {
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".loop_status", Main.SONG_PLAYER.loopSong ? "yes" : "no"));
|
||||
return 1;
|
||||
})
|
||||
.then(literal("yes")
|
||||
.executes(context -> {
|
||||
Main.SONG_PLAYER.loopSong = true;
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".loop_enabled"));
|
||||
return 1;
|
||||
}))
|
||||
.then(literal("no")
|
||||
.executes(context -> {
|
||||
Main.SONG_PLAYER.loopSong = false;
|
||||
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".loop_disabled"));
|
||||
return 1;
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.*;
|
||||
import net.minecraft.world.GameMode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -60,13 +61,19 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
private int tuneInitialUntunedBlocks = -1;
|
||||
private HashMap<BlockPos, Pair<Integer, Long>> notePredictions = new HashMap<>();
|
||||
public boolean didSongReachEnd = false;
|
||||
public boolean loopSong = false;
|
||||
|
||||
public SongPlayer() {
|
||||
Main.TICK_LISTENERS.add(this);
|
||||
}
|
||||
|
||||
public @NotNull HashMap<Instrument, Instrument> instrumentMap = new HashMap<>(); // Toy
|
||||
public @NotNull HashMap<Instrument, @Nullable Instrument> instrumentMap = new HashMap<>(); // Toy
|
||||
public synchronized void startPlaybackThread() {
|
||||
if(Main.config.disableAsyncPlayback) {
|
||||
playbackThread = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.playbackThread = new Thread(() -> {
|
||||
Thread ownThread = this.playbackThread;
|
||||
while(ownThread == this.playbackThread) {
|
||||
@ -150,7 +157,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
GameMode gameMode = client.interactionManager == null ? null : client.interactionManager.getCurrentGameMode();
|
||||
// In the best case, gameMode would only be queried in sync Ticks, no here
|
||||
if (gameMode == null || !gameMode.isSurvivalLike()) {
|
||||
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.invalid_game_mode", gameMode.getTranslatableName()).formatted(Formatting.RED));
|
||||
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.invalid_game_mode", gameMode == null ? "unknown" : gameMode.getTranslatableName()).formatted(Formatting.RED));
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
@ -158,7 +165,12 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
long note = song.notes[index];
|
||||
final long now = System.currentTimeMillis();
|
||||
if ((short)note <= Math.round(tick)) {
|
||||
BlockPos blockPos = noteBlocks.get(Note.INSTRUMENTS[(byte)(note >> Note.INSTRUMENT_SHIFT)]).get((byte)(note >> Note.NOTE_SHIFT));
|
||||
@Nullable BlockPos blockPos = noteBlocks.get(Note.INSTRUMENTS[(byte)(note >> Note.INSTRUMENT_SHIFT)]).get((byte)(note >> Note.NOTE_SHIFT));
|
||||
if(blockPos == null) {
|
||||
// Instrument got likely mapped to "nothing". Skip it
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
if (!canInteractWith(client.player, blockPos)) {
|
||||
stop();
|
||||
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.to_far").formatted(Formatting.RED));
|
||||
@ -200,6 +212,9 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
if (index >= song.notes.length) {
|
||||
stop();
|
||||
didSongReachEnd = true;
|
||||
if(loopSong) {
|
||||
start(song);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@ -264,6 +279,13 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
if(!instrumentMap.isEmpty()) {
|
||||
HashMap<Instrument, ArrayList<BlockPos>> newNoteblocksForInstrument = new HashMap<>();
|
||||
for(Instrument orig : noteblocksForInstrument.keySet()) {
|
||||
Instrument mappedInstrument = instrumentMap.getOrDefault(orig, orig);
|
||||
if(mappedInstrument == null) {
|
||||
// Instrument got likely mapped to "nothing"
|
||||
newNoteblocksForInstrument.put(orig, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
newNoteblocksForInstrument.put(orig, noteblocksForInstrument.getOrDefault(instrumentMap.getOrDefault(orig, orig), new ArrayList<>()));
|
||||
}
|
||||
noteblocksForInstrument = newNoteblocksForInstrument;
|
||||
@ -273,6 +295,12 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
ArrayList<Note> capturedNotes = new ArrayList<>();
|
||||
for(Note note : song.uniqueNotes) {
|
||||
ArrayList<BlockPos> availableBlocks = noteblocksForInstrument.get(note.instrument);
|
||||
if(availableBlocks == null) {
|
||||
// Note was mapped to "nothing". Pretend it got captured, but just ignore it
|
||||
capturedNotes.add(note);
|
||||
getNotes(note.instrument).put(note.note, null);
|
||||
continue;
|
||||
}
|
||||
BlockPos bestBlockPos = null;
|
||||
int bestBlockTuningSteps = Integer.MAX_VALUE;
|
||||
for(BlockPos blockPos : availableBlocks) {
|
||||
@ -301,7 +329,9 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
|
||||
HashMap<Block, Integer> missing = new HashMap<>();
|
||||
for (Note note : missingNotes) {
|
||||
Block block = Note.INSTRUMENT_BLOCKS.get(instrumentMap.getOrDefault(note.instrument, note.instrument));
|
||||
Instrument mappedInstrument = instrumentMap.getOrDefault(note.instrument, note.instrument);
|
||||
if(mappedInstrument == null) continue; // Ignore if mapped to nothing
|
||||
Block block = Note.INSTRUMENT_BLOCKS.get(mappedInstrument);
|
||||
Integer got = missing.get(block);
|
||||
if (got == null) got = 0;
|
||||
missing.put(block, got + 1);
|
||||
@ -322,7 +352,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
}
|
||||
|
||||
if(lastInteractAt != -1L) {
|
||||
// Paper allows 8 interacts per 300 ms
|
||||
// Paper allows 8 interacts per 300 ms (actually 9 it turns out, but lets keep it a bit lower anyway)
|
||||
availableInteracts += ((System.currentTimeMillis() - lastInteractAt) / (310.0 / 8.0));
|
||||
availableInteracts = Math.min(8f, Math.max(0f, availableInteracts));
|
||||
}else {
|
||||
@ -360,7 +390,14 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
if(tuneInitialUntunedBlocks == -1 || tuneInitialUntunedBlocks < untunedNotes.size())
|
||||
tuneInitialUntunedBlocks = untunedNotes.size();
|
||||
|
||||
if(untunedNotes.isEmpty() && fullyTunedBlocks == song.uniqueNotes.size()) {
|
||||
int existingUniqueNotesCount = 0;
|
||||
for(Note n : song.uniqueNotes) {
|
||||
if(noteBlocks.get(n.instrument).get(n.note) != null)
|
||||
existingUniqueNotesCount++;
|
||||
}
|
||||
System.out.println("existingUniqueNotesCount = " + existingUniqueNotesCount);
|
||||
|
||||
if(untunedNotes.isEmpty() && fullyTunedBlocks == existingUniqueNotesCount) {
|
||||
// Wait roundrip + 100ms before considering tuned after changing notes (in case the server rejects an interact)
|
||||
if(lastInteractAt == -1 || System.currentTimeMillis() - lastInteractAt >= ping * 2 + 100) {
|
||||
tuned = true;
|
||||
@ -417,6 +454,14 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
|
||||
//client.getNetworkHandler().sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(((float) (System.currentTimeMillis() % 2000)) * (360f/2000f), (1 - roughTuneProgress) * 180 - 90, true));
|
||||
client.player.swingHand(Hand.MAIN_HAND);
|
||||
}
|
||||
}else if((playbackThread == null || !playbackThread.isAlive()) && running && Main.config.disableAsyncPlayback) {
|
||||
// Sync playback (off by default). Replacement for playback thread
|
||||
try {
|
||||
tickPlayback();
|
||||
}catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,11 +23,25 @@
|
||||
"disc_jockey.info_tuning": "Tuning: (Speed: %s)",
|
||||
"disc_jockey.info_playing": "Playing: [%s/%s] %s (Speed: %s)",
|
||||
"disc_jockey.info_finished": "Finished: %s (Speed: %s)",
|
||||
"disc_jockey.instrument_info": "This maps instruments to be played by noteblocks for a different instrument instead.",
|
||||
"disc_jockey.invalid_instrument": "Invalid instrument: %s",
|
||||
"disc_jockey.instrument_mapped": "Mapped %s to %s",
|
||||
"disc_jockey.instrument_mapped_all": "Mapped all instruments to %s",
|
||||
"disc_jockey.instrument_unmapped": "Unmapped %s",
|
||||
"disc_jockey.mapped_instruments": "Mapped instruments: %s",
|
||||
"disc_jockey.no_mapped_instruments": "No instruments mapped, yet.",
|
||||
"disc_jockey.instrument_maps_cleared": "Instrument mappings cleared.",
|
||||
"disc_jockey.loop_status": "Loop song: %s",
|
||||
"disc_jockey.loop_enabled": "Enabled looping of current song.",
|
||||
"disc_jockey.loop_disabled": "Disabled looping of current song.",
|
||||
"disc_jockey.warning": "WARNING!!! This mod is very likely to get false flagged as hacks, please contact a server administrator before using this mod! (You can disable this warning in the mod settings)",
|
||||
"key.category.disc_jockey": "Disc Jockey",
|
||||
"disc_jockey.key_bind.open_screen": "Open song selection screen",
|
||||
"text.autoconfig.disc_jockey.title": "Disc Jockey",
|
||||
"text.autoconfig.disc_jockey.option.hideWarning": "Hide Warning",
|
||||
"text.autoconfig.disc_jockey.option.disableAsyncPlayback": "Disable Async Playback",
|
||||
"text.autoconfig.disc_jockey.option.disableAsyncPlayback.@Tooltip[0]": "Will force notes to play synchronously with client ticks instead of in a separate thread.",
|
||||
"text.autoconfig.disc_jockey.option.disableAsyncPlayback.@Tooltip[1]": "This can lead to performance loss, especially when you client has low or inconsistent fps but can fix issues when playback does not happen at all.",
|
||||
"text.autoconfig.disc_jockey.option.monoNoteBlocks": "Non-Directional Note Block Sounds",
|
||||
"text.autoconfig.disc_jockey.option.monoNoteBlocks.@Tooltip[0]": "Makes all note block sounds when playing a song non-directional, creating a more pleasurable listening experience (clientside)",
|
||||
"text.autoconfig.disc_jockey.option.monoNoteBlocks.@Tooltip[1]": "If you don't know what that means, I recommend you just try it and hear the difference"
|
||||
|
Loading…
x
Reference in New Issue
Block a user