Compare commits

...

10 Commits

Author SHA1 Message Date
BRanulf
0ee059d8ba 1.21.4dj 2025-04-12 19:49:21 +08:00
Semmieboy YT
875d9ada1b Bump version 2024-07-12 22:15:34 +02:00
Semmieboy YT
9cb48dac82 Update embedded cloth config version 2024-07-12 22:10:05 +02:00
SemmieDev
7bf1089199
Merge pull request #28 from EnderKill98/feature/various-improvements-and-1.21-support
Fix ranges, fix #25 and support 1.21
2024-07-12 19:20:17 +02:00
EnderKill98
bb3eb426e5 Update to 1.21 and small cleanups 2024-07-07 20:15:54 +02:00
EnderKill98
1ee9f114c9 Implement playback delay feature request
Fixes #25
2024-07-07 19:57:03 +02:00
EnderKill98
fc57613eae Update block scanning distances for tuning accordingly 2024-07-07 18:59:48 +02:00
EnderKill98
18dea5499f Allow choosing expected server distance check and fix wrong checks on 1.20.5+ by default 2024-07-07 18:51:37 +02:00
Semmieboy YT
88423eeff9 Update to 1.20.6 2024-05-14 19:49:18 +02:00
Semmieboy YT
620f06930d Update to Minecraft 1.20.5, switch to java 21, and a couple general code improvements 2024-04-24 12:30:41 +02:00
18 changed files with 275 additions and 131 deletions

View File

@ -1,5 +1,5 @@
plugins {
id 'fabric-loom' version '0.11-SNAPSHOT'
id 'fabric-loom' version '1.9.2'
id 'maven-publish'
}
@ -8,11 +8,13 @@ group = project.maven_group
repositories {
maven { url "https://maven.shedaniel.me/" }
maven { url "https://maven.terraformersmc.com/" }
maven {url "https://maven.terraformersmc.com/"}
}
dependencies {
// To change the versions see the gradle.properties file
// implementation files("libs/modmenu-13.0.3.jar")
// implementation files("libs/modmenu-13.0.3.pom")
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
@ -20,11 +22,12 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
include modApi("me.shedaniel.cloth:cloth-config-fabric:13.0.121") {
include modApi("me.shedaniel.cloth:cloth-config-fabric:17.0.142") {
exclude(group: "net.fabricmc.fabric-api")
}
modCompileOnly("com.terraformersmc:modmenu:9.0.0")
// modCompileOnly("com.terraformersmc:modmenu:13.0.3")
modCompileOnly files("libs/modmenu-13.0.3.jar")
}
processResources {
@ -36,7 +39,7 @@ processResources {
}
}
def targetJavaVersion = 8
def targetJavaVersion = 21
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

@ -2,13 +2,16 @@
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.20.4
yarn_mappings=1.20.4+build.1
loader_version=0.15.0
minecraft_version=1.21.4
yarn_mappings=1.21.4+build.8
loader_version=0.16.10
# Mod Properties
mod_version=1.4.0
mod_version=1.14.514
maven_group=semmiedev
archives_base_name=disc_jockey
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.91.1+1.20.4
fabric_version=0.119.2+1.21.4
systemProp.http.sslVerify=false
systemProp.https.sslVerify=false
systemProp.javax.net.ssl.trustStoreType=WINDOWS-ROOT

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
distributionUrl=https://mirrors.aliyun.com/macports/distfiles/gradle/gradle-8.12.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

8
libs/modmenu-13.0.3.pom Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.terraformersmc</groupId>
<artifactId>modmenu</artifactId>
<version>13.0.3</version>
</project>

View File

@ -15,7 +15,7 @@ public class BinaryReader {
}
public int readInt() throws IOException {
return ((ByteBuffer)((ByteBuffer)buffer.clear()).put(readBytes(Integer.BYTES)).rewind()).getInt();
return 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 ((ByteBuffer)((ByteBuffer)buffer.clear()).put(readBytes(Short.BYTES)).rewind()).getShort();
return 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 ((ByteBuffer)((ByteBuffer)buffer.clear()).put(readBytes(Float.BYTES)).rewind()).getFloat();
return buffer.clear().put(readBytes(Float.BYTES)).rewind().getFloat();
}
/*private int getStringLength() throws IOException {

View File

@ -12,6 +12,32 @@ public class Config implements ConfigData {
@ConfigEntry.Gui.Tooltip(count = 2) public boolean disableAsyncPlayback;
@ConfigEntry.Gui.Tooltip(count = 2) public boolean omnidirectionalNoteBlockSounds = true;
public enum ExpectedServerVersion {
All,
v1_20_4_Or_Earlier,
v1_20_5_Or_Later;
@Override
public String toString() {
if(this == All) {
return "All (universal)";
}else if(this == v1_20_4_Or_Earlier) {
return "≤1.20.4";
}else if (this == v1_20_5_Or_Later) {
return "≥1.20.5";
}else {
return super.toString();
}
}
}
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
@ConfigEntry.Gui.Tooltip(count = 4)
public ExpectedServerVersion expectedServerVersion = ExpectedServerVersion.All;
@ConfigEntry.Gui.Tooltip(count = 1)
public float delayPlaybackStartBySecs = 0.0f;
@ConfigEntry.Gui.Excluded
public ArrayList<String> favorites = new ArrayList<>();
}

View File

@ -5,11 +5,10 @@ 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.block.enums.NoteBlockInstrument;
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;
@ -26,7 +25,7 @@ public class DiscjockeyCommand {
public static void register(CommandDispatcher<FabricClientCommandSource> commandDispatcher) {
final ArrayList<String> instrumentNames = new ArrayList<>();
for (Instrument instrument : Instrument.values()) {
for (NoteBlockInstrument instrument : NoteBlockInstrument.values()) {
instrumentNames.add(instrument.toString().toLowerCase());
}
final ArrayList<String> instrumentNamesAndAll = new ArrayList<>(instrumentNames);
@ -127,8 +126,8 @@ public class DiscjockeyCommand {
.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()) {
@Nullable NoteBlockInstrument originalInstrument = null, newInstrument = null;
for(NoteBlockInstrument maybeInstrument : NoteBlockInstrument.values()) {
if(maybeInstrument.toString().equalsIgnoreCase(originalInstrumentStr)) {
originalInstrument = maybeInstrument;
}
@ -152,7 +151,7 @@ public class DiscjockeyCommand {
if(originalInstrument == null) {
// All instruments
for(Instrument instrument : Instrument.values()) {
for(NoteBlockInstrument instrument : NoteBlockInstrument.values()) {
Main.SONG_PLAYER.instrumentMap.put(instrument, newInstrument);
}
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID + ".instrument_mapped_all", newInstrumentStr.toLowerCase()));
@ -171,8 +170,8 @@ public class DiscjockeyCommand {
.executes(context -> {
String instrumentStr = StringArgumentType.getString(context, "instrument");
Instrument instrument = null;
for(Instrument maybeInstrument : Instrument.values()) {
NoteBlockInstrument instrument = null;
for(NoteBlockInstrument maybeInstrument : NoteBlockInstrument.values()) {
if(maybeInstrument.toString().equalsIgnoreCase(instrumentStr)) {
instrument = maybeInstrument;
break;
@ -198,7 +197,7 @@ public class DiscjockeyCommand {
}
StringBuilder maps = new StringBuilder();
for(Map.Entry<Instrument, Instrument> entry : Main.SONG_PLAYER.instrumentMap.entrySet()) {
for(Map.Entry<NoteBlockInstrument, NoteBlockInstrument> entry : Main.SONG_PLAYER.instrumentMap.entrySet()) {
if(maps.length() > 0) {
maps.append(", ");
}

View File

@ -2,69 +2,53 @@ package semmiedev.disc_jockey;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.enums.Instrument;
import net.minecraft.block.enums.NoteBlockInstrument;
import java.util.HashMap;
public class Note {
public static final HashMap<Instrument, Block> INSTRUMENT_BLOCKS = new HashMap<>();
public record Note(NoteBlockInstrument instrument, byte note) {
public static final HashMap<NoteBlockInstrument, 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 = 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
public static final NoteBlockInstrument[] INSTRUMENTS = new NoteBlockInstrument[]{
NoteBlockInstrument.HARP,
NoteBlockInstrument.BASS,
NoteBlockInstrument.BASEDRUM,
NoteBlockInstrument.SNARE,
NoteBlockInstrument.HAT,
NoteBlockInstrument.GUITAR,
NoteBlockInstrument.FLUTE,
NoteBlockInstrument.BELL,
NoteBlockInstrument.CHIME,
NoteBlockInstrument.XYLOPHONE,
NoteBlockInstrument.IRON_XYLOPHONE,
NoteBlockInstrument.COW_BELL,
NoteBlockInstrument.DIDGERIDOO,
NoteBlockInstrument.BIT,
NoteBlockInstrument.BANJO,
NoteBlockInstrument.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;
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.HARP, Blocks.AIR);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BASEDRUM, Blocks.STONE);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.SNARE, Blocks.SAND);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.HAT, Blocks.GLASS);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BASS, Blocks.OAK_PLANKS);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.FLUTE, Blocks.CLAY);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BELL, Blocks.GOLD_BLOCK);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.GUITAR, Blocks.WHITE_WOOL);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.CHIME, Blocks.PACKED_ICE);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.XYLOPHONE, Blocks.BONE_BLOCK);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.IRON_XYLOPHONE, Blocks.IRON_BLOCK);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.COW_BELL, Blocks.SOUL_SAND);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.DIDGERIDOO, Blocks.PUMPKIN);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BIT, Blocks.EMERALD_BLOCK);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.BANJO, Blocks.HAY_BLOCK);
INSTRUMENT_BLOCKS.put(NoteBlockInstrument.PLING, Blocks.GLOWSTONE);
}
}

View File

@ -30,7 +30,7 @@ public class SongLoader {
try {
song = loadSong(file);
} catch (Exception exception) {
Main.LOGGER.error("Unable to read or parse song " + file.getName(), exception);
Main.LOGGER.error("Unable to read or parse song {}", file.getName(), exception);
}
if (song != null) SONGS.add(song);
}

View File

@ -4,7 +4,7 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.enums.Instrument;
import net.minecraft.block.enums.NoteBlockInstrument;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.network.ClientPlayerEntity;
@ -18,11 +18,9 @@ import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.Pair;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.*;
import net.minecraft.world.GameMode;
import org.apache.commons.lang3.NotImplementedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -37,7 +35,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
private int index;
private double tick; // Aka song position
private HashMap<Instrument, HashMap<Byte, BlockPos>> noteBlocks = null;
private HashMap<NoteBlockInstrument, HashMap<Byte, BlockPos>> noteBlocks = null;
public boolean tuned;
private long lastPlaybackTickAt = -1L;
@ -65,12 +63,13 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
private HashMap<BlockPos, Pair<Integer, Long>> notePredictions = new HashMap<>();
public boolean didSongReachEnd = false;
public boolean loopSong = false;
private long pausePlaybackUntil = -1L; // Set after tuning, if configured
public SongPlayer() {
Main.TICK_LISTENERS.add(this);
}
public @NotNull HashMap<Instrument, @Nullable Instrument> instrumentMap = new HashMap<>(); // Toy
public @NotNull HashMap<NoteBlockInstrument, @Nullable NoteBlockInstrument> instrumentMap = new HashMap<>(); // Toy
public synchronized void startPlaybackThread() {
if(Main.config.disableAsyncPlayback) {
playbackThread = null;
@ -155,6 +154,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
last100MsSpanEstimatedPackets = 0;
}
if(noteBlocks != null && tuned) {
if(pausePlaybackUntil != -1L && System.currentTimeMillis() <= pausePlaybackUntil) return;
while (running) {
MinecraftClient client = MinecraftClient.getInstance();
GameMode gameMode = client.interactionManager == null ? null : client.interactionManager.getCurrentGameMode();
@ -181,7 +181,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
}
Vec3d unit = Vec3d.ofCenter(blockPos, 0.5).subtract(client.player.getEyePos()).normalize();
if((lastLookSentAt == -1L || now - lastLookSentAt >= 50) && last100MsSpanEstimatedPackets < last100MsReducePacketsAfter && (reducePacketsUntil == -1L || reducePacketsUntil < now)) {
client.getNetworkHandler().sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(MathHelper.wrapDegrees((float) (MathHelper.atan2(unit.z, unit.x) * 57.2957763671875) - 90.0f), MathHelper.wrapDegrees((float) (-(MathHelper.atan2(unit.y, Math.sqrt(unit.x * unit.x + unit.z * unit.z)) * 57.2957763671875))), true));
client.getNetworkHandler().sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(MathHelper.wrapDegrees((float) (MathHelper.atan2(unit.z, unit.x) * 57.2957763671875) - 90.0f), MathHelper.wrapDegrees((float) (-(MathHelper.atan2(unit.y, Math.sqrt(unit.x * unit.x + unit.z * unit.z)) * 57.2957763671875))), true, false));
last100MsSpanEstimatedPackets++;
lastLookSentAt = now;
}else if(last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter){
@ -254,16 +254,32 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
ClientPlayerEntity player = client.player;
// Create list of available noteblock positions per used instrument
HashMap<Instrument, ArrayList<BlockPos>> noteblocksForInstrument = new HashMap<>();
for(Instrument instrument : Instrument.values())
HashMap<NoteBlockInstrument, ArrayList<BlockPos>> noteblocksForInstrument = new HashMap<>();
for(NoteBlockInstrument instrument : NoteBlockInstrument.values())
noteblocksForInstrument.put(instrument, new ArrayList<>());
final Vec3d playerPos = player.getEyePos();
final int[] orderedOffsets = new int[] { 0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5, -6, 6, -7, 7 };
for(Instrument instrument : noteblocksForInstrument.keySet().toArray(new Instrument[0])) {
final Vec3d playerEyePos = player.getEyePos();
final int maxOffset; // Rough estimates, of which blocks could be in reach
if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.v1_20_4_Or_Earlier) {
maxOffset = 7;
}else if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.v1_20_5_Or_Later) {
maxOffset = (int) Math.ceil(player.getBlockInteractionRange() + 1.0 + 1.0);
}else if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.All) {
maxOffset = Math.min(7, (int) Math.ceil(player.getBlockInteractionRange() + 1.0 + 1.0));
}else {
throw new NotImplementedException("ExpectedServerVersion Value not implemented: " + Main.config.expectedServerVersion.name());
}
final ArrayList<Integer> orderedOffsets = new ArrayList<>();
for(int offset = 0; offset <= maxOffset; offset++) {
orderedOffsets.add(offset);
if(offset != 0) orderedOffsets.add(offset * -1);
}
for(NoteBlockInstrument instrument : noteblocksForInstrument.keySet().toArray(new NoteBlockInstrument[0])) {
for (int y : orderedOffsets) {
for (int x : orderedOffsets) {
for (int z : orderedOffsets) {
Vec3d vec3d = playerPos.add(x, y, z);
Vec3d vec3d = playerEyePos.add(x, y, z);
BlockPos blockPos = new BlockPos(MathHelper.floor(vec3d.x), MathHelper.floor(vec3d.y), MathHelper.floor(vec3d.z));
if (!canInteractWith(player, blockPos))
continue;
@ -280,9 +296,9 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
// Remap instruments for funzies
if(!instrumentMap.isEmpty()) {
HashMap<Instrument, ArrayList<BlockPos>> newNoteblocksForInstrument = new HashMap<>();
for(Instrument orig : noteblocksForInstrument.keySet()) {
Instrument mappedInstrument = instrumentMap.getOrDefault(orig, orig);
HashMap<NoteBlockInstrument, ArrayList<BlockPos>> newNoteblocksForInstrument = new HashMap<>();
for(NoteBlockInstrument orig : noteblocksForInstrument.keySet()) {
NoteBlockInstrument mappedInstrument = instrumentMap.getOrDefault(orig, orig);
if(mappedInstrument == null) {
// Instrument got likely mapped to "nothing"
newNoteblocksForInstrument.put(orig, null);
@ -297,17 +313,17 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
// Find fitting noteblocks with the least amount of adjustments required (to reduce tuning time)
ArrayList<Note> capturedNotes = new ArrayList<>();
for(Note note : song.uniqueNotes) {
ArrayList<BlockPos> availableBlocks = noteblocksForInstrument.get(note.instrument);
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);
getNotes(note.instrument()).put(note.note(), null);
continue;
}
BlockPos bestBlockPos = null;
int bestBlockTuningSteps = Integer.MAX_VALUE;
for(BlockPos blockPos : availableBlocks) {
int wantedNote = note.note;
int wantedNote = note.note();
int currentNote = client.world.getBlockState(blockPos).get(Properties.NOTE);
int tuningSteps = wantedNote >= currentNote ? wantedNote - currentNote : (25 - currentNote) + wantedNote;
@ -320,7 +336,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
if(bestBlockPos != null) {
capturedNotes.add(note);
availableBlocks.remove(bestBlockPos);
getNotes(note.instrument).put(note.note, bestBlockPos);
getNotes(note.instrument()).put(note.note(), bestBlockPos);
} // else will be a missing note
}
@ -332,7 +348,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
HashMap<Block, Integer> missing = new HashMap<>();
for (Note note : missingNotes) {
Instrument mappedInstrument = instrumentMap.getOrDefault(note.instrument, note.instrument);
NoteBlockInstrument 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);
@ -356,7 +372,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
if(lastInteractAt != -1L) {
// 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 += ((System.currentTimeMillis() - lastInteractAt) / (310.0f / 8.0f));
availableInteracts = Math.min(8f, Math.max(0f, availableInteracts));
}else {
availableInteracts = 8f;
@ -366,17 +382,17 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
int fullyTunedBlocks = 0;
HashMap<BlockPos, Integer> untunedNotes = new HashMap<>();
for (Note note : song.uniqueNotes) {
if(noteBlocks == null || noteBlocks.get(note.instrument) == null)
if(noteBlocks == null || noteBlocks.get(note.instrument()) == null)
continue;
BlockPos blockPos = noteBlocks.get(note.instrument).get(note.note);
BlockPos blockPos = noteBlocks.get(note.instrument()).get(note.note());
if(blockPos == null) continue;
BlockState blockState = world.getBlockState(blockPos);
int assumedNote = notePredictions.containsKey(blockPos) ? notePredictions.get(blockPos).getLeft() : blockState.get(Properties.NOTE);
if (blockState.contains(Properties.NOTE)) {
if(assumedNote == note.note && blockState.get(Properties.NOTE) == note.note)
if(assumedNote == note.note() && blockState.get(Properties.NOTE) == note.note())
fullyTunedBlocks++;
if (assumedNote != note.note) {
if (assumedNote != note.note()) {
if (!canInteractWith(client.player, blockPos)) {
stop();
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.to_far").formatted(Formatting.RED));
@ -395,7 +411,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
int existingUniqueNotesCount = 0;
for(Note n : song.uniqueNotes) {
if(noteBlocks.get(n.instrument).get(n.note) != null)
if(noteBlocks.get(n.instrument()).get(n.note()) != null)
existingUniqueNotesCount++;
}
@ -403,7 +419,9 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
// 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;
pausePlaybackUntil = System.currentTimeMillis() + (long) (Math.abs(Main.config.delayPlaybackStartBySecs) * 1000);
tuneInitialUntunedBlocks = -1;
// Tuning finished
}
}
@ -445,7 +463,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
lastTunedNote = untunedNotes.get(blockPos);
untunedNotes.remove(blockPos);
int assumedNote = notePredictions.containsKey(blockPos) ? notePredictions.get(blockPos).getLeft() : client.world.getBlockState(blockPos).get(Properties.NOTE);
notePredictions.put(blockPos, new Pair((assumedNote + 1) % 25, System.currentTimeMillis() + ping * 2 + 100));
notePredictions.put(blockPos, new Pair<>((assumedNote + 1) % 25, System.currentTimeMillis() + ping * 2 + 100));
client.interactionManager.interactBlock(client.player, Hand.MAIN_HAND, new BlockHitResult(Vec3d.of(blockPos), Direction.UP, blockPos, false));
lastInteractAt = System.currentTimeMillis();
availableInteracts -= 1f;
@ -467,13 +485,28 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
}
}
private HashMap<Byte, BlockPos> getNotes(Instrument instrument) {
private HashMap<Byte, BlockPos> getNotes(NoteBlockInstrument instrument) {
return noteBlocks.computeIfAbsent(instrument, k -> new HashMap<>());
}
// The server limits interacts to 6 Blocks from Player Eye to Block Center
// Before 1.20.5, the server limits interacts to 6 Blocks from Player Eye to Block Center
// With 1.20.5 and later, the server does a more complex check, to the closest point of a full block hitbox
// (max distance is BlockInteractRange + 1.0).
private boolean canInteractWith(ClientPlayerEntity player, BlockPos blockPos) {
return player.getEyePos().squaredDistanceTo(new Vec3d(blockPos.getX() + 0.5, blockPos.getY() + 0.5, blockPos.getZ() + 0.5)) <= 6.0*6.0;
final Vec3d eyePos = player.getEyePos();
if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.v1_20_4_Or_Earlier) {
return eyePos.squaredDistanceTo(blockPos.toCenterPos()) <= 6.0 * 6.0;
}else if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.v1_20_5_Or_Later) {
double blockInteractRange = player.getBlockInteractionRange() + 1.0;
return new Box(blockPos).squaredMagnitude(eyePos) < blockInteractRange * blockInteractRange;
}else if(Main.config.expectedServerVersion == Config.ExpectedServerVersion.All) {
// Require both checks to succeed (aka use worst distance)
double blockInteractRange = player.getBlockInteractionRange() + 1.0;
return eyePos.squaredDistanceTo(blockPos.toCenterPos()) <= 6.0 * 6.0
&& new Box(blockPos).squaredMagnitude(eyePos) < blockInteractRange * blockInteractRange;
}else {
throw new NotImplementedException("ExpectedServerVersion Value not implemented: " + Main.config.expectedServerVersion.name());
}
}
public double getSongElapsedSeconds() {

View File

@ -22,7 +22,7 @@ public class SongListWidget extends EntryListWidget<SongListWidget.SongEntry> {
}
@Override
protected int getScrollbarPositionX() {
protected int getScrollbarX() {
return width - 12;
}
@ -41,7 +41,7 @@ public class SongListWidget extends EntryListWidget<SongListWidget.SongEntry> {
// 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");
private static final Identifier ICONS = Identifier.of(Main.MOD_ID, "textures/gui/icons.png");
public final int index;
public final Song song;
@ -70,7 +70,7 @@ public class SongListWidget extends EntryListWidget<SongListWidget.SongEntry> {
context.drawCenteredTextWithShadow(client.textRenderer, song.displayName, x + entryWidth / 2, y + 5, selected ? 0xFFFFFF : 0x808080);
RenderSystem.setShaderTexture(0, ICONS);
context.drawTexture(ICONS, x + 2, y + 2, (favorite ? 26 : 0) + (isOverFavoriteButton(mouseX, mouseY) ? 13 : 0), 0, 13, 12, 52, 12);
// context.drawTexture(ICONS, x + 2, y + 2, (favorite ? 26 : 0) + (isOverFavoriteButton(mouseX, mouseY) ? 13 : 0), 0, 13, 12, 52, 12);
}
@Override

View File

@ -4,6 +4,7 @@ import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.RenderTickCounter;
import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.ColorHelper;
@ -15,10 +16,10 @@ public class BlocksOverlay {
private static final ItemStack NOTE_BLOCK = Blocks.NOTE_BLOCK.asItem().getDefaultStack();
public static void render(DrawContext context, float tickDelta) {
public static void render(DrawContext context, RenderTickCounter tickCounter) {
if (itemStacks != null) {
context.fill(2, 2, 62, (itemStacks.length + 1) * 20 + 7, ColorHelper.Argb.getArgb(255, 22, 22, 27));
context.fill(4, 4, 60, (itemStacks.length + 1) * 20 + 5, ColorHelper.Argb.getArgb(255, 42, 42, 47));
context.fill(2, 2, 62, (itemStacks.length + 1) * 20 + 7, ColorHelper.getArgb(255, 22, 22, 27));
context.fill(4, 4, 60, (itemStacks.length + 1) * 20 + 5, ColorHelper.getArgb(255, 42, 42, 47));
MinecraftClient client = MinecraftClient.getInstance();
TextRenderer textRenderer = client.textRenderer;

View File

@ -89,7 +89,7 @@ public class DiscJockeyScreen extends Screen {
BlocksOverlay.amountOfNoteBlocks = entry.song.uniqueNotes.size();
for (Note note : entry.song.uniqueNotes) {
ItemStack itemStack = Note.INSTRUMENT_BLOCKS.get(note.instrument).asItem().getDefaultStack();
ItemStack itemStack = Note.INSTRUMENT_BLOCKS.get(note.instrument()).asItem().getDefaultStack();
int index = -1;
for (int i = 0; i < BlocksOverlay.itemStacks.length; i++) {
@ -136,11 +136,6 @@ public class DiscJockeyScreen extends Screen {
context.drawCenteredTextWithShadow(textRenderer, SELECT_SONG, width / 2, 20, 0xFFFFFF);
}
@Override
public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) {
renderBackgroundTexture(context);
}
@Override
public void tick() {
previewButton.setMessage(Main.PREVIEWER.running ? PREVIEW_STOP : PREVIEW);
@ -148,7 +143,7 @@ public class DiscJockeyScreen extends Screen {
if (shouldFilter) {
shouldFilter = false;
songListWidget.setScrollAmount(0);
// songListWidget.setScrollAmount(0);
songListWidget.children().clear();
boolean empty = query.isEmpty();
int favoriteIndex = 0;
@ -165,7 +160,7 @@ public class DiscJockeyScreen extends Screen {
}
@Override
public void filesDragged(List<Path> paths) {
public void onFilesDropped(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)+"...";

View File

@ -19,11 +19,39 @@ import semmiedev.disc_jockey.Main;
public class ClientWorldMixin {
@Shadow @Final private MinecraftClient client;
@Inject(method = "playSound(DDDLnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FFZJ)V", at = @At("HEAD"), cancellable = true)
private void makeNoteBlockSoundsOmnidirectional(double x, double y, double z, SoundEvent event, SoundCategory category, float volume, float pitch, boolean useDistance, long seed, CallbackInfo ci) {
if (((Main.config.omnidirectionalNoteBlockSounds && Main.SONG_PLAYER.running) || Main.PREVIEWER.running) && event.getId().getPath().startsWith("block.note_block")) {
@Inject(
method = "playSound(DDDLnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FFZJ)V",
at = @At("HEAD"),
cancellable = true
)
private void makeNoteBlockSoundsOmnidirectional(
double x, double y, double z,
SoundEvent event,
SoundCategory category,
float volume, float pitch,
boolean useDistance, long seed,
CallbackInfo ci
) {
if (
((Main.config.omnidirectionalNoteBlockSounds && Main.SONG_PLAYER.running) || Main.PREVIEWER.running) &&
// 关键修改点event.id() 替代 event.getId()
event.id().getPath().startsWith("block.note_block")
) {
ci.cancel();
client.getSoundManager().play(new PositionedSoundInstance(event.getId(), category, volume, pitch, Random.create(seed), false, 0, SoundInstance.AttenuationType.NONE, 0, 0, 0, true));
client.getSoundManager().play(
new PositionedSoundInstance(
event.id(), // 同样需要修改此处
category,
volume,
pitch,
Random.create(seed),
false,
0,
SoundInstance.AttenuationType.NONE,
0, 0, 0,
true
)
);
}
}
}

View File

@ -44,5 +44,12 @@
"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.omnidirectionalNoteBlockSounds": "Omnidirectional Note Block Sounds (clientside)",
"text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds.@Tooltip[0]": "Makes all note block sounds when playing a song omnidirectional, creating a more pleasing listening experience",
"text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds.@Tooltip[1]": "If you don't know what that means, I recommend you just try it and hear the difference"
"text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds.@Tooltip[1]": "If you don't know what that means, I recommend you just try it and hear the difference",
"text.autoconfig.disc_jockey.option.expectedServerVersion": "Expected Server Version",
"text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[0]": "Select the server version, you expect this mod to be used on.",
"text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[1]": "This affects how reachable NoteBlocks are determined.",
"text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[2]": "Selecting the wrong version could cause you not to be able to play some distant note blocks which could break/worsen playback",
"text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[3]": "If you're unsure, or play on many different server versions and don't mind not reaching every possible note block, select \"All\"",
"text.autoconfig.disc_jockey.option.delayPlaybackStartBySecs": "Delay playback by (seconds)",
"text.autoconfig.disc_jockey.option.delayPlaybackStartBySecs.@Tooltip": "Delays playback for specified seconds, after tuning finished, if any (e.g. 0.5 for half a second delay)."
}

View File

@ -0,0 +1,55 @@
{
"disc_jockey.screen.select_song": "选择歌曲",
"disc_jockey.screen.play": "播放",
"disc_jockey.screen.play.stop": "停止播放",
"disc_jockey.screen.preview": "试听",
"disc_jockey.screen.preview.stop": "停止试听",
"disc_jockey.screen.blocks.title": "音符盒",
"disc_jockey.screen.blocks": "音符盒",
"disc_jockey.screen.search": "搜索歌曲",
"disc_jockey.screen.drop_hint": "将歌曲文件拖入此窗口以添加",
"disc_jockey.screen.drop_confirm": "是否将以下歌曲添加到 Disc Jockey",
"disc_jockey.player.invalid_note_blocks": "附近的音符盒配置不正确。缺失:",
"disc_jockey.player.invalid_game_mode": "无法在 %s 模式下播放",
"disc_jockey.player.to_far": "你距离太远了",
"disc_jockey.still_loading": "歌曲仍在加载中",
"disc_jockey.reloading": "正在重新加载所有歌曲",
"disc_jockey.loading_done": "所有歌曲已加载完成",
"disc_jockey.song_not_found": "歌曲“%s”不存在",
"disc_jockey.not_playing": "未播放任何歌曲",
"disc_jockey.speed_changed": "播放速度已调整为 %s",
"disc_jockey.stopped_playing": "已停止播放“%s”",
"disc_jockey.info_not_running": "未播放歌曲(速度:%s",
"disc_jockey.info_tuning": "调音中(速度:%s",
"disc_jockey.info_playing": "播放中:[%s/%s] %s速度%s",
"disc_jockey.info_finished": "已播放:%s速度%s",
"disc_jockey.instrument_info": "此功能可将音符盒的乐器映射为其他乐器音色。",
"disc_jockey.invalid_instrument": "无效乐器:%s",
"disc_jockey.instrument_mapped": "已将 %s 映射为 %s",
"disc_jockey.instrument_mapped_all": "已将全部乐器映射为 %s",
"disc_jockey.instrument_unmapped": "已取消 %s 的映射",
"disc_jockey.mapped_instruments": "已映射乐器:%s",
"disc_jockey.no_mapped_instruments": "当前未映射任何乐器",
"disc_jockey.instrument_maps_cleared": "已清除所有乐器映射",
"disc_jockey.loop_status": "循环播放:%s",
"disc_jockey.loop_enabled": "已启用当前歌曲循环播放",
"disc_jockey.loop_disabled": "已禁用当前歌曲循环播放",
"disc_jockey.warning": "警告!此模组极易被误判为作弊工具,使用前请联系服务器管理员!(可在模组设置中关闭此警告)\n当前版本1.14.5141.21.4为非官方版本由BRanulf改版翻译也是这家伙提供的。\n仅供学习参考请支持官方别找我XD",
"key.category.disc_jockey": "Disc Jockey",
"disc_jockey.key_bind.open_screen": "打开歌曲选择界面",
"text.autoconfig.disc_jockey.title": "Disc Jockey",
"text.autoconfig.disc_jockey.option.hideWarning": "隐藏警告",
"text.autoconfig.disc_jockey.option.disableAsyncPlayback": "禁用异步播放",
"text.autoconfig.disc_jockey.option.disableAsyncPlayback.@Tooltip[0]": "强制音符与客户端刻同步播放,而非使用独立线程。",
"text.autoconfig.disc_jockey.option.disableAsyncPlayback.@Tooltip[1]": "可能导致性能下降(尤其在低帧率时),但可解决某些情况下无法播放的问题。",
"text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds": "全向音符盒音效(客户端)",
"text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds.@Tooltip[0]": "使音符盒音效全向传播,提升听觉体验",
"text.autoconfig.disc_jockey.option.omnidirectionalNoteBlockSounds.@Tooltip[1]": "若不确定效果,建议直接试听对比",
"text.autoconfig.disc_jockey.option.expectedServerVersion": "预期服务器版本",
"text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[0]": "选择你预计使用此模组的服务器版本。",
"text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[1]": "此设置影响音符盒可触达范围的判定逻辑。",
"text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[2]": "版本选择错误可能导致无法触发部分音符盒,影响播放效果",
"text.autoconfig.disc_jockey.option.expectedServerVersion.@Tooltip[3]": "若不确认版本,或需兼容多版本服务器,请选择“全部”",
"text.autoconfig.disc_jockey.option.delayPlaybackStartBySecs": "播放延迟(秒)",
"text.autoconfig.disc_jockey.option.delayPlaybackStartBySecs.@Tooltip": "调音完成后延迟指定秒数再开始播放(如 0.5 表示延迟半秒)。"
}

View File

@ -2,7 +2,7 @@
"required": true,
"minVersion": "0.8",
"package": "semmiedev.disc_jockey.mixin",
"compatibilityLevel": "JAVA_17",
"compatibilityLevel": "JAVA_21",
"mixins": [
"ClientWorldMixin"
],

View File

@ -6,7 +6,8 @@
"description": "Play note block songs in Minecraft",
"authors": [
"SemmieDev",
"EnderKill98"
"EnderKill98",
"BRanulf(非正式)"
],
"contact": {
"repo": "https://github.com/SemmieDev/Disc-Jockey"
@ -27,7 +28,8 @@
],
"depends": {
"fabric": "*",
"minecraft": "1.20.*",
"minecraft": "~1.21.4",
"java": ">=21",
"cloth-config": "*"
}
}